From: Peter Penz Date: Wed, 7 Mar 2012 21:12:07 +0000 (+0100) Subject: Allow custom sorting of details-view columns X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/08a485349f2bd73682ac806b97d3630c3a7dd3fd?ds=sidebyside Allow custom sorting of details-view columns Fix regression that Dolphin 2.0 did not allow to adjust the sorting of the details-view columns. BUG: 164696 FIXED-IN: 4.8.2 --- diff --git a/src/kitemviews/kitemlistheader.cpp b/src/kitemviews/kitemlistheader.cpp index 7b5549c73..bd7dfb831 100644 --- a/src/kitemviews/kitemlistheader.cpp +++ b/src/kitemviews/kitemlistheader.cpp @@ -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" diff --git a/src/kitemviews/kitemlistheader_p.h b/src/kitemviews/kitemlistheader_p.h index 41505585e..56f80c9dd 100644 --- a/src/kitemviews/kitemlistheader_p.h +++ b/src/kitemviews/kitemlistheader_p.h @@ -60,6 +60,11 @@ signals: qreal currentWidth, qreal previousWidth); + /** + * Is emitted if the position of the visible role has been changed. + */ + void visibleRoleMoved(const QByteArray& role, int currentIndex, int previousIndex); + /** * Is emitted if the user has changed the sort order by clicking on a * header item. The sort order of the model has already been adjusted to @@ -89,13 +94,30 @@ private slots: void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); private: - void paintRole(QPainter* painter, const QByteArray& role, const QRectF& rect, int orderIndex); + void paintRole(QPainter* painter, const QByteArray& role, const QRectF& rect, int orderIndex) const; void updatePressedRoleIndex(const QPointF& pos); void updateHoveredRoleIndex(const QPointF& pos); int roleIndexAt(const QPointF& pos) const; bool isAboveRoleGrip(const QPointF& pos, int roleIndex) const; + /** + * Creates a pixmap of the role with the index \a roleIndex that is shown + * during moving a role. + */ + QPixmap createRolePixmap(int roleIndex) const; + + /** + * @return Target index of the currently moving visible role based on the current + * state of m_movingRole. + */ + int targetOfMovingRole() const; + + /** + * @return x-position of the left border of the role \a role. + */ + qreal roleXPosition(const QByteArray& role) const; + private: enum RoleOperation { @@ -112,6 +134,14 @@ private: int m_pressedRoleIndex; RoleOperation m_roleOperation; QPointF m_pressedMousePos; + + struct MovingRole + { + QPixmap pixmap; + int x; + int xDec; + int index; + } m_movingRole; }; #endif diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 3d8224adb..a54e06ddc 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -619,6 +619,8 @@ void KItemListView::setHeaderShown(bool show) connect(m_header, SIGNAL(visibleRoleWidthChanged(QByteArray,qreal,qreal)), this, SLOT(slotVisibleRoleWidthChanged(QByteArray,qreal,qreal))); + connect(m_header, SIGNAL(visibleRoleMoved(QByteArray,int,int)), + this, SLOT(slotVisibleRoleMoved(QByteArray,int,int))); connect(m_header, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder))); connect(m_header, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), @@ -1237,6 +1239,23 @@ void KItemListView::slotVisibleRoleWidthChanged(const QByteArray& role, } } +void KItemListView::slotVisibleRoleMoved(const QByteArray& role, + int currentIndex, + int previousIndex) +{ + Q_ASSERT(m_visibleRoles[previousIndex] == role); + + const QList previous = m_visibleRoles; + + QList current = m_visibleRoles; + current.removeAt(previousIndex); + current.insert(currentIndex, role); + + setVisibleRoles(current); + + emit visibleRolesChanged(current, previous); +} + void KItemListView::triggerAutoScrolling() { if (!m_autoScrollTimer) { diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index 57f86ef64..b416888eb 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -298,6 +298,14 @@ signals: */ void sortRoleChanged(const QByteArray& current, const QByteArray& previous); + /** + * Is emitted if the user has changed the visible roles by moving a header + * item (see KItemListView::setHeaderShown()). Note that no signal will be + * emitted if the roles have been changed without user interaction by + * KItemListView::setVisibleRoles(). + */ + void visibleRolesChanged(const QList& current, const QList& previous); + protected: virtual void initializeItemListWidget(KItemListWidget* item); @@ -371,6 +379,14 @@ private slots: qreal currentWidth, qreal previousWidth); + /** + * Is invoked if a visible role has been moved by the user. Applies + * the moved role to the view. + */ + void slotVisibleRoleMoved(const QByteArray& role, + int currentIndex, + int previousIndex); + /** * Triggers the autoscrolling if autoScroll() is enabled by checking the * current mouse position. If the mouse position is within the autoscroll diff --git a/src/views/additionalinfoaccessor.cpp b/src/views/additionalinfoaccessor.cpp index 465a2b3dd..b102282a6 100644 --- a/src/views/additionalinfoaccessor.cpp +++ b/src/views/additionalinfoaccessor.cpp @@ -36,26 +36,17 @@ AdditionalInfoAccessor& AdditionalInfoAccessor::instance() QList AdditionalInfoAccessor::keys() const { - return m_infoList; + return m_map.keys(); } QByteArray AdditionalInfoAccessor::role(DolphinView::AdditionalInfo info) const { - QByteArray role; - switch (info) { - case DolphinView::NameInfo: role = "name"; break; - case DolphinView::SizeInfo: role = "size"; break; - case DolphinView::DateInfo: role = "date"; break; - case DolphinView::PermissionsInfo: role = "permissions"; break; - case DolphinView::OwnerInfo: role = "owner"; break; - case DolphinView::GroupInfo: role = "group"; break; - case DolphinView::TypeInfo: role = "type"; break; - case DolphinView::DestinationInfo: role = "destination"; break; - case DolphinView::PathInfo: role = "path"; break; - default: break; - } + return m_map[info]->role; +} - return role; +DolphinView::AdditionalInfo AdditionalInfoAccessor::additionalInfo(const QByteArray& role) const +{ + return m_infoForRole.value(role); } QString AdditionalInfoAccessor::actionCollectionName(DolphinView::AdditionalInfo info, @@ -64,11 +55,11 @@ QString AdditionalInfoAccessor::actionCollectionName(DolphinView::AdditionalInfo QString name; switch (type) { case SortByType: - name = QLatin1String("sort_by_") + QLatin1String(m_map[info]->actionCollectionName); + name = QLatin1String("sort_by_") + QLatin1String(m_map[info]->role); break; case AdditionalInfoType: - name = QLatin1String("show_") + QLatin1String(m_map[info]->actionCollectionName); + name = QLatin1String("show_") + QLatin1String(m_map[info]->role); break; } @@ -77,7 +68,7 @@ QString AdditionalInfoAccessor::actionCollectionName(DolphinView::AdditionalInfo QString AdditionalInfoAccessor::translation(DolphinView::AdditionalInfo info) const { - return i18nc(m_map[info]->context, m_map[info]->translation); + return i18nc(m_map[info]->roleTranslationContext, m_map[info]->roleTranslation); } QString AdditionalInfoAccessor::value(DolphinView::AdditionalInfo info) const @@ -91,11 +82,11 @@ DolphinView::Sorting AdditionalInfoAccessor::sorting(DolphinView::AdditionalInfo } AdditionalInfoAccessor::AdditionalInfoAccessor() : - m_infoList(), - m_map() + m_map(), + m_infoForRole() { static const AdditionalInfoAccessor::AdditionalInfo additionalInfo[] = { - // Entries for view-properties version 1: + // role roleTranslationContext roleTranslation value sorting { "size", I18N_NOOP2_NOSTRIP("@label", "Size"), "Size", DolphinView::SortBySize}, { "date", I18N_NOOP2_NOSTRIP("@label", "Date"), "Date", DolphinView::SortByDate}, { "permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions"), "Permissions", DolphinView::SortByPermissions}, @@ -115,16 +106,11 @@ AdditionalInfoAccessor::AdditionalInfoAccessor() : m_map.insert(DolphinView::DestinationInfo, &additionalInfo[6]); m_map.insert(DolphinView::PathInfo, &additionalInfo[7]); - // The m_infoList defines all available keys and the sort order - // (don't use m_information = m_map.keys(), as the order would be undefined). - m_infoList.append(DolphinView::SizeInfo); - m_infoList.append(DolphinView::DateInfo); - m_infoList.append(DolphinView::PermissionsInfo); - m_infoList.append(DolphinView::OwnerInfo); - m_infoList.append(DolphinView::GroupInfo); - m_infoList.append(DolphinView::TypeInfo); - m_infoList.append(DolphinView::DestinationInfo); - m_infoList.append(DolphinView::PathInfo); + QMapIterator it(m_map); + while (it.hasNext()) { + it.next(); + m_infoForRole.insert(it.value()->role, it.key()); + } } AdditionalInfoAccessor::~AdditionalInfoAccessor() diff --git a/src/views/additionalinfoaccessor.h b/src/views/additionalinfoaccessor.h index d6dcc8e3f..0fd7eed0c 100644 --- a/src/views/additionalinfoaccessor.h +++ b/src/views/additionalinfoaccessor.h @@ -64,6 +64,8 @@ public: QByteArray role(DolphinView::AdditionalInfo info) const; + DolphinView::AdditionalInfo additionalInfo(const QByteArray& role) const; + QString actionCollectionName(DolphinView::AdditionalInfo info, ActionCollectionType type) const; QString translation(DolphinView::AdditionalInfo info) const; @@ -72,6 +74,7 @@ public: * @return String representation of the value that is stored in the .directory * by ViewProperties. */ + // TODO Dolphin 3.0: Deprecate - just use role() instead. QString value(DolphinView::AdditionalInfo info) const; DolphinView::Sorting sorting(DolphinView::AdditionalInfo info) const; @@ -83,15 +86,15 @@ protected: private: struct AdditionalInfo { - const char* const actionCollectionName; - const char* const context; - const char* const translation; - const char* const value; + const char* const role; + const char* const roleTranslationContext; + const char* const roleTranslation; + const char* const value; // TODO Dolphin 3.0: Deprecate and use role instead const DolphinView::Sorting sorting; }; - QList m_infoList; QMap m_map; + QHash m_infoForRole; }; #endif diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index ea7441600..148459f68 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -161,6 +161,8 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : this, SLOT(slotSortOrderChangedByHeader(Qt::SortOrder,Qt::SortOrder))); connect(view, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), this, SLOT(slotSortRoleChangedByHeader(QByteArray,QByteArray))); + connect(view, SIGNAL(visibleRolesChanged(QList,QList)), + this, SLOT(slotVisibleRolesChangedByHeader(QList,QList))); KItemListSelectionManager* selectionManager = controller->selectionManager(); connect(selectionManager, SIGNAL(selectionChanged(QSet,QSet)), @@ -1157,6 +1159,29 @@ void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const Q emit sortingChanged(sorting); } +void DolphinView::slotVisibleRolesChangedByHeader(const QList& current, + const QList& previous) +{ + Q_UNUSED(previous); + Q_ASSERT(m_container->controller()->view()->visibleRoles() == current); + + const QList previousAdditionalInfoList = m_additionalInfoList; + + m_additionalInfoList.clear(); + m_additionalInfoList.reserve(current.count()); + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + foreach (const QByteArray& role, current) { + if (role != "name") { + m_additionalInfoList.append(infoAccessor.additionalInfo(role)); + } + } + + ViewProperties props(url()); + props.setAdditionalInfoList(m_additionalInfoList); + + emit additionalInfoListChanged(m_additionalInfoList, previousAdditionalInfoList); +} + KFileItemModel* DolphinView::fileItemModel() const { return static_cast(m_container->controller()->model()); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 7b7db3f9f..f2f15c592 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -111,7 +111,6 @@ public: enum AdditionalInfo { NoInfo = 0, - NameInfo, SizeInfo, DateInfo, PermissionsInfo, @@ -654,6 +653,13 @@ private slots: */ void slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous); + /** + * Is invoked when the visible roles have been changed by the user by dragging + * a header item. The view properties of the directory will get updated. + */ + void slotVisibleRolesChangedByHeader(const QList& current, + const QList& previous); + /** * Observes the item with the URL \a url. As soon as the directory * model indicates that the item is available, the item will diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index 9f8b4d8ba..36ec6bdb0 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -219,18 +219,7 @@ void ViewProperties::setAdditionalInfoList(const QListviewMode() == DolphinView::DetailsView) && !newInfoStringList.contains(CustomizedDetailsString); if (markCustomizedDetails) {