]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodel.h
Introduce "isExpandable" role
[dolphin.git] / src / kitemviews / kfileitemmodel.h
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #ifndef KFILEITEMMODEL_H
21 #define KFILEITEMMODEL_H
22
23 #include <libdolphin_export.h>
24 #include <KFileItemList>
25 #include <KUrl>
26 #include <kitemviews/kfileitemmodelfilter_p.h>
27 #include <kitemviews/kitemmodelbase.h>
28
29 #include <QHash>
30
31 class KDirLister;
32 class QTimer;
33
34 /**
35 * @brief KItemModelBase implementation for KFileItems.
36 *
37 * KFileItemModel is connected with one KDirLister. Each time the KDirLister
38 * emits new items, removes items or changes items the model gets synchronized.
39 *
40 * KFileItemModel supports sorting and grouping of items. Additional roles that
41 * are not part of KFileItem can be added with KFileItemModel::setData().
42 *
43 * Also the recursive expansion of sub-directories is supported by
44 * KFileItemModel::setExpanded().
45 *
46 * TODO: In the longterm instead of passing a KDirLister just an URL should
47 * be passed and a KDirLister used internally. This solves the following issues:
48 * - The user of the API does not need to decide whether he listens to KDirLister
49 * or KFileItemModel.
50 * - It resolves minor conceptual differences between KDirLister and KFileItemModel.
51 * E.g. there is no way for KFileItemModel to check whether a completed() signal
52 * will be emitted after newItems() will be send by KDirLister or not (in the case
53 * of setShowingDotFiles() no completed() signal will get emitted).
54 */
55 class LIBDOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase
56 {
57 Q_OBJECT
58
59 public:
60 explicit KFileItemModel(KDirLister* dirLister, QObject* parent = 0);
61 virtual ~KFileItemModel();
62
63 virtual int count() const;
64 virtual QHash<QByteArray, QVariant> data(int index) const;
65 virtual bool setData(int index, const QHash<QByteArray, QVariant>& values);
66
67 /**
68 * Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false).
69 */
70 void setSortFoldersFirst(bool foldersFirst);
71 bool sortFoldersFirst() const;
72
73 void setShowHiddenFiles(bool show);
74 bool showHiddenFiles() const;
75
76 /** @reimp */
77 virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
78
79 /** @reimp */
80 virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;
81
82 /** @reimp */
83 virtual bool supportsDropping(int index) const;
84
85 /** @reimp */
86 virtual QString roleDescription(const QByteArray& role) const;
87
88 /** @reimp */
89 virtual QList<QPair<int, QVariant> > groups() const;
90
91 /**
92 * @return The file-item for the index \a index. If the index is in a valid
93 * range it is assured that the file-item is not null. The runtime
94 * complexity of this call is O(1).
95 */
96 KFileItem fileItem(int index) const;
97
98 /**
99 * @return The file-item for the url \a url. If no file-item with the given
100 * URL is found KFileItem::isNull() will be true for the returned
101 * file-item. The runtime complexity of this call is O(1).
102 */
103 KFileItem fileItem(const KUrl& url) const;
104
105 /**
106 * @return The index for the file-item \a item. -1 is returned if no file-item
107 * is found or if the file-item is null. The runtime
108 * complexity of this call is O(1).
109 */
110 int index(const KFileItem& item) const;
111
112 /**
113 * @return The index for the URL \a url. -1 is returned if no file-item
114 * is found. The runtime complexity of this call is O(1).
115 */
116 int index(const KUrl& url) const;
117
118 /**
119 * @return Root item of all items.
120 */
121 KFileItem rootItem() const;
122
123 /**
124 * Clears all items of the model.
125 */
126 void clear();
127
128 // TODO: "name" + "isDir" is default in ctor
129 void setRoles(const QSet<QByteArray>& roles);
130 QSet<QByteArray> roles() const;
131
132 virtual bool setExpanded(int index, bool expanded);
133 virtual bool isExpanded(int index) const;
134 virtual bool isExpandable(int index) const;
135
136 QSet<KUrl> expandedUrls() const;
137
138 /**
139 * Marks the URLs in \a urls as subfolders which were expanded previously.
140 * They are re-expanded one by one each time the KDirLister's completed() signal is received.
141 * Note that a manual triggering of the KDirLister is required.
142 */
143 void restoreExpandedUrls(const QSet<KUrl>& urls);
144
145 /**
146 * Expands all parent-items of each URL given by \a urls.
147 */
148 void setExpanded(const QSet<KUrl>& urls);
149
150 void setNameFilter(const QString& nameFilter);
151 QString nameFilter() const;
152
153 signals:
154 /**
155 * Is emitted after the loading of a directory has been completed or new
156 * items have been inserted to an already loaded directory. Usually
157 * one or more itemsInserted() signals are emitted before loadingCompleted()
158 * (the only exception is loading an empty directory, where only a
159 * loadingCompleted() signal gets emitted).
160 */
161 void loadingCompleted();
162
163 protected:
164 virtual void onGroupedSortingChanged(bool current);
165 virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
166 virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
167
168 private slots:
169 /**
170 * Resorts all items dependent on the set sortRole(), sortOrder()
171 * and foldersFirst() settings.
172 */
173 void resortAllItems();
174
175 void slotCompleted();
176 void slotCanceled();
177 void slotNewItems(const KFileItemList& items);
178 void slotItemsDeleted(const KFileItemList& items);
179 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
180 void slotClear();
181 void slotClear(const KUrl& url);
182
183 void dispatchPendingItemsToInsert();
184
185 private:
186 enum Role {
187 NoRole,
188 NameRole,
189 SizeRole,
190 DateRole,
191 PermissionsRole,
192 OwnerRole,
193 GroupRole,
194 TypeRole,
195 DestinationRole,
196 PathRole,
197 CommentRole,
198 TagsRole,
199 RatingRole,
200 IsDirRole,
201 IsExpandedRole,
202 IsExpandableRole,
203 ExpansionLevelRole,
204 RolesCount // Mandatory last entry
205 };
206
207 struct ItemData
208 {
209 KFileItem item;
210 QHash<QByteArray, QVariant> values;
211 ItemData* parent;
212 };
213
214 void insertItems(const KFileItemList& items);
215 void removeItems(const KFileItemList& items);
216
217 /**
218 * Helper method for insertItems() and removeItems(): Creates
219 * a list of ItemData elements based on the given items.
220 * Note that the ItemData instances are created dynamically and
221 * must be deleted by the caller.
222 */
223 QList<ItemData*> createItemDataList(const KFileItemList& items) const;
224
225 void removeExpandedItems();
226
227 /**
228 * Resets all values from m_requestRole to false.
229 */
230 void resetRoles();
231
232 Role roleIndex(const QByteArray& role) const;
233
234 QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
235
236 /**
237 * @return True if the item-data \a a should be ordered before the item-data
238 * \b. The item-data may have different parent-items.
239 */
240 bool lessThan(const ItemData* a, const ItemData* b) const;
241
242 /**
243 * Helper method for lessThan() and expansionLevelsCompare(): Compares
244 * the passed item-data using m_sortRole as criteria. Both items must
245 * have the same parent item, otherwise the comparison will be wrong.
246 */
247 int sortRoleCompare(const ItemData* a, const ItemData* b) const;
248
249 /**
250 * Sorts the items by using lessThan() as comparison criteria.
251 * The merge sort algorithm is used to assure a worst-case
252 * of O(n * log(n)) and to keep the number of comparisons low.
253 */
254 void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
255
256 /** Helper method for sort(). */
257 void merge(QList<ItemData*>::iterator begin,
258 QList<ItemData*>::iterator pivot,
259 QList<ItemData*>::iterator end);
260
261 /** Helper method for sort(). */
262 QList<ItemData*>::iterator lowerBound(QList<ItemData*>::iterator begin,
263 QList<ItemData*>::iterator end,
264 const ItemData* value);
265
266 /** Helper method for sort(). */
267 QList<ItemData*>::iterator upperBound(QList<ItemData*>::iterator begin,
268 QList<ItemData*>::iterator end,
269 const ItemData* value);
270 /** Helper method for sort(). */
271 void reverse(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
272
273 int stringCompare(const QString& a, const QString& b) const;
274
275 /**
276 * Compares the expansion level of both items. The "expansion level" is defined
277 * by the number of parent directories. However simply comparing just the numbers
278 * is not sufficient, it is also important to check the hierarchy for having
279 * a correct order like shown in a tree.
280 */
281 int expansionLevelsCompare(const ItemData* a, const ItemData* b) const;
282
283 /**
284 * Helper method for expansionLevelCompare().
285 */
286 QString subPath(const KFileItem& item,
287 const QString& itemPath,
288 int start,
289 bool* isDir) const;
290
291 bool useMaximumUpdateInterval() const;
292
293 QList<QPair<int, QVariant> > nameRoleGroups() const;
294 QList<QPair<int, QVariant> > sizeRoleGroups() const;
295 QList<QPair<int, QVariant> > dateRoleGroups() const;
296 QList<QPair<int, QVariant> > permissionRoleGroups() const;
297 QList<QPair<int, QVariant> > ratingRoleGroups() const;
298 QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& role) const;
299
300 /**
301 * Helper method for all xxxRoleGroups() methods to check whether the
302 * item with the given index is a child-item. A child-item is defined
303 * as item having an expansion-level > 0. All xxxRoleGroups() methods
304 * should skip the grouping if the item is a child-item (although
305 * KItemListView would be capable to show sub-groups in groups this
306 * results in visual clutter for most usecases).
307 */
308 bool isChildItem(int index) const;
309
310 /**
311 * @return Recursive list of child items that have \a item as upper most parent.
312 */
313 KFileItemList childItems(const KFileItem& item) const;
314
315 private:
316 QWeakPointer<KDirLister> m_dirLister;
317
318 bool m_naturalSorting;
319 bool m_sortFoldersFirst;
320
321 Role m_sortRole;
322 QSet<QByteArray> m_roles;
323 Qt::CaseSensitivity m_caseSensitivity;
324
325 QList<ItemData*> m_itemData;
326 QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
327
328 KFileItemModelFilter m_filter;
329 QSet<KFileItem> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
330
331 bool m_requestRole[RolesCount];
332
333 QTimer* m_minimumUpdateIntervalTimer;
334 QTimer* m_maximumUpdateIntervalTimer;
335 QTimer* m_resortAllItemsTimer;
336 KFileItemList m_pendingItemsToInsert;
337 bool m_pendingEmitLoadingCompleted;
338
339 // Cache for KFileItemModel::groups()
340 mutable QList<QPair<int, QVariant> > m_groups;
341
342 // Stores the smallest expansion level of the root-URL. Is required to calculate
343 // the "expansionLevel" role in an efficient way. A value < 0 indicates a
344 // special meaning:
345 enum RootExpansionLevelTypes
346 {
347 // m_rootExpansionLevel is uninitialized and must be determined by checking
348 // the root URL from the KDirLister.
349 UninitializedRootExpansionLevel = -1,
350 // All items should be forced to get an expansion level of 0 even if they
351 // represent child items. This is useful for slaves that provide no parent items
352 // for child items like e.g. the search IO slaves.
353 ForceRootExpansionLevel = -2
354 };
355 mutable int m_rootExpansionLevel;
356
357 // Stores the URLs of the expanded folders.
358 QSet<KUrl> m_expandedUrls;
359
360 // URLs that must be expanded. The expanding is initially triggered in setExpanded()
361 // and done step after step in slotCompleted().
362 QSet<KUrl> m_urlsToExpand;
363
364 friend class KFileItemModelTest; // For unit testing
365 };
366
367 inline bool KFileItemModel::isChildItem(int index) const
368 {
369 return m_requestRole[ExpansionLevelRole] && m_itemData.at(index)->values.value("expansionLevel").toInt() > 0;
370 }
371
372 #endif
373
374