X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/255f9c1c95d0b73367fe483eef85937cbb6e103e..a26eda8d3809159615a5fe95b1cf8eb154b1374b:/src/kcategorizedview.cpp diff --git a/src/kcategorizedview.cpp b/src/kcategorizedview.cpp index 8c1bcad51..894da2cae 100644 --- a/src/kcategorizedview.cpp +++ b/src/kcategorizedview.cpp @@ -1,6 +1,6 @@ /** * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López + * Copyright (C) 2007 Rafael Fernández López * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,61 +29,25 @@ #include #include -#include #include -#include "kitemcategorizer.h" -#include "ksortfilterproxymodel.h" - -class LessThan -{ -public: - enum Purpose - { - GeneralPurpose = 0, - CategoryPurpose - }; - - inline LessThan(const KSortFilterProxyModel *proxyModel, - Purpose purpose) - : proxyModel(proxyModel) - , purpose(purpose) - { - } - - inline bool operator()(const QModelIndex &left, - const QModelIndex &right) const - { - if (purpose == GeneralPurpose) - { - return proxyModel->sortOrder() == Qt::AscendingOrder ? - proxyModel->lessThanGeneralPurpose(left, right) : - !proxyModel->lessThanGeneralPurpose(left, right); - } - - return proxyModel->sortOrder() == Qt::AscendingOrder ? - proxyModel->lessThanCategoryPurpose(left, right) : - !proxyModel->lessThanCategoryPurpose(left, right); - } - -private: - const KSortFilterProxyModel *proxyModel; - const Purpose purpose; -}; - - -//============================================================================== +#include "kcategorydrawer.h" +#include "kcategorizedsortfilterproxymodel.h" +// By defining DOLPHIN_DRAGANDDROP the custom drag and drop implementation of +// KCategorizedView is bypassed to have a consistent drag and drop look for all +// views. Hopefully transparent pixmaps for drag objects will be supported in +// Qt 4.4, so that this workaround can be skipped. +#define DOLPHIN_DRAGANDDROP KCategorizedView::Private::Private(KCategorizedView *listView) : listView(listView) - , itemCategorizer(0) + , categoryDrawer(0) , biggestItemSize(QSize(0, 0)) , mouseButtonPressed(false) , isDragging(false) , dragLeftViewport(false) , proxyModel(0) - , lastIndex(QModelIndex()) { } @@ -117,7 +81,7 @@ const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &r { middle = (top + bottom) / 2; - index = elementDictionary[proxyModel->index(middle, 0)]; + index = proxyModel->index(middle, 0); indexVisualRect = visualRect(index); // We need the whole height (not only the visualRect). This will help us to update // all needed indexes correctly (ereslibre) @@ -137,7 +101,7 @@ const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &r for (int i = middle; i < proxyModel->rowCount(); i++) { - index = elementDictionary[proxyModel->index(i, 0)]; + index = proxyModel->index(i, 0); indexVisualRect = visualRect(index); if (rect.intersects(indexVisualRect)) @@ -158,10 +122,20 @@ QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) if (!index.isValid()) return QRect(); - QString curCategory = elementsInfo[index].category; + QString curCategory = elementsInfo[index.row()].category; - QRect retRect(listView->spacing(), listView->spacing() * 2 + - itemCategorizer->categoryHeight(listView->viewOptions()), 0, 0); + QRect retRect; + + if (listView->layoutDirection() == Qt::LeftToRight) + { + retRect = QRect(listView->spacing(), listView->spacing() * 2 + + categoryDrawer->categoryHeight(listView->viewOptions()), 0, 0); + } + else + { + retRect = QRect(listView->viewport()->width() - listView->spacing(), listView->spacing() * 2 + + categoryDrawer->categoryHeight(listView->viewOptions()), 0, 0); + } int viewportWidth = listView->viewport()->width() - listView->spacing(); @@ -184,11 +158,22 @@ QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) if (!elementsPerRow) elementsPerRow++; - int column = elementsInfo[index].relativeOffsetToCategory % elementsPerRow; - int row = elementsInfo[index].relativeOffsetToCategory / elementsPerRow; + int column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow; + int row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow; - retRect.setLeft(retRect.left() + column * listView->spacing() + - column * itemWidth); + if (listView->layoutDirection() == Qt::LeftToRight) + { + retRect.setLeft(retRect.left() + column * listView->spacing() + + column * itemWidth); + } + else + { + retRect.setLeft(retRect.right() - column * listView->spacing() - + column * itemWidth - itemWidth); + + retRect.setRight(retRect.right() - column * listView->spacing() - + column * itemWidth); + } foreach (const QString &category, categories) { @@ -197,28 +182,23 @@ QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) float rows = (float) ((float) categoriesIndexes[category].count() / (float) elementsPerRow); + int rowsInt = categoriesIndexes[category].count() / elementsPerRow; if (rows - trunc(rows)) rowsInt++; + retRect.setTop(retRect.top() + + (rowsInt * itemHeight) + + categoryDrawer->categoryHeight(listView->viewOptions()) + + listView->spacing() * 2); + if (listView->gridSize().isEmpty()) { retRect.setTop(retRect.top() + - (rowsInt * listView->spacing()) + - (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + - listView->spacing() * 2); - } - else - { - retRect.setTop(retRect.top() + - (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + - listView->spacing()); + (rowsInt * listView->spacing())); } } - if (listView->gridSize().isEmpty()) { retRect.setTop(retRect.top() + row * listView->spacing() + @@ -231,13 +211,14 @@ QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) retRect.setWidth(itemWidth); + QModelIndex heightIndex = proxyModel->index(index.row(), 0); if (listView->gridSize().isEmpty()) { - retRect.setHeight(listView->sizeHintForIndex(proxyModel->mapFromSource(index)).height()); + retRect.setHeight(listView->sizeHintForIndex(heightIndex).height()); } else { - retRect.setHeight(qMin(listView->sizeHintForIndex(proxyModel->mapFromSource(index)).height(), + retRect.setHeight(qMin(listView->sizeHintForIndex(heightIndex).height(), listView->gridSize().height())); } @@ -290,24 +271,19 @@ QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &cat if (rows - trunc(rows)) rowsInt++; + retRect.setTop(retRect.top() + + (rowsInt * itemHeight) + + categoryDrawer->categoryHeight(listView->viewOptions()) + + listView->spacing() * 2); + if (listView->gridSize().isEmpty()) { retRect.setTop(retRect.top() + - (rowsInt * listView->spacing()) + - (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + - listView->spacing() * 2); - } - else - { - retRect.setTop(retRect.top() + - (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + - listView->spacing()); + (rowsInt * listView->spacing())); } } - retRect.setHeight(itemCategorizer->categoryHeight(listView->viewOptions())); + retRect.setHeight(categoryDrawer->categoryHeight(listView->viewOptions())); return retRect; } @@ -316,9 +292,9 @@ QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &cat const QRect &KCategorizedView::Private::cacheIndex(const QModelIndex &index) { QRect rect = visualRectInViewport(index); - elementsPosition[index] = rect; + elementsPosition[index.row()] = rect; - return elementsPosition[index]; + return elementsPosition[index.row()]; } // We're sure categoriesPosition doesn't contain category @@ -332,9 +308,9 @@ const QRect &KCategorizedView::Private::cacheCategory(const QString &category) const QRect &KCategorizedView::Private::cachedRectIndex(const QModelIndex &index) { - if (elementsPosition.contains(index)) // If we have it cached + if (elementsPosition.contains(index.row())) // If we have it cached { // return it - return elementsPosition[index]; + return elementsPosition[index.row()]; } else // Otherwise, cache it { // and return it @@ -356,9 +332,7 @@ const QRect &KCategorizedView::Private::cachedRectCategory(const QString &catego QRect KCategorizedView::Private::visualRect(const QModelIndex &index) { - QModelIndex mappedIndex = proxyModel->mapToSource(index); - - QRect retRect = cachedRectIndex(mappedIndex); + QRect retRect = cachedRectIndex(index); int dx = -listView->horizontalOffset(); int dy = -listView->verticalOffset(); retRect.adjust(dx, dy, dx, dy); @@ -377,30 +351,53 @@ QRect KCategorizedView::Private::categoryVisualRect(const QString &category) } void KCategorizedView::Private::drawNewCategory(const QModelIndex &index, - int sortRole, - const QStyleOption &option, - QPainter *painter) + int sortRole, + const QStyleOption &option, + QPainter *painter) { + if (!index.isValid()) + { + return; + } + QStyleOption optionCopy = option; - const QString category = itemCategorizer->categoryForItem(index, sortRole); + const QString category = proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryRole).toString(); + + optionCopy.state &= ~QStyle::State_Selected; if ((category == hoveredCategory) && !mouseButtonPressed) { optionCopy.state |= QStyle::State_MouseOver; } + else if ((category == hoveredCategory) && mouseButtonPressed) + { + QPoint initialPressPosition = listView->viewport()->mapFromGlobal(QCursor::pos()); + initialPressPosition.setY(initialPressPosition.y() + listView->verticalOffset()); + initialPressPosition.setX(initialPressPosition.x() + listView->horizontalOffset()); - itemCategorizer->drawCategory(index, - sortRole, - optionCopy, - painter); + if (initialPressPosition == this->initialPressPosition) + { + optionCopy.state |= QStyle::State_Selected; + } + } + + categoryDrawer->drawCategory(index, + sortRole, + optionCopy, + painter); } void KCategorizedView::Private::updateScrollbars() { + // find the last index in the last category + QModelIndex lastIndex = categoriesIndexes.isEmpty() ? QModelIndex() : categoriesIndexes[categories.last()].last(); + int lastItemBottom = cachedRectIndex(lastIndex).top() + listView->spacing() + (listView->gridSize().isEmpty() ? 0 : listView->gridSize().height()) - listView->viewport()->height(); + listView->horizontalScrollBar()->setRange(0, 0); + listView->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10); listView->verticalScrollBar()->setPageStep(listView->viewport()->height()); listView->verticalScrollBar()->setRange(0, lastItemBottom); @@ -425,6 +422,36 @@ void KCategorizedView::Private::drawDraggedItems(QPainter *painter) } } +void KCategorizedView::Private::layoutChanged(bool forceItemReload) +{ + if ((listView->viewMode() == KCategorizedView::IconMode) && proxyModel && + categoryDrawer && proxyModel->isCategorizedModel() && + ((forceItemReload || + (modelSortRole != proxyModel->sortRole()) || + (modelSortColumn != proxyModel->sortColumn()) || + (modelSortOrder != proxyModel->sortOrder()) || + (modelLastRowCount != proxyModel->rowCount()) || + (modelCategorized != proxyModel->isCategorizedModel())))) + { + // Force the view to update all elements + listView->rowsInsertedArtifficial(QModelIndex(), 0, proxyModel->rowCount() - 1); + + if (!forceItemReload) + { + modelSortRole = proxyModel->sortRole(); + modelSortColumn = proxyModel->sortColumn(); + modelSortOrder = proxyModel->sortOrder(); + modelLastRowCount = proxyModel->rowCount(); + modelCategorized = proxyModel->isCategorizedModel(); + } + } + else if ((listView->viewMode() == KCategorizedView::IconMode) && proxyModel && + categoryDrawer && proxyModel->isCategorizedModel()) + { + updateScrollbars(); + } +} + void KCategorizedView::Private::drawDraggedItems() { QRect rectToUpdate; @@ -463,6 +490,13 @@ KCategorizedView::~KCategorizedView() delete d; } +void KCategorizedView::setGridSize(const QSize &size) +{ + QListView::setGridSize(size); + + d->layoutChanged(true); +} + void KCategorizedView::setModel(QAbstractItemModel *model) { d->lastSelection = QItemSelection(); @@ -470,47 +504,68 @@ void KCategorizedView::setModel(QAbstractItemModel *model) d->forcedSelectionPosition = 0; d->elementsInfo.clear(); d->elementsPosition.clear(); - d->elementDictionary.clear(); - d->invertedElementDictionary.clear(); d->categoriesIndexes.clear(); d->categoriesPosition.clear(); d->categories.clear(); d->intersectedIndexes.clear(); - d->sourceModelIndexList.clear(); + d->modelIndexList.clear(); d->hovered = QModelIndex(); d->mouseButtonPressed = false; if (d->proxyModel) { QObject::disconnect(d->proxyModel, - SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); + SIGNAL(layoutChanged()), + this, SLOT(slotLayoutChanged())); + + QObject::disconnect(d->proxyModel, + SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotLayoutChanged())); QObject::disconnect(d->proxyModel, - SIGNAL(sortingRoleChanged()), - this, SLOT(slotSortingRoleChanged())); + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); } QListView::setModel(model); - d->proxyModel = dynamic_cast(model); + d->proxyModel = dynamic_cast(model); if (d->proxyModel) { + d->modelSortRole = d->proxyModel->sortRole(); + d->modelSortColumn = d->proxyModel->sortColumn(); + d->modelSortOrder = d->proxyModel->sortOrder(); + d->modelLastRowCount = d->proxyModel->rowCount(); + d->modelCategorized = d->proxyModel->isCategorizedModel(); + + QObject::connect(d->proxyModel, + SIGNAL(layoutChanged()), + this, SLOT(slotLayoutChanged())); + + QObject::connect(d->proxyModel, + SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotLayoutChanged())); + QObject::connect(d->proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int))); - QObject::connect(d->proxyModel, - SIGNAL(sortingRoleChanged()), - this, SLOT(slotSortingRoleChanged())); + if (d->proxyModel->rowCount()) + { + d->layoutChanged(true); + } + } + else + { + d->modelCategorized = false; } } QRect KCategorizedView::visualRect(const QModelIndex &index) const { if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return QListView::visualRect(index); } @@ -523,54 +578,66 @@ QRect KCategorizedView::visualRect(const QModelIndex &index) const return d->visualRect(index); } -KItemCategorizer *KCategorizedView::itemCategorizer() const +KCategoryDrawer *KCategorizedView::categoryDrawer() const { - return d->itemCategorizer; + return d->categoryDrawer; } -void KCategorizedView::setItemCategorizer(KItemCategorizer *itemCategorizer) +void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer) { d->lastSelection = QItemSelection(); d->currentViewIndex = QModelIndex(); d->forcedSelectionPosition = 0; d->elementsInfo.clear(); d->elementsPosition.clear(); - d->elementDictionary.clear(); - d->invertedElementDictionary.clear(); d->categoriesIndexes.clear(); d->categoriesPosition.clear(); d->categories.clear(); d->intersectedIndexes.clear(); - d->sourceModelIndexList.clear(); + d->modelIndexList.clear(); d->hovered = QModelIndex(); d->mouseButtonPressed = false; - if (!itemCategorizer && d->proxyModel) + if (!categoryDrawer && d->proxyModel) { QObject::disconnect(d->proxyModel, - SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); + SIGNAL(layoutChanged()), + this, SLOT(slotLayoutChanged())); QObject::disconnect(d->proxyModel, - SIGNAL(sortingRoleChanged()), - this, SLOT(slotSortingRoleChanged())); + SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotLayoutChanged())); + + QObject::disconnect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); } - else if (itemCategorizer && d->proxyModel) + else if (categoryDrawer && d->proxyModel) { QObject::connect(d->proxyModel, - SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); + SIGNAL(layoutChanged()), + this, SLOT(slotLayoutChanged())); QObject::connect(d->proxyModel, - SIGNAL(sortingRoleChanged()), - this, SLOT(slotSortingRoleChanged())); + SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotLayoutChanged())); + + QObject::connect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); } - d->itemCategorizer = itemCategorizer; + d->categoryDrawer = categoryDrawer; - if (itemCategorizer) + if (categoryDrawer) { - rowsInserted(QModelIndex(), 0, d->proxyModel->rowCount() - 1); + if (d->proxyModel) + { + if (d->proxyModel->rowCount()) + { + d->layoutChanged(true); + } + } } else { @@ -581,7 +648,7 @@ void KCategorizedView::setItemCategorizer(KItemCategorizer *itemCategorizer) QModelIndex KCategorizedView::indexAt(const QPoint &point) const { if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return QListView::indexAt(point); } @@ -609,13 +676,11 @@ void KCategorizedView::reset() d->forcedSelectionPosition = 0; d->elementsInfo.clear(); d->elementsPosition.clear(); - d->elementDictionary.clear(); - d->invertedElementDictionary.clear(); d->categoriesIndexes.clear(); d->categoriesPosition.clear(); d->categories.clear(); d->intersectedIndexes.clear(); - d->sourceModelIndexList.clear(); + d->modelIndexList.clear(); d->hovered = QModelIndex(); d->biggestItemSize = QSize(0, 0); d->mouseButtonPressed = false; @@ -624,7 +689,7 @@ void KCategorizedView::reset() void KCategorizedView::paintEvent(QPaintEvent *event) { if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { QListView::paintEvent(event); return; @@ -632,6 +697,11 @@ void KCategorizedView::paintEvent(QPaintEvent *event) QStyleOptionViewItemV3 option = viewOptions(); option.widget = this; + if (wordWrap()) + { + option.features |= QStyleOptionViewItemV2::WrapText; + } + QPainter painter(viewport()); QRect area = event->rect(); const bool focus = (hasFocus() || viewport()->hasFocus()) && @@ -645,7 +715,7 @@ void KCategorizedView::paintEvent(QPaintEvent *event) foreach (const QModelIndex &index, dirtyIndexes) { option.state = state; - option.rect = d->visualRect(index); + option.rect = visualRect(index); if (selectionModel() && selectionModel()->isSelected(index)) { @@ -684,6 +754,7 @@ void KCategorizedView::paintEvent(QPaintEvent *event) // Redraw categories QStyleOptionViewItem otherOption; + bool intersectedInThePast = false; foreach (const QString &category, d->categories) { otherOption = option; @@ -692,9 +763,18 @@ void KCategorizedView::paintEvent(QPaintEvent *event) if (otherOption.rect.intersects(area)) { - d->drawNewCategory(d->categoriesIndexes[category][0], + intersectedInThePast = true; + + QModelIndex indexToDraw = d->proxyModel->index(d->categoriesIndexes[category][0].row(), d->proxyModel->sortColumn()); + + d->drawNewCategory(indexToDraw, d->proxyModel->sortRole(), otherOption, &painter); } + else if (intersectedInThePast) + { + break; // the visible area has been finished, we don't need to keep asking, the rest won't intersect + // this is doable because we know that categories are correctly ordered on the list + } } if (d->mouseButtonPressed && !d->isDragging) @@ -747,7 +827,7 @@ void KCategorizedView::resizeEvent(QResizeEvent *event) d->forcedSelectionPosition = 0; if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -759,7 +839,7 @@ void KCategorizedView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) { if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { QListView::setSelection(rect, flags); return; @@ -768,20 +848,22 @@ void KCategorizedView::setSelection(const QRect &rect, if (!flags) return; - selectionModel()->clear(); - if (flags & QItemSelectionModel::Clear) { - d->lastSelection = QItemSelection(); + if (!rect.intersects(d->categoryVisualRect(d->hoveredCategory))) + { + d->lastSelection = QItemSelection(); + } } - QModelIndexList dirtyIndexes = d->intersectionSet(rect); + selectionModel()->clear(); - QItemSelection selection; + QModelIndexList dirtyIndexes = d->intersectionSet(rect); if (!dirtyIndexes.count()) { - if (d->lastSelection.count()) + if (!d->lastSelection.isEmpty() && + (rect.intersects(d->categoryVisualRect(d->hoveredCategory)) || d->mouseButtonPressed)) { selectionModel()->select(d->lastSelection, flags); } @@ -789,6 +871,8 @@ void KCategorizedView::setSelection(const QRect &rect, return; } + QItemSelection selection; + if (!d->mouseButtonPressed) { selection = QItemSelection(dirtyIndexes[0], dirtyIndexes[0]); @@ -816,13 +900,12 @@ void KCategorizedView::setSelection(const QRect &rect, selection << QItemSelectionRange(first, last); } - if (d->lastSelection.count() && !d->mouseButtonPressed) - { - selection.merge(d->lastSelection, flags); - } - else if (d->lastSelection.count()) + if (d->lastSelection.count()) { - selection.merge(d->lastSelection, QItemSelectionModel::Select); + if ((selection.count() == 1) && (selection[0].indexes().count() == 1)) + selection.merge(d->lastSelection, flags); + else + selection.merge(d->lastSelection, QItemSelectionModel::Select); } selectionModel()->select(selection, flags); @@ -833,7 +916,7 @@ void KCategorizedView::mouseMoveEvent(QMouseEvent *event) QListView::mouseMoveEvent(event); if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -904,6 +987,8 @@ void KCategorizedView::mousePressEvent(QMouseEvent *event) } QListView::mousePressEvent(event); + + viewport()->update(d->categoryVisualRect(d->hoveredCategory)); } void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) @@ -913,7 +998,7 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) QListView::mouseReleaseEvent(event); if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -923,6 +1008,7 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) initialPressPosition.setX(initialPressPosition.x() + horizontalOffset()); QItemSelection selection; + QItemSelection deselection; if (initialPressPosition == d->initialPressPosition) { @@ -932,10 +1018,20 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) { foreach (const QModelIndex &index, d->categoriesIndexes[category]) { - selection << QItemSelectionRange(d->proxyModel->mapFromSource(index)); + QModelIndex selectIndex = index.model()->index(index.row(), 0); + + if (!d->lastSelection.contains(selectIndex)) + { + selection << QItemSelectionRange(selectIndex); + } + else + { + deselection << QItemSelectionRange(selectIndex); + } } selectionModel()->select(selection, QItemSelectionModel::Select); + selectionModel()->select(deselection, QItemSelectionModel::Deselect); break; } @@ -945,7 +1041,7 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) d->lastSelection = selectionModel()->selection(); if (d->hovered.isValid()) - viewport()->update(d->visualRect(d->hovered)); + viewport()->update(visualRect(d->hovered)); else if (!d->hoveredCategory.isEmpty()) viewport()->update(d->categoryVisualRect(d->hoveredCategory)); } @@ -960,7 +1056,14 @@ void KCategorizedView::leaveEvent(QEvent *event) void KCategorizedView::startDrag(Qt::DropActions supportedActions) { + // FIXME: QAbstractItemView does far better here since it sets the + // pixmap of selected icons to the dragging cursor, but it sets a non + // ARGB window so it is no transparent. Use QAbstractItemView when + // this is fixed on Qt. + // QAbstractItemView::startDrag(supportedActions); +#if !defined(DOLPHIN_DRAGANDDROP) QListView::startDrag(supportedActions); +#endif d->isDragging = false; d->mouseButtonPressed = false; @@ -984,9 +1087,13 @@ void KCategorizedView::dragMoveEvent(QDragMoveEvent *event) d->dragLeftViewport = false; if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { +#if defined(DOLPHIN_DRAGANDDROP) + QAbstractItemView::dragMoveEvent(event); +#else QListView::dragMoveEvent(event); +#endif return; } @@ -997,14 +1104,31 @@ void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event) { d->dragLeftViewport = true; +#if defined(DOLPHIN_DRAGANDDROP) + QAbstractItemView::dragLeaveEvent(event); +#else QListView::dragLeaveEvent(event); +#endif +} + +void KCategorizedView::dropEvent(QDropEvent *event) +{ +#if defined(DOLPHIN_DRAGANDDROP) + QAbstractItemView::dropEvent(event); +#else + QListView::dropEvent(event); +#endif } QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) + Qt::KeyboardModifiers modifiers) { - if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + if ((viewMode() != KCategorizedView::IconMode) || + !d->proxyModel || + !d->categoryDrawer || + d->categories.isEmpty() || + !d->proxyModel->isCategorizedModel() + ) { return QListView::moveCursor(cursorAction, modifiers); } @@ -1026,9 +1150,10 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, int itemWidthPlusSeparation = spacing() + itemWidth; int elementsPerRow = viewportWidth / itemWidthPlusSeparation; - QString lastCategory = d->categories[0]; - QString theCategory = d->categories[0]; - QString afterCategory = d->categories[0]; + QString lastCategory = d->categories.first(); + QString theCategory = d->categories.first(); + QString afterCategory = d->categories.first(); + bool hasToBreak = false; foreach (const QString &category, d->categories) { @@ -1039,7 +1164,7 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, break; } - if (category == d->elementsInfo[d->proxyModel->mapToSource(current)].category) + if (category == d->elementsInfo[current.row()].category) { theCategory = category; @@ -1055,17 +1180,17 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, switch (cursorAction) { case QAbstractItemView::MoveUp: { - if (d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory >= elementsPerRow) + if (d->elementsInfo[current.row()].relativeOffsetToCategory >= elementsPerRow) { - int indexToMove = d->invertedElementDictionary[current].row(); - indexToMove -= qMin(((d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory) + d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition + (d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory % elementsPerRow)); + int indexToMove = current.row(); + indexToMove -= qMin(((d->elementsInfo[current.row()].relativeOffsetToCategory) + d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition + (d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow)); - return d->elementDictionary[d->proxyModel->index(indexToMove, 0)]; + return d->proxyModel->index(indexToMove, 0); } else { int lastCategoryLastRow = (d->categoriesIndexes[lastCategory].count() - 1) % elementsPerRow; - int indexToMove = d->invertedElementDictionary[current].row() - d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory; + int indexToMove = current.row() - d->elementsInfo[current.row()].relativeOffsetToCategory; if (d->forcedSelectionPosition >= lastCategoryLastRow) { @@ -1076,22 +1201,22 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1), d->forcedSelectionPosition + elementsPerRow + 1); } - return d->elementDictionary[d->proxyModel->index(indexToMove, 0)]; + return d->proxyModel->index(indexToMove, 0); } } case QAbstractItemView::MoveDown: { - if (d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow))) + if (d->elementsInfo[current.row()].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow))) { - int indexToMove = d->invertedElementDictionary[current].row(); - indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory); + int indexToMove = current.row(); + indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[current.row()].relativeOffsetToCategory); - return d->elementDictionary[d->proxyModel->index(indexToMove, 0)]; + return d->proxyModel->index(indexToMove, 0); } else { int afterCategoryLastRow = qMin(elementsPerRow, d->categoriesIndexes[afterCategory].count()); - int indexToMove = d->invertedElementDictionary[current].row() + (d->categoriesIndexes[theCategory].count() - d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory); + int indexToMove = current.row() + (d->categoriesIndexes[theCategory].count() - d->elementsInfo[current.row()].relativeOffsetToCategory); if (d->forcedSelectionPosition >= afterCategoryLastRow) { @@ -1102,25 +1227,45 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow); } - return d->elementDictionary[d->proxyModel->index(indexToMove, 0)]; + return d->proxyModel->index(indexToMove, 0); } } case QAbstractItemView::MoveLeft: - d->forcedSelectionPosition = d->elementsInfo[d->proxyModel->mapToSource(d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[current].row() - 1, 0)])].relativeOffsetToCategory % elementsPerRow; + if (layoutDirection() == Qt::RightToLeft) + { + d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow; + + if (d->forcedSelectionPosition < 0) + d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow; + + return d->proxyModel->index(current.row() + 1, 0); + } + + d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow; if (d->forcedSelectionPosition < 0) d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow; - return d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[current].row() - 1, 0)]; + return d->proxyModel->index(current.row() - 1, 0); case QAbstractItemView::MoveRight: - d->forcedSelectionPosition = d->elementsInfo[d->proxyModel->mapToSource(d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[current].row() + 1, 0)])].relativeOffsetToCategory % elementsPerRow; + if (layoutDirection() == Qt::RightToLeft) + { + d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow; + + if (d->forcedSelectionPosition < 0) + d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow; + + return d->proxyModel->index(current.row() - 1, 0); + } + + d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow; if (d->forcedSelectionPosition < 0) d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow; - return d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[current].row() + 1, 0)]; + return d->proxyModel->index(current.row() + 1, 0); default: break; @@ -1130,26 +1275,24 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, } void KCategorizedView::rowsInserted(const QModelIndex &parent, - int start, - int end) + int start, + int end) { QListView::rowsInserted(parent, start, end); if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { d->lastSelection = QItemSelection(); d->currentViewIndex = QModelIndex(); d->forcedSelectionPosition = 0; d->elementsInfo.clear(); d->elementsPosition.clear(); - d->elementDictionary.clear(); - d->invertedElementDictionary.clear(); d->categoriesIndexes.clear(); d->categoriesPosition.clear(); d->categories.clear(); d->intersectedIndexes.clear(); - d->sourceModelIndexList.clear(); + d->modelIndexList.clear(); d->hovered = QModelIndex(); d->biggestItemSize = QSize(0, 0); d->mouseButtonPressed = false; @@ -1161,8 +1304,8 @@ void KCategorizedView::rowsInserted(const QModelIndex &parent, } void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent, - int start, - int end) + int start, + int end) { Q_UNUSED(parent); @@ -1171,13 +1314,11 @@ void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent, d->forcedSelectionPosition = 0; d->elementsInfo.clear(); d->elementsPosition.clear(); - d->elementDictionary.clear(); - d->invertedElementDictionary.clear(); d->categoriesIndexes.clear(); d->categoriesPosition.clear(); d->categories.clear(); d->intersectedIndexes.clear(); - d->sourceModelIndexList.clear(); + d->modelIndexList.clear(); d->hovered = QModelIndex(); d->biggestItemSize = QSize(0, 0); d->mouseButtonPressed = false; @@ -1187,113 +1328,70 @@ void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent, return; } - // Add all elements mapped to the source model - for (int k = 0; k < d->proxyModel->rowCount(); k++) + // Add all elements mapped to the source model and explore categories + QString prevCategory = d->proxyModel->data(d->proxyModel->index(0, d->proxyModel->sortColumn()), KCategorizedSortFilterProxyModel::CategoryRole).toString(); + QString lastCategory = prevCategory; + QModelIndexList modelIndexList; + struct Private::ElementInfo elementInfo; + int offset = -1; + for (int k = 0; k < d->proxyModel->rowCount(); ++k) { - d->biggestItemSize = QSize(qMax(sizeHintForIndex(d->proxyModel->index(k, 0)).width(), + QModelIndex index = d->proxyModel->index(k, d->proxyModel->sortColumn()); + QModelIndex indexSize = d->proxyModel->index(k, 0); + + d->biggestItemSize = QSize(qMax(sizeHintForIndex(indexSize).width(), d->biggestItemSize.width()), - qMax(sizeHintForIndex(d->proxyModel->index(k, 0)).height(), + qMax(sizeHintForIndex(indexSize).height(), d->biggestItemSize.height())); - d->sourceModelIndexList << - d->proxyModel->mapToSource(d->proxyModel->index(k, 0)); - } - - // Sort them with the general purpose lessThan method - LessThan generalLessThan(d->proxyModel, - LessThan::GeneralPurpose); + d->modelIndexList << index; - qStableSort(d->sourceModelIndexList.begin(), d->sourceModelIndexList.end(), - generalLessThan); - - // Explore categories - QString prevCategory = - d->itemCategorizer->categoryForItem(d->sourceModelIndexList[0], - d->proxyModel->sortRole()); - QString lastCategory = prevCategory; - QModelIndexList modelIndexList; - struct Private::ElementInfo elementInfo; - foreach (const QModelIndex &index, d->sourceModelIndexList) - { - lastCategory = d->itemCategorizer->categoryForItem(index, - d->proxyModel->sortRole()); + lastCategory = d->proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryRole).toString(); elementInfo.category = lastCategory; if (prevCategory != lastCategory) { + offset = 0; d->categoriesIndexes.insert(prevCategory, modelIndexList); d->categories << prevCategory; modelIndexList.clear(); } + else + { + offset++; + } + + elementInfo.relativeOffsetToCategory = offset; modelIndexList << index; prevCategory = lastCategory; - d->elementsInfo.insert(index, elementInfo); + d->elementsInfo.insert(index.row(), elementInfo); } d->categoriesIndexes.insert(prevCategory, modelIndexList); d->categories << prevCategory; - // Sort items locally in their respective categories with the category - // purpose lessThan - LessThan categoryLessThan(d->proxyModel, - LessThan::CategoryPurpose); - - foreach (const QString &key, d->categories) - { - QModelIndexList &indexList = d->categoriesIndexes[key]; - - qStableSort(indexList.begin(), indexList.end(), categoryLessThan); - } - - d->lastIndex = d->categoriesIndexes[d->categories[d->categories.count() - 1]][d->categoriesIndexes[d->categories[d->categories.count() - 1]].count() - 1]; - - // Finally, fill data information of items situation. This will help when - // trying to compute an item place in the viewport - int i = 0; // position relative to the category beginning - int j = 0; // number of elements before current - foreach (const QString &key, d->categories) - { - foreach (const QModelIndex &index, d->categoriesIndexes[key]) - { - struct Private::ElementInfo &elementInfo = d->elementsInfo[index]; - - elementInfo.relativeOffsetToCategory = i; - - d->elementDictionary.insert(d->proxyModel->index(j, 0), - d->proxyModel->mapFromSource(index)); - - d->invertedElementDictionary.insert(d->proxyModel->mapFromSource(index), - d->proxyModel->index(j, 0)); - - i++; - j++; - } - - i = 0; - } - d->updateScrollbars(); } void KCategorizedView::rowsRemoved(const QModelIndex &parent, - int start, - int end) + int start, + int end) { if ((viewMode() == KCategorizedView::IconMode) && d->proxyModel && - d->itemCategorizer) + d->categoryDrawer && d->proxyModel->isCategorizedModel()) { // Force the view to update all elements - rowsInsertedArtifficial(parent, start, end); + rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1); } } void KCategorizedView::updateGeometries() { if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { QListView::updateGeometries(); return; @@ -1304,14 +1402,9 @@ void KCategorizedView::updateGeometries() QAbstractItemView::updateGeometries(); } -void KCategorizedView::slotSortingRoleChanged() +void KCategorizedView::slotLayoutChanged() { - if ((viewMode() == KCategorizedView::IconMode) && d->proxyModel && - d->itemCategorizer) - { - // Force the view to update all elements - rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1); - } + d->layoutChanged(); } #include "kcategorizedview.moc"