]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodel.h
Remove child items when a parent item gets deleted
[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 ExpansionLevelRole,
203 RolesCount // Mandatory last entry
204 };
205
206 struct ItemData
207 {
208 KFileItem item;
209 QHash<QByteArray, QVariant> values;
210 ItemData* parent;
211 };
212
213 void insertItems(const KFileItemList& items);
214 void removeItems(const KFileItemList& items);
215
216 /**
217 * Helper method for insertItems() and removeItems(): Creates
218 * a list of ItemData elements based on the given items.
219 * Note that the ItemData instances are created dynamically and
220 * must be deleted by the caller.
221 */
222 QList<ItemData*> createItemDataList(const KFileItemList& items) const;
223
224 void removeExpandedItems();
225
226 /**
227 * Resets all values from m_requestRole to false.
228 */
229 void resetRoles();
230
231 Role roleIndex(const QByteArray& role) const;
232
233 QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
234
235 /**
236 * @return True if the item-data \a a should be ordered before the item-data
237 * \b. The item-data may have different parent-items.
238 */
239 bool lessThan(const ItemData* a, const ItemData* b) const;
240
241 /**
242 * Helper method for lessThan() and expansionLevelsCompare(): Compares
243 * the passed item-data using m_sortRole as criteria. Both items must
244 * have the same parent item, otherwise the comparison will be wrong.
245 */
246 int sortRoleCompare(const ItemData* a, const ItemData* b) const;
247
248 /**
249 * Sorts the items by using lessThan() as comparison criteria.
250 * The merge sort algorithm is used to assure a worst-case
251 * of O(n * log(n)) and to keep the number of comparisons low.
252 */
253 void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
254
255 /** Helper method for sort(). */
256 void merge(QList<ItemData*>::iterator begin,
257 QList<ItemData*>::iterator pivot,
258 QList<ItemData*>::iterator end);
259
260 /** Helper method for sort(). */
261 QList<ItemData*>::iterator lowerBound(QList<ItemData*>::iterator begin,
262 QList<ItemData*>::iterator end,
263 const ItemData* value);
264
265 /** Helper method for sort(). */
266 QList<ItemData*>::iterator upperBound(QList<ItemData*>::iterator begin,
267 QList<ItemData*>::iterator end,
268 const ItemData* value);
269 /** Helper method for sort(). */
270 void reverse(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end);
271
272 int stringCompare(const QString& a, const QString& b) const;
273
274 /**
275 * Compares the expansion level of both items. The "expansion level" is defined
276 * by the number of parent directories. However simply comparing just the numbers
277 * is not sufficient, it is also important to check the hierarchy for having
278 * a correct order like shown in a tree.
279 */
280 int expansionLevelsCompare(const ItemData* a, const ItemData* b) const;
281
282 /**
283 * Helper method for expansionLevelCompare().
284 */
285 QString subPath(const KFileItem& item,
286 const QString& itemPath,
287 int start,
288 bool* isDir) const;
289
290 bool useMaximumUpdateInterval() const;
291
292 QList<QPair<int, QVariant> > nameRoleGroups() const;
293 QList<QPair<int, QVariant> > sizeRoleGroups() const;
294 QList<QPair<int, QVariant> > dateRoleGroups() const;
295 QList<QPair<int, QVariant> > permissionRoleGroups() const;
296 QList<QPair<int, QVariant> > ratingRoleGroups() const;
297 QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& role) const;
298
299 /**
300 * Helper method for all xxxRoleGroups() methods to check whether the
301 * item with the given index is a child-item. A child-item is defined
302 * as item having an expansion-level > 0. All xxxRoleGroups() methods
303 * should skip the grouping if the item is a child-item (although
304 * KItemListView would be capable to show sub-groups in groups this
305 * results in visual clutter for most usecases).
306 */
307 bool isChildItem(int index) const;
308
309 /**
310 * @return Recursive list of child items that have \a item as upper most parent.
311 */
312 KFileItemList childItems(const KFileItem& item) const;
313
314 private:
315 QWeakPointer<KDirLister> m_dirLister;
316
317 bool m_naturalSorting;
318 bool m_sortFoldersFirst;
319
320 Role m_sortRole;
321 QSet<QByteArray> m_roles;
322 Qt::CaseSensitivity m_caseSensitivity;
323
324 QList<ItemData*> m_itemData;
325 QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
326
327 KFileItemModelFilter m_filter;
328 QSet<KFileItem> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
329
330 bool m_requestRole[RolesCount];
331
332 QTimer* m_minimumUpdateIntervalTimer;
333 QTimer* m_maximumUpdateIntervalTimer;
334 QTimer* m_resortAllItemsTimer;
335 KFileItemList m_pendingItemsToInsert;
336 bool m_pendingEmitLoadingCompleted;
337
338 // Cache for KFileItemModel::groups()
339 mutable QList<QPair<int, QVariant> > m_groups;
340
341 // Stores the smallest expansion level of the root-URL. Is required to calculate
342 // the "expansionLevel" role in an efficient way. A value < 0 indicates a
343 // special meaning:
344 enum RootExpansionLevelTypes
345 {
346 // m_rootExpansionLevel is uninitialized and must be determined by checking
347 // the root URL from the KDirLister.
348 UninitializedRootExpansionLevel = -1,
349 // All items should be forced to get an expansion level of 0 even if they
350 // represent child items. This is useful for slaves that provide no parent items
351 // for child items like e.g. the search IO slaves.
352 ForceRootExpansionLevel = -2
353 };
354 mutable int m_rootExpansionLevel;
355
356 // Stores the URLs of the expanded folders.
357 QSet<KUrl> m_expandedUrls;
358
359 // URLs that must be expanded. The expanding is initially triggered in setExpanded()
360 // and done step after step in slotCompleted().
361 QSet<KUrl> m_urlsToExpand;
362
363 friend class KFileItemModelTest; // For unit testing
364 };
365
366 inline bool KFileItemModel::isChildItem(int index) const
367 {
368 return m_requestRole[ExpansionLevelRole] && m_itemData.at(index)->values.value("expansionLevel").toInt() > 0;
369 }
370
371 #endif
372
373