]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistheader.cpp
Allow custom sorting of details-view columns
[dolphin.git] / src / kitemviews / kitemlistheader.cpp
index 7b5549c739005cf08ff6c5ab91b9f95d8432984c..bd7dfb831383f24bc667e4ed1a1bd0cb7abf6db6 100644 (file)
@@ -38,8 +38,13 @@ KItemListHeader::KItemListHeader(QGraphicsWidget* parent) :
     m_hoveredRoleIndex(-1),
     m_pressedRoleIndex(-1),
     m_roleOperation(NoRoleOperation),
-    m_pressedMousePos()
+    m_pressedMousePos(),
+    m_movingRole()
 {
+    m_movingRole.x = 0;
+    m_movingRole.xDec = 0;
+    m_movingRole.index = -1;
+
     setAcceptHoverEvents(true);
 
     QStyleOptionHeader option;
@@ -147,6 +152,11 @@ void KItemListHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* o
     opt.rect = QRect(x, 0, size().width() - x, size().height());
     opt.state |= QStyle::State_Horizontal;
     style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, painter);
+
+    if (!m_movingRole.pixmap.isNull()) {
+        Q_ASSERT(m_roleOperation == MoveRoleOperation);
+        painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
+    }
 }
 
 void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent* event)
@@ -170,7 +180,8 @@ void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
         return;
     }
 
-    if (m_roleOperation == NoRoleOperation) {
+    switch (m_roleOperation) {
+    case NoRoleOperation: {
         // Only a click has been done and no moving or resizing has been started
         const QByteArray sortRole = m_model->sortRole();
         const int sortRoleIndex = m_visibleRoles.indexOf(sortRole);
@@ -184,23 +195,66 @@ void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
         } else {
             // Change the sort role
             const QByteArray previous = m_model->sortRole();
-            const QByteArray current = m_visibleRoles.at(m_pressedRoleIndex);
+            const QByteArray current = m_visibleRoles[m_pressedRoleIndex];
             m_model->setSortRole(current);
             emit sortRoleChanged(current, previous);
         }
+        break;
+    }
+
+    case MoveRoleOperation:
+        m_movingRole.pixmap = QPixmap();
+        m_movingRole.x = 0;
+        m_movingRole.xDec = 0;
+        m_movingRole.index = -1;
+        break;
+
+    default:
+        break;
     }
 
     m_pressedRoleIndex = -1;
     m_roleOperation = NoRoleOperation;
     update();
+
+    QApplication::restoreOverrideCursor();
 }
 
 void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
 {
     QGraphicsWidget::mouseMoveEvent(event);
 
-    if (m_roleOperation == ResizeRoleOperation) {
-        const QByteArray pressedRole = m_visibleRoles.at(m_pressedRoleIndex);
+    switch (m_roleOperation) {
+    case NoRoleOperation:
+        if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+            // A role gets dragged by the user. Create a pixmap of the role that will get
+            // synchronized on each furter mouse-move-event with the mouse-position.
+            m_roleOperation = MoveRoleOperation;
+            const int roleIndex = roleIndexAt(m_pressedMousePos);
+            m_movingRole.index = roleIndex;
+            if (roleIndex == 0) {
+                // TODO: It should be configurable whether moving the first role is allowed.
+                // In the context of Dolphin this is not required, however this should be
+                // changed if KItemViews are used in a more generic way.
+                QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
+            } else {
+                m_movingRole.pixmap = createRolePixmap(roleIndex);
+
+                qreal roleX = 0;
+                for (int i = 0; i < roleIndex; ++i) {
+                    const QByteArray role = m_visibleRoles[i];
+                    roleX += m_visibleRolesWidths.value(role);
+                }
+
+                m_movingRole.xDec = event->pos().x() - roleX;
+                m_movingRole.x = roleX;
+                update();
+            }
+        }
+        break;
+
+    case ResizeRoleOperation: {
+        const QByteArray pressedRole = m_visibleRoles[m_pressedRoleIndex];
 
         qreal previousWidth = m_visibleRolesWidths.value(pressedRole);
         qreal currentWidth = previousWidth;
@@ -211,9 +265,32 @@ void KItemListHeader::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
         update();
 
         emit visibleRoleWidthChanged(pressedRole, currentWidth, previousWidth);
-    } else if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
-        kDebug() << "Moving of role not supported yet";
-        m_roleOperation = MoveRoleOperation;
+        break;
+    }
+
+    case MoveRoleOperation: {
+        // TODO: It should be configurable whether moving the first role is allowed.
+        // In the context of Dolphin this is not required, however this should be
+        // changed if KItemViews are used in a more generic way.
+        if (m_movingRole.index > 0) {
+            m_movingRole.x = event->pos().x() - m_movingRole.xDec;
+            update();
+
+            const int targetIndex = targetOfMovingRole();
+            if (targetIndex > 0 && targetIndex != m_movingRole.index) {
+                const QByteArray role = m_visibleRoles[m_movingRole.index];
+                const int previousIndex = m_movingRole.index;
+                m_movingRole.index = targetIndex;
+                emit visibleRoleMoved(role, targetIndex, previousIndex);
+
+                m_movingRole.xDec = event->pos().x() - roleXPosition(role);
+            }
+        }
+        break;
+    }
+
+    default:
+        break;
     }
 }
 
@@ -262,7 +339,7 @@ void KItemListHeader::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder
 void KItemListHeader::paintRole(QPainter* painter,
                                 const QByteArray& role,
                                 const QRectF& rect,
-                                int orderIndex)
+                                int orderIndex) const
 {
     // The following code is based on the code from QHeaderView::paintSection().
     // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
@@ -342,7 +419,7 @@ bool KItemListHeader::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
 {
     qreal x = 0;
     for (int i = 0; i <= roleIndex; ++i) {
-        const QByteArray role = m_visibleRoles.at(i);
+        const QByteArray role = m_visibleRoles[i];
         x += m_visibleRolesWidths.value(role);
     }
 
@@ -350,4 +427,73 @@ bool KItemListHeader::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
     return pos.x() >= (x - grip) && pos.x() <= x;
 }
 
+QPixmap KItemListHeader::createRolePixmap(int roleIndex) const
+{
+    const QByteArray role = m_visibleRoles[roleIndex];
+    const qreal roleWidth = m_visibleRolesWidths.value(role);
+    const QRect rect(0, 0, roleWidth, size().height());
+
+    QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
+
+    QPainter painter(&image);
+    paintRole(&painter, role, rect, roleIndex);
+
+    // Apply a highlighting-color
+    const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
+    QColor highlightColor = palette().color(group, QPalette::Highlight);
+    highlightColor.setAlpha(64);
+    painter.fillRect(rect, highlightColor);
+
+    // Make the image transparent
+    painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+    painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192));
+
+    return QPixmap::fromImage(image);
+}
+
+int KItemListHeader::targetOfMovingRole() const
+{
+    const int movingWidth = m_movingRole.pixmap.width();
+    const int movingLeft = m_movingRole.x;
+    const int movingRight = movingLeft + movingWidth - 1;
+
+    int targetIndex = 0;
+    qreal targetLeft = 0;
+    while (targetIndex < m_visibleRoles.count()) {
+        const QByteArray role = m_visibleRoles[targetIndex];
+        const qreal targetWidth = m_visibleRolesWidths.value(role);
+        const qreal targetRight = targetLeft + targetWidth - 1;
+
+        const bool isInTarget = (targetWidth >= movingWidth &&
+                                 movingLeft  >= targetLeft  &&
+                                 movingRight <= targetRight) ||
+                                (targetWidth <  movingWidth &&
+                                 movingLeft  <= targetLeft  &&
+                                 movingRight >= targetRight);
+
+        if (isInTarget) {
+            return targetIndex;
+        }
+
+        targetLeft += targetWidth;
+        ++targetIndex;
+    }
+
+    return m_movingRole.index;
+}
+
+qreal KItemListHeader::roleXPosition(const QByteArray& role) const
+{
+    qreal x = 0;
+    foreach (const QByteArray& visibleRole, m_visibleRoles) {
+        if (visibleRole == role) {
+            return x;
+        }
+
+        x += m_visibleRolesWidths.value(visibleRole);
+    }
+
+    return -1;
+}
+
 #include "kitemlistheader_p.moc"