]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemlistview.cpp
Fix a bunch of clazy warnings
[dolphin.git] / src / kitemviews / kfileitemlistview.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kfileitemlistview.h"
8
9 #include "kfileitemlistwidget.h"
10 #include "kfileitemmodel.h"
11 #include "kfileitemmodelrolesupdater.h"
12 #include "private/kpixmapmodifier.h"
13
14 #include <KIconLoader>
15
16 #include <QGraphicsScene>
17 #include <QGraphicsView>
18 #include <QIcon>
19 #include <QMimeDatabase>
20 #include <QPainter>
21 #include <QTimer>
22
23 // #define KFILEITEMLISTVIEW_DEBUG
24
25 namespace
26 {
27 // If the visible index range changes, KFileItemModelRolesUpdater is not
28 // informed immediately, but with a short delay. This ensures that scrolling
29 // always feels smooth and is not interrupted by icon loading (which can be
30 // quite expensive if a disk access is required to determine the final icon).
31 const int ShortInterval = 50;
32
33 // If the icon size changes, a longer delay is used. This prevents that
34 // the expensive re-generation of all previews is triggered repeatedly when
35 // changing the zoom level.
36 const int LongInterval = 300;
37 }
38
39 KFileItemListView::KFileItemListView(QGraphicsWidget *parent)
40 : KStandardItemListView(parent)
41 , m_modelRolesUpdater(nullptr)
42 , m_updateVisibleIndexRangeTimer(nullptr)
43 , m_updateIconSizeTimer(nullptr)
44 {
45 setAcceptDrops(true);
46
47 setScrollOrientation(Qt::Vertical);
48
49 m_updateVisibleIndexRangeTimer = new QTimer(this);
50 m_updateVisibleIndexRangeTimer->setSingleShot(true);
51 m_updateVisibleIndexRangeTimer->setInterval(ShortInterval);
52 connect(m_updateVisibleIndexRangeTimer, &QTimer::timeout, this, &KFileItemListView::updateVisibleIndexRange);
53
54 m_updateIconSizeTimer = new QTimer(this);
55 m_updateIconSizeTimer->setSingleShot(true);
56 m_updateIconSizeTimer->setInterval(LongInterval);
57 connect(m_updateIconSizeTimer, &QTimer::timeout, this, &KFileItemListView::updateIconSize);
58
59 setVisibleRoles({"text"});
60 }
61
62 KFileItemListView::~KFileItemListView()
63 {
64 }
65
66 void KFileItemListView::setPreviewsShown(bool show)
67 {
68 if (!m_modelRolesUpdater) {
69 return;
70 }
71
72 if (m_modelRolesUpdater->previewsShown() != show) {
73 beginTransaction();
74 m_modelRolesUpdater->setPreviewsShown(show);
75 onPreviewsShownChanged(show);
76 endTransaction();
77 }
78 }
79
80 bool KFileItemListView::previewsShown() const
81 {
82 return m_modelRolesUpdater ? m_modelRolesUpdater->previewsShown() : false;
83 }
84
85 void KFileItemListView::setEnlargeSmallPreviews(bool enlarge)
86 {
87 if (m_modelRolesUpdater) {
88 m_modelRolesUpdater->setEnlargeSmallPreviews(enlarge);
89 }
90 }
91
92 bool KFileItemListView::enlargeSmallPreviews() const
93 {
94 return m_modelRolesUpdater ? m_modelRolesUpdater->enlargeSmallPreviews() : false;
95 }
96
97 void KFileItemListView::setEnabledPlugins(const QStringList &list)
98 {
99 if (m_modelRolesUpdater) {
100 m_modelRolesUpdater->setEnabledPlugins(list);
101 }
102 }
103
104 QStringList KFileItemListView::enabledPlugins() const
105 {
106 return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList();
107 }
108
109 void KFileItemListView::setLocalFileSizePreviewLimit(const qlonglong size)
110 {
111 if (m_modelRolesUpdater) {
112 m_modelRolesUpdater->setLocalFileSizePreviewLimit(size);
113 }
114 }
115
116 qlonglong KFileItemListView::localFileSizePreviewLimit() const
117 {
118 return m_modelRolesUpdater ? m_modelRolesUpdater->localFileSizePreviewLimit() : 0;
119 }
120
121 QPixmap KFileItemListView::createDragPixmap(const KItemSet &indexes) const
122 {
123 if (!model()) {
124 return QPixmap();
125 }
126
127 const int itemCount = indexes.count();
128 Q_ASSERT(itemCount > 0);
129 if (itemCount == 1) {
130 return KItemListView::createDragPixmap(indexes);
131 }
132
133 // If more than one item is dragged, align the items inside a
134 // rectangular grid. The maximum grid size is limited to 5 x 5 items.
135 int xCount;
136 int size;
137 if (itemCount > 16) {
138 xCount = 5;
139 size = KIconLoader::SizeSmall;
140 } else if (itemCount > 9) {
141 xCount = 4;
142 size = KIconLoader::SizeSmallMedium;
143 } else {
144 xCount = 3;
145 size = KIconLoader::SizeMedium;
146 }
147
148 if (itemCount < xCount) {
149 xCount = itemCount;
150 }
151
152 int yCount = itemCount / xCount;
153 if (itemCount % xCount != 0) {
154 ++yCount;
155 }
156 if (yCount > xCount) {
157 yCount = xCount;
158 }
159
160 const qreal dpr = scene()->views()[0]->devicePixelRatio();
161 // Draw the selected items into the grid cells.
162 QPixmap dragPixmap(QSize(xCount * size + xCount, yCount * size + yCount) * dpr);
163 dragPixmap.setDevicePixelRatio(dpr);
164 dragPixmap.fill(Qt::transparent);
165
166 QPainter painter(&dragPixmap);
167 int x = 0;
168 int y = 0;
169
170 for (int index : indexes) {
171 QPixmap pixmap = model()->data(index).value("iconPixmap").value<QPixmap>();
172 if (pixmap.isNull()) {
173 QIcon icon = QIcon::fromTheme(model()->data(index).value("iconName").toString());
174 if (icon.isNull()) {
175 icon = QIcon::fromTheme(QStringLiteral("unknown"));
176 }
177 if (!icon.isNull()) {
178 pixmap = icon.pixmap(size, size);
179 } else {
180 pixmap = QPixmap(size, size);
181 pixmap.fill(Qt::transparent);
182 }
183 } else {
184 KPixmapModifier::scale(pixmap, QSize(size, size) * dpr);
185 }
186
187 painter.drawPixmap(x, y, pixmap);
188
189 x += size + 1;
190 if (x >= dragPixmap.width()) {
191 x = 0;
192 y += size + 1;
193 }
194
195 if (y >= dragPixmap.height()) {
196 break;
197 }
198 }
199
200 return dragPixmap;
201 }
202
203 void KFileItemListView::setHoverSequenceState(const QUrl &itemUrl, int seqIdx)
204 {
205 if (m_modelRolesUpdater) {
206 m_modelRolesUpdater->setHoverSequenceState(itemUrl, seqIdx);
207 }
208 }
209
210 KItemListWidgetCreatorBase *KFileItemListView::defaultWidgetCreator() const
211 {
212 return new KItemListWidgetCreator<KFileItemListWidget>();
213 }
214
215 void KFileItemListView::initializeItemListWidget(KItemListWidget *item)
216 {
217 KStandardItemListView::initializeItemListWidget(item);
218
219 // Make sure that the item has an icon.
220 QHash<QByteArray, QVariant> data = item->data();
221 if (!data.contains("iconName") && data["iconPixmap"].value<QPixmap>().isNull()) {
222 Q_ASSERT(qobject_cast<KFileItemModel *>(model()));
223 KFileItemModel *fileItemModel = static_cast<KFileItemModel *>(model());
224
225 const KFileItem fileItem = fileItemModel->fileItem(item->index());
226 QString iconName = fileItem.iconName();
227 if (!QIcon::hasThemeIcon(iconName)) {
228 QMimeDatabase mimeDb;
229 iconName = mimeDb.mimeTypeForName(fileItem.mimetype()).genericIconName();
230 }
231 data.insert("iconName", iconName);
232 item->setData(data, {"iconName"});
233 }
234 }
235
236 void KFileItemListView::onPreviewsShownChanged(bool shown)
237 {
238 Q_UNUSED(shown)
239 }
240
241 void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
242 {
243 KStandardItemListView::onItemLayoutChanged(current, previous);
244 triggerVisibleIndexRangeUpdate();
245 }
246
247 void KFileItemListView::onModelChanged(KItemModelBase *current, KItemModelBase *previous)
248 {
249 Q_ASSERT(qobject_cast<KFileItemModel *>(current));
250 KStandardItemListView::onModelChanged(current, previous);
251
252 delete m_modelRolesUpdater;
253 m_modelRolesUpdater = nullptr;
254
255 if (current) {
256 m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel *>(current), this);
257 m_modelRolesUpdater->setIconSize(availableIconSize());
258
259 applyRolesToModel();
260 }
261 }
262
263 void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous)
264 {
265 KStandardItemListView::onScrollOrientationChanged(current, previous);
266 triggerVisibleIndexRangeUpdate();
267 }
268
269 void KFileItemListView::onItemSizeChanged(const QSizeF &current, const QSizeF &previous)
270 {
271 Q_UNUSED(current)
272 Q_UNUSED(previous)
273 triggerVisibleIndexRangeUpdate();
274 }
275
276 void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous)
277 {
278 KStandardItemListView::onScrollOffsetChanged(current, previous);
279 triggerVisibleIndexRangeUpdate();
280 }
281
282 void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray> &current, const QList<QByteArray> &previous)
283 {
284 KStandardItemListView::onVisibleRolesChanged(current, previous);
285 applyRolesToModel();
286 }
287
288 void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption &current, const KItemListStyleOption &previous)
289 {
290 KStandardItemListView::onStyleOptionChanged(current, previous);
291 triggerIconSizeUpdate();
292 }
293
294 void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding)
295 {
296 applyRolesToModel();
297 KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding);
298 triggerVisibleIndexRangeUpdate();
299 }
300
301 void KFileItemListView::onTransactionBegin()
302 {
303 if (m_modelRolesUpdater) {
304 m_modelRolesUpdater->setPaused(true);
305 }
306 }
307
308 void KFileItemListView::onTransactionEnd()
309 {
310 if (!m_modelRolesUpdater) {
311 return;
312 }
313
314 // Only unpause the model-roles-updater if no timer is active. If one
315 // timer is still active the model-roles-updater will be unpaused later as
316 // soon as the timer has been exceeded.
317 const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() || m_updateIconSizeTimer->isActive();
318 if (!timerActive) {
319 m_modelRolesUpdater->setPaused(false);
320 }
321 }
322
323 void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent *event)
324 {
325 KStandardItemListView::resizeEvent(event);
326 triggerVisibleIndexRangeUpdate();
327 }
328
329 void KFileItemListView::slotItemsRemoved(const KItemRangeList &itemRanges)
330 {
331 KStandardItemListView::slotItemsRemoved(itemRanges);
332 }
333
334 void KFileItemListView::slotSortRoleChanged(const QByteArray &current, const QByteArray &previous)
335 {
336 const QByteArray sortRole = model()->sortRole();
337 if (!visibleRoles().contains(sortRole)) {
338 applyRolesToModel();
339 }
340
341 KStandardItemListView::slotSortRoleChanged(current, previous);
342 }
343
344 void KFileItemListView::triggerVisibleIndexRangeUpdate()
345 {
346 if (!model()) {
347 return;
348 }
349 m_modelRolesUpdater->setPaused(true);
350
351 // If the icon size has been changed recently, wait until
352 // m_updateIconSizeTimer expires.
353 if (!m_updateIconSizeTimer->isActive()) {
354 m_updateVisibleIndexRangeTimer->start();
355 }
356 }
357
358 void KFileItemListView::updateVisibleIndexRange()
359 {
360 if (!m_modelRolesUpdater) {
361 return;
362 }
363
364 const int index = firstVisibleIndex();
365 const int count = lastVisibleIndex() - index + 1;
366 m_modelRolesUpdater->setMaximumVisibleItems(maximumVisibleItems());
367 m_modelRolesUpdater->setVisibleIndexRange(index, count);
368 m_modelRolesUpdater->setPaused(isTransactionActive());
369 }
370
371 void KFileItemListView::triggerIconSizeUpdate()
372 {
373 if (!model()) {
374 return;
375 }
376 m_modelRolesUpdater->setPaused(true);
377 m_updateIconSizeTimer->start();
378
379 // The visible index range will be updated when m_updateIconSizeTimer expires.
380 // Stop m_updateVisibleIndexRangeTimer to prevent an expensive re-generation
381 // of all previews (note that the user might change the icon size again soon).
382 m_updateVisibleIndexRangeTimer->stop();
383 }
384
385 void KFileItemListView::updateIconSize()
386 {
387 if (!m_modelRolesUpdater) {
388 return;
389 }
390
391 m_modelRolesUpdater->setIconSize(availableIconSize());
392
393 // Update the visible index range (which has most likely changed after the
394 // icon size change) before unpausing m_modelRolesUpdater.
395 const int index = firstVisibleIndex();
396 const int count = lastVisibleIndex() - index + 1;
397 m_modelRolesUpdater->setVisibleIndexRange(index, count);
398
399 m_modelRolesUpdater->setPaused(isTransactionActive());
400 }
401
402 void KFileItemListView::applyRolesToModel()
403 {
404 if (!model()) {
405 return;
406 }
407
408 Q_ASSERT(qobject_cast<KFileItemModel *>(model()));
409 KFileItemModel *fileItemModel = static_cast<KFileItemModel *>(model());
410
411 // KFileItemModel does not distinct between "visible" and "invisible" roles.
412 // Add all roles that are mandatory for having a working KFileItemListView:
413 const auto visibleRoles = this->visibleRoles();
414 auto roles = QSet<QByteArray>(visibleRoles.constBegin(), visibleRoles.constEnd());
415 roles.insert("iconPixmap");
416 roles.insert("iconName");
417 roles.insert("text");
418 roles.insert("isDir");
419 roles.insert("isLink");
420 roles.insert("isHidden");
421 if (supportsItemExpanding()) {
422 roles.insert("isExpanded");
423 roles.insert("isExpandable");
424 roles.insert("expandedParentsCount");
425 }
426
427 // Assure that the role that is used for sorting will be determined
428 roles.insert(fileItemModel->sortRole());
429
430 fileItemModel->setRoles(roles);
431 m_modelRolesUpdater->setRoles(roles);
432 }
433
434 QSize KFileItemListView::availableIconSize() const
435 {
436 const KItemListStyleOption &option = styleOption();
437 const int iconSize = option.iconSize;
438 if (itemLayout() == IconsLayout) {
439 const int maxIconWidth = itemSize().width() - 2 * option.padding;
440 return QSize(maxIconWidth, iconSize);
441 }
442
443 return QSize(iconSize, iconSize);
444 }
445
446 #include "moc_kfileitemlistview.cpp"