From: Nick Shaforostoff Date: Sat, 21 Jul 2007 00:31:26 +0000 (+0000) Subject: ok, reverting as requested by Peter Penz. X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/4c96ec2698084f2c2d9fa2d6f78ce87157efde37 ok, reverting as requested by Peter Penz. svn path=/trunk/KDE/kdebase/apps/; revision=690417 --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ff73c560..2484b3b05 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ set(dolphinprivate_LIB_SRCS dolphincolumnview.cpp dolphinitemcategorizer.cpp kcategorizedview.cpp + ksortfilterproxymodel.cpp kitemcategorizer.cpp dolphinsettings.cpp viewproperties.cpp diff --git a/src/dolphinsortfilterproxymodel.cpp b/src/dolphinsortfilterproxymodel.cpp index dcb1af0e9..592b0aa1c 100644 --- a/src/dolphinsortfilterproxymodel.cpp +++ b/src/dolphinsortfilterproxymodel.cpp @@ -50,10 +50,16 @@ static DolphinView::Sorting sortingTypeTable[] = }; DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) : - KDirSortFilterProxyModel(parent), + KSortFilterProxyModel(parent), m_sorting(DolphinView::SortByName), m_sortOrder(Qt::AscendingOrder) { + setDynamicSortFilter(true); + + // sort by the user visible string for now + setSortRole(DolphinView::SortByName); + setSortCaseSensitivity(Qt::CaseInsensitive); + sort(KDirModel::Name, Qt::AscendingOrder); } DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel() @@ -77,7 +83,19 @@ void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder) m_sorting = sortingForColumn(column); m_sortOrder = sortOrder; setSortRole(m_sorting); - KDirSortFilterProxyModel::sort(column, sortOrder); + KSortFilterProxyModel::sort(column, sortOrder); +} + +bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex& parent) const +{ + const QModelIndex sourceParent = mapToSource(parent); + return sourceModel()->hasChildren(sourceParent); +} + +bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex& parent) const +{ + const QModelIndex sourceParent = mapToSource(parent); + return sourceModel()->canFetchMore(sourceParent); } DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column) @@ -178,12 +196,27 @@ bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex &left bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { -#ifdef HAVE_NEPOMUK KDirModel* dirModel = static_cast(sourceModel()); const KFileItem* leftFileItem = dirModel->itemForIndex(left); const KFileItem* rightFileItem = dirModel->itemForIndex(right); + // If we are sorting by rating, folders and files are citizens of the same + // class. Same if we are sorting by tags. +#ifdef HAVE_NEPOMUK + if ((sortRole() != DolphinView::SortByRating) && + (sortRole() != DolphinView::SortByTags)) + { +#endif + // On our priority, folders go above regular files. + if (leftFileItem->isDir() && !rightFileItem->isDir()) { + return true; + } else if (!leftFileItem->isDir() && rightFileItem->isDir()) { + return false; + } +#ifdef HAVE_NEPOMUK + } +#endif // Hidden elements go before visible ones, if they both are // folders or files. @@ -194,60 +227,164 @@ bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left, } switch (sortRole()) { - case DolphinView::SortByRating: { - const quint32 leftRating = ratingForIndex(left); - const quint32 rightRating = ratingForIndex(right); - - if (leftRating == rightRating) { - // On our priority, folders go above regular files. - // This checks are needed (don't think it's the same doing it here - // than above). Here we make dirs citizens of first class because - // we know we are on the same category. On the check we do on the - // top of the method we don't know, so we remove that check when we - // are sorting by rating. (ereslibre) - if (leftFileItem->isDir() && !rightFileItem->isDir()) { - return true; - } else if (!leftFileItem->isDir() && rightFileItem->isDir()) { - return false; - } + case DolphinView::SortByName: { + // So we are in the same priority, what counts now is their names. + const QVariant leftData = dirModel->data(left, KDirModel::Name); + const QVariant rightData = dirModel->data(right, KDirModel::Name); + const QString leftValueString(leftData.toString()); + const QString rightValueString(rightData.toString()); + + return sortCaseSensitivity() ? + (naturalCompare(leftValueString, rightValueString) < 0) : + (naturalCompare(leftValueString.toLower(), rightValueString.toLower()) < 0); + } - return sortCaseSensitivity() ? - (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : - (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + case DolphinView::SortBySize: { + // If we have two folders, what we have to measure is the number of + // items that contains each other + if (leftFileItem->isDir() && rightFileItem->isDir()) { + QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole); + int leftCount = leftValue.type() == QVariant::Int ? leftValue.toInt() : KDirModel::ChildCountUnknown; + + QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole); + int rightCount = rightValue.type() == QVariant::Int ? rightValue.toInt() : KDirModel::ChildCountUnknown; + + // 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) { + return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); } - return leftRating > rightRating; + // If they had different number of items, we sort them depending + // on how many items had each other. + return leftCount < rightCount; } - case DolphinView::SortByTags: { - const QString leftTags = tagsForIndex(left); - const QString rightTags = tagsForIndex(right); - - if (leftTags == rightTags) { - // On our priority, folders go above regular files. - // This checks are needed (don't think it's the same doing it here - // than above). Here we make dirs citizens of first class because - // we know we are on the same category. On the check we do on the - // top of the method we don't know, so we remove that check when we - // are sorting by tags. (ereslibre) - if (leftFileItem->isDir() && !rightFileItem->isDir()) { - return true; - } else if (!leftFileItem->isDir() && rightFileItem->isDir()) { - return false; - } + // 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()) { + return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } - return sortCaseSensitivity() ? - (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + // If their sizes are different, sort them by their sizes, as expected. + return leftFileItem->size() < rightFileItem->size(); + } + + case DolphinView::SortByDate: { + KDateTime leftTime, rightTime; + leftTime.setTime_t(leftFileItem->time(KIO::UDS_MODIFICATION_TIME)); + rightTime.setTime_t(rightFileItem->time(KIO::UDS_MODIFICATION_TIME)); + + if (leftTime == rightTime) { + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return leftTime > rightTime; + } + + case DolphinView::SortByPermissions: { + if (leftFileItem->permissionsString() == rightFileItem->permissionsString()) { + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return naturalCompare(leftFileItem->permissionsString(), + rightFileItem->permissionsString()) < 0; + } + + case DolphinView::SortByOwner: { + if (leftFileItem->user() == rightFileItem->user()) { + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return naturalCompare(leftFileItem->user(), rightFileItem->user()) < 0; + } + + case DolphinView::SortByGroup: { + if (leftFileItem->group() == rightFileItem->group()) { + return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return naturalCompare(leftFileItem->group(), + rightFileItem->group()) < 0; + } + + case DolphinView::SortByType: { + if (leftFileItem->mimetype() == rightFileItem->mimetype()) { + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return naturalCompare(leftFileItem->mimeComment(), + rightFileItem->mimeComment()) < 0; + } + +#ifdef HAVE_NEPOMUK + case DolphinView::SortByRating: { + const quint32 leftRating = ratingForIndex(left); + const quint32 rightRating = ratingForIndex(right); + + if (leftRating == rightRating) { + // On our priority, folders go above regular files. + // This checks are needed (don't think it's the same doing it here + // than above). Here we make dirs citizens of first class because + // we know we are on the same category. On the check we do on the + // top of the method we don't know, so we remove that check when we + // are sorting by rating. (ereslibre) + if (leftFileItem->isDir() && !rightFileItem->isDir()) { + return true; + } else if (!leftFileItem->isDir() && rightFileItem->isDir()) { + return false; } - return naturalCompare(leftTags, rightTags) < 0; + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); } + + return leftRating > rightRating; + } + + case DolphinView::SortByTags: { + const QString leftTags = tagsForIndex(left); + const QString rightTags = tagsForIndex(right); + + if (leftTags == rightTags) { + // On our priority, folders go above regular files. + // This checks are needed (don't think it's the same doing it here + // than above). Here we make dirs citizens of first class because + // we know we are on the same category. On the check we do on the + // top of the method we don't know, so we remove that check when we + // are sorting by tags. (ereslibre) + if (leftFileItem->isDir() && !rightFileItem->isDir()) { + return true; + } else if (!leftFileItem->isDir() && rightFileItem->isDir()) { + return false; + } + + return sortCaseSensitivity() ? + (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) : + (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0); + } + + return naturalCompare(leftTags, rightTags) < 0; } #endif + } + // We have set a SortRole and trust the ProxyModel to do // the right thing for now. - return KDirSortFilterProxyModel::lessThan(left, right); + return QSortFilterProxyModel::lessThan(left, right); } quint32 DolphinSortFilterProxyModel::ratingForIndex(const QModelIndex& index) @@ -295,10 +432,111 @@ QString DolphinSortFilterProxyModel::tagsForIndex(const QModelIndex& index) return tagsString; #else - Q_UNUSED(index); return QString(); #endif } +int DolphinSortFilterProxyModel::naturalCompare(const QString& a, + const QString& b) +{ + // This method chops the input a and b into pieces of + // digits and non-digits (a1.05 becomes a | 1 | . | 05) + // and compares these pieces of a and b to each other + // (first with first, second with second, ...). + // + // This is based on the natural sort order code code by Martin Pool + // http://sourcefrog.net/projects/natsort/ + // Martin Pool agreed to license this under LGPL or GPL. + + const QChar* currA = a.unicode(); // iterator over a + const QChar* currB = b.unicode(); // iterator over b + + if (currA == currB) { + return 0; + } + + const QChar* begSeqA = currA; // beginning of a new character sequence of a + const QChar* begSeqB = currB; + + while (!currA->isNull() && !currB->isNull()) { + // find sequence of characters ending at the first non-character + while (!currA->isNull() && !currA->isDigit()) { + ++currA; + } + + while (!currB->isNull() && !currB->isDigit()) { + ++currB; + } + + // compare these sequences + const QString subA(begSeqA, currA - begSeqA); + const QString subB(begSeqB, currB - begSeqB); + const int cmp = QString::localeAwareCompare(subA, subB); + if (cmp != 0) { + return cmp; + } + + if (currA->isNull() || currB->isNull()) { + break; + } + + // now some digits follow... + if ((*currA == '0') || (*currB == '0')) { + // one digit-sequence starts with 0 -> assume we are in a fraction part + // do left aligned comparison (numbers are considered left aligned) + while (1) { + if (!currA->isDigit() && !currB->isDigit()) { + break; + } else if (!currA->isDigit()) { + return -1; + } else if (!currB->isDigit()) { + return + 1; + } else if (*currA < *currB) { + return -1; + } else if (*currA > *currB) { + return + 1; + } + ++currA; + ++currB; + } + } else { + // No digit-sequence starts with 0 -> assume we are looking at some integer + // do right aligned comparison. + // + // The longest run of digits wins. That aside, the greatest + // value wins, but we can't know that it will until we've scanned + // both numbers to know that they have the same magnitude. + + int weight = 0; + while (1) { + if (!currA->isDigit() && !currB->isDigit()) { + if (weight != 0) { + return weight; + } + break; + } else if (!currA->isDigit()) { + return -1; + } else if (!currB->isDigit()) { + return + 1; + } else if ((*currA < *currB) && (weight == 0)) { + weight = -1; + } else if ((*currA > *currB) && (weight == 0)) { + weight = + 1; + } + ++currA; + ++currB; + } + } + + begSeqA = currA; + begSeqB = currB; + } + + if (currA->isNull() && currB->isNull()) { + return 0; + } + + return currA->isNull() ? -1 : + 1; +} #include "dolphinsortfilterproxymodel.moc" diff --git a/src/dolphinsortfilterproxymodel.h b/src/dolphinsortfilterproxymodel.h index ef55bab56..56ee77dba 100644 --- a/src/dolphinsortfilterproxymodel.h +++ b/src/dolphinsortfilterproxymodel.h @@ -20,7 +20,7 @@ #ifndef DOLPHINSORTFILTERPROXYMODEL_H #define DOLPHINSORTFILTERPROXYMODEL_H -#include +#include #include #include @@ -39,7 +39,7 @@ * * It is assured that directories are always sorted before files. */ -class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KDirSortFilterProxyModel +class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KSortFilterProxyModel { Q_OBJECT @@ -62,6 +62,12 @@ public: virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + /** Reimplemented from QAbstractItemModel. Returns true for directories. */ + virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; + + /** Reimplemented from QAbstractItemModel. Returns true for empty directories. */ + virtual bool canFetchMore(const QModelIndex& parent) const; + /** * Helper method to get the DolphinView::Sorting type for a given * column \a column. If the column is smaller 0 or greater than the @@ -69,39 +75,9 @@ public: */ static DolphinView::Sorting sortingForColumn(int column); - /** - * This method is essential on the categorized view. - * It will does a "basic" sorting, just for finding out categories, - * and their order. Then over those elements DISORDERED on categories, - * the lessThan method will be applied for each category. - * - * The easy explanation is that not always folders go first. That will depend. - * Imagine we sort by Rating. Categories will be created by 10 stars, - * 9 stars, 8 stars... but a category with only a file with rating 10 - * will go before a category with a folder with rating 8. - * That's the main reason, and that's lessThanGeneralPurpose() method. - * That will go category by category creating sets of elements... - */ virtual bool lessThanGeneralPurpose(const QModelIndex &left, const QModelIndex &right) const; - /** - * Then for each set of elements lessThanCategoryPurpose() will be applied, - * because for each category we wan't first folders and bla bla bla... - * That's the main reason of that method existence. - * - * For that reason, is not that clear that we want ALWAYS folders first. - * On each category, yes, that's true. But that's not true always, - * as I have pointed out on the example before. - */ - bool lessThanCategoryPurpose(const QModelIndex &left, - const QModelIndex &right) const - { - //when we sort inside 1 category its the usual lessThan() - //from KDirSortFilterProxyModel(+nepomuk) - return lessThan(left,right); - } - protected: virtual bool lessThan(const QModelIndex& left, const QModelIndex& right) const; @@ -119,6 +95,8 @@ private: */ static QString tagsForIndex(const QModelIndex& index); + static int naturalCompare(const QString& a, const QString& b); + private: DolphinView::Sorting m_sorting; Qt::SortOrder m_sortOrder; diff --git a/src/kcategorizedview.cpp b/src/kcategorizedview.cpp index b42d066b5..d250c42c9 100644 --- a/src/kcategorizedview.cpp +++ b/src/kcategorizedview.cpp @@ -33,7 +33,7 @@ #include #include "kitemcategorizer.h" -#include "dolphinsortfilterproxymodel.h" +#include "ksortfilterproxymodel.h" class LessThan { @@ -44,7 +44,7 @@ public: CategoryPurpose }; - inline LessThan(const DolphinSortFilterProxyModel *proxyModel, + inline LessThan(const KSortFilterProxyModel *proxyModel, Purpose purpose) : proxyModel(proxyModel) , purpose(purpose) @@ -67,7 +67,7 @@ public: } private: - const DolphinSortFilterProxyModel *proxyModel; + const KSortFilterProxyModel *proxyModel; const Purpose purpose; }; @@ -483,7 +483,7 @@ void KCategorizedView::setModel(QAbstractItemModel *model) QListView::setModel(model); - d->proxyModel = dynamic_cast(model); + d->proxyModel = dynamic_cast(model); if (d->proxyModel) { diff --git a/src/kcategorizedview_p.h b/src/kcategorizedview_p.h index 4e5a55dba..45d89aa21 100644 --- a/src/kcategorizedview_p.h +++ b/src/kcategorizedview_p.h @@ -21,7 +21,7 @@ #ifndef KCATEGORIZEDVIEW_P_H #define KCATEGORIZEDVIEW_P_H -class DolphinSortFilterProxyModel; +class KSortFilterProxyModel; /** * @internal @@ -152,7 +152,7 @@ public: QRect lastSelectionRect; // Attributes for speed reasons - DolphinSortFilterProxyModel *proxyModel; + KSortFilterProxyModel *proxyModel; QModelIndexList sourceModelIndexList; // in source model QModelIndex lastIndex; }; diff --git a/src/ksortfilterproxymodel.cpp b/src/ksortfilterproxymodel.cpp new file mode 100644 index 000000000..9b08c420f --- /dev/null +++ b/src/ksortfilterproxymodel.cpp @@ -0,0 +1,52 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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() +{ +} + +void KSortFilterProxyModel::sort(int column, Qt::SortOrder order) +{ + QSortFilterProxyModel::sort(column, order); + + m_sortOrder = order; + + emit sortingRoleChanged(); +} + +Qt::SortOrder KSortFilterProxyModel::sortOrder() const +{ + return m_sortOrder; +} + +bool KSortFilterProxyModel::lessThanCategoryPurpose(const QModelIndex &left, + const QModelIndex &right) const +{ + return lessThan(left, right); +} + +#include "ksortfilterproxymodel.moc" diff --git a/src/ksortfilterproxymodel.h b/src/ksortfilterproxymodel.h new file mode 100644 index 000000000..1c653f67c --- /dev/null +++ b/src/ksortfilterproxymodel.h @@ -0,0 +1,61 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 + +#include + +/** + * @internal + * + * This class is meant to be used with KListView class + * + * @see KListView + */ +class LIBDOLPHINPRIVATE_EXPORT KSortFilterProxyModel + : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + KSortFilterProxyModel(QObject *parent = 0); + ~KSortFilterProxyModel(); + + virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + + Qt::SortOrder sortOrder() const; + + virtual bool lessThanGeneralPurpose(const QModelIndex &left, + const QModelIndex &right) const = 0; + + virtual bool lessThanCategoryPurpose(const QModelIndex &left, + const QModelIndex &right) const; + +Q_SIGNALS: + void sortingRoleChanged(); + +private: + Qt::SortOrder m_sortOrder; +}; + +#endif // KSORTFILTERPROXYMODEL_H