┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Fernández López <[email protected]>2007-07-06 17:41:04 +0000
committerRafael Fernández López <[email protected]>2007-07-06 17:41:04 +0000
commit98d01c5e23e9f9dc6b6a1071e4c6e6bd740dbab3 (patch)
treee2fde20a54d0a3cf38ee724aa8a8641faea409fe
parent0661c42dd9893c67a7ceaea6585df2cf7aa3a440 (diff)
Make KListView capable of drawing categories on our own way. This make things easier
when we are trying to customize it. We can also benefit from KStyle if some day it supports category drawing. KListView keyboard navigation. Tricier than I thought. Pending renaming to KCategorizedView. Seems a good name. svn path=/trunk/KDE/kdebase/apps/; revision=684478
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/dolphiniconsview.cpp14
-rw-r--r--src/dolphinitemcategorizer.cpp187
-rw-r--r--src/dolphinitemcategorizer.h5
-rw-r--r--src/kitemcategorizer.cpp100
-rw-r--r--src/kitemcategorizer.h43
-rw-r--r--src/klistview.cpp352
-rw-r--r--src/klistview.h18
-rw-r--r--src/klistview_p.h11
-rw-r--r--src/ksortfilterproxymodel.h7
10 files changed, 593 insertions, 145 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9bc0665c2..6b3b5e9ee 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,6 +17,7 @@ set(dolphinprivate_LIB_SRCS
dolphinitemcategorizer.cpp
klistview.cpp
ksortfilterproxymodel.cpp
+ kitemcategorizer.cpp
dolphinsettings.cpp
viewproperties.cpp
dolphinsortfilterproxymodel.cpp
diff --git a/src/dolphiniconsview.cpp b/src/dolphiniconsview.cpp
index bac7b5e4b..968d7c94c 100644
--- a/src/dolphiniconsview.cpp
+++ b/src/dolphiniconsview.cpp
@@ -147,12 +147,14 @@ void DolphinIconsView::dragMoveEvent(QDragMoveEvent* event)
void DolphinIconsView::dropEvent(QDropEvent* event)
{
- const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
- if (!urls.isEmpty()) {
- m_controller->indicateDroppedUrls(urls,
- indexAt(event->pos()),
- event->source());
- event->acceptProposedAction();
+ if (!selectionModel()->isSelected(indexAt(event->pos()))) {
+ const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
+ if (!urls.isEmpty()) {
+ m_controller->indicateDroppedUrls(urls,
+ indexAt(event->pos()),
+ event->source());
+ event->acceptProposedAction();
+ }
}
KListView::dropEvent(event);
m_dragging = false;
diff --git a/src/dolphinitemcategorizer.cpp b/src/dolphinitemcategorizer.cpp
index dfb384062..a4c84736a 100644
--- a/src/dolphinitemcategorizer.cpp
+++ b/src/dolphinitemcategorizer.cpp
@@ -32,11 +32,18 @@
#include <kdatetime.h>
#include <kdirmodel.h>
#include <kfileitem.h>
+#include <kiconloader.h>
#include <klocale.h>
#include <kurl.h>
+#include <kuser.h>
+#include <kmimetype.h>
+#include <kstandarddirs.h>
+#include <kpixmapeffect.h>
#include <QList>
#include <QSortFilterProxyModel>
+#include <QPainter>
+#include <QDir>
DolphinItemCategorizer::DolphinItemCategorizer() :
KItemCategorizer()
@@ -48,7 +55,7 @@ DolphinItemCategorizer::~DolphinItemCategorizer()
}
QString DolphinItemCategorizer::categoryForItem(const QModelIndex& index,
- int sortRole)
+ int sortRole) const
{
QString retString;
@@ -167,11 +174,8 @@ QString DolphinItemCategorizer::categoryForItem(const QModelIndex& index,
#ifdef HAVE_NEPOMUK
case DolphinView::SortByRating: {
const quint32 rating = DolphinSortFilterProxyModel::ratingForIndex(index);
- if (rating) {
- retString = i18np("1 star", "%1 stars", rating);
- } else {
- retString = i18n("Not yet rated");
- }
+
+ retString = QString::number(rating);
break;
}
@@ -188,3 +192,174 @@ QString DolphinItemCategorizer::categoryForItem(const QModelIndex& index,
return retString;
}
+
+void DolphinItemCategorizer::drawCategory(const QModelIndex &index,
+ int sortRole,
+ const QStyleOption &option,
+ QPainter *painter) const
+{
+ QRect starRect = option.rect;
+ int iconSize = KIconLoader::global()->theme()->defaultSize(K3Icon::Small);
+
+ const QString category = categoryForItem(index, sortRole);
+
+ 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.state & QStyle::State_MouseOver)
+ {
+ const QPalette::ColorGroup group =
+ option.state & QStyle::State_Enabled ?
+ QPalette::Normal : QPalette::Disabled;
+
+ QLinearGradient gradient(option.rect.topLeft(),
+ option.rect.bottomRight());
+ gradient.setColorAt(0,
+ option.palette.color(group,
+ QPalette::Highlight).light());
+ gradient.setColorAt(1, Qt::transparent);
+
+ painter->fillRect(option.rect, gradient);
+ }
+
+ 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);
+
+ opt.rect.setLeft(opt.rect.left() + (iconSize / 4));
+ starRect.setLeft(starRect.left() + (iconSize / 4));
+ starRect.setRight(starRect.right() + (iconSize / 4));
+
+ bool paintIcon = true;
+ bool paintText = true;
+
+ QPixmap icon;
+ switch (sortRole) {
+ case DolphinView::SortByName:
+ paintIcon = false;
+ break;
+
+ case DolphinView::SortByDate:
+ paintIcon = false;
+ break;
+
+ case DolphinView::SortByPermissions:
+ paintIcon = false; // FIXME: let's think about how to represent permissions
+ break;
+
+ case DolphinView::SortByOwner: {
+ opt.rect.setTop(option.rect.top() + (iconSize / 4));
+ KUser user(category);
+ icon = QPixmap::fromImage(QImage(user.homeDir() + QDir::separator() + ".face.icon")).scaled(iconSize, iconSize);
+ break;
+ }
+
+ case DolphinView::SortByGroup:
+ paintIcon = false;
+ break;
+
+ case DolphinView::SortBySize:
+ paintIcon = false;
+ break;
+
+ case DolphinView::SortByType: {
+ opt.rect.setTop(option.rect.top() + (option.rect.height() / 2) - (iconSize / 2));
+ const KDirModel *model = static_cast<const KDirModel*>(index.model());
+ KFileItem *item = model->itemForIndex(index);
+ icon = KIconLoader::global()->loadIcon(KMimeType::iconNameForUrl(item->url()),
+ K3Icon::Small);
+ break;
+ }
+
+#ifdef HAVE_NEPOMUK
+ case DolphinView::SortByRating: {
+ paintText = false;
+ paintIcon = false;
+
+ starRect.setTop(option.rect.top() + (option.rect.height() / 2) - (iconSize / 2));
+ starRect.setSize(QSize(iconSize, iconSize));
+
+ QPixmap pixmap = KIconLoader::global()->loadIcon("rating", K3Icon::Small);
+ QPixmap smallPixmap = KIconLoader::global()->loadIcon("rating", K3Icon::NoGroup, iconSize / 2);
+ QPixmap disabledPixmap = KIconLoader::global()->loadIcon("rating", K3Icon::Small);
+
+ KPixmapEffect::toGray(disabledPixmap, false);
+
+ int rating = category.toInt();
+
+ for (int i = 0; i < rating - (rating % 2); i += 2) {
+ painter->drawPixmap(starRect, pixmap);
+ starRect.setLeft(starRect.left() + iconSize + (iconSize / 4) /* separator between stars */);
+ }
+
+ if (rating && rating % 2) {
+ starRect.setTop(option.rect.top() + (option.rect.height() / 2) - (iconSize / 4));
+ starRect.setSize(QSize(iconSize / 2, iconSize / 2));
+ painter->drawPixmap(starRect, smallPixmap);
+ starRect.setTop(opt.rect.top() + (option.rect.height() / 2) - (iconSize / 2));
+ starRect.setSize(QSize(iconSize / 2, iconSize / 2));
+ starRect.setLeft(starRect.left() + (iconSize / 2) + (iconSize / 4));
+ starRect.setSize(QSize(iconSize, iconSize));
+ }
+
+ for (int i = rating; i < 9; i += 2) {
+ painter->drawPixmap(starRect, disabledPixmap);
+ starRect.setLeft(starRect.left() + iconSize + (iconSize / 4));
+ }
+
+ break;
+ }
+
+ case DolphinView::SortByTags:
+ paintIcon = false;
+ break;
+#endif
+ }
+
+ if (paintIcon) {
+ painter->drawPixmap(QRect(opt.rect.left(), opt.rect.top(), iconSize, iconSize), icon);
+ opt.rect.setLeft(opt.rect.left() + iconSize + (iconSize / 4));
+ }
+
+ if (paintText) {
+ opt.rect.setTop(option.rect.top() + (iconSize / 4));
+ opt.rect.setBottom(opt.rect.bottom() - 2);
+ painter->setPen(color);
+
+ painter->drawText(opt.rect, Qt::AlignVCenter | Qt::AlignLeft,
+ metrics.elidedText(category, Qt::ElideRight, opt.rect.width()));
+ }
+
+ painter->restore();
+}
+
+int DolphinItemCategorizer::categoryHeight(const QStyleOption &option) const
+{
+ int iconSize = KIconLoader::global()->theme()->defaultSize(K3Icon::Small);
+
+ return qMax(option.fontMetrics.height() + (iconSize / 4) * 2 + 2, iconSize + (iconSize / 4) * 2 + 2) /* 2 gradient */;
+}
diff --git a/src/dolphinitemcategorizer.h b/src/dolphinitemcategorizer.h
index 6be060bd0..65a7340ac 100644
--- a/src/dolphinitemcategorizer.h
+++ b/src/dolphinitemcategorizer.h
@@ -31,7 +31,10 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinItemCategorizer : public KItemCategorizer
public:
DolphinItemCategorizer();
virtual ~DolphinItemCategorizer();
- virtual QString categoryForItem(const QModelIndex &index, int sortRole);
+ virtual QString categoryForItem(const QModelIndex &index, int sortRole) const;
+ virtual void drawCategory(const QModelIndex &index, int sortRole,
+ const QStyleOption &option, QPainter *painter) const;
+ virtual int categoryHeight(const QStyleOption &option) const;
};
#endif // DOLPHINITEMCATEGORIZER_H
diff --git a/src/kitemcategorizer.cpp b/src/kitemcategorizer.cpp
new file mode 100644
index 000000000..93969c3a8
--- /dev/null
+++ b/src/kitemcategorizer.cpp
@@ -0,0 +1,100 @@
+/**
+ * This file is part of the KDE project
+ * Copyright (C) 2007 Rafael Fernández López <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "kitemcategorizer.h"
+
+#include <QPainter>
+#include <QStyleOption>
+
+KItemCategorizer::KItemCategorizer()
+{
+}
+
+KItemCategorizer::~KItemCategorizer()
+{
+}
+
+void KItemCategorizer::drawCategory(const QModelIndex &index,
+ int sortRole,
+ const QStyleOption &option,
+ QPainter *painter) const
+{
+ const QString category = categoryForItem(index, sortRole);
+
+ 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.state & QStyle::State_MouseOver)
+ {
+ const QPalette::ColorGroup group =
+ option.state & QStyle::State_Enabled ?
+ QPalette::Normal : QPalette::Disabled;
+
+ QLinearGradient gradient(option.rect.topLeft(),
+ option.rect.bottomRight());
+ gradient.setColorAt(0,
+ option.palette.color(group,
+ QPalette::Highlight).light());
+ gradient.setColorAt(1, Qt::transparent);
+
+ painter->fillRect(option.rect, gradient);
+ }
+
+ QFont painterFont = painter->font();
+ painterFont.setWeight(QFont::Bold);
+ painterFont.setPointSize(painterFont.pointSize() + 2);
+ 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();
+}
+
+int KItemCategorizer::categoryHeight(const QStyleOption &option) const
+{
+ return option.fontMetrics.height() + 6 /* 4 separator; 2 gradient */;
+}
diff --git a/src/kitemcategorizer.h b/src/kitemcategorizer.h
index 5c6531a1d..aff048e29 100644
--- a/src/kitemcategorizer.h
+++ b/src/kitemcategorizer.h
@@ -24,20 +24,49 @@
#include <libdolphin_export.h>
class QString;
+class QPainter;
class QModelIndex;
+class QStyleOption;
+/**
+ * @short Class for item categorizing on KListView
+ *
+ * This class is meant to be used with KListView class. Its purpose is
+ * to decide to which category belongs a given index with the given role.
+ * Additionally it will let you to customize the way categories are drawn,
+ * only in the case that you want to do so
+ *
+ * @see KListView
+ *
+ * @author Rafael Fernández López <[email protected]>
+ */
class LIBDOLPHINPRIVATE_EXPORT KItemCategorizer
{
public:
- KItemCategorizer()
- {
- }
+ KItemCategorizer();
+
+ virtual ~KItemCategorizer();
+
+ /**
+ * This method will return the category where @param index fit on with the
+ * given @param sortRole role
+ */
+ virtual QString categoryForItem(const QModelIndex &index,
+ int sortRole) const = 0;
- virtual ~KItemCategorizer()
- {
- }
+ /**
+ * This method purpose is to draw a category represented by the given
+ * @param index with the given @param sortRole sorting role
+ *
+ * @note This method will be called one time per category, always with the
+ * first element in that category
+ */
+ virtual void drawCategory(const QModelIndex &index,
+ int sortRole,
+ const QStyleOption &option,
+ QPainter *painter) const;
- virtual QString categoryForItem(const QModelIndex &index, int sortRole) = 0;
+ virtual int categoryHeight(const QStyleOption &option) const;
};
#endif // KITEMCATEGORIZER_H
diff --git a/src/klistview.cpp b/src/klistview.cpp
index e3ea89e41..4db18e1f4 100644
--- a/src/klistview.cpp
+++ b/src/klistview.cpp
@@ -146,7 +146,7 @@ QRect KListView::Private::visualRectInViewport(const QModelIndex &index) const
QString curCategory = elementsInfo[index].category;
QRect retRect(listView->spacing(), listView->spacing() * 2 +
- 30 /* categoryHeight */, 0, 0);
+ itemCategorizer->categoryHeight(listView->viewOptions()), 0, 0);
int viewportWidth = listView->viewport()->width() - listView->spacing();
@@ -183,7 +183,7 @@ QRect KListView::Private::visualRectInViewport(const QModelIndex &index) const
retRect.setTop(retRect.top() +
(rowsInt * listView->spacing()) +
(rowsInt * itemHeight) +
- 30 /* categoryHeight */ +
+ itemCategorizer->categoryHeight(listView->viewOptions()) +
listView->spacing() * 2);
}
@@ -238,11 +238,11 @@ QRect KListView::Private::visualCategoryRectInViewport(const QString &category)
retRect.setTop(retRect.top() +
(rowsInt * listView->spacing()) +
(rowsInt * itemHeight) +
- 30 /* categoryHeight */ +
+ itemCategorizer->categoryHeight(listView->viewOptions()) +
listView->spacing() * 2);
}
- retRect.setHeight(30 /* categoryHeight */);
+ retRect.setHeight(itemCategorizer->categoryHeight(listView->viewOptions()));
return retRect;
}
@@ -311,69 +311,23 @@ QRect KListView::Private::categoryVisualRect(const QString &category)
return retRect;
}
-void KListView::Private::drawNewCategory(const QString &category,
+void KListView::Private::drawNewCategory(const QModelIndex &index,
+ int sortRole,
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;
+ QStyleOption optionCopy = option;
+ const QString category = itemCategorizer->categoryForItem(index, sortRole);
if ((category == hoveredCategory) && !mouseButtonPressed)
{
- const QPalette::ColorGroup group =
- option.state & QStyle::State_Enabled ?
- QPalette::Normal : QPalette::Disabled;
-
- QLinearGradient gradient(option.rect.topLeft(),
- option.rect.bottomRight());
- gradient.setColorAt(0,
- option.palette.color(group,
- QPalette::Highlight).light());
- gradient.setColorAt(1, Qt::transparent);
-
- painter->fillRect(option.rect, gradient);
+ optionCopy.state |= QStyle::State_MouseOver;
}
- /*if (const KStyle *style = dynamic_cast<const KStyle*>(QApplication::style()))
- {
- 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();
+ itemCategorizer->drawCategory(index,
+ sortRole,
+ optionCopy,
+ painter);
}
@@ -424,11 +378,9 @@ void KListView::Private::drawDraggedItems()
}
}
- listView->viewport()->update(lastDraggedItemsRect);
+ listView->viewport()->update(lastDraggedItemsRect.united(rectToUpdate));
lastDraggedItemsRect = rectToUpdate;
-
- listView->viewport()->update(rectToUpdate);
}
@@ -448,6 +400,21 @@ KListView::~KListView()
void KListView::setModel(QAbstractItemModel *model)
{
+ 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->hovered = QModelIndex();
+ d->mouseButtonPressed = false;
+
if (d->proxyModel)
{
QObject::disconnect(d->proxyModel,
@@ -498,6 +465,21 @@ KItemCategorizer *KListView::itemCategorizer() const
void KListView::setItemCategorizer(KItemCategorizer *itemCategorizer)
{
+ 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->hovered = QModelIndex();
+ d->mouseButtonPressed = false;
+
if (!itemCategorizer && d->proxyModel)
{
QObject::disconnect(d->proxyModel,
@@ -557,15 +539,13 @@ void KListView::reset()
{
QListView::reset();
- if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
- !d->itemCategorizer)
- {
- return;
- }
-
+ 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();
@@ -635,6 +615,22 @@ void KListView::paintEvent(QPaintEvent *event)
itemDelegate(index)->paint(&painter, option, index);
}
+ // Redraw categories
+ int i = 0;
+ QStyleOptionViewItem otherOption;
+ foreach (const QString &category, d->categories)
+ {
+ otherOption = option;
+ otherOption.rect = d->categoryVisualRect(category);
+ otherOption.state &= ~QStyle::State_MouseOver;
+
+ if (otherOption.rect.intersects(area))
+ {
+ d->drawNewCategory(d->categoriesIndexes[category][0],
+ d->proxyModel->sortRole(), otherOption, &painter);
+ }
+ }
+
if (d->mouseButtonPressed && !d->isDragging)
{
QPoint start, end, initialPressPosition;
@@ -666,19 +662,6 @@ void KListView::paintEvent(QPaintEvent *event)
painter.restore();
}
- // Redraw categories
- QStyleOptionViewItem otherOption;
- foreach (const QString &category, d->categories)
- {
- otherOption = option;
- otherOption.rect = d->categoryVisualRect(category);
-
- if (otherOption.rect.intersects(area))
- {
- d->drawNewCategory(category, otherOption, &painter);
- }
- }
-
if (d->isDragging && !d->dragLeftViewport)
{
painter.setOpacity(0.5);
@@ -692,16 +675,17 @@ void KListView::resizeEvent(QResizeEvent *event)
{
QListView::resizeEvent(event);
+ // Clear the items positions cache
+ d->elementsPosition.clear();
+ d->categoriesPosition.clear();
+ d->forcedSelectionPosition = 0;
+
if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
!d->itemCategorizer)
{
return;
}
- // Clear the items positions cache
- d->elementsPosition.clear();
- d->categoriesPosition.clear();
-
d->updateScrollbars();
}
@@ -726,6 +710,7 @@ void KListView::setSelection(const QRect &rect,
}
QModelIndexList dirtyIndexes = d->intersectionSet(rect);
+
QItemSelection selection;
if (!dirtyIndexes.count())
@@ -741,6 +726,7 @@ void KListView::setSelection(const QRect &rect,
if (!d->mouseButtonPressed)
{
selection = QItemSelection(dirtyIndexes[0], dirtyIndexes[0]);
+ d->currentViewIndex = dirtyIndexes[0];
}
else
{
@@ -786,6 +772,8 @@ void KListView::mouseMoveEvent(QMouseEvent *event)
return;
}
+ const QString previousHoveredCategory = d->hoveredCategory;
+
d->mousePosition = event->pos();
d->hoveredCategory = QString();
@@ -795,9 +783,13 @@ void KListView::mouseMoveEvent(QMouseEvent *event)
if (d->categoryVisualRect(category).intersects(QRect(event->pos(), event->pos())))
{
d->hoveredCategory = category;
+ viewport()->update(d->categoryVisualRect(category));
+ }
+ else if ((category == previousHoveredCategory) &&
+ (!d->categoryVisualRect(previousHoveredCategory).intersects(QRect(event->pos(), event->pos()))))
+ {
+ viewport()->update(d->categoryVisualRect(category));
}
-
- viewport()->update(d->categoryVisualRect(category));
}
QRect rect;
@@ -822,11 +814,9 @@ void KListView::mouseMoveEvent(QMouseEvent *event)
end = d->mousePosition;
}
- viewport()->update(d->lastSelectionRect);
-
rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
- viewport()->update(rect);
+ //viewport()->update(rect.united(d->lastSelectionRect));
d->lastSelectionRect = rect;
}
@@ -834,14 +824,6 @@ void KListView::mouseMoveEvent(QMouseEvent *event)
void KListView::mousePressEvent(QMouseEvent *event)
{
- QListView::mousePressEvent(event);
-
- if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
- !d->itemCategorizer)
- {
- return;
- }
-
d->dragLeftViewport = false;
if (event->button() == Qt::LeftButton)
@@ -854,10 +836,14 @@ void KListView::mousePressEvent(QMouseEvent *event)
d->initialPressPosition.setX(d->initialPressPosition.x() +
horizontalOffset());
}
+
+ QListView::mousePressEvent(event);
}
void KListView::mouseReleaseEvent(QMouseEvent *event)
{
+ d->mouseButtonPressed = false;
+
QListView::mouseReleaseEvent(event);
if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
@@ -866,8 +852,6 @@ void KListView::mouseReleaseEvent(QMouseEvent *event)
return;
}
- d->mouseButtonPressed = false;
-
QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos());
initialPressPosition.setY(initialPressPosition.y() + verticalOffset());
initialPressPosition.setX(initialPressPosition.x() + horizontalOffset());
@@ -902,16 +886,10 @@ void KListView::mouseReleaseEvent(QMouseEvent *event)
void KListView::leaveEvent(QEvent *event)
{
- QListView::leaveEvent(event);
-
- if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
- !d->itemCategorizer)
- {
- return;
- }
-
d->hovered = QModelIndex();
d->hoveredCategory = QString();
+
+ QListView::leaveEvent(event);
}
void KListView::startDrag(Qt::DropActions supportedActions)
@@ -920,17 +898,12 @@ void KListView::startDrag(Qt::DropActions supportedActions)
d->isDragging = false;
d->mouseButtonPressed = false;
+
+ viewport()->update(d->lastDraggedItemsRect);
}
void KListView::dragMoveEvent(QDragMoveEvent *event)
{
- if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
- !d->itemCategorizer)
- {
- QListView::dragMoveEvent(event);
- return;
- }
-
d->mousePosition = event->pos();
if (d->mouseButtonPressed)
@@ -944,20 +917,21 @@ void KListView::dragMoveEvent(QDragMoveEvent *event)
d->dragLeftViewport = false;
- d->drawDraggedItems();
-}
-
-void KListView::dragLeaveEvent(QDragLeaveEvent *event)
-{
- QListView::dragLeaveEvent(event);
-
if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
!d->itemCategorizer)
{
+ QListView::dragMoveEvent(event);
return;
}
+ d->drawDraggedItems();
+}
+
+void KListView::dragLeaveEvent(QDragLeaveEvent *event)
+{
d->dragLeftViewport = true;
+
+ QListView::dragLeaveEvent(event);
}
QModelIndex KListView::moveCursor(CursorAction cursorAction,
@@ -969,6 +943,119 @@ QModelIndex KListView::moveCursor(CursorAction cursorAction,
return QListView::moveCursor(cursorAction, modifiers);
}
+ const QModelIndex current = selectionModel()->currentIndex();
+
+ int viewportWidth = viewport()->width() - spacing();
+ // 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 = spacing() + itemWidth;
+ int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
+
+ QString lastCategory = d->categories[0];
+ QString theCategory = d->categories[0];
+ QString afterCategory = d->categories[0];
+ bool hasToBreak = false;
+ foreach (const QString &category, d->categories)
+ {
+ if (hasToBreak)
+ {
+ afterCategory = category;
+
+ break;
+ }
+
+ if (category == d->elementsInfo[d->proxyModel->mapToSource(current)].category)
+ {
+ theCategory = category;
+
+ hasToBreak = true;
+ }
+
+ if (!hasToBreak)
+ {
+ lastCategory = category;
+ }
+ }
+
+ switch (cursorAction)
+ {
+ case QAbstractItemView::MoveUp: {
+ if (d->elementsInfo[d->proxyModel->mapToSource(current)].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));
+
+ return d->elementDictionary[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;
+
+ if (d->forcedSelectionPosition >= lastCategoryLastRow)
+ {
+ indexToMove -= 1;
+ }
+ else
+ {
+ indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1), d->forcedSelectionPosition + elementsPerRow + 1);
+ }
+
+ return d->elementDictionary[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)))
+ {
+ int indexToMove = d->invertedElementDictionary[current].row();
+ indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[d->proxyModel->mapToSource(current)].relativeOffsetToCategory);
+
+ return d->elementDictionary[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);
+
+ if (d->forcedSelectionPosition >= afterCategoryLastRow)
+ {
+ indexToMove += afterCategoryLastRow - 1;
+ }
+ else
+ {
+ indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow);
+ }
+
+ return d->elementDictionary[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 (d->forcedSelectionPosition < 0)
+ d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
+
+ return d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[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 (d->forcedSelectionPosition < 0)
+ d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
+
+ return d->elementDictionary[d->proxyModel->index(d->invertedElementDictionary[current].row() + 1, 0)];
+
+ default:
+ break;
+ }
+
return QListView::moveCursor(cursorAction, modifiers);
}
@@ -981,6 +1068,21 @@ void KListView::rowsInserted(const QModelIndex &parent,
if ((viewMode() != KListView::IconMode) || !d->proxyModel ||
!d->itemCategorizer)
{
+ 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->hovered = QModelIndex();
+ d->mouseButtonPressed = false;
+
return;
}
@@ -994,9 +1096,12 @@ void KListView::rowsInsertedArtifficial(const QModelIndex &parent,
Q_UNUSED(parent);
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();
@@ -1083,6 +1188,9 @@ void KListView::rowsInsertedArtifficial(const QModelIndex &parent,
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++;
}
diff --git a/src/klistview.h b/src/klistview.h
index 6626db3d5..57a414c2f 100644
--- a/src/klistview.h
+++ b/src/klistview.h
@@ -27,6 +27,17 @@
class KItemCategorizer;
+/**
+ * @short Item view for listing items
+ *
+ * KListView allows you to use it as it were a QListView. You can add an
+ * itemCategorizer to it, so your items became categorized depending on the
+ * KItemCategorizer inherited class rules.
+ *
+ * @see KItemCategorizer, KSortFilterProxyModel
+ *
+ * @author Rafael Fernández López <[email protected]>
+ */
class LIBDOLPHINPRIVATE_EXPORT KListView
: public QListView
{
@@ -41,8 +52,15 @@ public:
virtual QRect visualRect(const QModelIndex &index) const;
+ /**
+ * Will return the current categorizer. If none set, this method will
+ * return 0
+ */
KItemCategorizer *itemCategorizer() const;
+ /**
+ * Sets the categorizer to be used. Causes the item view to repaint
+ */
void setItemCategorizer(KItemCategorizer *itemCategorizer);
virtual QModelIndex indexAt(const QPoint &point) const;
diff --git a/src/klistview_p.h b/src/klistview_p.h
index b590b2307..4d0cec138 100644
--- a/src/klistview_p.h
+++ b/src/klistview_p.h
@@ -85,10 +85,12 @@ public:
QRect categoryVisualRect(const QString &category);
/**
- * This method will draw a new category with name @p category on the rect
- * specified by @p option.rect, with painter @p painter
+ * This method will draw a new category represented by index
+ * @param index on the rect specified by @p option.rect, with
+ * painter @p painter
*/
- void drawNewCategory(const QString &category,
+ void drawNewCategory(const QModelIndex &index,
+ int sortRole,
const QStyleOption &option,
QPainter *painter);
@@ -131,6 +133,8 @@ public:
QPoint initialPressPosition;
QPoint mousePosition;
QItemSelection lastSelection;
+ QModelIndex currentViewIndex;
+ int forcedSelectionPosition;
// Cache data
// We cannot merge some of them into structs because it would affect
@@ -138,6 +142,7 @@ public:
QHash<QModelIndex, struct ElementInfo> elementsInfo; // in source model
QHash<QModelIndex, QRect> elementsPosition; // in source model
QHash<QModelIndex, QModelIndex> elementDictionary; // mapped indexes
+ QHash<QModelIndex, QModelIndex> invertedElementDictionary; // mapped indexes
QHash<QString, QModelIndexList> categoriesIndexes;
QHash<QString, QRect> categoriesPosition;
QStringList categories;
diff --git a/src/ksortfilterproxymodel.h b/src/ksortfilterproxymodel.h
index 2748c64ec..1c653f67c 100644
--- a/src/ksortfilterproxymodel.h
+++ b/src/ksortfilterproxymodel.h
@@ -25,6 +25,13 @@
#include <libdolphin_export.h>
+/**
+ * @internal
+ *
+ * This class is meant to be used with KListView class
+ *
+ * @see KListView
+ */
class LIBDOLPHINPRIVATE_EXPORT KSortFilterProxyModel
: public QSortFilterProxyModel
{