X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/39c7c343cffcb540491c70a7db82983be83e6b26..e6ea3ab4c41dcc115143a237aafd3a1152849433:/src/kitemviews/kfileitemmodelrolesupdater.cpp diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index b157a0183..c28e240a5 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -20,39 +20,30 @@ #include "kfileitemmodelrolesupdater.h" #include "kfileitemmodel.h" +#include "private/kdirectorycontentscounter.h" +#include "private/kpixmapmodifier.h" #include #include -#include -#include -#include -#include #include #include - -#include "private/kpixmapmodifier.h" +#include +#include +#include +#include +#include + +#ifdef HAVE_BALOO +#include "private/kbaloorolesprovider.h" +#include +#include +#endif #include #include -#include #include #include -#include - -#ifdef HAVE_NEPOMUK - #include "private/knepomukrolesprovider.h" - #include -#endif - -// Required includes for subItemsCount(): -#ifdef Q_WS_WIN - #include -#else - #include - #include -#endif - // #define KFILEITEMMODELROLESUPDATER_DEBUG namespace { @@ -88,57 +79,62 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO m_resolvableRoles(), m_enabledPlugins(), m_pendingSortRoleItems(), - m_pendingSortRoleIndexes(), m_pendingIndexes(), m_pendingPreviewItems(), m_previewJob(), - m_recentlyChangedItemsTimer(0), + m_recentlyChangedItemsTimer(nullptr), m_recentlyChangedItems(), m_changedItems(), - m_dirWatcher(0), - m_watchedDirs() - #ifdef HAVE_NEPOMUK - , m_nepomukResourceWatcher(0), - m_nepomukUriItems() + m_directoryContentsCounter(nullptr) + #ifdef HAVE_BALOO + , m_balooFileMonitor(nullptr) #endif { Q_ASSERT(model); - const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings"); - m_enabledPlugins = globalConfig.readEntry("Plugins", QStringList() - << "directorythumbnail" - << "imagethumbnail" - << "jpegthumbnail"); - - connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), - this, SLOT(slotItemsInserted(KItemRangeList))); - connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), - this, SLOT(slotItemsRemoved(KItemRangeList))); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); - connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), - this, SLOT(slotItemsMoved(KItemRange,QList))); - connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), - this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); + m_enabledPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); + + connect(m_model, &KFileItemModel::itemsInserted, + this, &KFileItemModelRolesUpdater::slotItemsInserted); + connect(m_model, &KFileItemModel::itemsRemoved, + this, &KFileItemModelRolesUpdater::slotItemsRemoved); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); + connect(m_model, &KFileItemModel::itemsMoved, + this, &KFileItemModelRolesUpdater::slotItemsMoved); + connect(m_model, &KFileItemModel::sortRoleChanged, + this, &KFileItemModelRolesUpdater::slotSortRoleChanged); // Use a timer to prevent that each call of slotItemsChanged() results in a synchronous - // resolving of the roles. Postpone the resolving until no update has been done for 1 second. + // resolving of the roles. Postpone the resolving until no update has been done for 100 ms. m_recentlyChangedItemsTimer = new QTimer(this); - m_recentlyChangedItemsTimer->setInterval(1000); + m_recentlyChangedItemsTimer->setInterval(100); m_recentlyChangedItemsTimer->setSingleShot(true); - connect(m_recentlyChangedItemsTimer, SIGNAL(timeout()), this, SLOT(resolveRecentlyChangedItems())); + connect(m_recentlyChangedItemsTimer, &QTimer::timeout, this, &KFileItemModelRolesUpdater::resolveRecentlyChangedItems); m_resolvableRoles.insert("size"); m_resolvableRoles.insert("type"); m_resolvableRoles.insert("isExpandable"); -#ifdef HAVE_NEPOMUK - m_resolvableRoles += KNepomukRolesProvider::instance().roles(); +#ifdef HAVE_BALOO + m_resolvableRoles += KBalooRolesProvider::instance().roles(); #endif - // When folders are expandable or the item-count is shown for folders, it is necessary - // to watch the number of items of the sub-folder to be able to react on changes. - m_dirWatcher = new KDirWatch(this); - connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString))); + m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this); + connect(m_directoryContentsCounter, &KDirectoryContentsCounter::result, + this, &KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived); + + auto plugins = KPluginLoader::instantiatePlugins(QStringLiteral("kf5/overlayicon"), nullptr, qApp); + foreach (QObject *it, plugins) { + auto plugin = qobject_cast(it); + if (plugin) { + m_overlayIconsPlugin.append(plugin); + connect(plugin, &KOverlayIconPlugin::overlaysChanged, this, &KFileItemModelRolesUpdater::slotOverlaysChanged); + } else { + // not our/valid plugin, so delete the created object + it->deleteLater(); + } + } } KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater() @@ -261,8 +257,9 @@ void KFileItemModelRolesUpdater::setPaused(bool paused) resolveNextSortRole(); } else { m_state = Idle; - startUpdating(); } + + startUpdating(); } } @@ -271,32 +268,29 @@ void KFileItemModelRolesUpdater::setRoles(const QSet& roles) if (m_roles != roles) { m_roles = roles; -#ifdef HAVE_NEPOMUK +#ifdef HAVE_BALOO // Check whether there is at least one role that must be resolved - // with the help of Nepomuk. If this is the case, a (quite expensive) + // with the help of Baloo. If this is the case, a (quite expensive) // resolving will be done in KFileItemModelRolesUpdater::rolesData() and // the role gets watched for changes. - const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance(); - bool hasNepomukRole = false; + const KBalooRolesProvider& rolesProvider = KBalooRolesProvider::instance(); + bool hasBalooRole = false; QSetIterator it(roles); while (it.hasNext()) { const QByteArray& role = it.next(); if (rolesProvider.roles().contains(role)) { - hasNepomukRole = true; + hasBalooRole = true; break; } } - if (hasNepomukRole && !m_nepomukResourceWatcher) { - Q_ASSERT(m_nepomukUriItems.isEmpty()); - - m_nepomukResourceWatcher = new Nepomuk2::ResourceWatcher(this); - connect(m_nepomukResourceWatcher, SIGNAL(propertyChanged(Nepomuk2::Resource,Nepomuk2::Types::Property,QVariantList,QVariantList)), - this, SLOT(applyChangedNepomukRoles(Nepomuk2::Resource))); - } else if (!hasNepomukRole && m_nepomukResourceWatcher) { - delete m_nepomukResourceWatcher; - m_nepomukResourceWatcher = 0; - m_nepomukUriItems.clear(); + if (hasBalooRole && m_balooConfig.fileIndexingEnabled() && !m_balooFileMonitor) { + m_balooFileMonitor = new Baloo::FileMonitor(this); + connect(m_balooFileMonitor, &Baloo::FileMonitor::fileMetaDataChanged, + this, &KFileItemModelRolesUpdater::applyChangedBalooRoles); + } else if (!hasBalooRole && m_balooFileMonitor) { + delete m_balooFileMonitor; + m_balooFileMonitor = nullptr; } #endif @@ -345,16 +339,13 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRan applySortProgressToModel(); - // If there are still items whose sort role is unknown, return - // and handle them asynchronously. - if (!m_pendingSortRoleItems.isEmpty()) { - if (m_state != ResolvingSortRole) { - // Trigger the asynchronous determination of the sort role. - killPreviewJob(); - m_state = ResolvingSortRole; - resolveNextSortRole(); - } - return; + // If there are still items whose sort role is unknown, check if the + // asynchronous determination of the sort role is already in progress, + // and start it if that is not the case. + if (!m_pendingSortRoleItems.isEmpty() && m_state != ResolvingSortRole) { + killPreviewJob(); + m_state = ResolvingSortRole; + resolveNextSortRole(); } } @@ -363,53 +354,23 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRan void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRanges) { - Q_UNUSED(itemRanges); + Q_UNUSED(itemRanges) const bool allItemsRemoved = (m_model->count() == 0); - if (!m_watchedDirs.isEmpty()) { - // Don't let KDirWatch watch for removed items +#ifdef HAVE_BALOO + if (m_balooFileMonitor) { + // Don't let the FileWatcher watch for removed items if (allItemsRemoved) { - foreach (const QString& path, m_watchedDirs) { - m_dirWatcher->removeDir(path); - } - m_watchedDirs.clear(); + m_balooFileMonitor->clear(); } else { - QMutableSetIterator it(m_watchedDirs); - while (it.hasNext()) { - const QString& path = it.next(); - if (m_model->index(KUrl(path)) < 0) { - m_dirWatcher->removeDir(path); - it.remove(); + QStringList newFileList; + foreach (const QString& file, m_balooFileMonitor->files()) { + if (m_model->index(QUrl::fromLocalFile(file)) >= 0) { + newFileList.append(file); } } - } - } - -#ifdef HAVE_NEPOMUK - if (m_nepomukResourceWatcher) { - // Don't let the ResourceWatcher watch for removed items - if (allItemsRemoved) { - m_nepomukResourceWatcher->setResources(QList()); - m_nepomukResourceWatcher->stop(); - m_nepomukUriItems.clear(); - } else { - QList newResources; - const QList oldResources = m_nepomukResourceWatcher->resources(); - foreach (const Nepomuk2::Resource& resource, oldResources) { - const QUrl uri = resource.uri(); - const KUrl itemUrl = m_nepomukUriItems.value(uri); - if (m_model->index(itemUrl) >= 0) { - newResources.append(resource); - } else { - m_nepomukUriItems.remove(uri); - } - } - m_nepomukResourceWatcher->setResources(newResources); - if (newResources.isEmpty()) { - Q_ASSERT(m_nepomukUriItems.isEmpty()); - m_nepomukResourceWatcher->stop(); - } + m_balooFileMonitor->setFiles(newFileList); } } #endif @@ -419,7 +380,6 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang m_finishedItems.clear(); m_pendingSortRoleItems.clear(); - m_pendingSortRoleIndexes.clear(); m_pendingIndexes.clear(); m_pendingPreviewItems.clear(); m_recentlyChangedItems.clear(); @@ -444,13 +404,10 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang } } -void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QList movedToIndexes) +void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, const QList &movedToIndexes) { - Q_UNUSED(itemRange); - Q_UNUSED(movedToIndexes); - - // The indexes of the items with missing sort role are not valid any more. - m_pendingSortRoleIndexes.clear(); + Q_UNUSED(itemRange) + Q_UNUSED(movedToIndexes) // The visible items might have changed. startUpdating(); @@ -459,7 +416,7 @@ void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QLi void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRanges, const QSet& roles) { - Q_UNUSED(roles); + Q_UNUSED(roles) // Find out if slotItemsChanged() has been done recently. If that is the // case, resolving the roles is postponed until a timer has exceeded @@ -487,12 +444,11 @@ void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRang void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) if (m_resolvableRoles.contains(current)) { m_pendingSortRoleItems.clear(); - m_pendingSortRoleIndexes.clear(); m_finishedItems.clear(); const int count = m_model->count(); @@ -519,7 +475,6 @@ void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current, } else { m_state = Idle; m_pendingSortRoleItems.clear(); - m_pendingSortRoleIndexes.clear(); applySortProgressToModel(); } } @@ -539,10 +494,9 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi QPixmap scaledPixmap = pixmap; - const QString mimeType = item.mimetype(); - const int slashIndex = mimeType.indexOf(QLatin1Char('/')); - const QString mimeTypeGroup = mimeType.left(slashIndex); - if (mimeTypeGroup == QLatin1String("image")) { + if (!pixmap.hasAlpha() + && m_iconSize.width() > KIconLoader::SizeSmallMedium + && m_iconSize.height() > KIconLoader::SizeSmallMedium) { if (m_enlargeSmallPreviews) { KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); } else { @@ -552,7 +506,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi const bool enlargingRequired = scaledPixmap.width() < contentSize.width() && scaledPixmap.height() < contentSize.height(); if (enlargingRequired) { - QSize frameSize = scaledPixmap.size(); + QSize frameSize = scaledPixmap.size() / scaledPixmap.devicePixelRatio(); frameSize.scale(m_iconSize, Qt::KeepAspectRatio); QPixmap largeFrame(frameSize); @@ -561,28 +515,45 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi KPixmapModifier::applyFrame(largeFrame, frameSize); QPainter painter(&largeFrame); - painter.drawPixmap((largeFrame.width() - scaledPixmap.width()) / 2, - (largeFrame.height() - scaledPixmap.height()) / 2, + painter.drawPixmap((largeFrame.width() - scaledPixmap.width() / scaledPixmap.devicePixelRatio()) / 2, + (largeFrame.height() - scaledPixmap.height() / scaledPixmap.devicePixelRatio()) / 2, scaledPixmap); scaledPixmap = largeFrame; } else { - // The image must be shrinked as it is too large to fit into + // The image must be shrunk as it is too large to fit into // the available icon size KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); } } } else { - KPixmapModifier::scale(scaledPixmap, m_iconSize); + KPixmapModifier::scale(scaledPixmap, m_iconSize * qApp->devicePixelRatio()); + scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio()); } QHash data = rolesData(item); + + const QStringList overlays = data["iconOverlays"].toStringList(); + // Strangely KFileItem::overlays() returns empty string-values, so + // we need to check first whether an overlay must be drawn at all. + // It is more efficient to do it here, as KIconLoader::drawOverlays() + // assumes that an overlay will be drawn and has some additional + // setup time. + foreach (const QString& overlay, overlays) { + if (!overlay.isEmpty()) { + // There is at least one overlay, draw all overlays above m_pixmap + // and cancel the check + KIconLoader::global()->drawOverlays(overlays, scaledPixmap, KIconLoader::Desktop); + break; + } + } + data.insert("iconPixmap", scaledPixmap); - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_finishedItems.insert(item); } @@ -600,22 +571,20 @@ void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item) QHash data; data.insert("iconPixmap", QPixmap()); - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); - applyResolvedRoles(item, ResolveAll); + applyResolvedRoles(index, ResolveAll); m_finishedItems.insert(item); } } -void KFileItemModelRolesUpdater::slotPreviewJobFinished(KJob* job) +void KFileItemModelRolesUpdater::slotPreviewJobFinished() { - Q_UNUSED(job); - - m_previewJob = 0; + m_previewJob = nullptr; if (m_state != PreviewJobRunning) { return; @@ -624,7 +593,7 @@ void KFileItemModelRolesUpdater::slotPreviewJobFinished(KJob* job) m_state = Idle; if (!m_pendingPreviewItems.isEmpty()) { - startPreviewJob(m_pendingPreviewItems); + startPreviewJob(); } else { if (!m_changedItems.isEmpty()) { updateChangedItems(); @@ -638,77 +607,42 @@ void KFileItemModelRolesUpdater::resolveNextSortRole() return; } - if (m_pendingSortRoleItems.count() != m_pendingSortRoleIndexes.count()) { - // The indexes with missing sort role have to be updated. - m_pendingSortRoleIndexes.clear(); - foreach (const KFileItem& item, m_pendingSortRoleItems) { - const int index = m_model->index(item); - if (index < 0) { - m_pendingSortRoleItems.remove(item); - } else { - m_pendingSortRoleIndexes.append(index); - } - } - - std::sort(m_pendingSortRoleIndexes.begin(), m_pendingSortRoleIndexes.end()); - } - - // Try to update an item in the visible range. - QList::iterator it = std::lower_bound(m_pendingSortRoleIndexes.begin(), - m_pendingSortRoleIndexes.end(), - m_firstVisibleIndex); - - // It seems that there is no such item. Start with the first item in the list. - if (it == m_pendingSortRoleIndexes.end()) { - it = m_pendingSortRoleIndexes.begin(); - } - - while (it != m_pendingSortRoleIndexes.end()) { - // TODO: Note that removing an index from the list m_pendingSortRoleIndexes - // at a random position is O(N). We might need a better solution - // to make sure that this does not harm the performance if - // many items have to be sorted. - const int index = *it; - const KFileItem item = m_model->fileItem(index); + QSet::iterator it = m_pendingSortRoleItems.begin(); + while (it != m_pendingSortRoleItems.end()) { + const KFileItem item = *it; + const int index = m_model->index(item); // Continue if the sort role has already been determined for the // item, and the item has not been changed recently. if (!m_changedItems.contains(item) && m_model->data(index).contains(m_model->sortRole())) { - m_pendingSortRoleItems.remove(item); - m_pendingSortRoleIndexes.erase(it); - - // Check if we are at the end of the list (note that the list's end has changed). - if (it != m_pendingSortRoleIndexes.end()) { - ++it; - } + it = m_pendingSortRoleItems.erase(it); continue; } applySortRole(index); - m_pendingSortRoleItems.remove(item); - m_pendingSortRoleIndexes.erase(it); + m_pendingSortRoleItems.erase(it); break; } if (!m_pendingSortRoleItems.isEmpty()) { applySortProgressToModel(); - QTimer::singleShot(0, this, SLOT(resolveNextSortRole())); + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::resolveNextSortRole); } else { m_state = Idle; // Prevent that we try to update the items twice. - disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), - this, SLOT(slotItemsMoved(KItemRange,QList))); + disconnect(m_model, &KFileItemModel::itemsMoved, + this, &KFileItemModelRolesUpdater::slotItemsMoved); applySortProgressToModel(); - connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), - this, SLOT(slotItemsMoved(KItemRange,QList))); + connect(m_model, &KFileItemModel::itemsMoved, + this, &KFileItemModelRolesUpdater::slotItemsMoved); startUpdating(); } } void KFileItemModelRolesUpdater::resolveNextPendingRoles() { - if (m_state != ResolvingAllRoles && m_state != PreviewJobRunning) { + if (m_state != ResolvingAllRoles) { return; } @@ -720,21 +654,15 @@ void KFileItemModelRolesUpdater::resolveNextPendingRoles() continue; } - if (m_previewShown) { - // Only determine the icon. The other roles are resolved when the preview is received. - applyResolvedRoles(item, ResolveFast); - } else { - applyResolvedRoles(item, ResolveAll); - m_finishedItems.insert(item); - m_changedItems.remove(item); - } - + applyResolvedRoles(index, ResolveAll); + m_finishedItems.insert(item); + m_changedItems.remove(item); break; } if (!m_pendingIndexes.isEmpty()) { - QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); - } else if (m_state != PreviewJobRunning) { + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::resolveNextPendingRoles); + } else { m_state = Idle; if (m_clearPreviews) { @@ -743,15 +671,15 @@ void KFileItemModelRolesUpdater::resolveNextPendingRoles() QHash data; data.insert("iconPixmap", QPixmap()); - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); for (int index = 0; index <= m_model->count(); ++index) { if (m_model->data(index).contains("iconPixmap")) { m_model->setData(index, data); } } - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); } m_clearPreviews = false; @@ -770,67 +698,77 @@ void KFileItemModelRolesUpdater::resolveRecentlyChangedItems() updateChangedItems(); } -void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resource& resource) +void KFileItemModelRolesUpdater::applyChangedBalooRoles(const QString& file) { -#ifdef HAVE_NEPOMUK - const KUrl itemUrl = m_nepomukUriItems.value(resource.uri()); - const KFileItem item = m_model->fileItem(itemUrl); +#ifdef HAVE_BALOO + const KFileItem item = m_model->fileItem(QUrl::fromLocalFile(file)); if (item.isNull()) { // itemUrl is not in the model anymore, probably because // the corresponding file has been deleted in the meantime. return; } + applyChangedBalooRolesForItem(item); +#else + Q_UNUSED(file) +#endif +} - QHash data = rolesData(item); +void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem &item) +{ +#ifdef HAVE_BALOO + Baloo::File file(item.localPath()); + file.load(); - const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance(); - QHashIterator it(rolesProvider.roleValues(resource, m_roles)); + const KBalooRolesProvider& rolesProvider = KBalooRolesProvider::instance(); + QHash data; + + foreach (const QByteArray& role, rolesProvider.roles()) { + // Overwrite all the role values with an empty QVariant, because the roles + // provider doesn't overwrite it when the property value list is empty. + // See bug 322348 + data.insert(role, QVariant()); + } + + QHashIterator it(rolesProvider.roleValues(file, m_roles)); while (it.hasNext()) { it.next(); data.insert(it.key(), it.value()); } - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); const int index = m_model->index(item); m_model->setData(index, data); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); #else #ifndef Q_CC_MSVC - Q_UNUSED(resource); + Q_UNUSED(item) #endif #endif } -void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count, long size) { const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); if (getSizeRole || getIsExpandableRole) { - const int index = m_model->index(KUrl(path)); + const int index = m_model->index(QUrl::fromLocalFile(path)); if (index >= 0) { - if (!m_model->fileItem(index).isDir()) { - // If INotify is used, KDirWatch issues the dirty() signal - // also for changed files inside the directory, even if we - // don't enable this behavior explicitly (see bug 309740). - return; - } - QHash data; - const int count = subItemsCount(path); if (getSizeRole) { - data.insert("size", count); + data.insert("count", count); + if (size != -1) { + data.insert("size", QVariant::fromValue(size)); + } } if (getIsExpandableRole) { data.insert("isExpandable", count > 0); } - // Note that we do not block the itemsChanged signal here. - // This ensures that a new preview will be generated. m_model->setData(index, data); } } @@ -838,9 +776,7 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path) void KFileItemModelRolesUpdater::startUpdating() { - // Updating the items in and near the visible area makes sense only - // if sorting is finished. - if (m_state == ResolvingSortRole || m_state == Paused) { + if (m_state == Paused) { return; } @@ -850,16 +786,6 @@ void KFileItemModelRolesUpdater::startUpdating() return; } - int lastVisibleIndex = m_lastVisibleIndex; - if (lastVisibleIndex <= 0) { - // Guess a reasonable value for the last visible index if the view - // has not told us about the real value yet. - lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1); - if (lastVisibleIndex <= 0) { - lastVisibleIndex = qMin(200, m_model->count() - 1); - } - } - // Terminate all updates that are currently active. killPreviewJob(); m_pendingIndexes.clear(); @@ -868,51 +794,69 @@ void KFileItemModelRolesUpdater::startUpdating() timer.start(); // Determine the icons for the visible items synchronously. - int index; - for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) { - const KFileItem item = m_model->fileItem(index); - applyResolvedRoles(item, ResolveFast); + updateVisibleIcons(); + + // A detailed update of the items in and near the visible area + // only makes sense if sorting is finished. + if (m_state == ResolvingSortRole) { + return; } - const int firstIndexWithoutIcon = index; // Start the preview job or the asynchronous resolving of all roles. QList indexes = indexesToResolve(); if (m_previewShown) { - KFileItemList itemsToResolve; + m_pendingPreviewItems.clear(); + m_pendingPreviewItems.reserve(indexes.count()); + foreach (int index, indexes) { const KFileItem item = m_model->fileItem(index); if (!m_finishedItems.contains(item)) { - itemsToResolve.append(m_model->fileItem(index)); - - // Remember the items which have no icon yet. A fast - // asynchronous resolving will be done to make sure - // that icons are loaded as quickly as possible, i.e., - // before the previews arrive. - if (index < m_firstVisibleIndex || index >= firstIndexWithoutIcon) { - m_pendingIndexes.append(index); - } + m_pendingPreviewItems.append(item); } } - startPreviewJob(itemsToResolve); - - // Determine the icons asynchronously as fast as possible. - QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + startPreviewJob(); } else { m_pendingIndexes = indexes; // Trigger the asynchronous resolving of all roles. m_state = ResolvingAllRoles; - QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::resolveNextPendingRoles); + } +} + +void KFileItemModelRolesUpdater::updateVisibleIcons() +{ + int lastVisibleIndex = m_lastVisibleIndex; + if (lastVisibleIndex <= 0) { + // Guess a reasonable value for the last visible index if the view + // has not told us about the real value yet. + lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1); + if (lastVisibleIndex <= 0) { + lastVisibleIndex = qMin(200, m_model->count() - 1); + } } + + QElapsedTimer timer; + timer.start(); + + // Try to determine the final icons for all visible items. + int index; + for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) { + applyResolvedRoles(index, ResolveFast); + } + + // KFileItemListView::initializeItemListWidget(KItemListWidget*) will load + // preliminary icons (i.e., without mime type determination) for the + // remaining items. } -void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList items) +void KFileItemModelRolesUpdater::startPreviewJob() { m_state = PreviewJobRunning; - if (items.isEmpty()) { - QMetaObject::invokeMethod(this, "slotPreviewJobFinished", Qt::QueuedConnection, Q_ARG(KJob*, 0)); + if (m_pendingPreviewItems.isEmpty()) { + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::slotPreviewJobFinished); return; } @@ -926,58 +870,45 @@ void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList items) // KIO::filePreview() will request the MIME-type of all passed items, which (in the // worst case) might block the application for several seconds. To prevent such - // a blocking, we only pass items with known mime type to the preview job - // (if the icon has already been determined for an item in startUpdating() - // or resolveNextPendingRoles(), the type is known). - // This also prevents that repeated expensive mime type determinations are - // triggered here if a huge folder is loaded, and startUpdating() is called - // repeatedly. - // - // Note that we always pass at least one item to the preview job to prevent - // that we get an endless startPreviewJob()/slotPreviewJobFinished() loop - // if there are no items with known mime types yet for some reason. - const int count = items.count(); - int previewJobItemCount = 1; - - // TODO: This will start a job with one item only if this function is - // called from slotPreviewJobFinished(), and resolveNextPendingRoles() - // has not reached the items yet. This can happen if the previous preview - // job has finished very fast because generating previews failed for all - // items. - // - // Idea to improve this: if the mime type of the first item is unknown, - // determine mime types synchronously for a while. - while (previewJobItemCount < qMin(count, m_maximumVisibleItems) && - items.at(previewJobItemCount).isMimeTypeKnown()) { - ++previewJobItemCount; - } - + // a blocking, we only pass items with known mime type to the preview job. + const int count = m_pendingPreviewItems.count(); KFileItemList itemSubSet; - itemSubSet.reserve(previewJobItemCount); - m_pendingPreviewItems.clear(); - m_pendingPreviewItems.reserve(count - previewJobItemCount); - - for (int i = 0; i < previewJobItemCount; ++i) { - itemSubSet.append(items.at(i)); - } + itemSubSet.reserve(count); + + if (m_pendingPreviewItems.first().isMimeTypeKnown()) { + // Some mime types are known already, probably because they were + // determined when loading the icons for the visible items. Start + // a preview job for all items at the beginning of the list which + // have a known mime type. + do { + itemSubSet.append(m_pendingPreviewItems.takeFirst()); + } while (!m_pendingPreviewItems.isEmpty() && m_pendingPreviewItems.first().isMimeTypeKnown()); + } else { + // Determine mime types for MaxBlockTimeout ms, and start a preview + // job for the corresponding items. + QElapsedTimer timer; + timer.start(); - for (int i = previewJobItemCount; i < count; ++i) { - m_pendingPreviewItems.append(items.at(i)); + do { + const KFileItem item = m_pendingPreviewItems.takeFirst(); + item.determineMimeType(); + itemSubSet.append(item); + } while (!m_pendingPreviewItems.isEmpty() && timer.elapsed() < MaxBlockTimeout); } KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins); job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile()); - if (job->ui()) { - job->ui()->setWindow(qApp->activeWindow()); + if (job->uiDelegate()) { + KJobWidgets::setWindow(job, qApp->activeWindow()); } - connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)), - this, SLOT(slotGotPreview(KFileItem,QPixmap))); - connect(job, SIGNAL(failed(KFileItem)), - this, SLOT(slotPreviewFailed(KFileItem))); - connect(job, SIGNAL(finished(KJob*)), - this, SLOT(slotPreviewJobFinished(KJob*))); + connect(job, &KIO::PreviewJob::gotPreview, + this, &KFileItemModelRolesUpdater::slotGotPreview); + connect(job, &KIO::PreviewJob::failed, + this, &KFileItemModelRolesUpdater::slotPreviewFailed); + connect(job, &KIO::PreviewJob::finished, + this, &KFileItemModelRolesUpdater::slotPreviewJobFinished); m_previewJob = job; } @@ -1002,7 +933,7 @@ void KFileItemModelRolesUpdater::updateChangedItems() // asynchronous determination of the sort role. killPreviewJob(); m_state = ResolvingSortRole; - QTimer::singleShot(0, this, SLOT(resolveNextSortRole())); + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::resolveNextSortRole); } return; @@ -1029,21 +960,16 @@ void KFileItemModelRolesUpdater::updateChangedItems() std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end()); if (m_previewShown) { - KFileItemList visibleChangedItems; - KFileItemList invisibleChangedItems; - foreach (int index, visibleChangedIndexes) { - visibleChangedItems.append(m_model->fileItem(index)); + m_pendingPreviewItems.append(m_model->fileItem(index)); } foreach (int index, invisibleChangedIndexes) { - invisibleChangedItems.append(m_model->fileItem(index)); + m_pendingPreviewItems.append(m_model->fileItem(index)); } - if (m_previewJob) { - m_pendingPreviewItems += visibleChangedItems + invisibleChangedItems; - } else { - startPreviewJob(visibleChangedItems + invisibleChangedItems); + if (!m_previewJob) { + startPreviewJob(); } } else { const bool resolvingInProgress = !m_pendingIndexes.isEmpty(); @@ -1051,7 +977,7 @@ void KFileItemModelRolesUpdater::updateChangedItems() if (!resolvingInProgress) { // Trigger the asynchronous resolving of the changed roles. m_state = ResolvingAllRoles; - QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + QTimer::singleShot(0, this, &KFileItemModelRolesUpdater::resolveNextPendingRoles); } } } @@ -1061,11 +987,6 @@ void KFileItemModelRolesUpdater::applySortRole(int index) QHash data; const KFileItem item = m_model->fileItem(index); - if (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex) { - // Determine the icon. - applyResolvedRoles(item, ResolveFast); - } - if (m_model->sortRole() == "type") { if (!item.isMimeTypeKnown()) { item.determineMimeType(); @@ -1074,17 +995,17 @@ void KFileItemModelRolesUpdater::applySortRole(int index) data.insert("type", item.mimeComment()); } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) { const QString path = item.localPath(); - data.insert("size", subItemsCount(path)); + m_directoryContentsCounter->scanDirectory(path); } else { - // Probably the sort role is a Nepomuk role - just determine all roles. + // Probably the sort role is a baloo role - just determine all roles. data = rolesData(item); } - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); } void KFileItemModelRolesUpdater::applySortProgressToModel() @@ -1095,29 +1016,20 @@ void KFileItemModelRolesUpdater::applySortProgressToModel() m_model->emitSortProgress(resolvedCount); } -bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, ResolveHint hint) +bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) { - if (item.isNull()) { - return false; - } - + const KFileItem item = m_model->fileItem(index); const bool resolveAll = (hint == ResolveAll); bool iconChanged = false; if (!item.isMimeTypeKnown() || !item.isFinalIconKnown()) { item.determineMimeType(); iconChanged = true; - } else if (m_state == ResolvingSortRole || m_state == PreviewJobRunning) { - // We are currently performing a fast determination of all icons - // in the visible area. - const int index = m_model->index(item); - if (!m_model->data(index).contains("iconName")) { - iconChanged = true; - } + } else if (!m_model->data(index).contains("iconName")) { + iconChanged = true; } if (iconChanged || resolveAll || m_clearPreviews) { - const int index = m_model->index(item); if (index < 0) { return false; } @@ -1133,18 +1045,18 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, Resol data.insert("iconPixmap", QPixmap()); } - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); return true; } return false; } -QHash KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const +QHash KFileItemModelRolesUpdater::rolesData(const KFileItem& item) { QHash data; @@ -1153,19 +1065,10 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte if ((getSizeRole || getIsExpandableRole) && item.isDir()) { if (item.isLocalFile()) { + // Tell m_directoryContentsCounter that we want to count the items + // inside the directory. The result will be received in slotDirectoryContentsCountReceived. const QString path = item.localPath(); - const int count = subItemsCount(path); - if (getSizeRole) { - data.insert("size", count); - } - if (getIsExpandableRole) { - data.insert("isExpandable", count > 0); - } - - if (!m_dirWatcher->contains(path)) { - m_dirWatcher->addDir(path); - m_watchedDirs.insert(path); - } + m_directoryContentsCounter->scanDirectory(path); } else if (getSizeRole) { data.insert("size", -1); // -1 indicates an unknown number of items } @@ -1175,93 +1078,35 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte data.insert("type", item.mimeComment()); } - data.insert("iconOverlays", item.overlays()); - -#ifdef HAVE_NEPOMUK - if (m_nepomukResourceWatcher) { - const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance(); - Nepomuk2::Resource resource(item.nepomukUri()); - QHashIterator it(rolesProvider.roleValues(resource, m_roles)); - while (it.hasNext()) { - it.next(); - data.insert(it.key(), it.value()); - } - - QUrl uri = resource.uri(); - if (uri.isEmpty()) { - // TODO: Is there another way to explicitly create a resource? - // We need a resource to be able to track it for changes. - resource.setRating(0); - uri = resource.uri(); - } - if (!uri.isEmpty() && !m_nepomukUriItems.contains(uri)) { - m_nepomukResourceWatcher->addResource(resource); - - if (m_nepomukUriItems.isEmpty()) { - m_nepomukResourceWatcher->start(); - } + QStringList overlays = item.overlays(); + foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) { + overlays.append(it->getOverlays(item.url())); + } + data.insert("iconOverlays", overlays); - m_nepomukUriItems.insert(uri, item.url()); - } +#ifdef HAVE_BALOO + if (m_balooFileMonitor) { + m_balooFileMonitor->addFile(item.localPath()); + applyChangedBalooRolesForItem(item); } #endif - return data; } -int KFileItemModelRolesUpdater::subItemsCount(const QString& path) const +void KFileItemModelRolesUpdater::slotOverlaysChanged(const QUrl& url, const QStringList &) { - const bool countHiddenFiles = m_model->showHiddenFiles(); - const bool showFoldersOnly = m_model->showDirectoriesOnly(); - -#ifdef Q_WS_WIN - QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; - if (countHiddenFiles) { - filters |= QDir::Hidden; - } - if (showFoldersOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; + const KFileItem item = m_model->fileItem(url); + if (item.isNull()) { + return; } - return dir.entryList(filters).count(); -#else - // Taken from kdelibs/kio/kio/kdirmodel.cpp - // Copyright (C) 2006 David Faure - - int count = -1; - DIR* dir = ::opendir(QFile::encodeName(path)); - if (dir) { // krazy:exclude=syscalls - count = 0; - struct dirent *dirEntry = 0; - while ((dirEntry = ::readdir(dir))) { - if (dirEntry->d_name[0] == '.') { - if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { - // Skip "." or hidden files - continue; - } - if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { - // Skip ".." - continue; - } - } - - // If only directories are counted, consider an unknown file type and links also - // as directory instead of trying to do an expensive stat() - // (see bugs 292642 and 299997). - const bool countEntry = !showFoldersOnly || - dirEntry->d_type == DT_DIR || - dirEntry->d_type == DT_LNK || - dirEntry->d_type == DT_UNKNOWN; - if (countEntry) { - ++count; - } - } - ::closedir(dir); + const int index = m_model->index(item); + QHash data = m_model->data(index); + QStringList overlays = item.overlays(); + foreach (KOverlayIconPlugin *it, m_overlayIconsPlugin) { + overlays.append(it->getOverlays(url)); } - return count; -#endif + data.insert("iconOverlays", overlays); + m_model->setData(index, data); } void KFileItemModelRolesUpdater::updateAllPreviews() @@ -1277,14 +1122,14 @@ void KFileItemModelRolesUpdater::updateAllPreviews() void KFileItemModelRolesUpdater::killPreviewJob() { if (m_previewJob) { - disconnect(m_previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)), - this, SLOT(slotGotPreview(KFileItem,QPixmap))); - disconnect(m_previewJob, SIGNAL(failed(KFileItem)), - this, SLOT(slotPreviewFailed(KFileItem))); - disconnect(m_previewJob, SIGNAL(finished(KJob*)), - this, SLOT(slotPreviewJobFinished(KJob*))); + disconnect(m_previewJob, &KIO::PreviewJob::gotPreview, + this, &KFileItemModelRolesUpdater::slotGotPreview); + disconnect(m_previewJob, &KIO::PreviewJob::failed, + this, &KFileItemModelRolesUpdater::slotPreviewFailed); + disconnect(m_previewJob, &KIO::PreviewJob::finished, + this, &KFileItemModelRolesUpdater::slotPreviewJobFinished); m_previewJob->kill(); - m_previewJob = 0; + m_previewJob = nullptr; m_pendingPreviewItems.clear(); } } @@ -1303,7 +1148,7 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const // We need a reasonable upper limit for number of items to resolve after // and before the visible range. m_maximumVisibleItems can be quite large - // when using Compace View. + // when using Compact View. const int readAheadItems = qMin(ReadAheadPages * m_maximumVisibleItems, ResolveAllItemsLimit / 2); // Add items after the visible range. @@ -1346,4 +1191,3 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const return result; } -#include "kfileitemmodelrolesupdater.moc"