]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodelrolesupdater.cpp
Introduce "isExpandable" role
[dolphin.git] / src / kitemviews / kfileitemmodelrolesupdater.cpp
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 #include "kfileitemmodelrolesupdater.h"
21
22 #include "kfileitemmodel.h"
23 #include "kpixmapmodifier_p.h"
24
25 #include <KConfig>
26 #include <KConfigGroup>
27 #include <KDebug>
28 #include <KFileItem>
29 #include <KGlobal>
30 #include <KIO/PreviewJob>
31 #include <QPainter>
32 #include <QPixmap>
33 #include <QElapsedTimer>
34 #include <QTimer>
35
36 // Required includes for subDirectoriesCount():
37 #ifdef Q_WS_WIN
38 #include <QDir>
39 #else
40 #include <dirent.h>
41 #include <QFile>
42 #endif
43
44 // #define KFILEITEMMODELROLESUPDATER_DEBUG
45
46 namespace {
47 // Maximum time in ms that the KFileItemModelRolesUpdater
48 // may perform a blocking operation
49 const int MaxBlockTimeout = 200;
50
51 // Maximum number of items that will get resolved synchronously.
52 // The value should roughly represent the number of maximum visible
53 // items, as it does not make sense to resolve more items synchronously
54 // and probably reach the MaxBlockTimeout because of invisible items.
55 const int MaxResolveItemsCount = 100;
56 }
57
58 KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent) :
59 QObject(parent),
60 m_paused(false),
61 m_previewChangedDuringPausing(false),
62 m_iconSizeChangedDuringPausing(false),
63 m_rolesChangedDuringPausing(false),
64 m_previewShown(false),
65 m_clearPreviews(false),
66 m_model(model),
67 m_iconSize(),
68 m_firstVisibleIndex(0),
69 m_lastVisibleIndex(-1),
70 m_roles(),
71 m_enabledPlugins(),
72 m_pendingVisibleItems(),
73 m_pendingInvisibleItems(),
74 m_previewJobs(),
75 m_resolvePendingRolesTimer(0)
76 {
77 Q_ASSERT(model);
78
79 const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings");
80 m_enabledPlugins = globalConfig.readEntry("Plugins", QStringList()
81 << "directorythumbnail"
82 << "imagethumbnail"
83 << "jpegthumbnail");
84
85 connect(m_model, SIGNAL(itemsInserted(KItemRangeList)),
86 this, SLOT(slotItemsInserted(KItemRangeList)));
87 connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
88 this, SLOT(slotItemsRemoved(KItemRangeList)));
89 connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
90 this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
91
92 // A timer with a minimal timeout is used to merge several triggerPendingRolesResolving() calls
93 // to only one call of resolvePendingRoles().
94 m_resolvePendingRolesTimer = new QTimer(this);
95 m_resolvePendingRolesTimer->setInterval(1);
96 m_resolvePendingRolesTimer->setSingleShot(true);
97 connect(m_resolvePendingRolesTimer, SIGNAL(timeout()), this, SLOT(resolvePendingRoles()));
98 }
99
100 KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
101 {
102 }
103
104 void KFileItemModelRolesUpdater::setIconSize(const QSize& size)
105 {
106 if (size != m_iconSize) {
107 m_iconSize = size;
108 if (m_paused) {
109 m_iconSizeChangedDuringPausing = true;
110 } else if (m_previewShown) {
111 // An icon size change requires the regenerating of
112 // all previews
113 sortAndResolveAllRoles();
114 } else {
115 sortAndResolvePendingRoles();
116 }
117 }
118 }
119
120 QSize KFileItemModelRolesUpdater::iconSize() const
121 {
122 return m_iconSize;
123 }
124
125 void KFileItemModelRolesUpdater::setVisibleIndexRange(int index, int count)
126 {
127 if (index < 0) {
128 index = 0;
129 }
130 if (count < 0) {
131 count = 0;
132 }
133
134 if (index == m_firstVisibleIndex && count == m_lastVisibleIndex - m_firstVisibleIndex + 1) {
135 // The range has not been changed
136 return;
137 }
138
139 m_firstVisibleIndex = index;
140 m_lastVisibleIndex = qMin(index + count - 1, m_model->count() - 1);
141
142 if (hasPendingRoles() && !m_paused) {
143 sortAndResolvePendingRoles();
144 }
145 }
146
147 void KFileItemModelRolesUpdater::setPreviewShown(bool show)
148 {
149 if (show == m_previewShown) {
150 return;
151 }
152
153 m_previewShown = show;
154 if (!show) {
155 m_clearPreviews = true;
156 }
157
158 if (m_paused) {
159 m_previewChangedDuringPausing = true;
160 } else {
161 sortAndResolveAllRoles();
162 }
163 }
164
165 bool KFileItemModelRolesUpdater::isPreviewShown() const
166 {
167 return m_previewShown;
168 }
169
170 void KFileItemModelRolesUpdater::setEnabledPlugins(const QStringList& list)
171 {
172 if (m_enabledPlugins == list) {
173 return;
174 }
175
176 m_enabledPlugins = list;
177 if (m_previewShown) {
178 if (m_paused) {
179 m_previewChangedDuringPausing = true;
180 } else {
181 sortAndResolveAllRoles();
182 }
183 }
184 }
185
186 void KFileItemModelRolesUpdater::setPaused(bool paused)
187 {
188 if (paused == m_paused) {
189 return;
190 }
191
192 m_paused = paused;
193 if (paused) {
194 if (hasPendingRoles()) {
195 foreach (KJob* job, m_previewJobs) {
196 job->kill();
197 }
198 Q_ASSERT(m_previewJobs.isEmpty());
199 }
200 } else {
201 const bool resolveAll = (m_iconSizeChangedDuringPausing && m_previewShown) ||
202 m_previewChangedDuringPausing ||
203 m_rolesChangedDuringPausing;
204 if (resolveAll) {
205 sortAndResolveAllRoles();
206 } else {
207 sortAndResolvePendingRoles();
208 }
209
210 m_iconSizeChangedDuringPausing = false;
211 m_previewChangedDuringPausing = false;
212 m_rolesChangedDuringPausing = false;
213 }
214 }
215
216 void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles)
217 {
218 if (roles.count() == m_roles.count()) {
219 bool isEqual = true;
220 foreach (const QByteArray& role, roles) {
221 if (!m_roles.contains(role)) {
222 isEqual = false;
223 break;
224 }
225 }
226 if (isEqual) {
227 return;
228 }
229 }
230
231 m_roles = roles;
232
233 if (m_paused) {
234 m_rolesChangedDuringPausing = true;
235 } else {
236 sortAndResolveAllRoles();
237 }
238 }
239
240 QSet<QByteArray> KFileItemModelRolesUpdater::roles() const
241 {
242 return m_roles;
243 }
244
245 bool KFileItemModelRolesUpdater::isPaused() const
246 {
247 return m_paused;
248 }
249
250 QStringList KFileItemModelRolesUpdater::enabledPlugins() const
251 {
252 return m_enabledPlugins;
253 }
254
255 void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges)
256 {
257 startUpdating(itemRanges);
258 }
259
260 void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRanges)
261 {
262 Q_UNUSED(itemRanges);
263 m_firstVisibleIndex = 0;
264 m_lastVisibleIndex = -1;
265 if (!hasPendingRoles()) {
266 return;
267 }
268
269 if (m_model->count() == 0) {
270 // Most probably a directory change is done. Clear all pending items
271 // and also kill all ongoing preview-jobs.
272 resetPendingRoles();
273 } else {
274 // Remove all items from m_pendingVisibleItems and m_pendingInvisibleItems
275 // that are not part of the model anymore.
276 for (int i = 0; i <= 1; ++i) {
277 QSet<KFileItem>& pendingItems = (i == 0) ? m_pendingVisibleItems : m_pendingInvisibleItems;
278 QMutableSetIterator<KFileItem> it(pendingItems);
279 while (it.hasNext()) {
280 const KFileItem item = it.next();
281 if (m_model->index(item) < 0) {
282 pendingItems.remove(item);
283 }
284 }
285 }
286 }
287 }
288
289 void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRanges,
290 const QSet<QByteArray>& roles)
291 {
292 Q_UNUSED(roles);
293 startUpdating(itemRanges);
294 }
295
296 void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap)
297 {
298 m_pendingVisibleItems.remove(item);
299 m_pendingInvisibleItems.remove(item);
300
301 const int index = m_model->index(item);
302 if (index < 0) {
303 return;
304 }
305
306 QPixmap scaledPixmap = pixmap;
307
308 const QString mimeType = item.mimetype();
309 const int slashIndex = mimeType.indexOf(QLatin1Char('/'));
310 const QString mimeTypeGroup = mimeType.left(slashIndex);
311 if (mimeTypeGroup == QLatin1String("image")) {
312 KPixmapModifier::applyFrame(scaledPixmap, m_iconSize);
313 } else {
314 KPixmapModifier::scale(scaledPixmap, m_iconSize);
315 }
316
317 QHash<QByteArray, QVariant> data = rolesData(item);
318 data.insert("iconPixmap", scaledPixmap);
319
320 disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
321 this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
322 m_model->setData(index, data);
323 connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
324 this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
325 }
326
327 void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item)
328 {
329 m_pendingVisibleItems.remove(item);
330 m_pendingInvisibleItems.remove(item);
331
332 const bool clearPreviews = m_clearPreviews;
333 m_clearPreviews = true;
334 applyResolvedRoles(item, ResolveAll);
335 m_clearPreviews = clearPreviews;
336 }
337
338 void KFileItemModelRolesUpdater::slotPreviewJobFinished(KJob* job)
339 {
340 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
341 kDebug() << "Preview job finished. Pending visible:" << m_pendingVisibleItems.count() << "invisible:" << m_pendingInvisibleItems.count();
342 #endif
343
344 m_previewJobs.removeOne(job);
345 if (!m_previewJobs.isEmpty() || !hasPendingRoles()) {
346 return;
347 }
348
349 const KFileItemList visibleItems = sortedItems(m_pendingVisibleItems);
350 startPreviewJob(visibleItems + m_pendingInvisibleItems.toList());
351 }
352
353 void KFileItemModelRolesUpdater::resolvePendingRoles()
354 {
355 int resolvedCount = 0;
356
357 const bool hasSlowRoles = m_previewShown
358 || m_roles.contains("size")
359 || m_roles.contains("type")
360 || m_roles.contains("isExpandable");
361 const ResolveHint resolveHint = hasSlowRoles ? ResolveFast : ResolveAll;
362
363 // Resolving the MIME type can be expensive. Assure that not more than MaxBlockTimeout ms are
364 // spend for resolving them synchronously. Usually this is more than enough to determine
365 // all visible items, but there are corner cases where this limit gets easily exceeded.
366 QElapsedTimer timer;
367 timer.start();
368
369 // Resolve the MIME type of all visible items
370 QSetIterator<KFileItem> visibleIt(m_pendingVisibleItems);
371 while (visibleIt.hasNext()) {
372 const KFileItem item = visibleIt.next();
373 applyResolvedRoles(item, resolveHint);
374 if (!hasSlowRoles) {
375 Q_ASSERT(!m_pendingInvisibleItems.contains(item));
376 // All roles have been resolved already by applyResolvedRoles()
377 m_pendingVisibleItems.remove(item);
378 }
379 ++resolvedCount;
380
381 if (timer.elapsed() > MaxBlockTimeout) {
382 break;
383 }
384 }
385
386 // Resolve the MIME type of the invisible items at least until the timeout
387 // has been exceeded or the maximum number of items has been reached
388 KFileItemList invisibleItems;
389 if (m_lastVisibleIndex >= 0) {
390 // The visible range is valid, don't care about the order how the MIME
391 // type of invisible items get resolved
392 invisibleItems = m_pendingInvisibleItems.toList();
393 } else {
394 // The visible range is temporary invalid (e.g. happens when loading
395 // a directory) so take care to sort the currently invisible items where
396 // a part will get visible later
397 invisibleItems = sortedItems(m_pendingInvisibleItems);
398 }
399
400 int index = 0;
401 while (resolvedCount < MaxResolveItemsCount && index < invisibleItems.count() && timer.elapsed() <= MaxBlockTimeout) {
402 const KFileItem item = invisibleItems.at(index);
403 applyResolvedRoles(item, resolveHint);
404
405 if (!hasSlowRoles) {
406 // All roles have been resolved already by applyResolvedRoles()
407 m_pendingInvisibleItems.remove(item);
408 }
409 ++index;
410 ++resolvedCount;
411 }
412
413 if (m_previewShown) {
414 KFileItemList items = sortedItems(m_pendingVisibleItems);
415 items += invisibleItems;
416 startPreviewJob(items);
417 } else {
418 QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
419 }
420
421 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
422 if (timer.elapsed() > MaxBlockTimeout) {
423 kDebug() << "Maximum time of" << MaxBlockTimeout
424 << "ms exceeded, skipping items... Remaining visible:" << m_pendingVisibleItems.count()
425 << "invisible:" << m_pendingInvisibleItems.count();
426 }
427 kDebug() << "[TIME] Resolved pending roles:" << timer.elapsed();
428 #endif
429 }
430
431 void KFileItemModelRolesUpdater::resolveNextPendingRoles()
432 {
433 if (m_paused) {
434 return;
435 }
436
437 if (m_previewShown) {
438 // The preview has been turned on since the last run. Skip
439 // resolving further pending roles as this is done as soon
440 // as a preview has been received.
441 return;
442 }
443
444 int resolvedCount = 0;
445 bool changed = false;
446 for (int i = 0; i <= 1; ++i) {
447 QSet<KFileItem>& pendingItems = (i == 0) ? m_pendingVisibleItems : m_pendingInvisibleItems;
448 QSetIterator<KFileItem> it(pendingItems);
449 while (it.hasNext() && !changed && resolvedCount < MaxResolveItemsCount) {
450 const KFileItem item = it.next();
451 pendingItems.remove(item);
452 changed = applyResolvedRoles(item, ResolveAll);
453 ++resolvedCount;
454 }
455 }
456
457 if (hasPendingRoles()) {
458 QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
459 } else {
460 m_clearPreviews = false;
461 }
462
463 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
464 static int callCount = 0;
465 ++callCount;
466 if (callCount % 100 == 0) {
467 kDebug() << "Remaining visible roles to resolve:" << m_pendingVisibleItems.count()
468 << "invisible:" << m_pendingInvisibleItems.count();
469 }
470 #endif
471 }
472
473 void KFileItemModelRolesUpdater::startUpdating(const KItemRangeList& itemRanges)
474 {
475 // If no valid index range is given assume that all items are visible.
476 // A cleanup will be done later as soon as the index range has been set.
477 const bool hasValidIndexRange = (m_lastVisibleIndex >= 0);
478
479 if (hasValidIndexRange) {
480 // Move all current pending visible items that are not visible anymore
481 // to the pending invisible items.
482 QSetIterator<KFileItem> it(m_pendingVisibleItems);
483 while (it.hasNext()) {
484 const KFileItem item = it.next();
485 const int index = m_model->index(item);
486 if (index < m_firstVisibleIndex || index > m_lastVisibleIndex) {
487 m_pendingVisibleItems.remove(item);
488 m_pendingInvisibleItems.insert(item);
489 }
490 }
491 }
492
493 int rangesCount = 0;
494
495 foreach (const KItemRange& range, itemRanges) {
496 rangesCount += range.count;
497
498 // Add the inserted items to the pending visible and invisible items
499 const int lastIndex = range.index + range.count - 1;
500 for (int i = range.index; i <= lastIndex; ++i) {
501 const KFileItem item = m_model->fileItem(i);
502 if (!hasValidIndexRange || (i >= m_firstVisibleIndex && i <= m_lastVisibleIndex)) {
503 m_pendingVisibleItems.insert(item);
504 } else {
505 m_pendingInvisibleItems.insert(item);
506 }
507 }
508 }
509
510 triggerPendingRolesResolving(rangesCount);
511 }
512
513 void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList& items)
514 {
515 if (items.isEmpty() || m_paused) {
516 return;
517 }
518
519 // PreviewJob internally caches items always with the size of
520 // 128 x 128 pixels or 256 x 256 pixels. A (slow) downscaling is done
521 // by PreviewJob if a smaller size is requested. For images KFileItemModelRolesUpdater must
522 // do a downscaling anyhow because of the frame, so in this case only the provided
523 // cache sizes are requested.
524 const QSize cacheSize = (m_iconSize.width() > 128) || (m_iconSize.height() > 128)
525 ? QSize(256, 256) : QSize(128, 128);
526
527 // KIO::filePreview() will request the MIME-type of all passed items, which (in the
528 // worst case) might block the application for several seconds. To prevent such
529 // a blocking the MIME-type of the items will determined until the MaxBlockTimeout
530 // has been reached and only those items will get passed. As soon as the MIME-type
531 // has been resolved once KIO::filePreview() can already access the resolved
532 // MIME-type in a fast way.
533 QElapsedTimer timer;
534 timer.start();
535 KFileItemList itemSubSet;
536 for (int i = 0; i < items.count(); ++i) {
537 KFileItem item = items.at(i);
538 item.determineMimeType();
539 itemSubSet.append(items.at(i));
540 if (timer.elapsed() > MaxBlockTimeout) {
541 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
542 kDebug() << "Maximum time of" << MaxBlockTimeout << "ms exceeded, creating only previews for"
543 << (i + 1) << "items," << (items.count() - (i + 1)) << "will be resolved later";
544 #endif
545 break;
546 }
547 }
548 KJob* job = KIO::filePreview(itemSubSet, cacheSize, &m_enabledPlugins);
549
550 connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)),
551 this, SLOT(slotGotPreview(KFileItem,QPixmap)));
552 connect(job, SIGNAL(failed(KFileItem)),
553 this, SLOT(slotPreviewFailed(KFileItem)));
554 connect(job, SIGNAL(finished(KJob*)),
555 this, SLOT(slotPreviewJobFinished(KJob*)));
556
557 m_previewJobs.append(job);
558 }
559
560
561 bool KFileItemModelRolesUpdater::hasPendingRoles() const
562 {
563 return !m_pendingVisibleItems.isEmpty() || !m_pendingInvisibleItems.isEmpty();
564 }
565
566 void KFileItemModelRolesUpdater::resetPendingRoles()
567 {
568 m_pendingVisibleItems.clear();
569 m_pendingInvisibleItems.clear();
570
571 foreach (KJob* job, m_previewJobs) {
572 job->kill();
573 }
574 Q_ASSERT(m_previewJobs.isEmpty());
575 }
576
577 void KFileItemModelRolesUpdater::triggerPendingRolesResolving(int count)
578 {
579 if (count == m_model->count()) {
580 // When initially loading a directory a synchronous resolving prevents a minor
581 // flickering when opening directories. This is also fine from a performance point
582 // of view as it is assured in resolvePendingRoles() to never block the event-loop
583 // for more than 200 ms.
584 resolvePendingRoles();
585 } else {
586 // Items have been added. This can be done in several small steps within one loop
587 // because of the sorting and hence may not trigger any expensive operation.
588 m_resolvePendingRolesTimer->start();
589 }
590 }
591
592 void KFileItemModelRolesUpdater::sortAndResolveAllRoles()
593 {
594 if (m_paused) {
595 return;
596 }
597
598 resetPendingRoles();
599 Q_ASSERT(m_pendingVisibleItems.isEmpty());
600 Q_ASSERT(m_pendingInvisibleItems.isEmpty());
601
602 if (m_model->count() == 0) {
603 return;
604 }
605
606 // Determine all visible items
607 Q_ASSERT(m_firstVisibleIndex >= 0);
608 for (int i = m_firstVisibleIndex; i <= m_lastVisibleIndex; ++i) {
609 const KFileItem item = m_model->fileItem(i);
610 if (!item.isNull()) {
611 m_pendingVisibleItems.insert(item);
612 }
613 }
614
615 // Determine all invisible items
616 for (int i = 0; i < m_firstVisibleIndex; ++i) {
617 const KFileItem item = m_model->fileItem(i);
618 if (!item.isNull()) {
619 m_pendingInvisibleItems.insert(item);
620 }
621 }
622 for (int i = m_lastVisibleIndex + 1; i < m_model->count(); ++i) {
623 const KFileItem item = m_model->fileItem(i);
624 if (!item.isNull()) {
625 m_pendingInvisibleItems.insert(item);
626 }
627 }
628
629 triggerPendingRolesResolving(m_pendingVisibleItems.count() +
630 m_pendingInvisibleItems.count());
631 }
632
633 void KFileItemModelRolesUpdater::sortAndResolvePendingRoles()
634 {
635 Q_ASSERT(!m_paused);
636 if (m_model->count() == 0) {
637 return;
638 }
639
640 // If no valid index range is given assume that all items are visible.
641 // A cleanup will be done later as soon as the index range has been set.
642 const bool hasValidIndexRange = (m_lastVisibleIndex >= 0);
643
644 // Trigger a preview generation of all pending items. Assure that the visible
645 // pending items get generated first.
646 QSet<KFileItem> pendingItems;
647 pendingItems += m_pendingVisibleItems;
648 pendingItems += m_pendingInvisibleItems;
649
650 resetPendingRoles();
651 Q_ASSERT(m_pendingVisibleItems.isEmpty());
652 Q_ASSERT(m_pendingInvisibleItems.isEmpty());
653
654 QSetIterator<KFileItem> it(pendingItems);
655 while (it.hasNext()) {
656 const KFileItem item = it.next();
657 if (item.isNull()) {
658 continue;
659 }
660
661 const int index = m_model->index(item);
662 if (!hasValidIndexRange || (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex)) {
663 m_pendingVisibleItems.insert(item);
664 } else {
665 m_pendingInvisibleItems.insert(item);
666 }
667 }
668
669 triggerPendingRolesResolving(m_pendingVisibleItems.count() +
670 m_pendingInvisibleItems.count());
671 }
672
673 bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, ResolveHint hint)
674 {
675 if (item.isNull()) {
676 return false;
677 }
678
679 const bool resolveAll = (hint == ResolveAll);
680
681 bool mimeTypeChanged = false;
682 if (!item.isMimeTypeKnown()) {
683 item.determineMimeType();
684 mimeTypeChanged = true;
685 }
686
687 if (mimeTypeChanged || resolveAll || m_clearPreviews) {
688 const int index = m_model->index(item);
689 if (index < 0) {
690 return false;
691 }
692
693 QHash<QByteArray, QVariant> data;
694 if (resolveAll) {
695 data = rolesData(item);
696 }
697
698 data.insert("iconName", item.iconName());
699
700 if (m_clearPreviews) {
701 data.insert("iconPixmap", QString());
702 }
703
704 disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
705 this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
706 m_model->setData(index, data);
707 connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
708 this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
709 return true;
710 }
711
712 return false;
713 }
714
715 QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const
716 {
717 QHash<QByteArray, QVariant> data;
718
719 const bool getSizeRole = m_roles.contains("size");
720 const bool getIsExpandableRole = m_roles.contains("isExpandable");
721
722 if ((getSizeRole || getIsExpandableRole) && item.isDir() && item.isLocalFile()) {
723 const QString path = item.localPath();
724 const int count = subDirectoriesCount(path);
725 if (count >= 0) {
726 if (getSizeRole) {
727 data.insert("size", KIO::filesize_t(count));
728 }
729 if (getIsExpandableRole) {
730 data.insert("isExpandable", count > 0);
731 }
732 }
733 }
734
735 if (m_roles.contains("type")) {
736 data.insert("type", item.mimeComment());
737 }
738
739 data.insert("iconOverlays", item.overlays());
740
741 return data;
742 }
743
744 KFileItemList KFileItemModelRolesUpdater::sortedItems(const QSet<KFileItem>& items) const
745 {
746 KFileItemList itemList;
747 if (items.isEmpty()) {
748 return itemList;
749 }
750
751 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
752 QElapsedTimer timer;
753 timer.start();
754 #endif
755
756 QList<int> indexes;
757 indexes.reserve(items.count());
758
759 QSetIterator<KFileItem> it(items);
760 while (it.hasNext()) {
761 const KFileItem item = it.next();
762 const int index = m_model->index(item);
763 if (index >= 0) {
764 indexes.append(index);
765 }
766 }
767 qSort(indexes);
768
769 itemList.reserve(items.count());
770 foreach (int index, indexes) {
771 itemList.append(m_model->fileItem(index));
772 }
773
774 #ifdef KFILEITEMMODELROLESUPDATER_DEBUG
775 kDebug() << "[TIME] Sorting of items:" << timer.elapsed();
776 #endif
777 return itemList;
778 }
779
780 int KFileItemModelRolesUpdater::subDirectoriesCount(const QString& path) const
781 {
782 const bool countHiddenFiles = m_model->showHiddenFiles();
783
784 #ifdef Q_WS_WIN
785 QDir dir(path);
786 QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System;
787 if (countHiddenFiles) {
788 filters |= QDir::Hidden;
789 }
790 return dir.entryList(filters).count();
791 #else
792 // Taken from kdelibs/kio/kio/kdirmodel.cpp
793 // Copyright (C) 2006 David Faure <faure@kde.org>
794
795 int count = -1;
796 DIR* dir = ::opendir(QFile::encodeName(path));
797 if (dir) {
798 count = 0;
799 struct dirent *dirEntry = 0;
800 while ((dirEntry = ::readdir(dir))) { // krazy:exclude=syscalls
801 if (dirEntry->d_name[0] == '.') {
802 if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
803 // Skip "." or hidden files
804 continue;
805 }
806 if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
807 // Skip ".."
808 continue;
809 }
810 }
811 ++count;
812 }
813 ::closedir(dir);
814 }
815 return count;
816 #endif
817 }
818
819 #include "kfileitemmodelrolesupdater.moc"