dolphinsortfilterproxymodel.cpp 9.2 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>

Peter Penz's avatar
Peter Penz committed
27
static const int dolphinMapSize = 7;
28
static int dolphinViewToDirModelColumn[] =
Peter Penz's avatar
Peter Penz committed
29
30
31
32
33
34
35
36
37
{
    KDirModel::Name,         // DolphinView::SortByName
    KDirModel::Size,         // DolphinView::SortBySize
    KDirModel::ModifiedTime, // DolphinView::SortByDate
    KDirModel::Permissions,  // DolphinView::SortByPermissions
    KDirModel::Owner,        // DolphinView::SortByOwner
    KDirModel::Group,        // DolphinView::SortByGroup
    KDirModel::Type          // DolphinView::SortByType
};
38
39

static DolphinView::Sorting dirModelColumnToDolphinView[] =
Peter Penz's avatar
Peter Penz committed
40
41
42
43
44
45
46
47
48
{
    DolphinView::SortByName,        // KDirModel::Name
    DolphinView::SortBySize,        // KDirModel::Size
    DolphinView::SortByDate,        // KDirModel::ModifiedTime
    DolphinView::SortByPermissions, // KDirModel::Permissions
    DolphinView::SortByOwner,       // KDirModel::Owner
    DolphinView::SortByGroup,       // KDirModel::Group
    DolphinView::SortByType         // KDirModel::Type
};
49
50


51
DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
52
53
54
55
    QSortFilterProxyModel(parent),
    m_sortColumn(0),
    m_sorting(DolphinView::SortByName),
    m_sortOrder(Qt::AscendingOrder)
56
{
57
58
    setDynamicSortFilter(true);

59
    // sort by the user visible string for now
60
61
    setSortRole(Qt::DisplayRole);
    setSortCaseSensitivity(Qt::CaseInsensitive);
62
    sort(KDirModel::Name, Qt::AscendingOrder);
63
64
65
}

DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
66
{}
67
68
69

void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting)
{
70
71
    // Update the sort column by mapping DolpginView::Sorting to
    // KDirModel::ModelColumns. We will keep the sortOrder.
72
    Q_ASSERT(static_cast<int>(sorting) >= 0 && static_cast<int>(sorting) < dolphinMapSize);
73
    sort(dolphinViewToDirModelColumn[static_cast<int>(sorting)],
74
         m_sortOrder);
75
76
}

77
78
79
80
81
82
void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
{
    // change the sort order by keeping the current column
    sort(dolphinViewToDirModelColumn[m_sorting], sortOrder);
}

83
84
void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
{
85
    m_sortColumn = column;
86
    m_sortOrder = sortOrder;
87
    m_sorting = (column >= 0) && (column < dolphinMapSize) ?
88
89
                dirModelColumnToDolphinView[column]  :
                DolphinView::SortByName;
90
    QSortFilterProxyModel::sort(column, sortOrder);
91
92
}

93
94
95
96
97
98
99
100
101
102
103
104
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);
}

105
106
DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
{
107
    if ((column >= 0) && (column < dolphinMapSize)) {
108
109
110
111
112
        return dirModelColumnToDolphinView[column];
    }
    return DolphinView::SortByName;
}

113
bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
114
        const QModelIndex& right) const
115
{
116
117
118
119
120
121
122
    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)) {
        // assure that directories are always sorted before files
123
124
125
126
127
128
129
130
131
132
133
        // if the sorting is done by the 'Name' column
        if (m_sortColumn == KDirModel::Name) {
            const bool leftIsDir  = dirModel->itemForIndex(left)->isDir();
            const bool rightIsDir = dirModel->itemForIndex(right)->isDir();
            if (leftIsDir && !rightIsDir) {
                return true;
            }

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

136
137
        const QString leftStr  = leftData.toString();
        const QString rightStr = rightData.toString();
138
139

        return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
140
               (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
141
142
143
144
145
    }

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

148
int DolphinSortFilterProxyModel::naturalCompare(const QString& a,
149
        const QString& b)
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
{
    // 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
181
182
183
        const QString subA(begSeqA, currA - begSeqA);
        const QString subB(begSeqB, currB - begSeqB);
        const int cmp = QString::localeAwareCompare(subA, subB);
184
185
186
187
188
189
190
191
192
193
194
        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
195
            // do left aligned comparison (numbers are considered left aligned)
196
            while (1) {
197
198
                if (!currA->isDigit() && !currB->isDigit()) {
                    break;
199
                } else if (!currA->isDigit()) {
200
                    return -1;
201
202
203
                } else if (!currB->isDigit()) {
                    return + 1;
                } else if (*currA < *currB) {
204
                    return -1;
205
206
                } else if (*currA > *currB) {
                    return + 1;
207
208
209
210
                }
                ++currA;
                ++currB;
            }
211
        } else {
212
213
214
215
            // 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
216
            // value wins, but we can't know that it will until we've scanned
217
            // both numbers to know that they have the same magnitude.
218

219
            int weight = 0;
220
221
            while (1) {
                if (!currA->isDigit() && !currB->isDigit()) {
222
223
                    if (weight != 0) {
                        return weight;
224
225
                    }
                    break;
226
                } else if (!currA->isDigit()) {
227
                    return -1;
228
229
230
                } else if (!currB->isDigit()) {
                    return + 1;
                } else if ((*currA < *currB) && (weight == 0)) {
231
                    weight = -1;
232
233
                } else if ((*currA > *currB) && (weight == 0)) {
                    weight = + 1;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
                }
                ++currA;
                ++currB;
            }
        }

        begSeqA = currA;
        begSeqB = currB;
    }

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

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

251
#include "dolphinsortfilterproxymodel.moc"