dolphinsortfilterproxymodel.cpp 8.13 KB
Newer Older
1
/***************************************************************************
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*   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            *
***************************************************************************/
21
22
23
24
25
26

#include "dolphinsortfilterproxymodel.h"

#include <kdirmodel.h>
#include <kfileitem.h>

27
28
29
30
31
static const int dolphinMapSize = 3;
static int dolphinViewToDirModelColumn[] = {
    KDirModel::Name,        // DolphinView::SortByName
    KDirModel::Size,        // DolphinView::SortBySize
    KDirModel::ModifiedTime // DolphinView::SortByDate
32
33
};

34
35
36
37
static DolphinView::Sorting dirModelColumnToDolphinView[] = {
    DolphinView::SortByName, // KDirModel::Name
    DolphinView::SortBySize, // KDirModel::Size
    DolphinView::SortByDate  // KDirModel::ModifiedTime
38
39
40
};


41
DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
42
    QSortFilterProxyModel(parent)
43
{
44
45
    setDynamicSortFilter(true);

46
    // sort by the user visible string for now
47
48
49
    setSortRole(Qt::DisplayRole);
    setSortCaseSensitivity(Qt::CaseInsensitive);
    sort(KDirModel::Name, Qt::Ascending);
50
51
52
53
54
55
56
57
}

DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
{
}

void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting)
{
58
59
60
61
    // 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)],
62
63
64
         m_sortOrder );
}

65
66
67
68
69
70
void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
{
    // change the sort order by keeping the current column
    sort(dolphinViewToDirModelColumn[m_sorting], sortOrder);
}

71
72
73
void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
{
    m_sortOrder = sortOrder;
74
75
76
    m_sorting = (column >= 0) && (column <= dolphinMapSize) ?
                dirModelColumnToDolphinView[column]  :
                DolphinView::SortByName;
77
    QSortFilterProxyModel::sort(column,sortOrder);
78
79
}

80
81
bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
                                           const QModelIndex& right) const
82
{
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());

    QVariant leftData  = dirModel->data(left,  sortRole());
    QVariant rightData = dirModel->data(right, sortRole());

    if ((leftData.type() == QVariant::String) && (rightData.type() == QVariant::String)) {
        const QString leftStr  = leftData.toString();
        const QString rightStr = rightData.toString();

        const bool leftIsDir  = dirModel->itemForIndex(left)->isDir();
        const bool rightIsDir = dirModel->itemForIndex(right)->isDir();

        // assure that directories are always sorted before files
        if (leftIsDir && !rightIsDir) {
            return true;
        }

        if (!leftIsDir && rightIsDir) {
            return false;
        }

        return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
                                       (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
    }

    // We have set a SortRole and trust the ProxyModel to do
    // the right thing for now.
    return QSortFilterProxyModel::lessThan(left, right);
111
112
}

113
int DolphinSortFilterProxyModel::naturalCompare(const QString& a,
114
                                                const QString& b)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
{
    // 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
146
147
148
        const QString subA(begSeqA, currA - begSeqA);
        const QString subB(begSeqB, currB - begSeqB);
        const int cmp = QString::localeAwareCompare(subA, subB);
149
150
151
152
153
154
155
156
157
158
159
        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
160
            // do left aligned comparison (numbers are considered left aligned)
161
            while (1) {
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
                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 {
182
183
184
185
            // 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
186
187
            // 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
188
189
190
191
            // remember the values in 'valueA' and 'valueB'.

            int valueA = 0;
            int valueB = 0;
192
193
194

            while (1) {
                if (!currA->isDigit() && !currB->isDigit()) {
195
196
                    if (valueA != valueB) {
                        return valueA - valueB;
197
198
199
200
201
202
203
204
205
                    }
                    break;
                }
                else if (!currA->isDigit()) {
                    return -1;
                }
                else if (!currB->isDigit()) {
                    return +1;
                }
206
207
208
                else {
                    valueA = (valueA * 10) + currA->digitValue();
                    valueB = (valueB * 10) + currB->digitValue();
209
                }
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
                ++currA;
                ++currB;
            }
        }

        begSeqA = currA;
        begSeqB = currB;
    }

    if (currA->isNull() && currB->isNull()) {
        return 0;
    }

    return currA->isNull() ? -1 : +1;
}

227
#include "dolphinsortfilterproxymodel.moc"