1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
20 #include "kitemlistheader_p.h"
24 #include "kitemmodelbase.h"
26 #include <QApplication>
27 #include <QGraphicsSceneHoverEvent>
29 #include <QStyleOptionHeader>
33 KItemListHeader::KItemListHeader(QGraphicsWidget
* parent
) :
34 QGraphicsWidget(parent
),
37 m_visibleRolesWidths(),
38 m_hoveredRoleIndex(-1),
39 m_pressedRoleIndex(-1),
40 m_roleOperation(NoRoleOperation
),
45 m_movingRole
.xDec
= 0;
46 m_movingRole
.index
= -1;
48 setAcceptHoverEvents(true);
50 QStyleOptionHeader option
;
51 const QSize headerSize
= style()->sizeFromContents(QStyle::CT_HeaderSection
, &option
, QSize());
52 resize(0, headerSize
.height());
55 KItemListHeader::~KItemListHeader()
59 void KItemListHeader::setModel(KItemModelBase
* model
)
61 if (m_model
== model
) {
66 disconnect(m_model
, SIGNAL(sortRoleChanged(QByteArray
,QByteArray
)),
67 this, SLOT(slotSortRoleChanged(QByteArray
,QByteArray
)));
68 disconnect(m_model
, SIGNAL(sortOrderChanged(Qt::SortOrder
,Qt::SortOrder
)),
69 this, SLOT(slotSortOrderChanged(Qt::SortOrder
,Qt::SortOrder
)));
75 connect(m_model
, SIGNAL(sortRoleChanged(QByteArray
,QByteArray
)),
76 this, SLOT(slotSortRoleChanged(QByteArray
,QByteArray
)));
77 connect(m_model
, SIGNAL(sortOrderChanged(Qt::SortOrder
,Qt::SortOrder
)),
78 this, SLOT(slotSortOrderChanged(Qt::SortOrder
,Qt::SortOrder
)));
82 KItemModelBase
* KItemListHeader::model() const
87 void KItemListHeader::setVisibleRoles(const QList
<QByteArray
>& roles
)
89 m_visibleRoles
= roles
;
93 QList
<QByteArray
> KItemListHeader::visibleRoles() const
95 return m_visibleRoles
;
98 void KItemListHeader::setVisibleRolesWidths(const QHash
<QByteArray
, qreal
>& rolesWidths
)
100 m_visibleRolesWidths
= rolesWidths
;
102 // Assure that no width is smaller than the minimum allowed width
103 const qreal minWidth
= minimumRoleWidth();
104 QMutableHashIterator
<QByteArray
, qreal
> it(m_visibleRolesWidths
);
105 while (it
.hasNext()) {
107 if (it
.value() < minWidth
) {
108 m_visibleRolesWidths
.insert(it
.key(), minWidth
);
115 QHash
<QByteArray
, qreal
> KItemListHeader::visibleRolesWidths() const
117 return m_visibleRolesWidths
;
120 qreal
KItemListHeader::minimumRoleWidth() const
122 QFontMetricsF
fontMetrics(font());
123 return fontMetrics
.height() * 4;
126 void KItemListHeader::paint(QPainter
* painter
, const QStyleOptionGraphicsItem
* option
, QWidget
* widget
)
136 painter
->setFont(font());
137 painter
->setPen(palette().text().color());
141 foreach (const QByteArray
& role
, m_visibleRoles
) {
142 const qreal roleWidth
= m_visibleRolesWidths
.value(role
);
143 const QRectF
rect(x
, 0, roleWidth
, size().height());
144 paintRole(painter
, role
, rect
, orderIndex
, widget
);
149 // Draw background without roles
152 opt
.rect
= QRect(x
, 0, size().width() - x
, size().height());
153 opt
.state
|= QStyle::State_Horizontal
;
154 style()->drawControl(QStyle::CE_HeaderEmptyArea
, &opt
, painter
);
156 if (!m_movingRole
.pixmap
.isNull()) {
157 Q_ASSERT(m_roleOperation
== MoveRoleOperation
);
158 painter
->drawPixmap(m_movingRole
.x
, 0, m_movingRole
.pixmap
);
162 void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent
* event
)
164 if (event
->button() & Qt::LeftButton
) {
165 updatePressedRoleIndex(event
->pos());
166 m_pressedMousePos
= event
->pos();
167 m_roleOperation
= isAboveRoleGrip(m_pressedMousePos
, m_pressedRoleIndex
) ?
168 ResizeRoleOperation
: NoRoleOperation
;
175 void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent
* event
)
177 QGraphicsWidget::mouseReleaseEvent(event
);
179 if (m_pressedRoleIndex
== -1) {
183 switch (m_roleOperation
) {
184 case NoRoleOperation
: {
185 // Only a click has been done and no moving or resizing has been started
186 const QByteArray sortRole
= m_model
->sortRole();
187 const int sortRoleIndex
= m_visibleRoles
.indexOf(sortRole
);
188 if (m_pressedRoleIndex
== sortRoleIndex
) {
189 // Toggle the sort order
190 const Qt::SortOrder previous
= m_model
->sortOrder();
191 const Qt::SortOrder current
= (m_model
->sortOrder() == Qt::AscendingOrder
) ?
192 Qt::DescendingOrder
: Qt::AscendingOrder
;
193 m_model
->setSortOrder(current
);
194 emit
sortOrderChanged(current
, previous
);
196 // Change the sort role
197 const QByteArray previous
= m_model
->sortRole();
198 const QByteArray current
= m_visibleRoles
[m_pressedRoleIndex
];
199 m_model
->setSortRole(current
);
200 emit
sortRoleChanged(current
, previous
);
205 case MoveRoleOperation
:
206 m_movingRole
.pixmap
= QPixmap();
208 m_movingRole
.xDec
= 0;
209 m_movingRole
.index
= -1;
216 m_pressedRoleIndex
= -1;
217 m_roleOperation
= NoRoleOperation
;
220 QApplication::restoreOverrideCursor();
223 void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent
* event
)
225 QGraphicsWidget::mouseMoveEvent(event
);
227 switch (m_roleOperation
) {
228 case NoRoleOperation
:
229 if ((event
->pos() - m_pressedMousePos
).manhattanLength() >= QApplication::startDragDistance()) {
230 // A role gets dragged by the user. Create a pixmap of the role that will get
231 // synchronized on each furter mouse-move-event with the mouse-position.
232 m_roleOperation
= MoveRoleOperation
;
233 const int roleIndex
= roleIndexAt(m_pressedMousePos
);
234 m_movingRole
.index
= roleIndex
;
235 if (roleIndex
== 0) {
236 // TODO: It should be configurable whether moving the first role is allowed.
237 // In the context of Dolphin this is not required, however this should be
238 // changed if KItemViews are used in a more generic way.
239 QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor
));
241 m_movingRole
.pixmap
= createRolePixmap(roleIndex
);
244 for (int i
= 0; i
< roleIndex
; ++i
) {
245 const QByteArray role
= m_visibleRoles
[i
];
246 roleX
+= m_visibleRolesWidths
.value(role
);
249 m_movingRole
.xDec
= event
->pos().x() - roleX
;
250 m_movingRole
.x
= roleX
;
256 case ResizeRoleOperation
: {
257 const QByteArray pressedRole
= m_visibleRoles
[m_pressedRoleIndex
];
259 qreal previousWidth
= m_visibleRolesWidths
.value(pressedRole
);
260 qreal currentWidth
= previousWidth
;
261 currentWidth
+= event
->pos().x() - event
->lastPos().x();
262 currentWidth
= qMax(minimumRoleWidth(), currentWidth
);
264 m_visibleRolesWidths
.insert(pressedRole
, currentWidth
);
267 emit
visibleRoleWidthChanged(pressedRole
, currentWidth
, previousWidth
);
271 case MoveRoleOperation
: {
272 // TODO: It should be configurable whether moving the first role is allowed.
273 // In the context of Dolphin this is not required, however this should be
274 // changed if KItemViews are used in a more generic way.
275 if (m_movingRole
.index
> 0) {
276 m_movingRole
.x
= event
->pos().x() - m_movingRole
.xDec
;
279 const int targetIndex
= targetOfMovingRole();
280 if (targetIndex
> 0 && targetIndex
!= m_movingRole
.index
) {
281 const QByteArray role
= m_visibleRoles
[m_movingRole
.index
];
282 const int previousIndex
= m_movingRole
.index
;
283 m_movingRole
.index
= targetIndex
;
284 emit
visibleRoleMoved(role
, targetIndex
, previousIndex
);
286 m_movingRole
.xDec
= event
->pos().x() - roleXPosition(role
);
297 void KItemListHeader::hoverEnterEvent(QGraphicsSceneHoverEvent
* event
)
299 QGraphicsWidget::hoverEnterEvent(event
);
300 updateHoveredRoleIndex(event
->pos());
303 void KItemListHeader::hoverLeaveEvent(QGraphicsSceneHoverEvent
* event
)
305 QGraphicsWidget::hoverLeaveEvent(event
);
306 if (m_hoveredRoleIndex
!= -1) {
307 m_hoveredRoleIndex
= -1;
312 void KItemListHeader::hoverMoveEvent(QGraphicsSceneHoverEvent
* event
)
314 QGraphicsWidget::hoverMoveEvent(event
);
316 const QPointF
& pos
= event
->pos();
317 updateHoveredRoleIndex(pos
);
318 if (m_hoveredRoleIndex
>= 0 && isAboveRoleGrip(pos
, m_hoveredRoleIndex
)) {
319 setCursor(Qt::SplitHCursor
);
325 void KItemListHeader::slotSortRoleChanged(const QByteArray
& current
, const QByteArray
& previous
)
332 void KItemListHeader::slotSortOrderChanged(Qt::SortOrder current
, Qt::SortOrder previous
)
339 void KItemListHeader::paintRole(QPainter
* painter
,
340 const QByteArray
& role
,
343 QWidget
* widget
) const
345 // The following code is based on the code from QHeaderView::paintSection().
346 // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
347 QStyleOptionHeader option
;
348 option
.section
= orderIndex
;
349 option
.state
= QStyle::State_None
| QStyle::State_Raised
| QStyle::State_Horizontal
;
351 option
.state
|= QStyle::State_Enabled
;
353 if (window() && window()->isActiveWindow()) {
354 option
.state
|= QStyle::State_Active
;
356 if (m_hoveredRoleIndex
== orderIndex
) {
357 option
.state
|= QStyle::State_MouseOver
;
359 if (m_pressedRoleIndex
== orderIndex
) {
360 option
.state
|= QStyle::State_Sunken
;
362 if (m_model
->sortRole() == role
) {
363 option
.sortIndicator
= (m_model
->sortOrder() == Qt::AscendingOrder
) ?
364 QStyleOptionHeader::SortDown
: QStyleOptionHeader::SortUp
;
366 option
.rect
= rect
.toRect();
368 if (m_visibleRoles
.count() == 1) {
369 option
.position
= QStyleOptionHeader::OnlyOneSection
;
370 } else if (orderIndex
== 0) {
371 option
.position
= QStyleOptionHeader::Beginning
;
372 } else if (orderIndex
== m_visibleRoles
.count() - 1) {
373 option
.position
= QStyleOptionHeader::End
;
375 option
.position
= QStyleOptionHeader::Middle
;
378 option
.orientation
= Qt::Horizontal
;
379 option
.selectedPosition
= QStyleOptionHeader::NotAdjacent
;
380 option
.text
= m_model
->roleDescription(role
);
382 style()->drawControl(QStyle::CE_Header
, &option
, painter
, widget
);
385 void KItemListHeader::updatePressedRoleIndex(const QPointF
& pos
)
387 const int pressedIndex
= roleIndexAt(pos
);
388 if (m_pressedRoleIndex
!= pressedIndex
) {
389 m_pressedRoleIndex
= pressedIndex
;
394 void KItemListHeader::updateHoveredRoleIndex(const QPointF
& pos
)
396 const int hoverIndex
= roleIndexAt(pos
);
397 if (m_hoveredRoleIndex
!= hoverIndex
) {
398 m_hoveredRoleIndex
= hoverIndex
;
403 int KItemListHeader::roleIndexAt(const QPointF
& pos
) const
408 foreach (const QByteArray
& role
, m_visibleRoles
) {
410 x
+= m_visibleRolesWidths
.value(role
);
419 bool KItemListHeader::isAboveRoleGrip(const QPointF
& pos
, int roleIndex
) const
422 for (int i
= 0; i
<= roleIndex
; ++i
) {
423 const QByteArray role
= m_visibleRoles
[i
];
424 x
+= m_visibleRolesWidths
.value(role
);
427 const int grip
= style()->pixelMetric(QStyle::PM_HeaderGripMargin
);
428 return pos
.x() >= (x
- grip
) && pos
.x() <= x
;
431 QPixmap
KItemListHeader::createRolePixmap(int roleIndex
) const
433 const QByteArray role
= m_visibleRoles
[roleIndex
];
434 const qreal roleWidth
= m_visibleRolesWidths
.value(role
);
435 const QRect
rect(0, 0, roleWidth
, size().height());
437 QImage
image(rect
.size(), QImage::Format_ARGB32_Premultiplied
);
439 QPainter
painter(&image
);
440 paintRole(&painter
, role
, rect
, roleIndex
);
442 // Apply a highlighting-color
443 const QPalette::ColorGroup group
= isActiveWindow() ? QPalette::Active
: QPalette::Inactive
;
444 QColor highlightColor
= palette().color(group
, QPalette::Highlight
);
445 highlightColor
.setAlpha(64);
446 painter
.fillRect(rect
, highlightColor
);
448 // Make the image transparent
449 painter
.setCompositionMode(QPainter::CompositionMode_DestinationIn
);
450 painter
.fillRect(0, 0, image
.width(), image
.height(), QColor(0, 0, 0, 192));
452 return QPixmap::fromImage(image
);
455 int KItemListHeader::targetOfMovingRole() const
457 const int movingWidth
= m_movingRole
.pixmap
.width();
458 const int movingLeft
= m_movingRole
.x
;
459 const int movingRight
= movingLeft
+ movingWidth
- 1;
462 qreal targetLeft
= 0;
463 while (targetIndex
< m_visibleRoles
.count()) {
464 const QByteArray role
= m_visibleRoles
[targetIndex
];
465 const qreal targetWidth
= m_visibleRolesWidths
.value(role
);
466 const qreal targetRight
= targetLeft
+ targetWidth
- 1;
468 const bool isInTarget
= (targetWidth
>= movingWidth
&&
469 movingLeft
>= targetLeft
&&
470 movingRight
<= targetRight
) ||
471 (targetWidth
< movingWidth
&&
472 movingLeft
<= targetLeft
&&
473 movingRight
>= targetRight
);
479 targetLeft
+= targetWidth
;
483 return m_movingRole
.index
;
486 qreal
KItemListHeader::roleXPosition(const QByteArray
& role
) const
489 foreach (const QByteArray
& visibleRole
, m_visibleRoles
) {
490 if (visibleRole
== role
) {
494 x
+= m_visibleRolesWidths
.value(visibleRole
);
500 #include "kitemlistheader_p.moc"