diff options
| author | Rafael Fernández López <[email protected]> | 2007-06-17 15:32:31 +0000 |
|---|---|---|
| committer | Rafael Fernández López <[email protected]> | 2007-06-17 15:32:31 +0000 |
| commit | 11f0a8c50310ebbcaa93318cb097077482268cdd (patch) | |
| tree | 06df222f3e7812bea96db8b8c1a40256547ec7b0 /src/klistview.cpp | |
| parent | 1c0341958df6d5cea0a2725afb6245ce498422b2 (diff) | |
New and powerful KListView. Still pending class renaming. There are two
methods that I need to think about it, and boost. Small issues like
reloading all data when sorting role suddenly changes. In general terms
it will work nice when you sort by name or size. We have to work further
when we sort by other roles. Nice times.
svn path=/trunk/KDE/kdebase/apps/; revision=676732
Diffstat (limited to 'src/klistview.cpp')
| -rw-r--r-- | src/klistview.cpp | 927 |
1 files changed, 720 insertions, 207 deletions
diff --git a/src/klistview.cpp b/src/klistview.cpp index 1d4e41536..b38f18263 100644 --- a/src/klistview.cpp +++ b/src/klistview.cpp @@ -18,25 +18,64 @@ * Boston, MA 02110-1301, USA. */ -// NOTE: rectForIndex() not virtual on QListView !! relevant ? #include "klistview.h" #include "klistview_p.h" -#include <QtGui/QPainter> -#include <QtGui/QScrollBar> -#include <QtGui/QKeyEvent> -#include <QtGui/QSortFilterProxyModel> +#include <math.h> // trunc + +#include <QApplication> +#include <QPainter> +#include <QScrollBar> +#include <QPaintEvent> #include <kdebug.h> +#include <kstyle.h> #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->lessThanGeneralPurpose(left, right); + } + + return proxyModel->lessThanCategoryPurpose(left, right); + } + +private: + const KSortFilterProxyModel *proxyModel; + const Purpose purpose; +}; + + +//============================================================================== + KListView::Private::Private(KListView *listView) : listView(listView) - , modelSortCapable(false) , itemCategorizer(0) - , numCategories(0) + , mouseButtonPressed(false) , proxyModel(0) + , lastIndex(QModelIndex()) { } @@ -44,148 +83,365 @@ KListView::Private::~Private() { } -QModelIndexList KListView::Private::intersectionSet(const QRect &rect) const +const QModelIndexList &KListView::Private::intersectionSet(const QRect &rect) { - // FIXME: boost me, I suck (ereslibre) + QModelIndex index; + QRect indexVisualRect; - QModelIndexList modelIndexList; + intersectedIndexes.clear(); - QModelIndex index; - for (int i = 0; i < listView->model()->rowCount(); i++) + // Lets find out where we should start + int top = proxyModel->rowCount() - 1; + int bottom = 0; + int middle = (top + bottom) / 2; + while (bottom <= top) { - index = listView->model()->index(i, 0); + middle = (top + bottom) / 2; + + index = elementDictionary[proxyModel->index(middle, 0)]; + indexVisualRect = visualRect(index); - if (rect.intersects(listView->visualRect(index))) - modelIndexList.append(index); + if (qMax(indexVisualRect.topLeft().y(), + indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(), + rect.bottomRight().y())) + { + bottom = middle + 1; + } + else + { + top = middle - 1; + } } - return modelIndexList; -} + int j = 0; + for (int i = middle; i < proxyModel->rowCount(); i++) + { + index = elementDictionary[proxyModel->index(i, 0)]; + indexVisualRect = visualRect(index); -KListView::KListView(QWidget *parent) - : QListView(parent) - , d(new Private(this)) -{ + if (rect.intersects(indexVisualRect)) + intersectedIndexes.append(index); + + // If we passed next item, stop searching for hits + if (qMax(rect.bottomRight().y(), rect.topLeft().y()) < + indexVisualRect.topLeft().y()) + break; + + j++; + } + + return intersectedIndexes; } -KListView::~KListView() +QRect KListView::Private::visualRectInViewport(const QModelIndex &index) const { - if (d->proxyModel) + if (!index.isValid()) + return QRect(); + + QString curCategory = elementsInfo[index].category; + + QRect retRect(listView->spacing(), listView->spacing() * 2 + + 30 /* categoryHeight */, 0, 0); + + int viewportWidth = listView->viewport()->width() - listView->spacing(); + + // We really need all items to be of same size. Otherwise we cannot do this + // (ereslibre) + // QSize itemSize = + // listView->sizeHintForIndex(proxyModel->mapFromSource(index)); + // int itemHeight = itemSize.height(); + // int itemWidth = itemSize.width();*/ + int itemHeight = 107; + int itemWidth = 130; + int itemWidthPlusSeparation = listView->spacing() + itemWidth; + int elementsPerRow = viewportWidth / itemWidthPlusSeparation; + if (!elementsPerRow) + elementsPerRow++; + + int column = elementsInfo[index].relativeOffsetToCategory % elementsPerRow; + int row = elementsInfo[index].relativeOffsetToCategory / elementsPerRow; + + retRect.setLeft(retRect.left() + column * listView->spacing() + + column * itemWidth); + + float rows; + int rowsInt; + foreach (const QString &category, categories) { - QObject::disconnect(this->model(), SIGNAL(layoutChanged()), - this , SLOT(itemsLayoutChanged())); + if (category == curCategory) + break; + + rows = (float) ((float) categoriesIndexes[category].count() / + (float) elementsPerRow); + rowsInt = categoriesIndexes[category].count() / elementsPerRow; + + if (rows - trunc(rows)) rowsInt++; + + retRect.setTop(retRect.top() + + (rowsInt * listView->spacing()) + + (rowsInt * itemHeight) + + 30 /* categoryHeight */ + + listView->spacing() * 2); } - delete d; + retRect.setTop(retRect.top() + row * listView->spacing() + + row * itemHeight); + + retRect.setWidth(itemWidth); + retRect.setHeight(itemHeight); + + return retRect; } -void KListView::setModel(QAbstractItemModel *model) +QRect KListView::Private::visualCategoryRectInViewport(const QString &category) + const { - QSortFilterProxyModel *proxyModel = - qobject_cast<QSortFilterProxyModel*>(model); + QRect retRect(listView->spacing(), + listView->spacing(), + listView->viewport()->width() - listView->spacing() * 2, + 0); - if (this->model() && this->model()->rowCount()) - { - QObject::disconnect(this->model(), SIGNAL(layoutChanged()), - this , SLOT(itemsLayoutChanged())); + if (!proxyModel->rowCount() || !categories.contains(category)) + return QRect(); - rowsAboutToBeRemovedArtifficial(QModelIndex(), 0, - this->model()->rowCount() - 1); - } + QModelIndex index = proxyModel->index(0, 0, QModelIndex()); - d->modelSortCapable = (proxyModel != 0); - d->proxyModel = proxyModel; + int viewportWidth = listView->viewport()->width() - listView->spacing(); - // If the model was initialized before applying to the view, we update - // internal data structure of the view with the model information - if (model->rowCount()) + // We really need all items to be of same size. Otherwise we cannot do this + // (ereslibre) + // QSize itemSize = listView->sizeHintForIndex(index); + // int itemHeight = itemSize.height(); + // int itemWidth = itemSize.width(); + int itemHeight = 107; + int itemWidth = 130; + int itemWidthPlusSeparation = listView->spacing() + itemWidth; + int elementsPerRow = viewportWidth / itemWidthPlusSeparation; + + if (!elementsPerRow) + elementsPerRow++; + + float rows; + int rowsInt; + foreach (const QString &itCategory, categories) { - rowsInsertedArtifficial(QModelIndex(), 0, model->rowCount() - 1); + if (itCategory == category) + break; + + rows = (float) ((float) categoriesIndexes[itCategory].count() / + (float) elementsPerRow); + rowsInt = categoriesIndexes[itCategory].count() / elementsPerRow; + + if (rows - trunc(rows)) rowsInt++; + + retRect.setTop(retRect.top() + + (rowsInt * listView->spacing()) + + (rowsInt * itemHeight) + + 30 /* categoryHeight */ + + listView->spacing() * 2); } - QListView::setModel(model); + retRect.setHeight(30 /* categoryHeight */); - QObject::connect(model, SIGNAL(layoutChanged()), - this , SLOT(itemsLayoutChanged())); + return retRect; } -QRect KListView::visualRect(const QModelIndex &index) const +// We're sure elementsPosition doesn't contain index +const QRect &KListView::Private::cacheIndex(const QModelIndex &index) { - // FIXME: right to left languages (ereslibre) - // FIXME: drag & drop support (ereslibre) - // FIXME: do not forget to remove itemWidth's hard-coded values that were - // only for testing purposes. We might like to calculate the best - // width, but what we would really like for sure is that all items - // have the same width, as well as the same height (ereslibre) + QRect rect = visualRectInViewport(index); + elementsPosition[index] = rect; - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || - !d->itemCategorizer) - { - return QListView::visualRect(index); + return elementsPosition[index]; +} + +// We're sure categoriesPosition doesn't contain category +const QRect &KListView::Private::cacheCategory(const QString &category) +{ + QRect rect = visualCategoryRectInViewport(category); + categoriesPosition[category] = rect; + + return categoriesPosition[category]; +} + +const QRect &KListView::Private::cachedRectIndex(const QModelIndex &index) +{ + if (elementsPosition.contains(index)) // If we have it cached + { // return it + return elementsPosition[index]; + } + else // Otherwise, cache it + { // and return it + return cacheIndex(index); } +} - QRect retRect(spacing(), spacing(), 0, 0); - int viewportWidth = viewport()->width() - spacing(); - int dx = -horizontalOffset(); - int dy = -verticalOffset(); +const QRect &KListView::Private::cachedRectCategory(const QString &category) +{ + if (categoriesPosition.contains(category)) // If we have it cached + { // return it + return categoriesPosition[category]; + } + else // Otherwise, cache it and + { // return it + return cacheCategory(category); + } +} - if (verticalScrollBar() && !verticalScrollBar()->isHidden()) - viewportWidth -= verticalScrollBar()->width(); +QRect KListView::Private::visualRect(const QModelIndex &index) +{ + QModelIndex mappedIndex = proxyModel->mapToSource(index); - int itemHeight = sizeHintForIndex(index).height(); - int itemWidth = 130; // NOTE: ghosts in here ! - int itemWidthPlusSeparation = spacing() + itemWidth; - int elementsPerRow = viewportWidth / itemWidthPlusSeparation; - if (!elementsPerRow) - elementsPerRow++; - QModelIndex currentIndex = d->proxyModel->index(index.row(), 0); - QString itemCategory = d->itemCategorizer->categoryForItem(currentIndex, - d->proxyModel->sortRole()); - int naturalRow = index.row() / elementsPerRow; - int naturalTop = naturalRow * itemHeight + naturalRow * spacing(); + QRect retRect = cachedRectIndex(mappedIndex); + int dx = -listView->horizontalOffset(); + int dy = -listView->verticalOffset(); + retRect.adjust(dx, dy, dx, dy); + + return retRect; +} + +QRect KListView::Private::categoryVisualRect(const QString &category) +{ + QRect retRect = cachedRectCategory(category); + int dx = -listView->horizontalOffset(); + int dy = -listView->verticalOffset(); + retRect.adjust(dx, dy, dx, dy); - int rowsForCategory; - int lastIndexShown = -1; - foreach (QString category, d->categories) + return retRect; +} + +void KListView::Private::drawNewCategory(const QString &category, + const QStyleOption &option, + QPainter *painter) +{ + QColor color = option.palette.color(QPalette::Text); + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + QStyleOptionButton opt; + + opt.rect = option.rect; + opt.palette = option.palette; + opt.direction = option.direction; + opt.text = category; + + if (option.rect.contains(listView->viewport()->mapFromGlobal(QCursor::pos())) && + !mouseButtonPressed) { - retRect.setTop(retRect.top() + spacing()); + const QPalette::ColorGroup group = + option.state & QStyle::State_Enabled ? + QPalette::Normal : QPalette::Disabled; - if (category == itemCategory) - { - break; - } + QLinearGradient gradient(option.rect.topLeft(), + option.rect.bottomRight()); + gradient.setColorAt(0, + option.palette.color(group, + QPalette::Highlight).light()); + gradient.setColorAt(1, Qt::transparent); - rowsForCategory = (d->elementsPerCategory[category] / elementsPerRow); + painter->fillRect(option.rect, gradient); + } - if ((d->elementsPerCategory[category] % elementsPerRow) || - !rowsForCategory) + /*if (const KStyle *style = dynamic_cast<const KStyle*>(QApplication::style())) { - rowsForCategory++; + style->drawControl(KStyle::CE_Category, &opt, painter, this); } + else + {*/ + QFont painterFont = painter->font(); + painterFont.setWeight(QFont::Bold); + QFontMetrics metrics(painterFont); + painter->setFont(painterFont); + + QPainterPath path; + path.addRect(option.rect.left(), + option.rect.bottom() - 2, + option.rect.width(), + 2); + + QLinearGradient gradient(option.rect.topLeft(), + option.rect.bottomRight()); + gradient.setColorAt(0, color); + gradient.setColorAt(1, Qt::transparent); + + painter->setBrush(gradient); + painter->fillPath(path, gradient); + + painter->setPen(color); + + painter->drawText(option.rect, Qt::AlignVCenter | Qt::AlignLeft, + metrics.elidedText(category, Qt::ElideRight, option.rect.width())); + //} + painter->restore(); +} + + +void KListView::Private::updateScrollbars() +{ + int lastItemBottom = cachedRectIndex(lastIndex).bottom() + + listView->spacing() - listView->viewport()->height(); + listView->verticalScrollBar()->setRange(0, lastItemBottom); +} - lastIndexShown += d->elementsPerCategory[category]; - retRect.setTop(retRect.top() + categoryHeight(viewOptions()) + - (rowsForCategory * spacing() * 2) + - (rowsForCategory * itemHeight)); +//============================================================================== + + +KListView::KListView(QWidget *parent) + : QListView(parent) + , d(new Private(this)) +{ +} + +KListView::~KListView() +{ + delete d; +} + +void KListView::setModel(QAbstractItemModel *model) +{ + if (d->proxyModel) + { + QObject::disconnect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); + + QObject::disconnect(d->proxyModel, + SIGNAL(sortingRoleChanged()), + this, SLOT(slotSortingRoleChanged())); } - int rowToPosition = (index.row() - (lastIndexShown + 1)) / elementsPerRow; - int columnToPosition = (index.row() - (lastIndexShown + 1)) % - elementsPerRow; + QListView::setModel(model); - retRect.setTop(retRect.top() + (rowToPosition * spacing() * 2) + - (rowToPosition * itemHeight)); + d->proxyModel = dynamic_cast<KSortFilterProxyModel*>(model); - retRect.setLeft(retRect.left() + (columnToPosition * spacing()) + - (columnToPosition * itemWidth)); + if (d->proxyModel) + { + QObject::connect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); - retRect.setWidth(130); // NOTE: ghosts in here ! - retRect.setHeight(itemHeight); + QObject::connect(d->proxyModel, + SIGNAL(sortingRoleChanged()), + this, SLOT(slotSortingRoleChanged())); + } +} - retRect.adjust(dx, dy, dx, dy); +QRect KListView::visualRect(const QModelIndex &index) const +{ + if ((viewMode() == KListView::ListMode) || !d->proxyModel || + !d->itemCategorizer) + { + return QListView::visualRect(index); + } - return retRect; + if (!qobject_cast<const QSortFilterProxyModel*>(index.model())) + { + return d->visualRect(d->proxyModel->mapFromSource(index)); + } + + return d->visualRect(index); } KItemCategorizer *KListView::itemCategorizer() const @@ -195,73 +451,88 @@ KItemCategorizer *KListView::itemCategorizer() const void KListView::setItemCategorizer(KItemCategorizer *itemCategorizer) { + if (!itemCategorizer && d->proxyModel) + { + QObject::disconnect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); + + QObject::disconnect(d->proxyModel, + SIGNAL(sortingRoleChanged()), + this, SLOT(slotSortingRoleChanged())); + } + else if (itemCategorizer && d->proxyModel) + { + QObject::connect(d->proxyModel, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); + + QObject::connect(d->proxyModel, + SIGNAL(sortingRoleChanged()), + this, SLOT(slotSortingRoleChanged())); + } + d->itemCategorizer = itemCategorizer; if (itemCategorizer) - itemsLayoutChanged(); + { + rowsInserted(QModelIndex(), 0, d->proxyModel->rowCount() - 1); + } } QModelIndex KListView::indexAt(const QPoint &point) const { - QModelIndex index; - - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { return QListView::indexAt(point); } + QModelIndex index; + QModelIndexList item = d->intersectionSet(QRect(point, point)); if (item.count() == 1) + { index = item[0]; + } d->hovered = index; return index; } -int KListView::sizeHintForRow(int row) const +void KListView::reset() { - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + QListView::reset(); + + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { - return QListView::sizeHintForRow(row); + return; } - QModelIndex index = d->proxyModel->index(0, 0); - - if (!index.isValid()) - return 0; - - return sizeHintForIndex(index).height() + categoryHeight(viewOptions()) + - spacing(); -} - -void KListView::drawNewCategory(const QString &category, - const QStyleOptionViewItem &option, - QPainter *painter) -{ - painter->drawText(option.rect.topLeft(), category); -} - -int KListView::categoryHeight(const QStyleOptionViewItem &option) const -{ - return option.fontMetrics.height(); + d->elementsInfo.clear(); + d->elementsPosition.clear(); + d->elementDictionary.clear(); + d->categoriesIndexes.clear(); + d->categoriesPosition.clear(); + d->categories.clear(); + d->intersectedIndexes.clear(); + d->sourceModelIndexList.clear(); + d->hovered = QModelIndex(); + d->mouseButtonPressed = false; } void KListView::paintEvent(QPaintEvent *event) { - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { QListView::paintEvent(event); return; } - if (!itemDelegate()) - return; - QStyleOptionViewItemV3 option = viewOptions(); QPainter painter(viewport()); QRect area = event->rect(); @@ -270,16 +541,19 @@ void KListView::paintEvent(QPaintEvent *event) const QStyle::State state = option.state; const bool enabled = (state & QStyle::State_Enabled) != 0; - int totalHeight = 0; - QModelIndex index; - QString prevCategory; + painter.save(); + QModelIndexList dirtyIndexes = d->intersectionSet(area); foreach (const QModelIndex &index, dirtyIndexes) { option.state = state; - option.rect = visualRect(index); + option.rect = d->visualRect(index); + if (selectionModel() && selectionModel()->isSelected(index)) + { option.state |= QStyle::State_Selected; + } + if (enabled) { QPalette::ColorGroup cg; @@ -294,6 +568,7 @@ void KListView::paintEvent(QPaintEvent *event) } option.palette.setCurrentColorGroup(cg); } + if (focus && currentIndex() == index) { option.state |= QStyle::State_HasFocus; @@ -301,149 +576,387 @@ void KListView::paintEvent(QPaintEvent *event) option.state |= QStyle::State_Editing; } - if (index == d->hovered) + if ((index == d->hovered) && !d->mouseButtonPressed) option.state |= QStyle::State_MouseOver; else option.state &= ~QStyle::State_MouseOver; - if (prevCategory != d->itemCategorizer->categoryForItem(index, - d->proxyModel->sortRole())) + itemDelegate(index)->paint(&painter, option, index); + } + + // Redraw categories + QStyleOptionViewItem otherOption; + foreach (const QString &category, d->categories) + { + otherOption = option; + otherOption.rect = d->categoryVisualRect(category); + + if (otherOption.rect.intersects(area)) { - prevCategory = d->itemCategorizer->categoryForItem(index, - d->proxyModel->sortRole()); - drawNewCategory(prevCategory, option, &painter); + d->drawNewCategory(category, otherOption, &painter); } - itemDelegate(index)->paint(&painter, option, index); } + + if (d->mouseButtonPressed) + { + QPoint start, end, initialPressPosition; + + initialPressPosition = d->initialPressPosition; + + initialPressPosition.setY(initialPressPosition.y() - verticalOffset()); + initialPressPosition.setX(initialPressPosition.x() - horizontalOffset()); + + if (d->initialPressPosition.x() > d->mousePosition.x() || + d->initialPressPosition.y() > d->mousePosition.y()) + { + start = d->mousePosition; + end = initialPressPosition; + } + else + { + start = initialPressPosition; + end = d->mousePosition; + } + + QStyleOptionRubberBand yetAnotherOption; + yetAnotherOption.initFrom(this); + yetAnotherOption.shape = QRubberBand::Rectangle; + yetAnotherOption.opaque = false; + yetAnotherOption.rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16)); + painter.save(); + style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter); + painter.restore(); + } + + painter.restore(); } -void KListView::setSelection(const QRect &rect, - QItemSelectionModel::SelectionFlags flags) +void KListView::resizeEvent(QResizeEvent *event) { - // TODO: implement me + QListView::resizeEvent(event); - QListView::setSelection(rect, flags); + if ((viewMode() == KListView::ListMode) || !d->proxyModel || + !d->itemCategorizer) + { + return; + } - /*if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + // Clear the items positions cache + d->elementsPosition.clear(); + d->categoriesPosition.clear(); + + d->updateScrollbars(); +} + +void KListView::setSelection(const QRect &rect, + QItemSelectionModel::SelectionFlags flags) +{ + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { QListView::setSelection(rect, flags); return; } - QModelIndex index; - for (int i = 0; i < d->proxyModel->rowCount(); i++) + // FIXME: I have to rethink and rewrite this method (ereslibre) + + QModelIndexList dirtyIndexes = d->intersectionSet(rect); + foreach (const QModelIndex &index, dirtyIndexes) { - index = d->proxyModel->index(i, 0); - if (rect.intersects(visualRect(index))) + if (!d->mouseButtonPressed && rect.intersects(visualRect(index))) { - selectionModel()->select(index, QItemSelectionModel::Select); + selectionModel()->select(index, flags); } else { - selectionModel()->select(index, QItemSelectionModel::Deselect); + selectionModel()->select(index, QItemSelectionModel::Select); + + if (d->mouseButtonPressed) + d->tempSelected.append(index); } - }*/ + } + + if (d->mouseButtonPressed) + { + foreach (const QModelIndex &index, selectionModel()->selectedIndexes()) + { + if (!rect.intersects(visualRect(index))) + { + selectionModel()->select(index, QItemSelectionModel::Deselect); - //selectionModel()->select(selection, flags); + if (d->mouseButtonPressed) + { + d->tempSelected.removeAll(index); + } + } + } + } } -void KListView::timerEvent(QTimerEvent *event) +void KListView::mouseMoveEvent(QMouseEvent *event) { - QListView::timerEvent(event); + QListView::mouseMoveEvent(event); + + d->mousePosition = event->pos(); - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { return; } -} -void KListView::rowsInserted(const QModelIndex &parent, - int start, - int end) -{ - QListView::rowsInserted(parent, start, end); - rowsInsertedArtifficial(parent, start, end); + event->accept(); + + viewport()->update(); } -void KListView::rowsAboutToBeRemoved(const QModelIndex &parent, - int start, - int end) +void KListView::mousePressEvent(QMouseEvent *event) { - QListView::rowsAboutToBeRemoved(parent, start, end); - rowsAboutToBeRemovedArtifficial(parent, start, end); + QListView::mousePressEvent(event); + + d->tempSelected.clear(); + + if ((viewMode() == KListView::ListMode) || !d->proxyModel || + !d->itemCategorizer) + { + return; + } + + event->accept(); + + if (event->button() == Qt::LeftButton) + { + d->mouseButtonPressed = true; + + d->initialPressPosition = event->pos(); + d->initialPressPosition.setY(d->initialPressPosition.y() + + verticalOffset()); + d->initialPressPosition.setX(d->initialPressPosition.x() + + horizontalOffset()); + } + + viewport()->update(); } -void KListView::rowsInsertedArtifficial(const QModelIndex &parent, - int start, - int end) +void KListView::mouseReleaseEvent(QMouseEvent *event) { - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + QListView::mouseReleaseEvent(event); + + d->mouseButtonPressed = false; + + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { return; } - QString category; - QModelIndex index; - for (int i = start; i <= end; i++) - { - index = d->proxyModel->index(i, 0, parent); - category = d->itemCategorizer->categoryForItem(index, - d->proxyModel->sortRole()); + event->accept(); - if (d->elementsPerCategory.contains(category)) - d->elementsPerCategory[category]++; - else + // FIXME: I have to rethink and rewrite this method (ereslibre) + + QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos()); + initialPressPosition.setY(initialPressPosition.y() + verticalOffset()); + initialPressPosition.setX(initialPressPosition.x() + horizontalOffset()); + + if (initialPressPosition == d->initialPressPosition) + { + foreach(const QString &category, d->categories) { - d->elementsPerCategory.insert(category, 1); - d->categories.append(category); + if (d->categoryVisualRect(category).contains(event->pos())) + { + QModelIndex index; + QItemSelectionModel::SelectionFlag flag; + foreach (const QModelIndex &mappedIndex, + d->categoriesIndexes[category]) + { + index = d->proxyModel->mapFromSource(mappedIndex); + + if (selectionModel()->selectedIndexes().contains(index)) + { + flag = QItemSelectionModel::Deselect; + } + else + { + flag = QItemSelectionModel::Select; + } + + selectionModel()->select(index, flag); + } + } } } + + viewport()->update(); } -void KListView::rowsAboutToBeRemovedArtifficial(const QModelIndex &parent, - int start, - int end) +void KListView::leaveEvent(QEvent *event) { - if ((viewMode() == KListView::ListMode) || !d->modelSortCapable || + QListView::leaveEvent(event); + + d->hovered = QModelIndex(); + + if ((viewMode() == KListView::ListMode) || !d->proxyModel || !d->itemCategorizer) { return; } + event->accept(); + + viewport()->update(); +} + +void KListView::rowsInserted(const QModelIndex &parent, + int start, + int end) +{ + QListView::rowsInserted(parent, start, end); + + if ((viewMode() == KListView::ListMode) || !d->proxyModel || + !d->itemCategorizer) + { + return; + } + + rowsInsertedArtifficial(parent, start, end); +} + +void KListView::rowsInsertedArtifficial(const QModelIndex &parent, + int start, + int end) +{ + d->elementsInfo.clear(); + d->elementsPosition.clear(); + d->elementDictionary.clear(); + d->categoriesIndexes.clear(); + d->categoriesPosition.clear(); + d->categories.clear(); + d->intersectedIndexes.clear(); + d->sourceModelIndexList.clear(); d->hovered = QModelIndex(); + d->mouseButtonPressed = false; - QString category; - QModelIndex index; - for (int i = start; i <= end; i++) + if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount()) { - index = d->proxyModel->index(i, 0, parent); - category = d->itemCategorizer->categoryForItem(index, + return; + } + + // Add all elements mapped to the source model + for (int i = 0; i < d->proxyModel->rowCount(); i++) + { + d->sourceModelIndexList << + d->proxyModel->mapToSource(d->proxyModel->index(i, 0)); + } + + // 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()); - if (d->elementsPerCategory.contains(category)) + elementInfo.category = lastCategory; + + if (prevCategory != lastCategory) { - d->elementsPerCategory[category]--; + d->categoriesIndexes.insert(prevCategory, modelIndexList); + d->categories << prevCategory; + modelIndexList.clear(); + } - if (!d->elementsPerCategory[category]) - { - d->elementsPerCategory.remove(category); - d->categories.removeAll(category); - } + modelIndexList << index; + prevCategory = lastCategory; + + d->elementsInfo.insert(index, 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)); + + i++; + j++; } + + i = 0; } + + d->updateScrollbars(); } -void KListView::itemsLayoutChanged() +void KListView::rowsRemoved(const QModelIndex &parent, + int start, + int end) { - d->elementsPerCategory.clear(); - d->categories.clear(); + if (d->proxyModel) + { + // Force the view to update all elements + rowsInsertedArtifficial(parent, start, end); + } +} + +void KListView::updateGeometries() +{ + if ((viewMode() == KListView::ListMode) || !d->proxyModel || + !d->itemCategorizer) + { + QListView::updateGeometries(); + return; + } - if (d->proxyModel && d->proxyModel->rowCount()) - rowsInsertedArtifficial(QModelIndex(), 0, - d->proxyModel->rowCount() - 1); + // Avoid QListView::updateGeometries(), since it will try to set another + // range to our scroll bars, what we don't want (ereslibre) + QAbstractItemView::updateGeometries(); +} + +void KListView::slotSortingRoleChanged() +{ + if (d->proxyModel) + { + // Force the view to update all elements + rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - + 1); + } } #include "klistview.moc" |
