]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodel.h
Don't show a expanding-toggle in the Folders Panel if there are no subdirectories
[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 /**
77 * If set to true, only folders are shown as items of the model. Files
78 * are ignored.
79 */
80 void setShowFoldersOnly(bool enabled);
81 bool showFoldersOnly() const;
82
83 /** @reimp */
84 virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
85
86 /** @reimp */
87 virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;
88
89 /** @reimp */
90 virtual bool supportsDropping(int index) const;
91
92 /** @reimp */
93 virtual QString roleDescription(const QByteArray& role) const;
94
95 /** @reimp */
96 virtual QList<QPair<int, QVariant> > groups() const;
97
98 /**
99 * @return The file-item for the index \a index. If the index is in a valid
100 * range it is assured that the file-item is not null. The runtime
101 * complexity of this call is O(1).
102 */
103 KFileItem fileItem(int index) const;
104
105 /**
106 * @return The file-item for the url \a url. If no file-item with the given
107 * URL is found KFileItem::isNull() will be true for the returned
108 * file-item. The runtime complexity of this call is O(1).
109 */
110 KFileItem fileItem(const KUrl& url) const;
111
112 /**
113 * @return The index for the file-item \a item. -1 is returned if no file-item
114 * is found or if the file-item is null. The runtime
115 * complexity of this call is O(1).
116 */
117 int index(const KFileItem& item) const;
118
119 /**
120 * @return The index for the URL \a url. -1 is returned if no file-item
121 * is found. The runtime complexity of this call is O(1).
122 */
123 int index(const KUrl& url) const;
124
125 /**
126 * @return Root item of all items.
127 */
128 KFileItem rootItem() const;
129
130 /**
131 * Clears all items of the model.
132 */
133 void clear();
134
135 // TODO: "name" + "isDir" is default in ctor
136 void setRoles(const QSet<QByteArray>& roles);
137 QSet<QByteArray> roles() const;
138
139 virtual bool setExpanded(int index, bool expanded);
140 virtual bool isExpanded(int index) const;
141 virtual bool isExpandable(int index) const;
142
143 QSet<KUrl> expandedUrls() const;
144
145 /**
146 * Marks the URLs in \a urls as subfolders which were expanded previously.
147 * They are re-expanded one by one each time the KDirLister's completed() signal is received.
148 * Note that a manual triggering of the KDirLister is required.
149 */
150 void restoreExpandedUrls(const QSet<KUrl>& urls);
151
152 /**
153 * Expands all parent-items of each URL given by \a urls.
154 */
155 void setExpanded(const QSet<KUrl>& urls);
156
157 void setNameFilter(const QString& nameFilter);
158 QString nameFilter() const;
159
160 signals:
161 /**
162 * Is emitted after the loading of a directory has been completed or new
163 * items have been inserted to an already loaded directory. Usually
164 * one or more itemsInserted() signals are emitted before loadingCompleted()
165 * (the only exception is loading an empty directory, where only a
166 * loadingCompleted() signal gets emitted).
167 */
168 void loadingCompleted();
169
170 protected:
171 virtual void onGroupedSortingChanged(bool current);
172 virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
173 virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
174
175 private slots:
176 /**
177 * Resorts all items dependent on the set sortRole(), sortOrder()
178 * and foldersFirst() settings.
179 */
180 void resortAllItems();
181
182 void slotCompleted();
183 void slotCanceled();
184 void slotNewItems(const KFileItemList& items);
185 void slotItemsDeleted(const KFileItemList& items);
186 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
187 void slotClear();
188 void slotClear(const KUrl& url);
189
190 void dispatchPendingItemsToInsert();
191
192 private:
193 enum Role {
194 NoRole,
195 NameRole,
196 SizeRole,
197 DateRole,
198 PermissionsRole,
199 OwnerRole,
200 GroupRole,
201 TypeRole,
202 DestinationRole,
203 PathRole,
204 CommentRole,
205 TagsRole,
206 RatingRole,
207 IsDirRole,
208 IsExpandedRole,
209 IsExpandableRole,
210 ExpansionLevelRole,
211 RolesCount // Mandatory last entry
212 };
213
214 struct ItemData
215 {
216 KFileItem item;
217 QHash<QByteArray, QVariant> values;
218 ItemData* parent;
219 };
220
221 void insertItems(const KFileItemList& items);
222 void removeItems(const KFileItemList& items);
223
224 /**
225 * Helper method for insertItems() and removeItems(): Creates
226 * a list of ItemData elements based on the given items.
227 * Note that the ItemData instances are created dynamically and
228 * must be deleted by the caller.
229 */
230 QList<ItemData*> createItemDataList(const KFileItemList& items) const;
231
232 void removeExpandedItems();
233
234 /**
235 * Resets all values from m_requestRole to false.
236 */
237 void resetRoles();
238
239 Role roleIndex(const QByteArray& role) const;
240
241 QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
242
243 /**
244 * @return True if the item-data \a a should be ordered before the item-data
245 * \b. The item-data may have different parent-items.
246 */
247 bool lessThan(const ItemData* a, const ItemData* b) const;
248
249 /**
250 * Helper method for lessThan() and expansionLevelsCompare(): Compares
251 * the passed item-data using m_sortRole as criteria. Both items must
252 * have the same parent item, otherwise the comparison will be wrong.
253 */
254 int sortRoleCompare(const ItemData* a, const ItemData* b) const;
255
256 /**
257 * Sorts the items by using lessThan() as comparison criteria.
258 * The merge sort algorithm is used to assure a worst-case
259 * of O(n * log(n)) and to keep the number of comparisons low.
260 */
261 void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
262
263 /** Helper method for sort(). */
264 void merge(QList<ItemData*>::iterator begin,
265 QList<ItemData*>::iterator pivot,
266 QList<ItemData*>::iterator end);
267
268 /** Helper method for sort(). */
269 QList<ItemData*>::iterator lowerBound(QList<ItemData*>::iterator begin,
270 QList<ItemData*>::iterator end,
271 const ItemData* value);
272
273 /** Helper method for sort(). */
274 QList<ItemData*>::iterator upperBound(QList<ItemData*>::iterator begin,
275 QList<ItemData*>::iterator end,
276 const ItemData* value);
277 /** Helper method for sort(). */
278 void reverse(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
279
280 int stringCompare(const QString& a, const QString& b) const;
281
282 /**
283 * Compares the expansion level of both items. The "expansion level" is defined
284 * by the number of parent directories. However simply comparing just the numbers
285 * is not sufficient, it is also important to check the hierarchy for having
286 * a correct order like shown in a tree.
287 */
288 int expansionLevelsCompare(const ItemData* a, const ItemData* b) const;
289
290 /**
291 * Helper method for expansionLevelCompare().
292 */
293 QString subPath(const KFileItem& item,
294 const QString& itemPath,
295 int start,
296 bool* isDir) const;
297
298 bool useMaximumUpdateInterval() const;
299
300 QList<QPair<int, QVariant> > nameRoleGroups() const;
301 QList<QPair<int, QVariant> > sizeRoleGroups() const;
302 QList<QPair<int, QVariant> > dateRoleGroups() const;
303 QList<QPair<int, QVariant> > permissionRoleGroups() const;
304 QList<QPair<int, QVariant> > ratingRoleGroups() const;
305 QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& role) const;
306
307 /**
308 * Helper method for all xxxRoleGroups() methods to check whether the
309 * item with the given index is a child-item. A child-item is defined
310 * as item having an expansion-level > 0. All xxxRoleGroups() methods
311 * should skip the grouping if the item is a child-item (although
312 * KItemListView would be capable to show sub-groups in groups this
313 * results in visual clutter for most usecases).
314 */
315 bool isChildItem(int index) const;
316
317 /**
318 * @return Recursive list of child items that have \a item as upper most parent.
319 */
320 KFileItemList childItems(const KFileItem& item) const;
321
322 private:
323 QWeakPointer<KDirLister> m_dirLister;
324
325 bool m_naturalSorting;
326 bool m_sortFoldersFirst;
327
328 Role m_sortRole;
329 QSet<QByteArray> m_roles;
330 Qt::CaseSensitivity m_caseSensitivity;
331
332 QList<ItemData*> m_itemData;
333 QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
334
335 KFileItemModelFilter m_filter;
336 QSet<KFileItem> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
337
338 bool m_requestRole[RolesCount];
339
340 QTimer* m_minimumUpdateIntervalTimer;
341 QTimer* m_maximumUpdateIntervalTimer;
342 QTimer* m_resortAllItemsTimer;
343 KFileItemList m_pendingItemsToInsert;
344 bool m_pendingEmitLoadingCompleted;
345
346 // Cache for KFileItemModel::groups()
347 mutable QList<QPair<int, QVariant> > m_groups;
348
349 // Stores the smallest expansion level of the root-URL. Is required to calculate
350 // the "expansionLevel" role in an efficient way. A value < 0 indicates a
351 // special meaning:
352 enum RootExpansionLevelTypes
353 {
354 // m_rootExpansionLevel is uninitialized and must be determined by checking
355 // the root URL from the KDirLister.
356 UninitializedRootExpansionLevel = -1,
357 // All items should be forced to get an expansion level of 0 even if they
358 // represent child items. This is useful for slaves that provide no parent items
359 // for child items like e.g. the search IO slaves.
360 ForceRootExpansionLevel = -2
361 };
362 mutable int m_rootExpansionLevel;
363
364 // Stores the URLs of the expanded folders.
365 QSet<KUrl> m_expandedUrls;
366
367 // URLs that must be expanded. The expanding is initially triggered in setExpanded()
368 // and done step after step in slotCompleted().
369 QSet<KUrl> m_urlsToExpand;
370
371 friend class KFileItemModelTest; // For unit testing
372 };
373
374 inline bool KFileItemModel::isChildItem(int index) const
375 {
376 return m_requestRole[ExpansionLevelRole] && m_itemData.at(index)->values.value("expansionLevel").toInt() > 0;
377 }
378
379 #endif
380
381