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 KFILEITEMMODELROLESUPDATER_H |
21 | #define KFILEITEMMODELROLESUPDATER_H |
22 | |
23 | #include <config-baloo.h> |
24 | |
25 | #include <KFileItem> |
26 | #include <kitemviews/kitemmodelbase.h> |
27 | |
28 | #include <libdolphin_export.h> |
29 | |
30 | #include <QObject> |
31 | #include <QSet> |
32 | #include <QSize> |
33 | #include <QStringList> |
34 | |
35 | class KDirectoryContentsCounter; |
36 | class KFileItemModel; |
37 | class KJob; |
38 | class QPixmap; |
39 | class QTimer; |
40 | |
41 | #ifdef HAVE_BALOO |
42 | namespace Baloo |
43 | { |
44 | class FileMonitor; |
45 | } |
46 | #endif |
47 | |
48 | /** |
49 | * @brief Resolves expensive roles asynchronously and applies them to the KFileItemModel. |
50 | * |
51 | * KFileItemModel only resolves roles that are inexpensive like e.g. the file name or |
52 | * the permissions. Creating previews or determining the MIME-type can be quite expensive |
53 | * and KFileItemModelRolesUpdater takes care to update such roles asynchronously. |
54 | * |
55 | * To prevent a huge CPU and I/O load, these roles are not updated for all |
56 | * items, but only for the visible items, some items around the visible area, |
57 | * and the items on the first and last pages of the view. This is a compromise |
58 | * that aims to minimize the risk that the user sees items with unknown icons |
59 | * in the view when scrolling or pressing Home or End. |
60 | * |
61 | * Determining the roles is done in several phases: |
62 | * |
63 | * 1. If the sort role is "slow", it is determined for all items. If this |
64 | * cannot be finished synchronously in 200 ms, the remaining items are |
65 | * handled asynchronously by \a resolveNextSortRole(). |
66 | * |
67 | * 2. The function startUpdating(), which is called if either the sort role |
68 | * has been successfully determined for all items, or items are inserted |
69 | * in the view, or the visible items might have changed because items |
70 | * were removed or moved, tries to determine the icons for all visible |
71 | * items synchronously for 200 ms. Then: |
72 | * |
73 | * (a) If previews are disabled, icons and all other roles are determined |
74 | * asynchronously for the interesting items. This is done by the |
75 | * function \a resolveNextPendingRoles(). |
76 | * |
77 | * (b) If previews are enabled, a \a KIO::PreviewJob is started that loads |
78 | * the previews for the interesting items. At the same time, the icons |
79 | * for these items are determined asynchronously as fast as possible |
80 | * by \a resolveNextPendingRoles(). This minimizes the risk that the |
81 | * user sees "unknown" icons when scrolling before the previews have |
82 | * arrived. |
83 | * |
84 | * 3. Finally, the entire process is repeated for any items that might have |
85 | * changed in the mean time. |
86 | */ |
87 | class LIBDOLPHINPRIVATE_EXPORT KFileItemModelRolesUpdater : public QObject |
88 | { |
89 | Q_OBJECT |
90 | |
91 | public: |
92 | explicit KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent = 0); |
93 | virtual ~KFileItemModelRolesUpdater(); |
94 | |
95 | void setIconSize(const QSize& size); |
96 | QSize iconSize() const; |
97 | |
98 | /** |
99 | * Sets the range of items that are visible currently. The roles |
100 | * of visible items are resolved first. |
101 | */ |
102 | void setVisibleIndexRange(int index, int count); |
103 | |
104 | void setMaximumVisibleItems(int count); |
105 | |
106 | /** |
107 | * If \a show is set to true, the "iconPixmap" role will be filled with a preview |
108 | * of the file. If \a show is false the MIME type icon will be used for the "iconPixmap" |
109 | * role. |
110 | */ |
111 | void setPreviewsShown(bool show); |
112 | bool previewsShown() const; |
113 | |
114 | /** |
115 | * If enabled a small preview gets upscaled to the icon size in case where |
116 | * the icon size is larger than the preview. Per default enlarging is |
117 | * enabled. |
118 | */ |
119 | void setEnlargeSmallPreviews(bool enlarge); |
120 | bool enlargeSmallPreviews() const; |
121 | |
122 | /** |
123 | * If \a paused is set to true the asynchronous resolving of roles will be paused. |
124 | * State changes during pauses like changing the icon size or the preview-shown |
125 | * will be remembered and handled after unpausing. |
126 | */ |
127 | void setPaused(bool paused); |
128 | bool isPaused() const; |
129 | |
130 | /** |
131 | * Sets the roles that should be resolved asynchronously. |
132 | */ |
133 | void setRoles(const QSet<QByteArray>& roles); |
134 | QSet<QByteArray> roles() const; |
135 | |
136 | /** |
137 | * Sets the list of enabled thumbnail plugins that are used for previews. |
138 | * Per default all plugins enabled in the KConfigGroup "PreviewSettings" |
139 | * are used. |
140 | * |
141 | * For a list of available plugins, call KServiceTypeTrader::self()->query("ThumbCreator"). |
142 | * |
143 | * @see enabledPlugins |
144 | */ |
145 | void setEnabledPlugins(const QStringList& list); |
146 | |
147 | /** |
148 | * Returns the list of enabled thumbnail plugins. |
149 | * @see setEnabledPlugins |
150 | */ |
151 | QStringList enabledPlugins() const; |
152 | |
153 | private slots: |
154 | void slotItemsInserted(const KItemRangeList& itemRanges); |
155 | void slotItemsRemoved(const KItemRangeList& itemRanges); |
156 | void slotItemsMoved(const KItemRange& itemRange, QList<int> movedToIndexes); |
157 | void slotItemsChanged(const KItemRangeList& itemRanges, |
158 | const QSet<QByteArray>& roles); |
159 | void slotSortRoleChanged(const QByteArray& current, |
160 | const QByteArray& previous); |
161 | |
162 | /** |
163 | * Is invoked after a preview has been received successfully. |
164 | * @see startPreviewJob() |
165 | */ |
166 | void slotGotPreview(const KFileItem& item, const QPixmap& pixmap); |
167 | |
168 | /** |
169 | * Is invoked after generating a preview has failed. |
170 | * @see startPreviewJob() |
171 | */ |
172 | void slotPreviewFailed(const KFileItem& item); |
173 | |
174 | /** |
175 | * Is invoked when the preview job has been finished. Starts a new preview |
176 | * job if there are any interesting items without previews left, or updates |
177 | * the changed items otherwise. * |
178 | * @see startPreviewJob() |
179 | */ |
180 | void slotPreviewJobFinished(); |
181 | |
182 | /** |
183 | * Resolves the sort role of the next item in m_pendingSortRole, applies it |
184 | * to the model, and invokes itself if there are any pending items left. If |
185 | * that is not the case, \a startUpdating() is called. |
186 | */ |
187 | void resolveNextSortRole(); |
188 | |
189 | /** |
190 | * Resolves the icon name and (if previews are disabled) all other roles |
191 | * for the next interesting item. If there are no pending items left, any |
192 | * changed items are updated. |
193 | */ |
194 | void resolveNextPendingRoles(); |
195 | |
196 | /** |
197 | * Resolves items that have not been resolved yet after the change has been |
198 | * notified by slotItemsChanged(). Is invoked if the m_changedItemsTimer |
199 | * expires. |
200 | */ |
201 | void resolveRecentlyChangedItems(); |
202 | |
203 | void applyChangedBalooRoles(const QString& file); |
204 | void applyChangedBalooRolesJobFinished(KJob* job); |
205 | |
206 | void slotDirectoryContentsCountReceived(const QString& path, int count); |
207 | |
208 | private: |
209 | /** |
210 | * Starts the updating of all roles. The visible items are handled first. |
211 | */ |
212 | void startUpdating(); |
213 | |
214 | /** |
215 | * Loads the icons for the visible items. After 200 ms, the function |
216 | * stops determining mime types and only loads preliminary icons. |
217 | * This is a compromise that prevents that |
218 | * (a) the GUI is blocked for more than 200 ms, and |
219 | * (b) "unknown" icons could be shown in the view. |
220 | */ |
221 | void updateVisibleIcons(); |
222 | |
223 | /** |
224 | * Creates previews for the items starting from the first item in |
225 | * m_pendingPreviewItems. |
226 | * @see slotGotPreview() |
227 | * @see slotPreviewFailed() |
228 | * @see slotPreviewJobFinished() |
229 | */ |
230 | void startPreviewJob(); |
231 | |
232 | /** |
233 | * Ensures that icons, previews, and other roles are determined for any |
234 | * items that have been changed. |
235 | */ |
236 | void updateChangedItems(); |
237 | |
238 | /** |
239 | * Resolves the sort role of the item and applies it to the model. |
240 | */ |
241 | void applySortRole(int index); |
242 | |
243 | void applySortProgressToModel(); |
244 | |
245 | enum ResolveHint { |
246 | ResolveFast, |
247 | ResolveAll |
248 | }; |
249 | bool applyResolvedRoles(int index, ResolveHint hint); |
250 | QHash<QByteArray, QVariant> rolesData(const KFileItem& item); |
251 | |
252 | /** |
253 | * @return The number of items of the path \a path. |
254 | */ |
255 | int subItemsCount(const QString& path) const; |
256 | |
257 | /** |
258 | * Must be invoked if a property has been changed that affects |
259 | * the look of the preview. Takes care to update all previews. |
260 | */ |
261 | void updateAllPreviews(); |
262 | |
263 | void killPreviewJob(); |
264 | |
265 | QList<int> indexesToResolve() const; |
266 | |
267 | private: |
268 | enum State { |
269 | Idle, |
270 | Paused, |
271 | ResolvingSortRole, |
272 | ResolvingAllRoles, |
273 | PreviewJobRunning |
274 | }; |
275 | |
276 | State m_state; |
277 | |
278 | // Property changes during pausing must be remembered to be able |
279 | // to react when unpausing again: |
280 | bool m_previewChangedDuringPausing; |
281 | bool m_iconSizeChangedDuringPausing; |
282 | bool m_rolesChangedDuringPausing; |
283 | |
284 | // Property for setPreviewsShown()/previewsShown(). |
285 | bool m_previewShown; |
286 | |
287 | // Property for setEnlargeSmallPreviews()/enlargeSmallPreviews() |
288 | bool m_enlargeSmallPreviews; |
289 | |
290 | // True if the role "iconPixmap" should be cleared when resolving the next |
291 | // role with resolveRole(). Is necessary if the preview gets disabled |
292 | // during the roles-updater has been paused by setPaused(). |
293 | bool m_clearPreviews; |
294 | |
295 | // Remembers which items have been handled already, to prevent that |
296 | // previews and other expensive roles are determined again. |
297 | QSet<KFileItem> m_finishedItems; |
298 | |
299 | KFileItemModel* m_model; |
300 | QSize m_iconSize; |
301 | int m_firstVisibleIndex; |
302 | int m_lastVisibleIndex; |
303 | int m_maximumVisibleItems; |
304 | QSet<QByteArray> m_roles; |
305 | QSet<QByteArray> m_resolvableRoles; |
306 | QStringList m_enabledPlugins; |
307 | |
308 | // Items for which the sort role still has to be determined. |
309 | QSet<KFileItem> m_pendingSortRoleItems; |
310 | |
311 | // Indexes of items which still have to be handled by |
312 | // resolveNextPendingRoles(). |
313 | QList<int> m_pendingIndexes; |
314 | |
315 | // Items which have been left over from the last call of startPreviewJob(). |
316 | // A new preview job will be started from them once the first one finishes. |
317 | KFileItemList m_pendingPreviewItems; |
318 | |
319 | KJob* m_previewJob; |
320 | |
321 | // When downloading or copying large files, the slot slotItemsChanged() |
322 | // will be called periodically within a quite short delay. To prevent |
323 | // a high CPU-load by generating e.g. previews for each notification, the update |
324 | // will be postponed until no file change has been done within a longer period |
325 | // of time. |
326 | QTimer* m_recentlyChangedItemsTimer; |
327 | QSet<KFileItem> m_recentlyChangedItems; |
328 | |
329 | // Items which have not been changed repeatedly recently. |
330 | QSet<KFileItem> m_changedItems; |
331 | |
332 | KDirectoryContentsCounter* m_directoryContentsCounter; |
333 | |
334 | #ifdef HAVE_BALOO |
335 | Baloo::FileMonitor* m_balooFileMonitor; |
336 | #endif |
337 | }; |
338 | |
339 | #endif |
340 | |
341 | |
342 | |