]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemlistview.cpp
Provide additional default groups for the Places Panel
[dolphin.git] / src / kitemviews / kfileitemlistview.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "kfileitemlistview.h"
21
22 #include "kfileitemmodelrolesupdater.h"
23 #include "kfileitemlistwidget.h"
24 #include "kfileitemmodel.h"
25 #include <KLocale>
26 #include <KStringHandler>
27 #include "private/kpixmapmodifier.h"
28
29 #include <KDebug>
30 #include <KIcon>
31 #include <KTextEdit>
32
33 #include <QPainter>
34 #include <QTextLine>
35 #include <QTimer>
36
37 // #define KFILEITEMLISTVIEW_DEBUG
38
39 namespace {
40 const int ShortInterval = 50;
41 const int LongInterval = 300;
42 }
43
44 KFileItemListView::KFileItemListView(QGraphicsWidget* parent) :
45 KStandardItemListView(parent),
46 m_modelRolesUpdater(0),
47 m_updateVisibleIndexRangeTimer(0),
48 m_updateIconSizeTimer(0)
49 {
50 setAcceptDrops(true);
51
52 setScrollOrientation(Qt::Vertical);
53
54 m_updateVisibleIndexRangeTimer = new QTimer(this);
55 m_updateVisibleIndexRangeTimer->setSingleShot(true);
56 m_updateVisibleIndexRangeTimer->setInterval(ShortInterval);
57 connect(m_updateVisibleIndexRangeTimer, SIGNAL(timeout()), this, SLOT(updateVisibleIndexRange()));
58
59 m_updateIconSizeTimer = new QTimer(this);
60 m_updateIconSizeTimer->setSingleShot(true);
61 m_updateIconSizeTimer->setInterval(ShortInterval);
62 connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize()));
63
64 setVisibleRoles(QList<QByteArray>() << "text");
65 }
66
67 KFileItemListView::~KFileItemListView()
68 {
69 delete m_modelRolesUpdater;
70 m_modelRolesUpdater = 0;
71 }
72
73 void KFileItemListView::setPreviewsShown(bool show)
74 {
75 if (!m_modelRolesUpdater) {
76 return;
77 }
78
79 if (m_modelRolesUpdater->previewsShown() != show) {
80 beginTransaction();
81 m_modelRolesUpdater->setPreviewsShown(show);
82 onPreviewsShownChanged(show);
83 endTransaction();
84 }
85 }
86
87 bool KFileItemListView::previewsShown() const
88 {
89 return m_modelRolesUpdater ? m_modelRolesUpdater->previewsShown() : false;
90 }
91
92 void KFileItemListView::setEnlargeSmallPreviews(bool enlarge)
93 {
94 if (m_modelRolesUpdater) {
95 m_modelRolesUpdater->setEnlargeSmallPreviews(enlarge);
96 }
97 }
98
99 bool KFileItemListView::enlargeSmallPreviews() const
100 {
101 return m_modelRolesUpdater ? m_modelRolesUpdater->enlargeSmallPreviews() : false;
102 }
103
104 void KFileItemListView::setEnabledPlugins(const QStringList& list)
105 {
106 if (m_modelRolesUpdater) {
107 m_modelRolesUpdater->setEnabledPlugins(list);
108 }
109 }
110
111 QStringList KFileItemListView::enabledPlugins() const
112 {
113 return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList();
114 }
115
116 QPixmap KFileItemListView::createDragPixmap(const QSet<int>& indexes) const
117 {
118 if (!model()) {
119 return QPixmap();
120 }
121
122 const int itemCount = indexes.count();
123 Q_ASSERT(itemCount > 0);
124
125 // If more than one item is dragged, align the items inside a
126 // rectangular grid. The maximum grid size is limited to 5 x 5 items.
127 int xCount;
128 int size;
129 if (itemCount > 16) {
130 xCount = 5;
131 size = KIconLoader::SizeSmall;
132 } else if (itemCount > 9) {
133 xCount = 4;
134 size = KIconLoader::SizeSmallMedium;
135 } else {
136 xCount = 3;
137 size = KIconLoader::SizeMedium;
138 }
139
140 if (itemCount < xCount) {
141 xCount = itemCount;
142 }
143
144 int yCount = itemCount / xCount;
145 if (itemCount % xCount != 0) {
146 ++yCount;
147 }
148 if (yCount > xCount) {
149 yCount = xCount;
150 }
151
152 // Draw the selected items into the grid cells.
153 QPixmap dragPixmap(xCount * size + xCount, yCount * size + yCount);
154 dragPixmap.fill(Qt::transparent);
155
156 QPainter painter(&dragPixmap);
157 int x = 0;
158 int y = 0;
159 QSetIterator<int> it(indexes);
160 while (it.hasNext()) {
161 const int index = it.next();
162
163 QPixmap pixmap = model()->data(index).value("iconPixmap").value<QPixmap>();
164 if (pixmap.isNull()) {
165 KIcon icon(model()->data(index).value("iconName").toString());
166 pixmap = icon.pixmap(size, size);
167 } else {
168 KPixmapModifier::scale(pixmap, QSize(size, size));
169 }
170
171 painter.drawPixmap(x, y, pixmap);
172
173 x += size + 1;
174 if (x >= dragPixmap.width()) {
175 x = 0;
176 y += size + 1;
177 }
178
179 if (y >= dragPixmap.height()) {
180 break;
181 }
182 }
183
184 return dragPixmap;
185 }
186
187 KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const
188 {
189 return new KItemListWidgetCreator<KFileItemListWidget>();
190 }
191
192 void KFileItemListView::onPreviewsShownChanged(bool shown)
193 {
194 Q_UNUSED(shown);
195 }
196
197 void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
198 {
199 if (previous == DetailsLayout || current == DetailsLayout) {
200 // The details-layout requires some invisible roles that
201 // must be added to the model if the new layout is "details".
202 // If the old layout was "details" the roles will get removed.
203 applyRolesToModel();
204 }
205 KStandardItemListView::onItemLayoutChanged(current, previous);
206 triggerVisibleIndexRangeUpdate();
207 }
208
209 void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous)
210 {
211 Q_ASSERT(qobject_cast<KFileItemModel*>(current));
212 KStandardItemListView::onModelChanged(current, previous);
213
214 delete m_modelRolesUpdater;
215 m_modelRolesUpdater = 0;
216
217 if (current) {
218 m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel*>(current), this);
219 m_modelRolesUpdater->setIconSize(availableIconSize());
220
221 applyRolesToModel();
222 }
223 }
224
225 void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous)
226 {
227 KStandardItemListView::onScrollOrientationChanged(current, previous);
228 triggerVisibleIndexRangeUpdate();
229 }
230
231 void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous)
232 {
233 Q_UNUSED(current);
234 Q_UNUSED(previous);
235 triggerVisibleIndexRangeUpdate();
236 }
237
238 void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous)
239 {
240 KStandardItemListView::onScrollOffsetChanged(current, previous);
241 triggerVisibleIndexRangeUpdate();
242 }
243
244 void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous)
245 {
246 KStandardItemListView::onVisibleRolesChanged(current, previous);
247 applyRolesToModel();
248 }
249
250 void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous)
251 {
252 KStandardItemListView::onStyleOptionChanged(current, previous);
253 triggerIconSizeUpdate();
254 }
255
256 void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding)
257 {
258 applyRolesToModel();
259 KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding);
260 triggerVisibleIndexRangeUpdate();
261 }
262
263 void KFileItemListView::onTransactionBegin()
264 {
265 if (m_modelRolesUpdater) {
266 m_modelRolesUpdater->setPaused(true);
267 }
268 }
269
270 void KFileItemListView::onTransactionEnd()
271 {
272 if (!m_modelRolesUpdater) {
273 return;
274 }
275
276 // Only unpause the model-roles-updater if no timer is active. If one
277 // timer is still active the model-roles-updater will be unpaused later as
278 // soon as the timer has been exceeded.
279 const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() ||
280 m_updateIconSizeTimer->isActive();
281 if (!timerActive) {
282 m_modelRolesUpdater->setPaused(false);
283 }
284 }
285
286 void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event)
287 {
288 KStandardItemListView::resizeEvent(event);
289 triggerVisibleIndexRangeUpdate();
290 }
291
292 void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
293 {
294 KStandardItemListView::slotItemsRemoved(itemRanges);
295 updateTimersInterval();
296 }
297
298 void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
299 {
300 const QByteArray sortRole = model()->sortRole();
301 if (!visibleRoles().contains(sortRole)) {
302 applyRolesToModel();
303 }
304
305 KStandardItemListView::slotSortRoleChanged(current, previous);
306 }
307
308 void KFileItemListView::triggerVisibleIndexRangeUpdate()
309 {
310 if (!model()) {
311 return;
312 }
313 m_modelRolesUpdater->setPaused(true);
314 m_updateVisibleIndexRangeTimer->start();
315 }
316
317 void KFileItemListView::updateVisibleIndexRange()
318 {
319 if (!m_modelRolesUpdater) {
320 return;
321 }
322
323 const int index = firstVisibleIndex();
324 const int count = lastVisibleIndex() - index + 1;
325 m_modelRolesUpdater->setVisibleIndexRange(index, count);
326
327 if (m_updateIconSizeTimer->isActive()) {
328 // If the icon-size update is pending do an immediate update
329 // of the icon-size before unpausing m_modelRolesUpdater. This prevents
330 // an unnecessary expensive recreation of all previews afterwards.
331 m_updateIconSizeTimer->stop();
332 m_modelRolesUpdater->setIconSize(availableIconSize());
333 }
334
335 m_modelRolesUpdater->setPaused(isTransactionActive());
336 updateTimersInterval();
337 }
338
339 void KFileItemListView::triggerIconSizeUpdate()
340 {
341 if (!model()) {
342 return;
343 }
344 m_modelRolesUpdater->setPaused(true);
345 m_updateIconSizeTimer->start();
346 }
347
348 void KFileItemListView::updateIconSize()
349 {
350 if (!m_modelRolesUpdater) {
351 return;
352 }
353
354 m_modelRolesUpdater->setIconSize(availableIconSize());
355
356 if (m_updateVisibleIndexRangeTimer->isActive()) {
357 // If the visibility-index-range update is pending do an immediate update
358 // of the range before unpausing m_modelRolesUpdater. This prevents
359 // an unnecessary expensive recreation of all previews afterwards.
360 m_updateVisibleIndexRangeTimer->stop();
361 const int index = firstVisibleIndex();
362 const int count = lastVisibleIndex() - index + 1;
363 m_modelRolesUpdater->setVisibleIndexRange(index, count);
364 }
365
366 m_modelRolesUpdater->setPaused(isTransactionActive());
367 updateTimersInterval();
368 }
369
370 void KFileItemListView::updateTimersInterval()
371 {
372 if (!model()) {
373 return;
374 }
375
376 // The ShortInterval is used for cases like switching the directory: If the
377 // model is empty and filled later the creation of the previews should be done
378 // as soon as possible. The LongInterval is used when the model already contains
379 // items and assures that operations like zooming don't result in too many temporary
380 // recreations of the previews.
381
382 const int interval = (model()->count() <= 0) ? ShortInterval : LongInterval;
383 m_updateVisibleIndexRangeTimer->setInterval(interval);
384 m_updateIconSizeTimer->setInterval(interval);
385 }
386
387 void KFileItemListView::applyRolesToModel()
388 {
389 if (!model()) {
390 return;
391 }
392
393 Q_ASSERT(qobject_cast<KFileItemModel*>(model()));
394 KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model());
395
396 // KFileItemModel does not distinct between "visible" and "invisible" roles.
397 // Add all roles that are mandatory for having a working KFileItemListView:
398 QSet<QByteArray> roles = visibleRoles().toSet();
399 roles.insert("iconPixmap");
400 roles.insert("iconName");
401 roles.insert("text");
402 roles.insert("isDir");
403 if (supportsItemExpanding()) {
404 roles.insert("isExpanded");
405 roles.insert("isExpandable");
406 roles.insert("expandedParentsCount");
407 }
408
409 // Assure that the role that is used for sorting will be determined
410 roles.insert(fileItemModel->sortRole());
411
412 fileItemModel->setRoles(roles);
413 m_modelRolesUpdater->setRoles(roles);
414 }
415
416 QSize KFileItemListView::availableIconSize() const
417 {
418 const KItemListStyleOption& option = styleOption();
419 const int iconSize = option.iconSize;
420 if (itemLayout() == IconsLayout) {
421 const int maxIconWidth = itemSize().width() - 2 * option.padding;
422 return QSize(maxIconWidth, iconSize);
423 }
424
425 return QSize(iconSize, iconSize);
426 }
427
428 #include "kfileitemlistview.moc"