From: Peter Penz Date: Thu, 11 Jan 2007 06:44:46 +0000 (+0000) Subject: Do a natural sorting of items (thanks to Dominic Battre and Martin Pool for the patch... X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/7954282089791685f5eda150d7ba0925bccf927f Do a natural sorting of items (thanks to Dominic Battre and Martin Pool for the patch!). 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 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 --- diff --git a/src/dolphinsortfilterproxymodel.cpp b/src/dolphinsortfilterproxymodel.cpp index fc79ec569..e983007c1 100644 --- a/src/dolphinsortfilterproxymodel.cpp +++ b/src/dolphinsortfilterproxymodel.cpp @@ -1,39 +1,40 @@ /*************************************************************************** - * Copyright (C) 2006 by Peter Penz * - * Copyright (C) 2006 by Gregor Kališnik * - * * - * 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 * +* Copyright (C) 2006 by Dominic Battre * +* Copyright (C) 2006 by Martin Pool * +* * +* 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 #include -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(sorting) >= 0 && static_cast(sorting) <= dolphin_map_size ); - sort(dolphin_view_to_dir_model_column[static_cast(sorting)], + // Update the sort column by mapping DolpginView::Sorting to + // KDirModel::ModelColumns. We will keep the sortOrder. + Q_ASSERT(static_cast(sorting) >= 0 && static_cast(sorting) <= dolphinMapSize); + sort(dolphinViewToDirModelColumn[static_cast(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); } diff --git a/src/dolphinsortfilterproxymodel.h b/src/dolphinsortfilterproxymodel.h index e2c605dc7..0ca1db535 100644 --- a/src/dolphinsortfilterproxymodel.h +++ b/src/dolphinsortfilterproxymodel.h @@ -27,8 +27,14 @@ * @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; }