+QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap &pixmap)
+{
+ QPixmap scaledPixmap = pixmap;
+
+ 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 {
+ // 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 * 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) {
+ 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<QByteArray, QVariant> 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>() << QPixmap()));
+ m_model->setData(index, data);
+ }
+
+ const QVector<QPixmap> pixmaps = data["hoverSequencePixmaps"].value<QVector<QPixmap>>();
+
+ const int loadSeqIdx = pixmaps.size();
+
+ float wap = -1.0f;
+ if (data.contains("hoverSequenceWraparoundPoint")) {
+ wap = data["hoverSequenceWraparoundPoint"].toFloat();
+ }
+ if (wap >= 1.0f && loadSeqIdx >= static_cast<int>(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;
+ }
+
+ 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;
+ }
+}
+