┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/kitemviews/kfileitemmodel.h
blob: b28887b2c79e901cbab50dd890fb5d5e4f331f11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
111
112
113
114
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
146
147
148
149
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/***************************************************************************
 *   Copyright (C) 2011 by Peter Penz <[email protected]>             *
 *                                                                         *
 *   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            *
 ***************************************************************************/

#ifndef KFILEITEMMODEL_H
#define KFILEITEMMODEL_H

#include <libdolphin_export.h>
#include <KFileItemList>
#include <KUrl>
#include <kitemviews/kitemmodelbase.h>

#include <QHash>

class KDirLister;
class QTimer;

/**
 * @brief KItemModelBase implementation for KFileItems.
 *
 * KFileItemModel is connected with one KDirLister. Each time the KDirLister
 * emits new items, removes items or changes items the model gets synchronized.
 *
 * KFileItemModel supports sorting and grouping of items. Additional roles that
 * are not part of KFileItem can be added with KFileItemModel::setData().
 *
 * Also the recursive expansion of sub-directories is supported by
 * KFileItemModel::setExpanded().
 */
class LIBDOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase
{
    Q_OBJECT

public:
    explicit KFileItemModel(KDirLister* dirLister, QObject* parent = 0);
    virtual ~KFileItemModel();

    virtual int count() const;
    virtual QHash<QByteArray, QVariant> data(int index) const;
    virtual bool setData(int index, const QHash<QByteArray, QVariant>& values);

    /**
     * Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false).
     */
    void setSortFoldersFirst(bool foldersFirst);
    bool sortFoldersFirst() const;

    /** @reimp */
    virtual QMimeData* createMimeData(const QSet<int>& indexes) const;

    /** @reimp */
    virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;

    /** @reimp */
    virtual bool supportsDropping(int index) const;

    /** @reimp */
    virtual QString roleDescription(const QByteArray& role) const;

    /** @reimp */
    virtual QList<QPair<int, QVariant> > groups() const;

    /**
     * @return The file-item for the index \a index. If the index is in a valid
     *         range it is assured that the file-item is not null. The runtime
     *         complexity of this call is O(1).
     */
    KFileItem fileItem(int index) const;

    /**
     * @return The file-item for the url \a url. If no file-item with the given
     *         URL is found KFileItem::isNull() will be true for the returned
     *         file-item. The runtime complexity of this call is O(1).
     */
    KFileItem fileItem(const KUrl& url) const;

    /**
     * @return The index for the file-item \a item. -1 is returned if no file-item
     *         is found or if the file-item is null. The runtime
     *         complexity of this call is O(1).
     */
    int index(const KFileItem& item) const;

    /**
     * @return The index for the URL \a url. -1 is returned if no file-item
     *         is found. The runtime complexity of this call is O(1).
     */
    int index(const KUrl& url) const;

    /**
     * @return Root item of all items.
     */
    KFileItem rootItem() const;

    /**
     * Clears all items of the model.
     */
    void clear();

    // TODO: "name" + "isDir" is default in ctor
    void setRoles(const QSet<QByteArray>& roles);
    QSet<QByteArray> roles() const;

    bool setExpanded(int index, bool expanded);
    bool isExpanded(int index) const;
    bool isExpandable(int index) const;
    QSet<KUrl> expandedUrls() const;
    void restoreExpandedUrls(const QSet<KUrl>& urls);

signals:
    void loadingCompleted();

protected:
    virtual void onGroupedSortingChanged(bool current);
    virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
    virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);

private slots:
    /**
     * Resorts all items dependent on the set sortRole(), sortOrder()
     * and foldersFirst() settings.
     */
    void resortAllItems();
    
    void slotCompleted();
    void slotCanceled();
    void slotNewItems(const KFileItemList& items);
    void slotItemsDeleted(const KFileItemList& items);
    void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
    void slotClear();
    void slotClear(const KUrl& url);

    void dispatchPendingItemsToInsert();

private:
    enum Role {
        NoRole,
        NameRole,
        SizeRole,
        DateRole,
        PermissionsRole,
        OwnerRole,
        GroupRole,
        TypeRole,
        DestinationRole,
        PathRole,
        CommentRole,
        TagsRole,
        RatingRole,
        IsDirRole,
        IsExpandedRole,
        ExpansionLevelRole,
        RolesCount // Mandatory last entry
    };

    struct ItemData
    {
        KFileItem item;
        QHash<QByteArray, QVariant> values;
    };
    
    void insertItems(const KFileItemList& items);
    void removeItems(const KFileItemList& items);
    
    /**
     * Helper method for insertItems() and removeItems(): Creates
     * a list of ItemData elements based on the given items.
     * Note that the ItemData instances are created dynamically and
     * must be deleted by the caller.
     */
    QList<ItemData*> createItemDataList(const KFileItemList& items) const;

    void removeExpandedItems();

    /**
     * Resets all values from m_requestRole to false.
     */
    void resetRoles();

    Role roleIndex(const QByteArray& role) const;

    QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
    
    bool lessThan(const ItemData* a, const ItemData* b) const;
    
    /**
     * Sorts the items by using lessThan() as comparison criteria.
     * The merge sort algorithm is used to assure a worst-case
     * of O(n * log(n)) and to keep the number of comparisons low.
     */
    void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
    
    /** Helper method for sort(). */
    void merge(QList<ItemData*>::iterator begin,
               QList<ItemData*>::iterator pivot,
               QList<ItemData*>::iterator end);

    /** Helper method for sort(). */
    QList<ItemData*>::iterator lowerBound(QList<ItemData*>::iterator begin,
                                          QList<ItemData*>::iterator end,
                                          const ItemData* value);
    
    /** Helper method for sort(). */
    QList<ItemData*>::iterator upperBound(QList<ItemData*>::iterator begin,
                                          QList<ItemData*>::iterator end,
                                          const ItemData* value);
    /** Helper method for sort(). */
    void reverse(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
    
    int stringCompare(const QString& a, const QString& b) const;

    /**
     * Compares the expansion level of both items. The "expansion level" is defined
     * by the number of parent directories. However simply comparing just the numbers
     * is not sufficient, it is also important to check the hierarchy for having
     * a correct order like shown in a tree.
     */
    int expansionLevelsCompare(const KFileItem& a, const KFileItem& b) const;

    /**
     * Helper method for expansionLevelCompare().
     */
    QString subPath(const KFileItem& item,
                    const QString& itemPath,
                    int start,
                    bool* isDir) const;

    bool useMaximumUpdateInterval() const;

    QList<QPair<int, QVariant> > nameRoleGroups() const;
    QList<QPair<int, QVariant> > sizeRoleGroups() const;
    QList<QPair<int, QVariant> > dateRoleGroups() const;
    QList<QPair<int, QVariant> > permissionRoleGroups() const;
    QList<QPair<int, QVariant> > ratingRoleGroups() const;
    QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& role) const;

    /**
     * Helper method for all xxxRoleGroups() methods to check whether the
     * item with the given index is a child-item. A child-item is defined
     * as item having an expansion-level > 0. All xxxRoleGroups() methods
     * should skip the grouping if the item is a child-item (although
     * KItemListView would be capable to show sub-groups in groups this
     * results in visual clutter for most usecases).
     */
    bool isChildItem(int index) const;

private:
    QWeakPointer<KDirLister> m_dirLister;

    bool m_naturalSorting;
    bool m_sortFoldersFirst;

    Role m_sortRole;
    QSet<QByteArray> m_roles;
    Qt::CaseSensitivity m_caseSensitivity;
        
    QList<ItemData*> m_itemData;
    QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)

    bool m_requestRole[RolesCount];

    QTimer* m_minimumUpdateIntervalTimer;
    QTimer* m_maximumUpdateIntervalTimer;
    QTimer* m_resortAllItemsTimer;
    KFileItemList m_pendingItemsToInsert;
    bool m_pendingEmitLoadingCompleted;

    // Cache for KFileItemModel::groups()
    mutable QList<QPair<int, QVariant> > m_groups;

    // Stores the smallest expansion level of the root-URL. Is required to calculate
    // the "expansionLevel" role in an efficient way. A value < 0 indicates that
    // it has not been initialized yet.
    mutable int m_rootExpansionLevel;

    // Stores the URLs of the expanded folders.
    QSet<KUrl> m_expandedUrls;

    // Stores the URLs which have to be expanded in order to restore a previous state of the model.
    QSet<KUrl> m_restoredExpandedUrls;

    friend class KFileItemModelTest; // For unit testing
};

inline bool KFileItemModel::isChildItem(int index) const
{
    return m_requestRole[ExpansionLevelRole] && m_itemData.at(index)->values.value("expansionLevel").toInt() > 0;
}

#endif