X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/aa69bcdc64d4cc335da9ab853ace2fbf54157d82..bbf9faa05396d66b3dbe101a0c309459e3ffacb1:/src/kcategorizedview.cpp diff --git a/src/kcategorizedview.cpp b/src/kcategorizedview.cpp index 72909e836..7d717abd1 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,19 @@ #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" 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()) { } @@ -98,6 +56,17 @@ const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &r intersectedIndexes.clear(); + int itemHeight; + + if (listView->gridSize().isEmpty()) + { + itemHeight = biggestItemSize.height(); + } + else + { + itemHeight = listView->gridSize().height(); + } + // Lets find out where we should start int top = proxyModel->rowCount() - 1; int bottom = 0; @@ -106,12 +75,15 @@ 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) + indexVisualRect.setHeight(indexVisualRect.height() + (itemHeight - indexVisualRect.height())); if (qMax(indexVisualRect.topLeft().y(), indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(), - rect.bottomRight().y())) + rect.bottomRight().y())) { bottom = middle + 1; } @@ -123,7 +95,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)) @@ -144,25 +116,58 @@ 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; - QRect retRect(listView->spacing(), listView->spacing() * 2 + - itemCategorizer->categoryHeight(listView->viewOptions()), 0, 0); + 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(); - int itemHeight = biggestItemSize.height(); - int itemWidth = biggestItemSize.width(); + int itemHeight; + int itemWidth; + + if (listView->gridSize().isEmpty()) + { + itemHeight = biggestItemSize.height(); + itemWidth = biggestItemSize.width(); + } + else + { + itemHeight = listView->gridSize().height(); + itemWidth = listView->gridSize().width(); + } + int itemWidthPlusSeparation = listView->spacing() + itemWidth; int elementsPerRow = viewportWidth / itemWidthPlusSeparation; 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; + + 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.setLeft(retRect.left() + column * listView->spacing() + - column * itemWidth); + retRect.setRight(retRect.right() - column * listView->spacing() - + column * itemWidth); + } foreach (const QString &category, categories) { @@ -171,22 +176,45 @@ 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 * listView->spacing()) + (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + + categoryDrawer->categoryHeight(listView->viewOptions()) + listView->spacing() * 2); + + if (listView->gridSize().isEmpty()) + { + retRect.setTop(retRect.top() + + (rowsInt * listView->spacing())); + } } - retRect.setTop(retRect.top() + row * listView->spacing() + - row * itemHeight); + if (listView->gridSize().isEmpty()) + { + retRect.setTop(retRect.top() + row * listView->spacing() + + (row * itemHeight)); + } + else + { + retRect.setTop(retRect.top() + (row * itemHeight)); + } retRect.setWidth(itemWidth); - retRect.setHeight(itemHeight); + + QModelIndex heightIndex = proxyModel->index(index.row(), 0); + if (listView->gridSize().isEmpty()) + { + retRect.setHeight(listView->sizeHintForIndex(heightIndex).height()); + } + else + { + retRect.setHeight(qMin(listView->sizeHintForIndex(heightIndex).height(), + listView->gridSize().height())); + } return retRect; } @@ -206,8 +234,20 @@ QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &cat int viewportWidth = listView->viewport()->width() - listView->spacing(); - int itemHeight = biggestItemSize.height(); - int itemWidth = biggestItemSize.width(); + int itemHeight; + int itemWidth; + + if (listView->gridSize().isEmpty()) + { + itemHeight = biggestItemSize.height(); + itemWidth = biggestItemSize.width(); + } + else + { + itemHeight = listView->gridSize().height(); + itemWidth = listView->gridSize().width(); + } + int itemWidthPlusSeparation = listView->spacing() + itemWidth; int elementsPerRow = viewportWidth / itemWidthPlusSeparation; @@ -226,13 +266,18 @@ QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &cat if (rows - trunc(rows)) rowsInt++; retRect.setTop(retRect.top() + - (rowsInt * listView->spacing()) + (rowsInt * itemHeight) + - itemCategorizer->categoryHeight(listView->viewOptions()) + + categoryDrawer->categoryHeight(listView->viewOptions()) + listView->spacing() * 2); + + if (listView->gridSize().isEmpty()) + { + retRect.setTop(retRect.top() + + (rowsInt * listView->spacing())); + } } - retRect.setHeight(itemCategorizer->categoryHeight(listView->viewOptions())); + retRect.setHeight(categoryDrawer->categoryHeight(listView->viewOptions())); return retRect; } @@ -241,9 +286,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 @@ -257,9 +302,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 @@ -281,9 +326,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); @@ -302,29 +345,50 @@ 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()); + + if (initialPressPosition == this->initialPressPosition) + { + optionCopy.state |= QStyle::State_Selected; + } + } - itemCategorizer->drawCategory(index, - sortRole, - optionCopy, - painter); + categoryDrawer->drawCategory(index, + sortRole, + optionCopy, + painter); } void KCategorizedView::Private::updateScrollbars() { - int lastItemBottom = cachedRectIndex(lastIndex).bottom() + - listView->spacing() - listView->viewport()->height(); + // 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->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10); listView->verticalScrollBar()->setPageStep(listView->viewport()->height()); @@ -350,6 +414,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; @@ -388,6 +482,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(); @@ -395,47 +496,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(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))); } 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); } @@ -448,54 +570,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(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))); } - 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 { @@ -506,7 +640,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); } @@ -534,13 +668,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; @@ -549,7 +681,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; @@ -557,6 +689,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()) && @@ -570,7 +707,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)) { @@ -608,8 +745,8 @@ void KCategorizedView::paintEvent(QPaintEvent *event) } // Redraw categories - int i = 0; QStyleOptionViewItem otherOption; + bool intersectedInThePast = false; foreach (const QString &category, d->categories) { otherOption = option; @@ -618,9 +755,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) @@ -673,7 +819,7 @@ void KCategorizedView::resizeEvent(QResizeEvent *event) d->forcedSelectionPosition = 0; if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -685,7 +831,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; @@ -694,20 +840,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); } @@ -715,6 +863,8 @@ void KCategorizedView::setSelection(const QRect &rect, return; } + QItemSelection selection; + if (!d->mouseButtonPressed) { selection = QItemSelection(dirtyIndexes[0], dirtyIndexes[0]); @@ -742,13 +892,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); @@ -759,7 +908,7 @@ void KCategorizedView::mouseMoveEvent(QMouseEvent *event) QListView::mouseMoveEvent(event); if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -830,6 +979,8 @@ void KCategorizedView::mousePressEvent(QMouseEvent *event) } QListView::mousePressEvent(event); + + viewport()->update(d->categoryVisualRect(d->hoveredCategory)); } void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) @@ -839,7 +990,7 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) QListView::mouseReleaseEvent(event); if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { return; } @@ -849,6 +1000,7 @@ void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) initialPressPosition.setX(initialPressPosition.x() + horizontalOffset()); QItemSelection selection; + QItemSelection deselection; if (initialPressPosition == d->initialPressPosition) { @@ -858,10 +1010,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; } @@ -871,7 +1033,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)); } @@ -886,6 +1048,11 @@ 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); QListView::startDrag(supportedActions); d->isDragging = false; @@ -910,7 +1077,7 @@ void KCategorizedView::dragMoveEvent(QDragMoveEvent *event) d->dragLeftViewport = false; if ((viewMode() != KCategorizedView::IconMode) || !d->proxyModel || - !d->itemCategorizer) + !d->categoryDrawer || !d->proxyModel->isCategorizedModel()) { QListView::dragMoveEvent(event); return; @@ -927,10 +1094,14 @@ void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event) } 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); } @@ -938,14 +1109,24 @@ QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, const QModelIndex current = selectionModel()->currentIndex(); int viewportWidth = viewport()->width() - spacing(); - int itemHeight = d->biggestItemSize.height(); - int itemWidth = d->biggestItemSize.width(); + int itemWidth; + + if (gridSize().isEmpty()) + { + itemWidth = d->biggestItemSize.width(); + } + else + { + itemWidth = gridSize().width(); + } + 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) { @@ -956,7 +1137,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; @@ -972,17 +1153,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) { @@ -993,22 +1174,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) { @@ -1019,25 +1200,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; @@ -1047,26 +1248,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; @@ -1078,8 +1277,8 @@ void KCategorizedView::rowsInserted(const QModelIndex &parent, } void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent, - int start, - int end) + int start, + int end) { Q_UNUSED(parent); @@ -1088,13 +1287,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; @@ -1104,113 +1301,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)); - } + d->modelIndexList << index; - // Sort them with the general purpose lessThan method - LessThan generalLessThan(d->proxyModel, - LessThan::GeneralPurpose); - - 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; @@ -1221,14 +1375,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"