-/***************************************************************************
- * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
- * *
- * 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 <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
#include "kfileitemlistview.h"
-#include "kfileitemmodelrolesupdater.h"
#include "kfileitemlistwidget.h"
#include "kfileitemmodel.h"
-#include <KLocale>
-#include <KStringHandler>
+#include "kfileitemmodelrolesupdater.h"
+#include "private/kitemviewsutils.h"
#include "private/kpixmapmodifier.h"
-#include <KDebug>
-#include <KIcon>
-#include <KTextEdit>
+#include <KIconLoader>
+#include <QGraphicsScene>
+#include <QGraphicsView>
+#include <QIcon>
+#include <QMimeDatabase>
#include <QPainter>
-#include <QTextLine>
#include <QTimer>
// #define KFILEITEMLISTVIEW_DEBUG
-namespace {
- const int ShortInterval = 50;
- const int LongInterval = 300;
-}
-
-KFileItemListView::KFileItemListView(QGraphicsWidget* parent) :
- KStandardItemListView(parent),
- m_modelRolesUpdater(0),
- m_updateVisibleIndexRangeTimer(0),
- m_updateIconSizeTimer(0)
+namespace
+{
+// If the visible index range changes, KFileItemModelRolesUpdater is not
+// informed immediately, but with a short delay. This ensures that scrolling
+// always feels smooth and is not interrupted by icon loading (which can be
+// quite expensive if a disk access is required to determine the final icon).
+const int ShortInterval = 50;
+
+// If the icon size changes, a longer delay is used. This prevents that
+// the expensive re-generation of all previews is triggered repeatedly when
+// changing the zoom level.
+const int LongInterval = 300;
+}
+
+KFileItemListView::KFileItemListView(QGraphicsWidget *parent)
+ : KStandardItemListView(parent)
+ , m_modelRolesUpdater(nullptr)
+ , m_updateVisibleIndexRangeTimer(nullptr)
+ , m_updateIconSizeTimer(nullptr)
{
setAcceptDrops(true);
m_updateVisibleIndexRangeTimer = new QTimer(this);
m_updateVisibleIndexRangeTimer->setSingleShot(true);
m_updateVisibleIndexRangeTimer->setInterval(ShortInterval);
- connect(m_updateVisibleIndexRangeTimer, SIGNAL(timeout()), this, SLOT(updateVisibleIndexRange()));
+ connect(m_updateVisibleIndexRangeTimer, &QTimer::timeout, this, &KFileItemListView::updateVisibleIndexRange);
m_updateIconSizeTimer = new QTimer(this);
m_updateIconSizeTimer->setSingleShot(true);
- m_updateIconSizeTimer->setInterval(ShortInterval);
- connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize()));
+ m_updateIconSizeTimer->setInterval(LongInterval);
+ connect(m_updateIconSizeTimer, &QTimer::timeout, this, &KFileItemListView::updateIconSize);
- setVisibleRoles(QList<QByteArray>() << "text");
+ setVisibleRoles({"text"});
}
KFileItemListView::~KFileItemListView()
return m_modelRolesUpdater ? m_modelRolesUpdater->enlargeSmallPreviews() : false;
}
-void KFileItemListView::setEnabledPlugins(const QStringList& list)
+void KFileItemListView::setEnabledPlugins(const QStringList &list)
{
if (m_modelRolesUpdater) {
m_modelRolesUpdater->setEnabledPlugins(list);
return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList();
}
-QPixmap KFileItemListView::createDragPixmap(const QSet<int>& indexes) const
+void KFileItemListView::setLocalFileSizePreviewLimit(const qlonglong size)
+{
+ if (m_modelRolesUpdater) {
+ m_modelRolesUpdater->setLocalFileSizePreviewLimit(size);
+ }
+}
+
+qlonglong KFileItemListView::localFileSizePreviewLimit() const
+{
+ return m_modelRolesUpdater ? m_modelRolesUpdater->localFileSizePreviewLimit() : 0;
+}
+
+QPixmap KFileItemListView::createDragPixmap(const KItemSet &indexes) const
{
if (!model()) {
return QPixmap();
yCount = xCount;
}
+ const qreal dpr = KItemViewsUtils::devicePixelRatio(this);
// Draw the selected items into the grid cells.
- QPixmap dragPixmap(xCount * size + xCount, yCount * size + yCount);
+ QPixmap dragPixmap(QSize(xCount * size + xCount, yCount * size + yCount) * dpr);
+ dragPixmap.setDevicePixelRatio(dpr);
dragPixmap.fill(Qt::transparent);
QPainter painter(&dragPixmap);
int x = 0;
int y = 0;
- QSetIterator<int> it(indexes);
- while (it.hasNext()) {
- const int index = it.next();
+ for (int index : indexes) {
QPixmap pixmap = model()->data(index).value("iconPixmap").value<QPixmap>();
if (pixmap.isNull()) {
- KIcon icon(model()->data(index).value("iconName").toString());
- pixmap = icon.pixmap(size, size);
+ QIcon icon = QIcon::fromTheme(model()->data(index).value("iconName").toString());
+ if (icon.isNull()) {
+ icon = QIcon::fromTheme(QStringLiteral("unknown"));
+ }
+ if (!icon.isNull()) {
+ pixmap = icon.pixmap(size, size);
+ } else {
+ pixmap = QPixmap(size, size);
+ pixmap.fill(Qt::transparent);
+ }
} else {
- KPixmapModifier::scale(pixmap, QSize(size, size));
+ KPixmapModifier::scale(pixmap, QSize(size, size) * dpr);
}
painter.drawPixmap(x, y, pixmap);
return dragPixmap;
}
-KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const
+void KFileItemListView::setHoverSequenceState(const QUrl &itemUrl, int seqIdx)
+{
+ if (m_modelRolesUpdater) {
+ m_modelRolesUpdater->setHoverSequenceState(itemUrl, seqIdx);
+ }
+}
+
+KItemListWidgetCreatorBase *KFileItemListView::defaultWidgetCreator() const
{
return new KItemListWidgetCreator<KFileItemListWidget>();
}
+void KFileItemListView::initializeItemListWidget(KItemListWidget *item)
+{
+ KStandardItemListView::initializeItemListWidget(item);
+
+ // Make sure that the item has an icon.
+ QHash<QByteArray, QVariant> data = item->data();
+ if (!data.contains("iconName") && data["iconPixmap"].value<QPixmap>().isNull()) {
+ Q_ASSERT(qobject_cast<KFileItemModel *>(model()));
+ KFileItemModel *fileItemModel = static_cast<KFileItemModel *>(model());
+
+ const KFileItem fileItem = fileItemModel->fileItem(item->index());
+ QString iconName = fileItem.iconName();
+ if (!QIcon::hasThemeIcon(iconName)) {
+ QMimeDatabase mimeDb;
+ iconName = mimeDb.mimeTypeForName(fileItem.mimetype()).genericIconName();
+ }
+ data.insert("iconName", iconName);
+ item->setData(data, {"iconName"});
+ }
+}
+
void KFileItemListView::onPreviewsShownChanged(bool shown)
{
- Q_UNUSED(shown);
+ Q_UNUSED(shown)
}
void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
{
- if (previous == DetailsLayout || current == DetailsLayout) {
- // The details-layout requires some invisible roles that
- // must be added to the model if the new layout is "details".
- // If the old layout was "details" the roles will get removed.
- applyRolesToModel();
- }
KStandardItemListView::onItemLayoutChanged(current, previous);
triggerVisibleIndexRangeUpdate();
}
-void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous)
+void KFileItemListView::onModelChanged(KItemModelBase *current, KItemModelBase *previous)
{
- Q_ASSERT(qobject_cast<KFileItemModel*>(current));
+ Q_ASSERT(qobject_cast<KFileItemModel *>(current));
KStandardItemListView::onModelChanged(current, previous);
delete m_modelRolesUpdater;
- m_modelRolesUpdater = 0;
+ m_modelRolesUpdater = nullptr;
if (current) {
- m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel*>(current), this);
+ m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel *>(current), this);
m_modelRolesUpdater->setIconSize(availableIconSize());
+ m_modelRolesUpdater->setDevicePixelRatio(KItemViewsUtils::devicePixelRatio(this));
applyRolesToModel();
}
triggerVisibleIndexRangeUpdate();
}
-void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous)
+void KFileItemListView::onItemSizeChanged(const QSizeF ¤t, const QSizeF &previous)
{
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
triggerVisibleIndexRangeUpdate();
}
triggerVisibleIndexRangeUpdate();
}
-void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous)
+void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray> ¤t, const QList<QByteArray> &previous)
{
KStandardItemListView::onVisibleRolesChanged(current, previous);
applyRolesToModel();
}
-void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous)
+void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption ¤t, const KItemListStyleOption &previous)
{
KStandardItemListView::onStyleOptionChanged(current, previous);
triggerIconSizeUpdate();
// Only unpause the model-roles-updater if no timer is active. If one
// timer is still active the model-roles-updater will be unpaused later as
// soon as the timer has been exceeded.
- const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() ||
- m_updateIconSizeTimer->isActive();
+ const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() || m_updateIconSizeTimer->isActive();
if (!timerActive) {
m_modelRolesUpdater->setPaused(false);
}
}
-void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event)
+void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
KStandardItemListView::resizeEvent(event);
triggerVisibleIndexRangeUpdate();
}
-void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
+void KFileItemListView::focusInEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event)
+ updateSelectedWidgets();
+}
+
+void KFileItemListView::focusOutEvent(QFocusEvent *event)
+{
+ Q_UNUSED(event)
+ updateSelectedWidgets();
+}
+
+void KFileItemListView::updateSelectedWidgets()
+{
+ const auto visibleWidgets = visibleItemListWidgets();
+ for (KItemListWidget *widget : visibleWidgets) {
+ if (widget->isSelected()) {
+ auto w = qobject_cast<KFileItemListWidget *>(widget);
+ if (w) {
+ w->forceUpdate();
+ }
+ }
+ }
+}
+
+void KFileItemListView::slotItemsRemoved(const KItemRangeList &itemRanges)
{
KStandardItemListView::slotItemsRemoved(itemRanges);
- updateTimersInterval();
}
-void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+void KFileItemListView::slotSortRoleChanged(const QByteArray ¤t, const QByteArray &previous)
{
const QByteArray sortRole = model()->sortRole();
if (!visibleRoles().contains(sortRole)) {
return;
}
m_modelRolesUpdater->setPaused(true);
- m_updateVisibleIndexRangeTimer->start();
+
+ // If the icon size has been changed recently, wait until
+ // m_updateIconSizeTimer expires.
+ if (!m_updateIconSizeTimer->isActive()) {
+ m_updateVisibleIndexRangeTimer->start();
+ }
}
void KFileItemListView::updateVisibleIndexRange()
const int count = lastVisibleIndex() - index + 1;
m_modelRolesUpdater->setMaximumVisibleItems(maximumVisibleItems());
m_modelRolesUpdater->setVisibleIndexRange(index, count);
-
- if (m_updateIconSizeTimer->isActive()) {
- // If the icon-size update is pending do an immediate update
- // of the icon-size before unpausing m_modelRolesUpdater. This prevents
- // an unnecessary expensive recreation of all previews afterwards.
- m_updateIconSizeTimer->stop();
- m_modelRolesUpdater->setIconSize(availableIconSize());
- }
-
m_modelRolesUpdater->setPaused(isTransactionActive());
- updateTimersInterval();
}
void KFileItemListView::triggerIconSizeUpdate()
}
m_modelRolesUpdater->setPaused(true);
m_updateIconSizeTimer->start();
+
+ // The visible index range will be updated when m_updateIconSizeTimer expires.
+ // Stop m_updateVisibleIndexRangeTimer to prevent an expensive re-generation
+ // of all previews (note that the user might change the icon size again soon).
+ m_updateVisibleIndexRangeTimer->stop();
}
void KFileItemListView::updateIconSize()
}
m_modelRolesUpdater->setIconSize(availableIconSize());
+ m_modelRolesUpdater->setDevicePixelRatio(KItemViewsUtils::devicePixelRatio(this));
- if (m_updateVisibleIndexRangeTimer->isActive()) {
- // If the visibility-index-range update is pending do an immediate update
- // of the range before unpausing m_modelRolesUpdater. This prevents
- // an unnecessary expensive recreation of all previews afterwards.
- m_updateVisibleIndexRangeTimer->stop();
- const int index = firstVisibleIndex();
- const int count = lastVisibleIndex() - index + 1;
- m_modelRolesUpdater->setVisibleIndexRange(index, count);
- }
+ // Update the visible index range (which has most likely changed after the
+ // icon size change) before unpausing m_modelRolesUpdater.
+ const int index = firstVisibleIndex();
+ const int count = lastVisibleIndex() - index + 1;
+ m_modelRolesUpdater->setVisibleIndexRange(index, count);
m_modelRolesUpdater->setPaused(isTransactionActive());
- updateTimersInterval();
-}
-
-void KFileItemListView::updateTimersInterval()
-{
- if (!model()) {
- return;
- }
-
- // The ShortInterval is used for cases like switching the directory: If the
- // model is empty and filled later the creation of the previews should be done
- // as soon as possible. The LongInterval is used when the model already contains
- // items and assures that operations like zooming don't result in too many temporary
- // recreations of the previews.
-
- const int interval = (model()->count() <= 0) ? ShortInterval : LongInterval;
- m_updateVisibleIndexRangeTimer->setInterval(interval);
- m_updateIconSizeTimer->setInterval(interval);
}
void KFileItemListView::applyRolesToModel()
return;
}
- Q_ASSERT(qobject_cast<KFileItemModel*>(model()));
- KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model());
+ Q_ASSERT(qobject_cast<KFileItemModel *>(model()));
+ KFileItemModel *fileItemModel = static_cast<KFileItemModel *>(model());
// KFileItemModel does not distinct between "visible" and "invisible" roles.
// Add all roles that are mandatory for having a working KFileItemListView:
- QSet<QByteArray> roles = visibleRoles().toSet();
+ const auto visibleRoles = this->visibleRoles();
+ auto roles = QSet<QByteArray>(visibleRoles.constBegin(), visibleRoles.constEnd());
roles.insert("iconPixmap");
roles.insert("iconName");
roles.insert("text");
roles.insert("isDir");
roles.insert("isLink");
+ roles.insert("isHidden");
if (supportsItemExpanding()) {
roles.insert("isExpanded");
roles.insert("isExpandable");
QSize KFileItemListView::availableIconSize() const
{
- const KItemListStyleOption& option = styleOption();
+ const KItemListStyleOption &option = styleOption();
const int iconSize = option.iconSize;
if (itemLayout() == IconsLayout) {
const int maxIconWidth = itemSize().width() - 2 * option.padding;
return QSize(iconSize, iconSize);
}
-#include "kfileitemlistview.moc"
+#include "moc_kfileitemlistview.cpp"