]> cloud.milkyroute.net Git - dolphin.git/commitdiff
ok, reverting as requested by Peter Penz.
authorNick Shaforostoff <shafff@ukr.net>
Sat, 21 Jul 2007 00:31:26 +0000 (00:31 +0000)
committerNick Shaforostoff <shafff@ukr.net>
Sat, 21 Jul 2007 00:31:26 +0000 (00:31 +0000)
svn path=/trunk/KDE/kdebase/apps/; revision=690417

src/CMakeLists.txt
src/dolphinsortfilterproxymodel.cpp
src/dolphinsortfilterproxymodel.h
src/kcategorizedview.cpp
src/kcategorizedview_p.h
src/ksortfilterproxymodel.cpp [new file with mode: 0644]
src/ksortfilterproxymodel.h [new file with mode: 0644]

index 7ff73c5605a0338862f8df0d065c0eb6e9f46454..2484b3b05884e9ed77e6e41aba4f1ab1337164dd 100644 (file)
@@ -16,6 +16,7 @@ set(dolphinprivate_LIB_SRCS
     dolphincolumnview.cpp
     dolphinitemcategorizer.cpp
     kcategorizedview.cpp
+    ksortfilterproxymodel.cpp
     kitemcategorizer.cpp
     dolphinsettings.cpp
     viewproperties.cpp
index dcb1af0e9710292d602927c51ea5d7ae76b2331a..592b0aa1ca9b464f72a8aa8e3bc942adb8bde6af 100644 (file)
@@ -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<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.
@@ -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"
index ef55bab56eb6045d2ea812cd79e84261d5d39d13..56ee77dba4833c13949c85c48bd88bb0deecfab3 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef DOLPHINSORTFILTERPROXYMODEL_H
 #define DOLPHINSORTFILTERPROXYMODEL_H
 
-#include <kdirsortfilterproxymodel.h>
+#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 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;
index b42d066b53e13d5b8c7270d02f63787f91aae681..d250c42c90d585c4094b5423f0964da260bad3d9 100644 (file)
@@ -33,7 +33,7 @@
 #include <kstyle.h>
 
 #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<DolphinSortFilterProxyModel*>(model);
+    d->proxyModel = dynamic_cast<KSortFilterProxyModel*>(model);
 
     if (d->proxyModel)
     {
index 4e5a55dbab5b31b9bf93e946cd3e33dde37826d0..45d89aa2105f7eb7c509dc257803ccc81907b25d 100644 (file)
@@ -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 (file)
index 0000000..9b08c42
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+  * This file is part of the KDE project
+  * Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
+  *
+  * 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 (file)
index 0000000..1c653f6
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+  * This file is part of the KDE project
+  * Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
+  *
+  * 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>
+
+/**
+  * @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