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"