X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/37deaaef2c39a4c4b410cd0313a55a4072d2b2ff..b2f18e411a929665257dad8c6a51f3e2e617cee5:/src/kitemviews/kfileitemmodelrolesupdater.cpp diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index c28e240a5..d9594de41 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -1,24 +1,12 @@ -/*************************************************************************** - * Copyright (C) 2011 by Peter Penz * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ +/* + * SPDX-FileCopyrightText: 2011 Peter Penz + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "kfileitemmodelrolesupdater.h" +#include "dolphindebug.h" #include "kfileitemmodel.h" #include "private/kdirectorycontentscounter.h" #include "private/kpixmapmodifier.h" @@ -30,7 +18,7 @@ #include #include #include -#include +#include #include #ifdef HAVE_BALOO @@ -40,7 +28,10 @@ #endif #include +#include +#include #include +#include #include #include @@ -78,22 +69,29 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO m_roles(), m_resolvableRoles(), m_enabledPlugins(), + m_localFileSizePreviewLimit(0), + m_scanDirectories(true), m_pendingSortRoleItems(), m_pendingIndexes(), m_pendingPreviewItems(), m_previewJob(), + m_hoverSequenceItem(), + m_hoverSequenceIndex(0), + m_hoverSequencePreviewJob(nullptr), + m_hoverSequenceNumSuccessiveFailures(0), m_recentlyChangedItemsTimer(nullptr), m_recentlyChangedItems(), m_changedItems(), m_directoryContentsCounter(nullptr) #ifdef HAVE_BALOO - , m_balooFileMonitor(nullptr) + , m_balooFileMonitor(nullptr) #endif { Q_ASSERT(model); const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); m_enabledPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); + m_localFileSizePreviewLimit = static_cast(globalConfig.readEntry("MaximumSize", 0)); connect(m_model, &KFileItemModel::itemsInserted, this, &KFileItemModelRolesUpdater::slotItemsInserted); @@ -124,15 +122,16 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO 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); + const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("kf5/overlayicon")); + for (const KPluginMetaData &data : plugins) { + auto instance = QPluginLoader(data.fileName()).instance(); + auto plugin = qobject_cast(instance); 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(); + delete instance; } } } @@ -317,6 +316,46 @@ QStringList KFileItemModelRolesUpdater::enabledPlugins() const return m_enabledPlugins; } +void KFileItemModelRolesUpdater::setLocalFileSizePreviewLimit(const qlonglong size) +{ + m_localFileSizePreviewLimit = size; +} + +qlonglong KFileItemModelRolesUpdater::localFileSizePreviewLimit() const +{ + return m_localFileSizePreviewLimit; +} + +void KFileItemModelRolesUpdater::setScanDirectories(bool enabled) +{ + m_scanDirectories = enabled; +} + +bool KFileItemModelRolesUpdater::scanDirectories() const +{ + return m_scanDirectories; +} + +void KFileItemModelRolesUpdater::setHoverSequenceState(const QUrl& itemUrl, int seqIdx) +{ + const KFileItem item = m_model->fileItem(itemUrl); + + if (item != m_hoverSequenceItem) { + killHoverSequencePreviewJob(); + } + + m_hoverSequenceItem = item; + m_hoverSequenceIndex = seqIdx; + + if (!m_previewShown) { + return; + } + + m_hoverSequenceNumSuccessiveFailures = 0; + + loadNextHoverSequencePreview(); +} + void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges) { QElapsedTimer timer; @@ -325,7 +364,7 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRan // Determine the sort role synchronously for as many items as possible. if (m_resolvableRoles.contains(m_model->sortRole())) { int insertedCount = 0; - foreach (const KItemRange& range, itemRanges) { + for (const KItemRange& range : itemRanges) { const int lastIndex = insertedCount + range.index + range.count - 1; for (int i = insertedCount + range.index; i <= lastIndex; ++i) { if (timer.elapsed() < MaxBlockTimeout) { @@ -365,7 +404,8 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang m_balooFileMonitor->clear(); } else { QStringList newFileList; - foreach (const QString& file, m_balooFileMonitor->files()) { + const QStringList oldFileList = m_balooFileMonitor->files(); + for (const QString& file : oldFileList) { if (m_model->index(QUrl::fromLocalFile(file)) >= 0) { newFileList.append(file); } @@ -385,6 +425,7 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang m_recentlyChangedItems.clear(); m_recentlyChangedItemsTimer->stop(); m_changedItems.clear(); + m_hoverSequenceLoadedItems.clear(); killPreviewJob(); } else { @@ -399,6 +440,16 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang } } + // Removed items won't have hover previews loaded anymore. + for (const KItemRange& itemRange : itemRanges) { + int index = itemRange.index; + for (int count = itemRange.count; count > 0; --count) { + const KFileItem item = m_model->fileItem(index); + m_hoverSequenceLoadedItems.remove(item); + ++index; + } + } + // The visible items might have changed. startUpdating(); } @@ -425,7 +476,7 @@ void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRang QSet& targetSet = itemsChangedRecently ? m_recentlyChangedItems : m_changedItems; - foreach (const KItemRange& itemRange, itemRanges) { + for (const KItemRange& itemRange : itemRanges) { int index = itemRange.index; for (int count = itemRange.count; count > 0; --count) { const KFileItem item = m_model->fileItem(index); @@ -492,43 +543,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi return; } - QPixmap scaledPixmap = pixmap; - - if (!pixmap.hasAlpha() - && m_iconSize.width() > KIconLoader::SizeSmallMedium - && m_iconSize.height() > KIconLoader::SizeSmallMedium) { - if (m_enlargeSmallPreviews) { - KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); - } else { - // Assure that small previews don't get enlarged. Instead they - // should be shown centered within the frame. - const QSize contentSize = KPixmapModifier::sizeInsideFrame(m_iconSize); - const bool enlargingRequired = scaledPixmap.width() < contentSize.width() && - scaledPixmap.height() < contentSize.height(); - if (enlargingRequired) { - QSize frameSize = scaledPixmap.size() / scaledPixmap.devicePixelRatio(); - frameSize.scale(m_iconSize, Qt::KeepAspectRatio); - - QPixmap largeFrame(frameSize); - largeFrame.fill(Qt::transparent); - - KPixmapModifier::applyFrame(largeFrame, frameSize); - - QPainter painter(&largeFrame); - painter.drawPixmap((largeFrame.width() - scaledPixmap.width() / scaledPixmap.devicePixelRatio()) / 2, - (largeFrame.height() - scaledPixmap.height() / scaledPixmap.devicePixelRatio()) / 2, - scaledPixmap); - scaledPixmap = largeFrame; - } else { - // 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 * qApp->devicePixelRatio()); - scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio()); - } + QPixmap scaledPixmap = transformPreviewPixmap(pixmap); QHash data = rolesData(item); @@ -538,12 +553,14 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi // 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; + if (!scaledPixmap.isNull()) { + for (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; + } } } @@ -601,6 +618,112 @@ void KFileItemModelRolesUpdater::slotPreviewJobFinished() } } +void KFileItemModelRolesUpdater::slotHoverSequenceGotPreview(const KFileItem& item, const QPixmap& pixmap) +{ + const int index = m_model->index(item); + if (index < 0) { + return; + } + + QHash data = m_model->data(index); + QVector pixmaps = data["hoverSequencePixmaps"].value>(); + const int loadedIndex = pixmaps.size(); + + float wap = m_hoverSequencePreviewJob->sequenceIndexWraparoundPoint(); + if (!m_hoverSequencePreviewJob->handlesSequences()) { + wap = 1.0f; + } + if (wap >= 0.0f) { + data["hoverSequenceWraparoundPoint"] = wap; + m_model->setData(index, data); + } + + // For hover sequence previews we never load index 0, because that's just the regular preview + // in "iconPixmap". But that means we'll load index 1 even for thumbnailers that don't support + // sequences, in which case we can just throw away the preview because it's the same as for + // index 0. Unfortunately we can't find it out earlier :( + if (wap < 0.0f || loadedIndex < static_cast(wap)) { + // Add the preview to the model data + + const QPixmap scaledPixmap = transformPreviewPixmap(pixmap); + + pixmaps.append(scaledPixmap); + data["hoverSequencePixmaps"] = QVariant::fromValue(pixmaps); + + m_model->setData(index, data); + + const auto loadedIt = std::find(m_hoverSequenceLoadedItems.begin(), + m_hoverSequenceLoadedItems.end(), item); + if (loadedIt == m_hoverSequenceLoadedItems.end()) { + m_hoverSequenceLoadedItems.push_back(item); + trimHoverSequenceLoadedItems(); + } + } + + m_hoverSequenceNumSuccessiveFailures = 0; +} + +void KFileItemModelRolesUpdater::slotHoverSequencePreviewFailed(const KFileItem& item) +{ + const int index = m_model->index(item); + if (index < 0) { + return; + } + + static const int numRetries = 2; + + QHash data = m_model->data(index); + QVector pixmaps = data["hoverSequencePixmaps"].value>(); + + qCDebug(DolphinDebug).nospace() + << "Failed to generate hover sequence preview #" << pixmaps.size() + << " for file " << item.url().toString() + << " (attempt " << (m_hoverSequenceNumSuccessiveFailures+1) + << "/" << (numRetries+1) << ")"; + + if (m_hoverSequenceNumSuccessiveFailures >= numRetries) { + // Give up and simply duplicate the previous sequence image (if any) + + pixmaps.append(pixmaps.empty() ? QPixmap() : pixmaps.last()); + data["hoverSequencePixmaps"] = QVariant::fromValue(pixmaps); + + if (!data.contains("hoverSequenceWraparoundPoint")) { + // hoverSequenceWraparoundPoint is only available when PreviewJob succeeds, so unless + // it has previously succeeded, it's best to assume that it just doesn't handle + // sequences instead of trying to load the next image indefinitely. + data["hoverSequenceWraparoundPoint"] = 1.0f; + } + + m_model->setData(index, data); + + m_hoverSequenceNumSuccessiveFailures = 0; + } else { + // Retry + + m_hoverSequenceNumSuccessiveFailures++; + } + + // Next image in the sequence (or same one if the retry limit wasn't reached yet) will be + // loaded automatically, because slotHoverSequencePreviewJobFinished() will be triggered + // even when PreviewJob fails. +} + +void KFileItemModelRolesUpdater::slotHoverSequencePreviewJobFinished() +{ + const int index = m_model->index(m_hoverSequenceItem); + if (index < 0) { + m_hoverSequencePreviewJob = nullptr; + return; + } + + // Since a PreviewJob can only have one associated sequence index, we can only generate + // one sequence image per job, so we have to start another one for the next index. + + // Load the next image in the sequence + m_hoverSequencePreviewJob = nullptr; + loadNextHoverSequencePreview(); +} + void KFileItemModelRolesUpdater::resolveNextSortRole() { if (m_state != ResolvingSortRole) { @@ -670,11 +793,14 @@ void KFileItemModelRolesUpdater::resolveNextPendingRoles() if (m_finishedItems.count() != m_model->count()) { QHash data; data.insert("iconPixmap", QPixmap()); + data.insert("hoverSequencePixmaps", QVariant::fromValue(QVector())); disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); for (int index = 0; index <= m_model->count(); ++index) { - if (m_model->data(index).contains("iconPixmap")) { + if (m_model->data(index).contains("iconPixmap") || + m_model->data(index).contains("hoverSequencePixmaps")) + { m_model->setData(index, data); } } @@ -723,7 +849,8 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem & const KBalooRolesProvider& rolesProvider = KBalooRolesProvider::instance(); QHash data; - foreach (const QByteArray& role, rolesProvider.roles()) { + const auto roles = rolesProvider.roles(); + for (const QByteArray& role : 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 @@ -761,15 +888,17 @@ void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QStrin if (getSizeRole) { data.insert("count", count); - if (size != -1) { - data.insert("size", QVariant::fromValue(size)); - } + data.insert("size", QVariant::fromValue(size)); } if (getIsExpandableRole) { data.insert("isExpandable", count > 0); } + disconnect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, + this, &KFileItemModelRolesUpdater::slotItemsChanged); } } } @@ -809,7 +938,7 @@ void KFileItemModelRolesUpdater::startUpdating() m_pendingPreviewItems.clear(); m_pendingPreviewItems.reserve(indexes.count()); - foreach (int index, indexes) { + for (int index : qAsConst(indexes)) { const KFileItem item = m_model->fileItem(index); if (!m_finishedItems.contains(item)) { m_pendingPreviewItems.append(item); @@ -898,7 +1027,7 @@ void KFileItemModelRolesUpdater::startPreviewJob() KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins); - job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile()); + job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile() && !itemSubSet.first().isSlow() && m_localFileSizePreviewLimit <= 0); if (job->uiDelegate()) { KJobWidgets::setWindow(job, qApp->activeWindow()); } @@ -913,6 +1042,129 @@ void KFileItemModelRolesUpdater::startPreviewJob() m_previewJob = job; } +QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap& pixmap) +{ + QPixmap scaledPixmap = pixmap; + + if (!pixmap.hasAlpha() && !pixmap.isNull() + && m_iconSize.width() > KIconLoader::SizeSmallMedium + && m_iconSize.height() > KIconLoader::SizeSmallMedium) { + if (m_enlargeSmallPreviews) { + KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); + } else { + // Assure that small previews don't get enlarged. Instead they + // should be shown centered within the frame. + const QSize contentSize = KPixmapModifier::sizeInsideFrame(m_iconSize); + const bool enlargingRequired = scaledPixmap.width() < contentSize.width() && + scaledPixmap.height() < contentSize.height(); + if (enlargingRequired) { + QSize frameSize = scaledPixmap.size() / scaledPixmap.devicePixelRatio(); + frameSize.scale(m_iconSize, Qt::KeepAspectRatio); + + QPixmap largeFrame(frameSize); + largeFrame.fill(Qt::transparent); + + KPixmapModifier::applyFrame(largeFrame, frameSize); + + QPainter painter(&largeFrame); + painter.drawPixmap((largeFrame.width() - scaledPixmap.width() / scaledPixmap.devicePixelRatio()) / 2, + (largeFrame.height() - scaledPixmap.height() / scaledPixmap.devicePixelRatio()) / 2, + scaledPixmap); + scaledPixmap = largeFrame; + } else { + // The image must be shrunk as it is too large to fit into + // the available icon size + KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); + } + } + } else if (!pixmap.isNull()) { + KPixmapModifier::scale(scaledPixmap, m_iconSize * qApp->devicePixelRatio()); + scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio()); + } + + return scaledPixmap; +} + +void KFileItemModelRolesUpdater::loadNextHoverSequencePreview() +{ + if (m_hoverSequenceItem.isNull() || m_hoverSequencePreviewJob) { + return; + } + + const int index = m_model->index(m_hoverSequenceItem); + if (index < 0) { + return; + } + + // We generate the next few sequence indices in advance (buffering) + const int maxSeqIdx = m_hoverSequenceIndex+5; + + QHash data = m_model->data(index); + + if (!data.contains("hoverSequencePixmaps")) { + // The pixmap at index 0 isn't used ("iconPixmap" will be used instead) + data.insert("hoverSequencePixmaps", QVariant::fromValue(QVector() << QPixmap())); + m_model->setData(index, data); + } + + const QVector pixmaps = data["hoverSequencePixmaps"].value>(); + + const int loadSeqIdx = pixmaps.size(); + + float wap = -1.0f; + if (data.contains("hoverSequenceWraparoundPoint")) { + wap = data["hoverSequenceWraparoundPoint"].toFloat(); + } + if (wap >= 1.0f && loadSeqIdx >= static_cast(wap)) { + // Reached the wraparound point -> no more previews to load. + return; + } + + if (loadSeqIdx > maxSeqIdx) { + // Wait until setHoverSequenceState() is called with a higher sequence index. + return; + } + + // PreviewJob internally caches items always with the size of + // 128 x 128 pixels or 256 x 256 pixels. A (slow) downscaling is done + // by PreviewJob if a smaller size is requested. For images KFileItemModelRolesUpdater must + // do a downscaling anyhow because of the frame, so in this case only the provided + // cache sizes are requested. + const QSize cacheSize = (m_iconSize.width() > 128) || (m_iconSize.height() > 128) + ? QSize(256, 256) : QSize(128, 128); + + KIO::PreviewJob* job = new KIO::PreviewJob({m_hoverSequenceItem}, cacheSize, &m_enabledPlugins); + + job->setSequenceIndex(loadSeqIdx); + job->setIgnoreMaximumSize(m_hoverSequenceItem.isLocalFile() && !m_hoverSequenceItem.isSlow() && m_localFileSizePreviewLimit <= 0); + if (job->uiDelegate()) { + KJobWidgets::setWindow(job, qApp->activeWindow()); + } + + connect(job, &KIO::PreviewJob::gotPreview, + this, &KFileItemModelRolesUpdater::slotHoverSequenceGotPreview); + connect(job, &KIO::PreviewJob::failed, + this, &KFileItemModelRolesUpdater::slotHoverSequencePreviewFailed); + connect(job, &KIO::PreviewJob::finished, + this, &KFileItemModelRolesUpdater::slotHoverSequencePreviewJobFinished); + + m_hoverSequencePreviewJob = job; +} + +void KFileItemModelRolesUpdater::killHoverSequencePreviewJob() +{ + if (m_hoverSequencePreviewJob) { + disconnect(m_hoverSequencePreviewJob, &KIO::PreviewJob::gotPreview, + this, &KFileItemModelRolesUpdater::slotHoverSequenceGotPreview); + disconnect(m_hoverSequencePreviewJob, &KIO::PreviewJob::failed, + this, &KFileItemModelRolesUpdater::slotHoverSequencePreviewFailed); + disconnect(m_hoverSequencePreviewJob, &KIO::PreviewJob::finished, + this, &KFileItemModelRolesUpdater::slotHoverSequencePreviewJobFinished); + m_hoverSequencePreviewJob->kill(); + m_hoverSequencePreviewJob = nullptr; + } +} + void KFileItemModelRolesUpdater::updateChangedItems() { if (m_state == Paused) { @@ -941,14 +1193,19 @@ void KFileItemModelRolesUpdater::updateChangedItems() QList visibleChangedIndexes; QList invisibleChangedIndexes; + visibleChangedIndexes.reserve(m_changedItems.size()); + invisibleChangedIndexes.reserve(m_changedItems.size()); - foreach (const KFileItem& item, m_changedItems) { + auto changedItemsIt = m_changedItems.begin(); + while (changedItemsIt != m_changedItems.end()) { + const auto& item = *changedItemsIt; const int index = m_model->index(item); if (index < 0) { - m_changedItems.remove(item); + changedItemsIt = m_changedItems.erase(changedItemsIt); continue; } + ++changedItemsIt; if (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex) { visibleChangedIndexes.append(index); @@ -960,11 +1217,11 @@ void KFileItemModelRolesUpdater::updateChangedItems() std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end()); if (m_previewShown) { - foreach (int index, visibleChangedIndexes) { + for (int index : qAsConst(visibleChangedIndexes)) { m_pendingPreviewItems.append(m_model->fileItem(index)); } - foreach (int index, invisibleChangedIndexes) { + for (int index : qAsConst(invisibleChangedIndexes)) { m_pendingPreviewItems.append(m_model->fileItem(index)); } @@ -995,7 +1252,9 @@ 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(); - m_directoryContentsCounter->scanDirectory(path); + if (m_scanDirectories) { + m_directoryContentsCounter->scanDirectory(path); + } } else { // Probably the sort role is a baloo role - just determine all roles. data = rolesData(item); @@ -1039,10 +1298,13 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) data = rolesData(item); } - data.insert("iconName", item.iconName()); + if (!item.iconName().isEmpty()) { + data.insert("iconName", item.iconName()); + } if (m_clearPreviews) { data.insert("iconPixmap", QPixmap()); + data.insert("hoverSequencePixmaps", QVariant::fromValue(QVector())); } disconnect(m_model, &KFileItemModel::itemsChanged, @@ -1067,19 +1329,25 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte 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(); - m_directoryContentsCounter->scanDirectory(path); + if (m_scanDirectories) { + const QString path = item.localPath(); + m_directoryContentsCounter->scanDirectory(path); + } } else if (getSizeRole) { data.insert("size", -1); // -1 indicates an unknown number of items } } + if (m_roles.contains("extension")) { + data.insert("extension", QFileInfo(item.name()).suffix()); + } + if (m_roles.contains("type")) { data.insert("type", item.mimeComment()); } QStringList overlays = item.overlays(); - foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) { + for (KOverlayIconPlugin *it : qAsConst(m_overlayIconsPlugin)) { overlays.append(it->getOverlays(item.url())); } data.insert("iconOverlays", overlays); @@ -1102,7 +1370,7 @@ void KFileItemModelRolesUpdater::slotOverlaysChanged(const QUrl& url, const QStr const int index = m_model->index(item); QHash data = m_model->data(index); QStringList overlays = item.overlays(); - foreach (KOverlayIconPlugin *it, m_overlayIconsPlugin) { + for (KOverlayIconPlugin *it : qAsConst(m_overlayIconsPlugin)) { overlays.append(it->getOverlays(url)); } data.insert("iconOverlays", overlays); @@ -1139,13 +1407,24 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const const int count = m_model->count(); QList result; - result.reserve(ResolveAllItemsLimit); + result.reserve(qMin(count, (m_lastVisibleIndex - m_firstVisibleIndex + 1) + + ResolveAllItemsLimit + + (2 * m_maximumVisibleItems))); // Add visible items. + // Resolve files first, their previews are quicker. + QList visibleDirs; for (int i = m_firstVisibleIndex; i <= m_lastVisibleIndex; ++i) { - result.append(i); + const KFileItem item = m_model->fileItem(i); + if (item.isDir()) { + visibleDirs.append(i); + } else { + result.append(i); + } } + result.append(visibleDirs); + // 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 Compact View. @@ -1164,14 +1443,14 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const } // Add items on the last page. - const int beginLastPage = qMax(qMin(endExtendedVisibleRange + 1, count - 1), count - m_maximumVisibleItems); + const int beginLastPage = qMax(endExtendedVisibleRange + 1, count - m_maximumVisibleItems); for (int i = beginLastPage; i < count; ++i) { result.append(i); } // Add items on the first page. - const int endFirstPage = qMin(qMax(beginExtendedVisibleRange - 1, 0), m_maximumVisibleItems); - for (int i = 0; i <= endFirstPage; ++i) { + const int endFirstPage = qMin(beginExtendedVisibleRange, m_maximumVisibleItems); + for (int i = 0; i < endFirstPage; ++i) { result.append(i); } @@ -1183,7 +1462,7 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const --remainingItems; } - for (int i = beginExtendedVisibleRange - 1; i > endFirstPage && remainingItems > 0; --i) { + for (int i = beginExtendedVisibleRange - 1; i >= endFirstPage && remainingItems > 0; --i) { result.append(i); --remainingItems; } @@ -1191,3 +1470,23 @@ QList KFileItemModelRolesUpdater::indexesToResolve() const return result; } +void KFileItemModelRolesUpdater::trimHoverSequenceLoadedItems() +{ + static const size_t maxLoadedItems = 20; + + size_t loadedItems = m_hoverSequenceLoadedItems.size(); + while (loadedItems > maxLoadedItems) { + const KFileItem item = m_hoverSequenceLoadedItems.front(); + + m_hoverSequenceLoadedItems.pop_front(); + loadedItems--; + + const int index = m_model->index(item); + if (index >= 0) { + QHash data = m_model->data(index); + data["hoverSequencePixmaps"] = QVariant::fromValue(QVector() << QPixmap()); + m_model->setData(index, data); + } + } +} +