};
DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
- KSortFilterProxyModel(parent),
+ KDirSortFilterProxyModel(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()
m_sorting = sortingForColumn(column);
m_sortOrder = sortOrder;
setSortRole(m_sorting);
- 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);
+ KDirSortFilterProxyModel::sort(column, sortOrder);
}
DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
const QModelIndex& right) const
{
+#ifdef HAVE_NEPOMUK
KDirModel* dirModel = static_cast<KDirModel*>(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.
}
switch (sortRole()) {
- 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);
- }
-
- 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);
- }
-
- // 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()) {
- return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
- (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 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::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::SortByGroup: {
- if (leftFileItem->group() == rightFileItem->group()) {
- return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
+ 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 sortCaseSensitivity() ?
- (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
- (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
+ return leftRating > rightRating;
}
- return leftRating > rightRating;
- }
-
- case DolphinView::SortByTags: {
- const QString leftTags = tagsForIndex(left);
- const QString rightTags = tagsForIndex(right);
+ 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 (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 sortCaseSensitivity() ?
- (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
- (naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
+ return naturalCompare(leftTags, rightTags) < 0;
}
-
- return naturalCompare(leftTags, rightTags) < 0;
}
#endif
- }
-
// We have set a SortRole and trust the ProxyModel to do
// the right thing for now.
- return QSortFilterProxyModel::lessThan(left, right);
+ return KDirSortFilterProxyModel::lessThan(left, right);
}
quint32 DolphinSortFilterProxyModel::ratingForIndex(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"
#ifndef DOLPHINSORTFILTERPROXYMODEL_H
#define DOLPHINSORTFILTERPROXYMODEL_H
-#include <ksortfilterproxymodel.h>
+#include <kdirsortfilterproxymodel.h>
#include <dolphinview.h>
#include <libdolphin_export.h>
*
* It is assured that directories are always sorted before files.
*/
-class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KSortFilterProxyModel
+class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KDirSortFilterProxyModel
{
Q_OBJECT
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
*/
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;
*/
static QString tagsForIndex(const QModelIndex& index);
- static int naturalCompare(const QString& a, const QString& b);
-
private:
DolphinView::Sorting m_sorting;
Qt::SortOrder m_sortOrder;