2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
4 * SPDX-License-Identifier: GPL-2.0-or-later
7 #include "kfileitemlistview.h"
9 #include "kfileitemlistwidget.h"
10 #include "kfileitemmodel.h"
11 #include "kfileitemmodelrolesupdater.h"
12 #include "private/kitemviewsutils.h"
13 #include "private/kpixmapmodifier.h"
15 #include <KIconLoader>
17 #include <QGraphicsScene>
18 #include <QGraphicsView>
20 #include <QMimeDatabase>
24 // #define KFILEITEMLISTVIEW_DEBUG
28 // If the visible index range changes, KFileItemModelRolesUpdater is not
29 // informed immediately, but with a short delay. This ensures that scrolling
30 // always feels smooth and is not interrupted by icon loading (which can be
31 // quite expensive if a disk access is required to determine the final icon).
32 const int ShortInterval
= 50;
34 // If the icon size changes, a longer delay is used. This prevents that
35 // the expensive re-generation of all previews is triggered repeatedly when
36 // changing the zoom level.
37 const int LongInterval
= 300;
40 KFileItemListView::KFileItemListView(QGraphicsWidget
*parent
)
41 : KStandardItemListView(parent
)
42 , m_modelRolesUpdater(nullptr)
43 , m_updateVisibleIndexRangeTimer(nullptr)
44 , m_updateIconSizeTimer(nullptr)
48 setScrollOrientation(Qt::Vertical
);
50 m_updateVisibleIndexRangeTimer
= new QTimer(this);
51 m_updateVisibleIndexRangeTimer
->setSingleShot(true);
52 m_updateVisibleIndexRangeTimer
->setInterval(ShortInterval
);
53 connect(m_updateVisibleIndexRangeTimer
, &QTimer::timeout
, this, &KFileItemListView::updateVisibleIndexRange
);
55 m_updateIconSizeTimer
= new QTimer(this);
56 m_updateIconSizeTimer
->setSingleShot(true);
57 m_updateIconSizeTimer
->setInterval(LongInterval
);
58 connect(m_updateIconSizeTimer
, &QTimer::timeout
, this, &KFileItemListView::updateIconSize
);
60 setVisibleRoles({"text"});
63 KFileItemListView::~KFileItemListView()
67 void KFileItemListView::setPreviewsShown(bool show
)
69 if (!m_modelRolesUpdater
) {
73 if (m_modelRolesUpdater
->previewsShown() != show
) {
75 m_modelRolesUpdater
->setPreviewsShown(show
);
76 onPreviewsShownChanged(show
);
81 bool KFileItemListView::previewsShown() const
83 return m_modelRolesUpdater
? m_modelRolesUpdater
->previewsShown() : false;
86 void KFileItemListView::setEnlargeSmallPreviews(bool enlarge
)
88 if (m_modelRolesUpdater
) {
89 m_modelRolesUpdater
->setEnlargeSmallPreviews(enlarge
);
93 bool KFileItemListView::enlargeSmallPreviews() const
95 return m_modelRolesUpdater
? m_modelRolesUpdater
->enlargeSmallPreviews() : false;
98 void KFileItemListView::setEnabledPlugins(const QStringList
&list
)
100 if (m_modelRolesUpdater
) {
101 m_modelRolesUpdater
->setEnabledPlugins(list
);
105 QStringList
KFileItemListView::enabledPlugins() const
107 return m_modelRolesUpdater
? m_modelRolesUpdater
->enabledPlugins() : QStringList();
110 void KFileItemListView::setLocalFileSizePreviewLimit(const qlonglong size
)
112 if (m_modelRolesUpdater
) {
113 m_modelRolesUpdater
->setLocalFileSizePreviewLimit(size
);
117 qlonglong
KFileItemListView::localFileSizePreviewLimit() const
119 return m_modelRolesUpdater
? m_modelRolesUpdater
->localFileSizePreviewLimit() : 0;
122 QPixmap
KFileItemListView::createDragPixmap(const KItemSet
&indexes
) const
128 const int itemCount
= indexes
.count();
129 Q_ASSERT(itemCount
> 0);
130 if (itemCount
== 1) {
131 return KItemListView::createDragPixmap(indexes
);
134 // If more than one item is dragged, align the items inside a
135 // rectangular grid. The maximum grid size is limited to 5 x 5 items.
138 if (itemCount
> 16) {
140 size
= KIconLoader::SizeSmall
;
141 } else if (itemCount
> 9) {
143 size
= KIconLoader::SizeSmallMedium
;
146 size
= KIconLoader::SizeMedium
;
149 if (itemCount
< xCount
) {
153 int yCount
= itemCount
/ xCount
;
154 if (itemCount
% xCount
!= 0) {
157 if (yCount
> xCount
) {
161 const qreal dpr
= KItemViewsUtils::devicePixelRatio(this);
162 // Draw the selected items into the grid cells.
163 QPixmap
dragPixmap(QSize(xCount
* size
+ xCount
, yCount
* size
+ yCount
) * dpr
);
164 dragPixmap
.setDevicePixelRatio(dpr
);
165 dragPixmap
.fill(Qt::transparent
);
167 QPainter
painter(&dragPixmap
);
171 for (int index
: indexes
) {
172 QPixmap pixmap
= model()->data(index
).value("iconPixmap").value
<QPixmap
>();
173 if (pixmap
.isNull()) {
174 QIcon icon
= QIcon::fromTheme(model()->data(index
).value("iconName").toString());
176 icon
= QIcon::fromTheme(QStringLiteral("unknown"));
178 if (!icon
.isNull()) {
179 pixmap
= icon
.pixmap(size
, size
);
181 pixmap
= QPixmap(size
, size
);
182 pixmap
.fill(Qt::transparent
);
185 KPixmapModifier::scale(pixmap
, QSize(size
, size
) * dpr
);
188 painter
.drawPixmap(x
, y
, pixmap
);
191 if (x
>= dragPixmap
.width()) {
196 if (y
>= dragPixmap
.height()) {
204 void KFileItemListView::setHoverSequenceState(const QUrl
&itemUrl
, int seqIdx
)
206 if (m_modelRolesUpdater
) {
207 m_modelRolesUpdater
->setHoverSequenceState(itemUrl
, seqIdx
);
211 KItemListWidgetCreatorBase
*KFileItemListView::defaultWidgetCreator() const
213 return new KItemListWidgetCreator
<KFileItemListWidget
>();
216 void KFileItemListView::initializeItemListWidget(KItemListWidget
*item
)
218 KStandardItemListView::initializeItemListWidget(item
);
220 // Make sure that the item has an icon.
221 QHash
<QByteArray
, QVariant
> data
= item
->data();
222 if (!data
.contains("iconName") && data
["iconPixmap"].value
<QPixmap
>().isNull()) {
223 Q_ASSERT(qobject_cast
<KFileItemModel
*>(model()));
224 KFileItemModel
*fileItemModel
= static_cast<KFileItemModel
*>(model());
226 const KFileItem fileItem
= fileItemModel
->fileItem(item
->index());
227 QString iconName
= fileItem
.iconName();
228 if (!QIcon::hasThemeIcon(iconName
)) {
229 QMimeDatabase mimeDb
;
230 iconName
= mimeDb
.mimeTypeForName(fileItem
.mimetype()).genericIconName();
232 data
.insert("iconName", iconName
);
233 item
->setData(data
, {"iconName"});
237 void KFileItemListView::onPreviewsShownChanged(bool shown
)
242 void KFileItemListView::onItemLayoutChanged(ItemLayout current
, ItemLayout previous
)
244 KStandardItemListView::onItemLayoutChanged(current
, previous
);
245 triggerVisibleIndexRangeUpdate();
248 void KFileItemListView::onModelChanged(KItemModelBase
*current
, KItemModelBase
*previous
)
250 Q_ASSERT(qobject_cast
<KFileItemModel
*>(current
));
251 KStandardItemListView::onModelChanged(current
, previous
);
253 delete m_modelRolesUpdater
;
254 m_modelRolesUpdater
= nullptr;
257 m_modelRolesUpdater
= new KFileItemModelRolesUpdater(static_cast<KFileItemModel
*>(current
), this);
258 m_modelRolesUpdater
->setIconSize(availableIconSize());
259 m_modelRolesUpdater
->setDevicePixelRatio(KItemViewsUtils::devicePixelRatio(this));
265 void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current
, Qt::Orientation previous
)
267 KStandardItemListView::onScrollOrientationChanged(current
, previous
);
268 triggerVisibleIndexRangeUpdate();
271 void KFileItemListView::onItemSizeChanged(const QSizeF
¤t
, const QSizeF
&previous
)
275 triggerVisibleIndexRangeUpdate();
278 void KFileItemListView::onScrollOffsetChanged(qreal current
, qreal previous
)
280 KStandardItemListView::onScrollOffsetChanged(current
, previous
);
281 triggerVisibleIndexRangeUpdate();
284 void KFileItemListView::onVisibleRolesChanged(const QList
<QByteArray
> ¤t
, const QList
<QByteArray
> &previous
)
286 KStandardItemListView::onVisibleRolesChanged(current
, previous
);
290 void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption
¤t
, const KItemListStyleOption
&previous
)
292 KStandardItemListView::onStyleOptionChanged(current
, previous
);
293 triggerIconSizeUpdate();
296 void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding
)
299 KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding
);
300 triggerVisibleIndexRangeUpdate();
303 void KFileItemListView::onTransactionBegin()
305 if (m_modelRolesUpdater
) {
306 m_modelRolesUpdater
->setPaused(true);
310 void KFileItemListView::onTransactionEnd()
312 if (!m_modelRolesUpdater
) {
316 // Only unpause the model-roles-updater if no timer is active. If one
317 // timer is still active the model-roles-updater will be unpaused later as
318 // soon as the timer has been exceeded.
319 const bool timerActive
= m_updateVisibleIndexRangeTimer
->isActive() || m_updateIconSizeTimer
->isActive();
321 m_modelRolesUpdater
->setPaused(false);
325 void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent
*event
)
327 KStandardItemListView::resizeEvent(event
);
328 triggerVisibleIndexRangeUpdate();
331 void KFileItemListView::focusInEvent(QFocusEvent
*event
)
334 updateSelectedWidgets();
337 void KFileItemListView::focusOutEvent(QFocusEvent
*event
)
340 updateSelectedWidgets();
343 void KFileItemListView::updateSelectedWidgets()
345 const auto visibleWidgets
= visibleItemListWidgets();
346 for (KItemListWidget
*widget
: visibleWidgets
) {
347 if (widget
->isSelected()) {
348 auto w
= qobject_cast
<KFileItemListWidget
*>(widget
);
356 void KFileItemListView::slotItemsRemoved(const KItemRangeList
&itemRanges
)
358 KStandardItemListView::slotItemsRemoved(itemRanges
);
361 void KFileItemListView::slotSortRoleChanged(const QByteArray
¤t
, const QByteArray
&previous
)
363 const QByteArray sortRole
= model()->sortRole();
364 if (!visibleRoles().contains(sortRole
)) {
368 KStandardItemListView::slotSortRoleChanged(current
, previous
);
371 void KFileItemListView::triggerVisibleIndexRangeUpdate()
376 m_modelRolesUpdater
->setPaused(true);
378 // If the icon size has been changed recently, wait until
379 // m_updateIconSizeTimer expires.
380 if (!m_updateIconSizeTimer
->isActive()) {
381 m_updateVisibleIndexRangeTimer
->start();
385 void KFileItemListView::updateVisibleIndexRange()
387 if (!m_modelRolesUpdater
) {
391 const int index
= firstVisibleIndex();
392 const int count
= lastVisibleIndex() - index
+ 1;
393 m_modelRolesUpdater
->setMaximumVisibleItems(maximumVisibleItems());
394 m_modelRolesUpdater
->setVisibleIndexRange(index
, count
);
395 m_modelRolesUpdater
->setPaused(isTransactionActive());
398 void KFileItemListView::triggerIconSizeUpdate()
403 m_modelRolesUpdater
->setPaused(true);
404 m_updateIconSizeTimer
->start();
406 // The visible index range will be updated when m_updateIconSizeTimer expires.
407 // Stop m_updateVisibleIndexRangeTimer to prevent an expensive re-generation
408 // of all previews (note that the user might change the icon size again soon).
409 m_updateVisibleIndexRangeTimer
->stop();
412 void KFileItemListView::updateIconSize()
414 if (!m_modelRolesUpdater
) {
418 m_modelRolesUpdater
->setIconSize(availableIconSize());
419 m_modelRolesUpdater
->setDevicePixelRatio(KItemViewsUtils::devicePixelRatio(this));
421 // Update the visible index range (which has most likely changed after the
422 // icon size change) before unpausing m_modelRolesUpdater.
423 const int index
= firstVisibleIndex();
424 const int count
= lastVisibleIndex() - index
+ 1;
425 m_modelRolesUpdater
->setVisibleIndexRange(index
, count
);
427 m_modelRolesUpdater
->setPaused(isTransactionActive());
430 void KFileItemListView::applyRolesToModel()
436 Q_ASSERT(qobject_cast
<KFileItemModel
*>(model()));
437 KFileItemModel
*fileItemModel
= static_cast<KFileItemModel
*>(model());
439 // KFileItemModel does not distinct between "visible" and "invisible" roles.
440 // Add all roles that are mandatory for having a working KFileItemListView:
441 const auto visibleRoles
= this->visibleRoles();
442 auto roles
= QSet
<QByteArray
>(visibleRoles
.constBegin(), visibleRoles
.constEnd());
443 roles
.insert("iconPixmap");
444 roles
.insert("iconName");
445 roles
.insert("text");
446 roles
.insert("isDir");
447 roles
.insert("isLink");
448 roles
.insert("isHidden");
449 if (supportsItemExpanding()) {
450 roles
.insert("isExpanded");
451 roles
.insert("isExpandable");
452 roles
.insert("expandedParentsCount");
455 // Assure that the role that is used for sorting will be determined
456 roles
.insert(fileItemModel
->sortRole());
458 fileItemModel
->setRoles(roles
);
459 m_modelRolesUpdater
->setRoles(roles
);
462 QSize
KFileItemListView::availableIconSize() const
464 const KItemListStyleOption
&option
= styleOption();
465 const int iconSize
= option
.iconSize
;
466 if (itemLayout() == IconsLayout
) {
467 const int maxIconWidth
= itemSize().width() - 2 * option
.padding
;
468 return QSize(maxIconWidth
, iconSize
);
471 return QSize(iconSize
, iconSize
);
474 #include "moc_kfileitemlistview.cpp"