X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/38c34eeca315c7be58e65d4d3fb72aaf7b866719..e57f6215659ee36877c7c36c9e3fcba0ba5d03a0:/src/kitemviews/kfileitemmodelrolesupdater.cpp diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index cf660cb9f..497c13b52 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -13,13 +13,17 @@ #include #include +#include #include #include +#include #include #include #include #include +#include "dolphin_contentdisplaysettings.h" + #if HAVE_BALOO #include "private/kbaloorolesprovider.h" #include @@ -65,6 +69,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO , m_finishedItems() , m_model(model) , m_iconSize() + , m_devicePixelRatio(1.0) , m_firstVisibleIndex(0) , m_lastVisibleIndex(-1) , m_maximumVisibleItems(50) @@ -72,7 +77,6 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO , m_resolvableRoles() , m_enabledPlugins() , m_localFileSizePreviewLimit(0) - , m_scanDirectories(true) , m_pendingSortRoleItems() , m_pendingIndexes() , m_pendingPreviewItems() @@ -91,7 +95,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO { Q_ASSERT(model); - const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); + const KConfigGroup globalConfig(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings")); m_enabledPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); m_localFileSizePreviewLimit = static_cast(globalConfig.readEntry("MaximumSize", 0)); @@ -118,7 +122,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this); connect(m_directoryContentsCounter, &KDirectoryContentsCounter::result, this, &KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived); - const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR)) + QStringLiteral("/overlayicon")); + const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/overlayicon"), {}, KPluginMetaData::AllowEmptyMetaData); for (const KPluginMetaData &data : plugins) { auto instance = QPluginLoader(data.fileName()).instance(); auto plugin = qobject_cast(instance); @@ -157,6 +161,25 @@ QSize KFileItemModelRolesUpdater::iconSize() const return m_iconSize; } +void KFileItemModelRolesUpdater::setDevicePixelRatio(qreal devicePixelRatio) +{ + if (m_devicePixelRatio != devicePixelRatio) { + m_devicePixelRatio = devicePixelRatio; + if (m_state == Paused) { + m_iconSizeChangedDuringPausing = true; + } else if (m_previewShown) { + // A dpr change requires the regenerating of all previews. + m_finishedItems.clear(); + startUpdating(); + } + } +} + +qreal KFileItemModelRolesUpdater::devicePixelRatio() const +{ + return m_devicePixelRatio; +} + void KFileItemModelRolesUpdater::setVisibleIndexRange(int index, int count) { if (index < 0) { @@ -320,16 +343,6 @@ 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); @@ -357,19 +370,28 @@ void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList &itemRan // Determine the sort role synchronously for as many items as possible. if (m_resolvableRoles.contains(m_model->sortRole())) { + QList dirsWithAddedItems; + int insertedCount = 0; for (const KItemRange &range : itemRanges) { const int lastIndex = insertedCount + range.index + range.count - 1; for (int i = insertedCount + range.index; i <= lastIndex; ++i) { + const auto fileItem = m_model->fileItem(i); + const auto fileItemParentFolderUrl = fileItem.url().adjusted(QUrl::RemoveFilename); + if (!dirsWithAddedItems.contains(fileItemParentFolderUrl)) { + dirsWithAddedItems.append(fileItemParentFolderUrl); + } if (timer.elapsed() < MaxBlockTimeout) { applySortRole(i); } else { - m_pendingSortRoleItems.insert(m_model->fileItem(i)); + m_pendingSortRoleItems.insert(fileItem); } } insertedCount += range.count; } + recountDirectoryItems(dirsWithAddedItems); + applySortProgressToModel(); // If there are still items whose sort role is unknown, check if the @@ -422,18 +444,29 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList &itemRang m_hoverSequenceLoadedItems.clear(); killPreviewJob(); + if (!m_model->showDirectoriesOnly()) { + m_directoryContentsCounter->stopWorker(); + } } else { + QList dirsWithDeletedItems; // Only remove the items from m_finishedItems. They will be removed // from the other sets later on. QSet::iterator it = m_finishedItems.begin(); while (it != m_finishedItems.end()) { if (m_model->index(*it) < 0) { + // Get the folder path of the file. + const auto folderUrl = it->url().adjusted(QUrl::RemoveFilename); + if (!dirsWithDeletedItems.contains(folderUrl)) { + dirsWithDeletedItems.append(folderUrl); + } it = m_finishedItems.erase(it); } else { ++it; } } + recountDirectoryItems(dirsWithDeletedItems); + // Removed items won't have hover previews loaded anymore. for (const KItemRange &itemRange : itemRanges) { int index = itemRange.index; @@ -535,32 +568,14 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem &item, const QPi return; } - QPixmap scaledPixmap = transformPreviewPixmap(pixmap); - - 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. - 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; - } - } - } - - data.insert("iconPixmap", scaledPixmap); + QHash data = rolesData(item, index); + data.insert("iconPixmap", transformPreviewPixmap(pixmap)); + data.insert("supportsSequencing", m_previewJob->handlesSequences()); disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + Q_EMIT previewJobFinished(); // For unit testing m_finishedItems.insert(item); } @@ -851,10 +866,10 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem & #endif } -void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long size) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long long size) { - const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); + const bool getSizeRole = m_roles.contains("size"); if (getSizeRole || getIsExpandableRole) { const int index = m_model->index(QUrl::fromLocalFile(path)); @@ -911,7 +926,7 @@ void KFileItemModelRolesUpdater::startUpdating() m_pendingPreviewItems.clear(); m_pendingPreviewItems.reserve(indexes.count()); - for (int index : qAsConst(indexes)) { + for (int index : std::as_const(indexes)) { const KFileItem item = m_model->fileItem(index); if (!m_finishedItems.contains(item)) { m_pendingPreviewItems.append(item); @@ -962,13 +977,6 @@ void KFileItemModelRolesUpdater::startPreviewJob() 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::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. @@ -997,8 +1005,8 @@ void KFileItemModelRolesUpdater::startPreviewJob() } while (!m_pendingPreviewItems.isEmpty() && timer.elapsed() < MaxBlockTimeout); } - KIO::PreviewJob *job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins); - + KIO::PreviewJob *job = new KIO::PreviewJob(itemSubSet, cacheSize(), &m_enabledPlugins); + job->setDevicePixelRatio(m_devicePixelRatio); job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile() && !itemSubSet.first().isSlow() && m_localFileSizePreviewLimit <= 0); if (job->uiDelegate()) { KJobWidgets::setWindow(job, qApp->activeWindow()); @@ -1015,7 +1023,11 @@ 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 (pixmap.isNull()) { + return scaledPixmap; + } + + if (!pixmap.hasAlpha() && m_iconSize.width() > KIconLoader::SizeSmallMedium && m_iconSize.height() > KIconLoader::SizeSmallMedium) { if (m_enlargeSmallPreviews) { KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); } else { @@ -1043,14 +1055,24 @@ QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap &pixmap KPixmapModifier::applyFrame(scaledPixmap, m_iconSize); } } - } else if (!pixmap.isNull()) { - KPixmapModifier::scale(scaledPixmap, m_iconSize * qApp->devicePixelRatio()); - scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio()); + } else { + KPixmapModifier::scale(scaledPixmap, m_iconSize * m_devicePixelRatio); + scaledPixmap.setDevicePixelRatio(m_devicePixelRatio); } return scaledPixmap; } +QSize KFileItemModelRolesUpdater::cacheSize() +{ + // 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. + return (m_iconSize.width() > 128) || (m_iconSize.height() > 128) ? QSize(256, 256) : QSize(128, 128); +} + void KFileItemModelRolesUpdater::loadNextHoverSequencePreview() { if (m_hoverSequenceItem.isNull() || m_hoverSequencePreviewJob) { @@ -1091,14 +1113,7 @@ void KFileItemModelRolesUpdater::loadNextHoverSequencePreview() 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); + 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); @@ -1176,11 +1191,11 @@ void KFileItemModelRolesUpdater::updateChangedItems() std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end()); if (m_previewShown) { - for (int index : qAsConst(visibleChangedIndexes)) { + for (int index : std::as_const(visibleChangedIndexes)) { m_pendingPreviewItems.append(m_model->fileItem(index)); } - for (int index : qAsConst(invisibleChangedIndexes)) { + for (int index : std::as_const(invisibleChangedIndexes)) { m_pendingPreviewItems.append(m_model->fileItem(index)); } @@ -1210,13 +1225,11 @@ 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(); - if (m_scanDirectories) { - m_directoryContentsCounter->scanDirectory(path); - } + startDirectorySizeCounting(item, index); + return; } else { // Probably the sort role is a baloo role - just determine all roles. - data = rolesData(item); + data = rolesData(item, index); } disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); @@ -1252,7 +1265,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) QHash data; if (resolveAll) { - data = rolesData(item); + data = rolesData(item, index); } if (!item.iconName().isEmpty()) { @@ -1273,7 +1286,100 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) return false; } -QHash KFileItemModelRolesUpdater::rolesData(const KFileItem &item) +void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) +{ + if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::None) { + return; + } + + // Set any remote files to unknown size (-1). + if (!item.isLocalFile()) { + resetSizeData(index, -1); + return; + } else { + resetSizeData(index); + } + + if (ContentDisplaySettings::directorySizeMode() == ContentDisplaySettings::EnumDirectorySizeMode::ContentCount || item.isSlow()) { + // fastpath no recursion necessary + auto data = m_model->data(index); + if (data.value("size") == -2) { + // means job already started + return; + } + + auto url = item.url(); + if (!item.localPath().isEmpty()) { + // optimization for desktop:/, trash:/ + url = QUrl::fromLocalFile(item.localPath()); + } + + data.insert("size", -2); // invalid size, -1 means size unknown + + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + + auto listJob = KIO::listDir(url, KIO::HideProgressInfo); + + QObject::connect(listJob, &KIO::ListJob::entries, this, [this, item](const KJob *job, const KIO::UDSEntryList &list) { + int index = m_model->index(item); + if (index < 0) { + return; + } + auto data = m_model->data(index); + int origCount = data.value("count").toInt(); + // Get the amount of processed items... + int entryCount = job->processedAmount(KJob::Bytes); + + // ...and then remove the unwanted items from the amount. + for (const KIO::UDSEntry &entry : list) { + const auto name = entry.stringValue(KIO::UDSEntry::UDS_NAME); + + if (name == QStringLiteral("..") || name == QStringLiteral(".")) { + --entryCount; + continue; + } + if (!m_model->showHiddenFiles() && name.startsWith(QLatin1Char('.'))) { + --entryCount; + continue; + } + if (m_model->showDirectoriesOnly() && !entry.isDir()) { + --entryCount; + continue; + } + } + + QHash newData; + QVariant expandable = data.value("isExpandable"); + if (expandable.isNull() || expandable.toBool() != (entryCount > 0)) { + // if expandable has changed + newData.insert("isExpandable", entryCount > 0); + } + + if (origCount != entryCount) { + newData.insert("count", entryCount); + } + + if (!newData.isEmpty()) { + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, newData); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + } + }); + return; + } + + // 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 auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High + : KDirectoryContentsCounter::PathCountPriority::Normal; + + m_directoryContentsCounter->scanDirectory(path, priority); +} + +QHash KFileItemModelRolesUpdater::rolesData(const KFileItem &item, int index) { QHash data; @@ -1281,19 +1387,11 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte const bool getIsExpandableRole = m_roles.contains("isExpandable"); 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. - 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 - } + startDirectorySizeCounting(item, index); } if (m_roles.contains("extension")) { + // TODO KF6 use KFileItem::suffix 464722 data.insert("extension", QFileInfo(item.name()).suffix()); } @@ -1302,7 +1400,7 @@ QHash KFileItemModelRolesUpdater::rolesData(const KFileIte } QStringList overlays = item.overlays(); - for (KOverlayIconPlugin *it : qAsConst(m_overlayIconsPlugin)) { + for (KOverlayIconPlugin *it : std::as_const(m_overlayIconsPlugin)) { overlays.append(it->getOverlays(item.url())); } if (!overlays.isEmpty()) { @@ -1327,7 +1425,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(); - for (KOverlayIconPlugin *it : qAsConst(m_overlayIconsPlugin)) { + for (KOverlayIconPlugin *it : std::as_const(m_overlayIconsPlugin)) { overlays.append(it->getOverlays(url)); } data.insert("iconOverlays", overlays); @@ -1441,3 +1539,28 @@ void KFileItemModelRolesUpdater::trimHoverSequenceLoadedItems() } } } + +void KFileItemModelRolesUpdater::resetSizeData(const int index, const int size) +{ + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + auto data = m_model->data(index); + data.insert("size", size); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); +} + +void KFileItemModelRolesUpdater::recountDirectoryItems(const QList directories) +{ + for (const auto &dir : directories) { + auto index = m_model->index(dir); + if (index < 0) { + continue; + } + auto item = m_model->fileItem(index); + if (item.isDir()) { + startDirectorySizeCounting(item, index); + } + } +} + +#include "moc_kfileitemmodelrolesupdater.cpp"