]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Do a natural sorting of items (thanks to Dominic Battre and Martin Pool for the patch...
authorPeter Penz <peter.penz19@gmail.com>
Thu, 11 Jan 2007 06:44:46 +0000 (06:44 +0000)
committerPeter Penz <peter.penz19@gmail.com>
Thu, 11 Jan 2007 06:44:46 +0000 (06:44 +0000)
 item_10.png
 item_1.png
 item_2.png

are sorted like
 item_1.png
 item_2.png
 item_10.png

TODO: corresponding to Ellen directory items should always be ordered as first items (have to go work now -> weekend task :-))

svn path=/trunk/playground/utils/dolphin/; revision=622241

src/dolphinsortfilterproxymodel.cpp
src/dolphinsortfilterproxymodel.h

index fc79ec5694ce97bd8bb88cab10be3490b99e0c85..e983007c17326f1c7773cb3512f44f8ba0c56544 100644 (file)
@@ -1,39 +1,40 @@
 /***************************************************************************
- *   Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>                  *
- *   Copyright (C) 2006 by Gregor Kališnik <gregor@podnapisi.net>          *
- *                                                                         *
- *   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  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
- ***************************************************************************/
+*   Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>                  *
+*   Copyright (C) 2006 by Dominic Battre <dominic battre de>              *
+*   Copyright (C) 2006 by Martin Pool <mbp canonical com>                 *
+*                                                                         *
+*   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  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+*   This program 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 General Public License for more details.                          *
+*                                                                         *
+*   You should have received a copy of the GNU General Public License     *
+*   along with this program; if not, write to the                         *
+*   Free Software Foundation, Inc.,                                       *
+*   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+***************************************************************************/
 
 #include "dolphinsortfilterproxymodel.h"
 
 #include <kdirmodel.h>
 #include <kfileitem.h>
 
-static const int dolphin_map_size = 3;
-static int dolphin_view_to_dir_model_column[] = {
-    /* SortByName */ KDirModel::Name,
-    /* SortBySize */ KDirModel::Size,
-    /* SortByDate */ KDirModel::ModifiedTime 
+static const int dolphinMapSize = 3;
+static int dolphinViewToDirModelColumn[] = {
+    KDirModel::Name,        // DolphinView::SortByName
+    KDirModel::Size,        // DolphinView::SortBySize
+    KDirModel::ModifiedTime // DolphinView::SortByDate
 };
 
-static DolphinView::Sorting dir_model_column_to_dolphin_view[] = {
-    /* KDirModel::Name */ DolphinView::SortByName,
-    /* KDirModel::Size */ DolphinView::SortBySize,
-    /* KDirModel::ModifiedTime */ DolphinView::SortByDate
+static DolphinView::Sorting dirModelColumnToDolphinView[] = {
+    DolphinView::SortByName, // KDirModel::Name
+    DolphinView::SortBySize, // KDirModel::Size
+    DolphinView::SortByDate  // KDirModel::ModifiedTime
 };
 
 
@@ -42,9 +43,7 @@ DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
 {
     setDynamicSortFilter(true);
 
-    /*
-     * sort by the user visible string for now
-     */
+    // sort by the user visible string for now
     setSortRole(Qt::DisplayRole);
     setSortCaseSensitivity(Qt::CaseInsensitive);
     sort(KDirModel::Name, Qt::Ascending);
@@ -54,48 +53,161 @@ DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
 {
 }
 
-/*
- * Update the sort column by mapping DolpginView::Sorting to
- * KDirModel::ModelColumns.
- * We will keep the sortOrder
- */
 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting)
 {
-    Q_ASSERT( static_cast<int>(sorting) >= 0 && static_cast<int>(sorting) <= dolphin_map_size );
-    sort(dolphin_view_to_dir_model_column[static_cast<int>(sorting)],
+    // Update the sort column by mapping DolpginView::Sorting to
+    // KDirModel::ModelColumns. We will keep the sortOrder.
+    Q_ASSERT(static_cast<int>(sorting) >= 0 && static_cast<int>(sorting) <= dolphinMapSize);
+    sort(dolphinViewToDirModelColumn[static_cast<int>(sorting)],
          m_sortOrder );
 }
 
-/**
- * @reimplemented, @internal
- *
- * If the view 'forces' sorting order to change we will
- * notice now.
- */
 void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
 {
     m_sortOrder = sortOrder;
-    m_sorting = column >= 0 && column <= dolphin_map_size ?
-                dir_model_column_to_dolphin_view[column]  :
-                DolphinView::SortByName; 
+    m_sorting = (column >= 0) && (column <= dolphinMapSize) ?
+                dirModelColumnToDolphinView[column]  :
+                DolphinView::SortByName;
     QSortFilterProxyModel::sort(column,sortOrder);
 }
 
-/*
- * change the sort order by keeping the current column
- */
 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
 {
-    sort(dolphin_view_to_dir_model_column[m_sorting], sortOrder);
+    // change the sort order by keeping the current column
+    sort(dolphinViewToDirModelColumn[m_sorting], sortOrder);
 }
 
+static int 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 sub_a(begSeqA, currA - begSeqA);
+        const QString sub_b(begSeqB, currB - begSeqB);
+        int cmp = QString::localeAwareCompare(sub_a, sub_b);
+        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 aligend)
+            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, so we
+            // remember it in 'weight'.
+
+            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;
+}
+
+
 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
                                            const QModelIndex& right) const
 {
-    /*
-     * We have set a SortRole and trust the ProxyModel to do
-     * the right thing for now
-     */
+
+    QVariant leftData = sourceModel()->data(left, sortRole());
+    QVariant rightData = sourceModel()->data(right, sortRole());
+
+    if (leftData.type() == QVariant::String && rightData.type() == QVariant::String) {
+        const QString left = leftData.toString();
+        const QString right = rightData.toString();
+
+        return sortCaseSensitivity() ? (naturalCompare(left, right) < 0) :
+                                       (naturalCompare(left.toLower(), right.toLower()) < 0);
+    }
+
+    // We have set a SortRole and trust the ProxyModel to do
+    // the right thing for now.
     return QSortFilterProxyModel::lessThan(left,right);
 }
 
index e2c605dc72015e0b0ae20a3f2b36af4379b1da35..0ca1db535ea0d380ef41d78851367e35ebacbba6 100644 (file)
  * @brief Acts as proxy model for KDirModel to sort and filter
  *        KFileItems.
  *
- * TODO: implementation does not work yet (the method lessThan is
- * not invoked)
+ * A natural sorting is done. This means that items like:
+ * - item_10.png
+ * - item_1.png
+ * - item_2.png
+ * are sorted like
+ * - item_1.png
+ * - item_2.png
+ * - item_10.png
  */
 class DolphinSortFilterProxyModel : public QSortFilterProxyModel
 {
@@ -41,7 +47,13 @@ public:
     void setSorting(DolphinView::Sorting sorting);
     DolphinView::Sorting sorting() const { return m_sorting; }
 
-    virtual void sort (int column, 
+    /**
+     * @reimplemented, @internal
+     *
+     * If the view 'forces' sorting order to change we will
+     * notice now.
+     */
+    virtual void sort (int column,
                        Qt::SortOrder order = Qt::AscendingOrder);
     void setSortOrder(Qt::SortOrder sortOrder);
     Qt::SortOrder sortOrder() const { return m_sortOrder; }