diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/dolphiniconsview.cpp | 2 | ||||
| -rw-r--r-- | src/dolphinitemcategorizer.cpp | 67 | ||||
| -rw-r--r-- | src/dolphinsortfilterproxymodel.cpp | 144 | ||||
| -rw-r--r-- | src/dolphinsortfilterproxymodel.h | 7 | ||||
| -rw-r--r-- | src/kitemcategorizer.h | 4 | ||||
| -rw-r--r-- | src/klistview.cpp | 927 | ||||
| -rw-r--r-- | src/klistview.h | 41 | ||||
| -rw-r--r-- | src/klistview_p.h | 114 | ||||
| -rw-r--r-- | src/ksortfilterproxymodel.cpp | 36 | ||||
| -rw-r--r-- | src/ksortfilterproxymodel.h | 42 |
11 files changed, 1114 insertions, 271 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04b4b9bd7..6ca332212 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(dolphinprivate_LIB_SRCS dolphiniconsview.cpp dolphinitemcategorizer.cpp klistview.cpp + ksortfilterproxymodel.cpp dolphinsettings.cpp viewproperties.cpp dolphinsortfilterproxymodel.cpp diff --git a/src/dolphiniconsview.cpp b/src/dolphiniconsview.cpp index 50d7f311e..cf5be61ef 100644 --- a/src/dolphiniconsview.cpp +++ b/src/dolphiniconsview.cpp @@ -38,7 +38,7 @@ DolphinIconsView::DolphinIconsView(QWidget* parent, DolphinController* controlle Q_ASSERT(controller != 0); setViewMode(QListView::IconMode); setResizeMode(QListView::Adjust); - + setSpacing(10); setMouseTracking(true); viewport()->setAttribute(Qt::WA_Hover); diff --git a/src/dolphinitemcategorizer.cpp b/src/dolphinitemcategorizer.cpp index 05c23a01d..cea6bebd5 100644 --- a/src/dolphinitemcategorizer.cpp +++ b/src/dolphinitemcategorizer.cpp @@ -41,38 +41,79 @@ QString DolphinItemCategorizer::categoryForItem(const QModelIndex& index, { QString retString; - if (!index.isValid()) { + if (!index.isValid()) + { + return retString; + } + + int indexColumn; + + switch (sortRole) + { + case DolphinView::SortByName: + indexColumn = KDirModel::Name; + break; + case DolphinView::SortBySize: + indexColumn = KDirModel::Size; + break; + default: return retString; } // KDirModel checks columns to know to which role are // we talking about QModelIndex theIndex = index.model()->index(index.row(), - sortRole, + indexColumn, index.parent()); - const QSortFilterProxyModel* proxyModel = static_cast<const QSortFilterProxyModel*>(index.model()); - const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel()); + if (!theIndex.isValid()) { + return retString; + } QVariant data = theIndex.model()->data(theIndex, Qt::DisplayRole); - QModelIndex mappedIndex = proxyModel->mapToSource(theIndex); - KFileItem* item = dirModel->itemForIndex(mappedIndex); + const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); + KFileItem* item = dirModel->itemForIndex(index); - switch (sortRole) { + switch (sortRole) + { case DolphinView::SortByName: - retString = data.toString().toUpper().at(0); + if (data.toString().size()) + { + if (!item->isHidden() && data.toString().at(0).isLetter()) + retString = data.toString().toUpper().at(0); + else if (item->isHidden() && data.toString().at(0) == '.' && + data.toString().at(1).isLetter()) + retString = i18n(".%1 (Hidden)", data.toString().toUpper().at(1)); + else if (item->isHidden() && data.toString().at(0) == '.' && + !data.toString().at(1).isLetter()) + retString = i18n("Others (Hidden)"); + else if (item->isHidden() && data.toString().at(0) != '.') + retString = i18n("%1 (Hidden)", data.toString().toUpper().at(0)); + else if (item->isHidden()) + retString = data.toString().toUpper().at(0); + else + retString = i18n("Others"); + } break; case DolphinView::SortBySize: int fileSize = (item) ? item->size() : -1; - if (item && item->isDir()) { - retString = i18n("Unknown"); - } else if (fileSize < 5242880) { + if (item && item->isDir() && !item->isHidden()) { + retString = i18n("Folders"); + } else if (fileSize < 5242880 && !item->isHidden()) { retString = i18n("Small"); - } else if (fileSize < 10485760) { + } else if (fileSize < 10485760 && !item->isHidden()) { retString = i18n("Medium"); - } else { + } else if (!item->isHidden()){ retString = i18n("Big"); + } else if (item && item->isDir() && item->isHidden()) { + retString = i18n("Folders (Hidden)"); + } else if (fileSize < 5242880 && item->isHidden()) { + retString = i18n("Small (Hidden)"); + } else if (fileSize < 10485760 && item->isHidden()) { + retString = i18n("Medium (Hidden)"); + } else if (item->isHidden()){ + retString = i18n("Big (Hidden)"); } break; } diff --git a/src/dolphinsortfilterproxymodel.cpp b/src/dolphinsortfilterproxymodel.cpp index 5f6cb3575..9b901d285 100644 --- a/src/dolphinsortfilterproxymodel.cpp +++ b/src/dolphinsortfilterproxymodel.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 by Peter Penz <[email protected]> * * Copyright (C) 2006 by Dominic Battre <[email protected]> * * Copyright (C) 2006 by Martin Pool <[email protected]> * + * Copyright (C) 2007 by Rafael Fernández López <[email protected]> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -49,7 +50,7 @@ static DolphinView::Sorting dirModelColumnToDolphinView[] = DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) : - QSortFilterProxyModel(parent), + KSortFilterProxyModel(parent), m_sortColumn(0), m_sorting(DolphinView::SortByName), m_sortOrder(Qt::AscendingOrder) @@ -57,7 +58,7 @@ DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) : setDynamicSortFilter(true); // sort by the user visible string for now - setSortRole(Qt::DisplayRole); + setSortRole(DolphinView::SortByName); setSortCaseSensitivity(Qt::CaseInsensitive); sort(KDirModel::Name, Qt::AscendingOrder); } @@ -87,6 +88,7 @@ void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder) m_sorting = (column >= 0) && (column < dolphinMapSize) ? dirModelColumnToDolphinView[column] : DolphinView::SortByName; + setSortRole(m_sorting); QSortFilterProxyModel::sort(column, sortOrder); } @@ -110,38 +112,150 @@ DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column) return DolphinView::SortByName; } +bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex &left, + const QModelIndex &right) const +{ + KDirModel* dirModel = static_cast<KDirModel*>(sourceModel()); + + const KFileItem *leftFileItem = dirModel->itemForIndex(left); + const KFileItem *rightFileItem = dirModel->itemForIndex(right); + + if (sortRole() == DolphinView::SortByName) // If we are sorting by name + { + const QVariant leftData = dirModel->data(left, sortRole()); + const QVariant rightData = dirModel->data(right, sortRole()); + + // Give preference to hidden items. They will be shown above regular + // items + if (leftFileItem->isHidden() && !rightFileItem->isHidden()) + return true; + else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) + return false; + + // If we are handling two items of the same preference, just take in + // count their names. There is no need to check for case sensitivity + // here, since this is the method that explores for new categories + return leftData.toString().toLower() < rightData.toString().toLower(); + } + else if (sortRole() == DolphinView::SortBySize) // If we are sorting by size + { + // Give preference to hidden items. They will be shown above regular + // items + if (leftFileItem->isHidden() && !rightFileItem->isHidden()) + return true; + else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) + return false; + + // If we are sorting by size, show folders first. We will sort them + // correctly later + if (leftFileItem->isDir() && !rightFileItem->isDir()) + return true; + + return false; + } +} + bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left, - const QModelIndex& right) const + const QModelIndex& right) const { KDirModel* dirModel = static_cast<KDirModel*>(sourceModel()); QVariant leftData = dirModel->data(left, sortRole()); QVariant rightData = dirModel->data(right, sortRole()); - if ((leftData.type() == QVariant::String) && (rightData.type() == QVariant::String)) { - // assure that directories are always sorted before files - // if the sorting is done by the 'Name' column - if (m_sortColumn == KDirModel::Name) { - const bool leftIsDir = dirModel->itemForIndex(left)->isDir(); - const bool rightIsDir = dirModel->itemForIndex(right)->isDir(); - if (leftIsDir && !rightIsDir) { + const KFileItem *leftFileItem = dirModel->itemForIndex(left); + const KFileItem *rightFileItem = dirModel->itemForIndex(right); + + if (sortRole() == DolphinView::SortByName) { // If we are sorting by name + if ((leftData.type() == QVariant::String) && (rightData.type() == + QVariant::String)) { + // Priority: hidden > folders > regular files. If an item is + // hidden (doesn't matter if file or folder) will have higher + // preference than a non-hidden item + if (leftFileItem->isHidden() && !rightFileItem->isHidden()) { return true; } + else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) { + return false; + } - if (!leftIsDir && rightIsDir) { + // On our priority, folders go above regular files + if (leftFileItem->isDir() && !rightFileItem->isDir()) { + return true; + } + else if (!leftFileItem->isDir() && rightFileItem->isDir()) { return false; } + + // So we are in the same priority, what counts now is their names + const QString leftStr = leftData.toString(); + const QString rightStr = rightData.toString(); + + return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) : + (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0); + } + } + else if (sortRole() == DolphinView::SortBySize) { // If we are sorting by size + // Priority: hidden > folders > regular files. If an item is + // hidden (doesn't matter if file or folder) will have higher + // preference than a non-hidden item + if (leftFileItem->isHidden() && !rightFileItem->isHidden()) { + return true; } + else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) { + return false; + } + + // On our priority, folders go above regular files + if (leftFileItem->isDir() && !rightFileItem->isDir()) { + return true; + } + else if (!leftFileItem->isDir() && rightFileItem->isDir()) { + return false; + } + + // If we have two folders, what we have to measure is the number of + // items that contains each other + if (leftFileItem->isDir() && rightFileItem->isDir()) { + const QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole); + const int leftCount = leftValue.type() == QVariant::Int ? leftValue.toInt() : KDirModel::ChildCountUnknown; + + const QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole); + const int rightCount = rightValue.type() == QVariant::Int ? rightValue.toInt() : KDirModel::ChildCountUnknown; - const QString leftStr = leftData.toString(); - const QString rightStr = rightData.toString(); + // In the case they two have the same child items, we sort them by + // their names. So we have always everything ordered. We also check + // if we are taking in count their cases + if (leftCount == rightCount) { + const QString leftStr = leftData.toString(); + const QString rightStr = rightData.toString(); - return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) : - (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0); + return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) : + (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0); + } + + // If they had different number of items, we sort them depending + // on how many items had each other + return leftCount < rightCount; + } + + // If what we are measuring is two files and they have the same size, + // sort them by their file names + if (leftFileItem->size() == rightFileItem->size()) { + const QString leftStr = leftData.toString(); + const QString rightStr = rightData.toString(); + + return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) : + (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0); + } + + // If their sizes are different, sort them by their sizes, as expected + return leftFileItem->size() < rightFileItem->size(); } // We have set a SortRole and trust the ProxyModel to do // the right thing for now. + return QSortFilterProxyModel::lessThan(left, right); } diff --git a/src/dolphinsortfilterproxymodel.h b/src/dolphinsortfilterproxymodel.h index 437ebca12..c00032f3f 100644 --- a/src/dolphinsortfilterproxymodel.h +++ b/src/dolphinsortfilterproxymodel.h @@ -20,7 +20,7 @@ #ifndef DOLPHINSORTFILTERPROXYMODEL_H #define DOLPHINSORTFILTERPROXYMODEL_H -#include <QtGui/QSortFilterProxyModel> +#include <ksortfilterproxymodel.h> #include <dolphinview.h> #include <libdolphin_export.h> @@ -39,7 +39,7 @@ * * It is assured that directories are always sorted before files. */ -class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public QSortFilterProxyModel +class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KSortFilterProxyModel { Q_OBJECT @@ -81,6 +81,9 @@ public: */ static DolphinView::Sorting sortingForColumn(int column); + virtual bool lessThanGeneralPurpose(const QModelIndex &left, + const QModelIndex &right) const; + protected: virtual bool lessThan(const QModelIndex& left, const QModelIndex& right) const; diff --git a/src/kitemcategorizer.h b/src/kitemcategorizer.h index 7bec65637..1d64206eb 100644 --- a/src/kitemcategorizer.h +++ b/src/kitemcategorizer.h @@ -18,8 +18,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __KITEMCATEGORIZER_H__ -#define __KITEMCATEGORIZER_H__ +#ifndef KITEMCATEGORIZER_H +#define KITEMCATEGORIZER_H #include <libdolphin_export.h> 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" diff --git a/src/klistview.h b/src/klistview.h index c8bd5215e..ee02b5ff7 100644 --- a/src/klistview.h +++ b/src/klistview.h @@ -18,8 +18,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __KLISTVIEW_H__ -#define __KLISTVIEW_H__ +#ifndef KLISTVIEW_H +#define KLISTVIEW_H #include <QtGui/QListView> @@ -37,9 +37,9 @@ public: ~KListView(); - void setModel(QAbstractItemModel *model); + virtual void setModel(QAbstractItemModel *model); - QRect visualRect(const QModelIndex &index) const; + virtual QRect visualRect(const QModelIndex &index) const; KItemCategorizer *itemCategorizer() const; @@ -47,42 +47,41 @@ public: virtual QModelIndex indexAt(const QPoint &point) const; - virtual int sizeHintForRow(int row) const; - +public Q_SLOTS: + virtual void reset(); protected: - virtual void drawNewCategory(const QString &category, - const QStyleOptionViewItem &option, - QPainter *painter); - - virtual int categoryHeight(const QStyleOptionViewItem &option) const; - virtual void paintEvent(QPaintEvent *event); + virtual void resizeEvent(QResizeEvent *event); + virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags); - virtual void timerEvent(QTimerEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + + virtual void mouseReleaseEvent(QMouseEvent *event); + + virtual void leaveEvent(QEvent *event); protected Q_SLOTS: virtual void rowsInserted(const QModelIndex &parent, int start, int end); - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, - int start, - int end); - virtual void rowsInsertedArtifficial(const QModelIndex &parent, int start, int end); - virtual void rowsAboutToBeRemovedArtifficial(const QModelIndex &parent, - int start, - int end); + virtual void rowsRemoved(const QModelIndex &parent, + int start, + int end); + + virtual void updateGeometries(); - virtual void itemsLayoutChanged(); + virtual void slotSortingRoleChanged(); private: diff --git a/src/klistview_p.h b/src/klistview_p.h index 5ada074e5..47be30c56 100644 --- a/src/klistview_p.h +++ b/src/klistview_p.h @@ -18,27 +18,121 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __KLISTVIEW_P_H__ -#define __KLISTVIEW_P_H__ +#ifndef KLISTVIEW_P_H +#define KLISTVIEW_P_H -class QSortFilterProxyModel; +class KSortFilterProxyModel; +/** + * @internal + */ class KListView::Private { public: Private(KListView *listView); ~Private(); - QModelIndexList intersectionSet(const QRect &rect) const; + // Methods + + /** + * Returns the list of items that intersects with @p rect + */ + const QModelIndexList &intersectionSet(const QRect &rect); + + /** + * Gets the item rect in the viewport for @p index + */ + QRect visualRectInViewport(const QModelIndex &index) const; + + /** + * Returns the category rect in the viewport for @p category + */ + QRect visualCategoryRectInViewport(const QString &category) const; + + /** + * Caches and returns the rect that corresponds to @p index + */ + const QRect &cacheIndex(const QModelIndex &index); + + /** + * Caches and returns the rect that corresponds to @p category + */ + const QRect &cacheCategory(const QString &category); + + /** + * Returns the rect that corresponds to @p index + * @note If the rect is not cached, it becomes cached + */ + const QRect &cachedRectIndex(const QModelIndex &index); + + /** + * Returns the rect that corresponds to @p category + * @note If the rect is not cached, it becomes cached + */ + const QRect &cachedRectCategory(const QString &category); + + /** + * Returns the visual rect (taking in count x and y offsets) for @p index + * @note If the rect is not cached, it becomes cached + */ + QRect visualRect(const QModelIndex &index); + + /** + * Returns the visual rect (taking in count x and y offsets) for @p category + * @note If the rect is not cached, it becomes cached + */ + 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 + */ + void drawNewCategory(const QString &category, + const QStyleOption &option, + QPainter *painter); + + /** + * This method will update scrollbars ranges. Called when our model changes + * or when the view is resized + */ + void updateScrollbars(); + + + // Attributes + + struct ElementInfo + { + QString category; + int relativeOffsetToCategory; + }; + + // Basic data KListView *listView; - QModelIndex hovered; - bool modelSortCapable; - int numCategories; - QList<QString> categories; - QHash<QString, int> elementsPerCategory; KItemCategorizer *itemCategorizer; - QSortFilterProxyModel *proxyModel; + + // Behavior data + bool mouseButtonPressed; + QModelIndex hovered; + QPoint initialPressPosition; + QPoint mousePosition; + + // Cache data + // We cannot merge some of them into structs because it would affect + // performance + QHash<QModelIndex, struct ElementInfo> elementsInfo; // in source model + QHash<QModelIndex, QRect> elementsPosition; // in source model + QHash<QModelIndex, QModelIndex> elementDictionary; // mapped indexes + QHash<QString, QModelIndexList> categoriesIndexes; + QHash<QString, QRect> categoriesPosition; + QStringList categories; + QModelIndexList intersectedIndexes; + QModelIndexList tempSelected; + + // Attributes for speed reasons + KSortFilterProxyModel *proxyModel; + QModelIndexList sourceModelIndexList; // in source model + QModelIndex lastIndex; }; #endif // __KLISTVIEW_P_H__ diff --git a/src/ksortfilterproxymodel.cpp b/src/ksortfilterproxymodel.cpp new file mode 100644 index 000000000..b716c399b --- /dev/null +++ b/src/ksortfilterproxymodel.cpp @@ -0,0 +1,36 @@ +/** + * 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 "ksortfilterproxymodel.h" + +KSortFilterProxyModel::KSortFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +KSortFilterProxyModel::~KSortFilterProxyModel() +{ +} + +bool KSortFilterProxyModel::lessThanCategoryPurpose(const QModelIndex &left, + const QModelIndex &right) const +{ + return lessThan(left, right); +} diff --git a/src/ksortfilterproxymodel.h b/src/ksortfilterproxymodel.h new file mode 100644 index 000000000..efe12cbc0 --- /dev/null +++ b/src/ksortfilterproxymodel.h @@ -0,0 +1,42 @@ +/** + * 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. + */ + +#ifndef KSORTFILTERPROXYMODEL_H +#define KSORTFILTERPROXYMODEL_H + +#include <QtGui/QSortFilterProxyModel> + +#include <libdolphin_export.h> + +class LIBDOLPHINPRIVATE_EXPORT KSortFilterProxyModel + : public QSortFilterProxyModel +{ +public: + KSortFilterProxyModel(QObject *parent = 0); + ~KSortFilterProxyModel(); + + virtual bool lessThanGeneralPurpose(const QModelIndex &left, + const QModelIndex &right) const = 0; + + virtual bool lessThanCategoryPurpose(const QModelIndex &left, + const QModelIndex &right) const; +}; + +#endif |
