]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placesitemmodel.cpp
Word-wrap KMessageWidget text
[dolphin.git] / src / panels / places / placesitemmodel.cpp
1 /***************************************************************************
2 * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * Based on KFilePlacesModel from kdelibs: *
5 * Copyright (C) 2007 Kevin Ottens <ervin@kde.org> *
6 * Copyright (C) 2007 David Faure <faure@kde.org> *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
22 ***************************************************************************/
23
24 #include "placesitemmodel.h"
25
26 #include "dolphin_generalsettings.h"
27 #include "dolphindebug.h"
28 #include "dolphinplacesmodelsingleton.h"
29 #include "placesitem.h"
30 #include "placesitemsignalhandler.h"
31 #include "views/dolphinview.h"
32 #include "views/viewproperties.h"
33
34 #include <KAboutData>
35 #include <KLocalizedString>
36 #include <KUrlMimeData>
37 #include <Solid/DeviceNotifier>
38 #include <Solid/OpticalDrive>
39
40 #include <QAction>
41 #include <QIcon>
42 #include <QMimeData>
43 #include <QTimer>
44
45 namespace {
46 static QList<QUrl> balooURLs = {
47 QUrl(QStringLiteral("timeline:/today")),
48 QUrl(QStringLiteral("timeline:/yesterday")),
49 QUrl(QStringLiteral("timeline:/thismonth")),
50 QUrl(QStringLiteral("timeline:/lastmonth")),
51 QUrl(QStringLiteral("search:/documents")),
52 QUrl(QStringLiteral("search:/images")),
53 QUrl(QStringLiteral("search:/audio")),
54 QUrl(QStringLiteral("search:/videos"))
55 };
56 }
57
58 PlacesItemModel::PlacesItemModel(QObject* parent) :
59 KStandardItemModel(parent),
60 m_hiddenItemsShown(false),
61 m_deviceToTearDown(nullptr),
62 m_storageSetupInProgress(),
63 m_sourceModel(DolphinPlacesModelSingleton::instance().placesModel())
64 {
65 cleanupBookmarks();
66 loadBookmarks();
67 initializeDefaultViewProperties();
68
69 connect(m_sourceModel, &KFilePlacesModel::rowsInserted, this, &PlacesItemModel::onSourceModelRowsInserted);
70 connect(m_sourceModel, &KFilePlacesModel::rowsAboutToBeRemoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeRemoved);
71 connect(m_sourceModel, &KFilePlacesModel::dataChanged, this, &PlacesItemModel::onSourceModelDataChanged);
72 connect(m_sourceModel, &KFilePlacesModel::rowsAboutToBeMoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeMoved);
73 connect(m_sourceModel, &KFilePlacesModel::rowsMoved, this, &PlacesItemModel::onSourceModelRowsMoved);
74 connect(m_sourceModel, &KFilePlacesModel::groupHiddenChanged, this, &PlacesItemModel::onSourceModelGroupHiddenChanged);
75 }
76
77 PlacesItemModel::~PlacesItemModel()
78 {
79 }
80
81 void PlacesItemModel::createPlacesItem(const QString& text,
82 const QUrl& url,
83 const QString& iconName,
84 int after)
85 {
86 m_sourceModel->addPlace(text, url, iconName, {}, mapToSource(after));
87 }
88
89 PlacesItem* PlacesItemModel::placesItem(int index) const
90 {
91 return dynamic_cast<PlacesItem*>(item(index));
92 }
93
94 int PlacesItemModel::hiddenCount() const
95 {
96 return m_sourceModel->hiddenCount();
97 }
98
99 void PlacesItemModel::setHiddenItemsShown(bool show)
100 {
101 if (m_hiddenItemsShown == show) {
102 return;
103 }
104
105 m_hiddenItemsShown = show;
106
107 if (show) {
108 for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
109 const QModelIndex index = m_sourceModel->index(r, 0);
110 if (!m_sourceModel->isHidden(index)) {
111 continue;
112 }
113 addItemFromSourceModel(index);
114 }
115 } else {
116 for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
117 const QModelIndex index = m_sourceModel->index(r, 0);
118 if (m_sourceModel->isHidden(index)) {
119 removeItemByIndex(index);
120 }
121 }
122 }
123 }
124
125 bool PlacesItemModel::hiddenItemsShown() const
126 {
127 return m_hiddenItemsShown;
128 }
129
130 int PlacesItemModel::closestItem(const QUrl& url) const
131 {
132 return mapFromSource(m_sourceModel->closestItem(url));
133 }
134
135 // look for the correct position for the item based on source model
136 void PlacesItemModel::insertSortedItem(PlacesItem* item)
137 {
138 if (!item) {
139 return;
140 }
141
142 const KBookmark iBookmark = item->bookmark();
143 const QString iBookmarkId = bookmarkId(iBookmark);
144 QModelIndex sourceIndex;
145 int pos = 0;
146
147 for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
148 sourceIndex = m_sourceModel->index(r, 0);
149 const KBookmark sourceBookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
150
151 if (bookmarkId(sourceBookmark) == iBookmarkId) {
152 break;
153 }
154
155 if (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex)) {
156 pos++;
157 }
158 }
159
160 m_indexMap.insert(pos, sourceIndex);
161 insertItem(pos, item);
162 }
163
164 void PlacesItemModel::onItemInserted(int index)
165 {
166 KStandardItemModel::onItemInserted(index);
167 }
168
169 void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem)
170 {
171 m_indexMap.removeAt(index);
172
173 KStandardItemModel::onItemRemoved(index, removedItem);
174 }
175
176 void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
177 {
178 const QModelIndex sourceIndex = mapToSource(index);
179 const PlacesItem *changedItem = placesItem(mapFromSource(sourceIndex));
180
181 if (!changedItem || !sourceIndex.isValid()) {
182 qWarning() << "invalid item changed signal";
183 return;
184 }
185 if (changedRoles.contains("isHidden")) {
186 if (m_sourceModel->isHidden(sourceIndex) != changedItem->isHidden()) {
187 m_sourceModel->setPlaceHidden(sourceIndex, changedItem->isHidden());
188 } else {
189 m_sourceModel->refresh();
190 }
191 }
192 KStandardItemModel::onItemChanged(index, changedRoles);
193 }
194
195 QAction* PlacesItemModel::ejectAction(int index) const
196 {
197 const PlacesItem* item = placesItem(index);
198 if (item && item->device().is<Solid::OpticalDisc>()) {
199 return new QAction(QIcon::fromTheme(QStringLiteral("media-eject")), i18nc("@item", "Eject"), nullptr);
200 }
201
202 return nullptr;
203 }
204
205 QAction* PlacesItemModel::teardownAction(int index) const
206 {
207 const PlacesItem* item = placesItem(index);
208 if (!item) {
209 return nullptr;
210 }
211
212 Solid::Device device = item->device();
213 const bool providesTearDown = device.is<Solid::StorageAccess>() &&
214 device.as<Solid::StorageAccess>()->isAccessible();
215 if (!providesTearDown) {
216 return nullptr;
217 }
218
219 Solid::StorageDrive* drive = device.as<Solid::StorageDrive>();
220 if (!drive) {
221 drive = device.parent().as<Solid::StorageDrive>();
222 }
223
224 bool hotPluggable = false;
225 bool removable = false;
226 if (drive) {
227 hotPluggable = drive->isHotpluggable();
228 removable = drive->isRemovable();
229 }
230
231 QString iconName;
232 QString text;
233 if (device.is<Solid::OpticalDisc>()) {
234 text = i18nc("@item", "Release");
235 } else if (removable || hotPluggable) {
236 text = i18nc("@item", "Safely Remove");
237 iconName = QStringLiteral("media-eject");
238 } else {
239 text = i18nc("@item", "Unmount");
240 iconName = QStringLiteral("media-eject");
241 }
242
243 if (iconName.isEmpty()) {
244 return new QAction(text, nullptr);
245 }
246
247 return new QAction(QIcon::fromTheme(iconName), text, nullptr);
248 }
249
250 void PlacesItemModel::requestEject(int index)
251 {
252 const PlacesItem* item = placesItem(index);
253 if (item) {
254 Solid::OpticalDrive* drive = item->device().parent().as<Solid::OpticalDrive>();
255 if (drive) {
256 connect(drive, &Solid::OpticalDrive::ejectDone,
257 this, &PlacesItemModel::slotStorageTearDownDone);
258 drive->eject();
259 } else {
260 const QString label = item->text();
261 const QString message = i18nc("@info", "The device '%1' is not a disk and cannot be ejected.", label);
262 emit errorMessage(message);
263 }
264 }
265 }
266
267 void PlacesItemModel::requestTearDown(int index)
268 {
269 const PlacesItem* item = placesItem(index);
270 if (item) {
271 Solid::StorageAccess *tmp = item->device().as<Solid::StorageAccess>();
272 if (tmp) {
273 m_deviceToTearDown = tmp;
274 // disconnect the Solid::StorageAccess::teardownRequested
275 // to prevent emitting PlacesItemModel::storageTearDownExternallyRequested
276 // after we have emitted PlacesItemModel::storageTearDownRequested
277 disconnect(tmp, &Solid::StorageAccess::teardownRequested,
278 item->signalHandler(), &PlacesItemSignalHandler::onTearDownRequested);
279 emit storageTearDownRequested(tmp->filePath());
280 }
281 }
282 }
283
284 bool PlacesItemModel::storageSetupNeeded(int index) const
285 {
286 const PlacesItem* item = placesItem(index);
287 return item ? item->storageSetupNeeded() : false;
288 }
289
290 void PlacesItemModel::requestStorageSetup(int index)
291 {
292 const PlacesItem* item = placesItem(index);
293 if (!item) {
294 return;
295 }
296
297 Solid::Device device = item->device();
298 const bool setup = device.is<Solid::StorageAccess>()
299 && !m_storageSetupInProgress.contains(device.as<Solid::StorageAccess>())
300 && !device.as<Solid::StorageAccess>()->isAccessible();
301 if (setup) {
302 Solid::StorageAccess* access = device.as<Solid::StorageAccess>();
303
304 m_storageSetupInProgress[access] = index;
305
306 connect(access, &Solid::StorageAccess::setupDone,
307 this, &PlacesItemModel::slotStorageSetupDone);
308
309 access->setup();
310 }
311 }
312
313 QMimeData* PlacesItemModel::createMimeData(const KItemSet& indexes) const
314 {
315 QList<QUrl> urls;
316 QByteArray itemData;
317
318 QDataStream stream(&itemData, QIODevice::WriteOnly);
319
320 for (int index : indexes) {
321 const QUrl itemUrl = placesItem(index)->url();
322 if (itemUrl.isValid()) {
323 urls << itemUrl;
324 }
325 stream << index;
326 }
327
328 QMimeData* mimeData = new QMimeData();
329 if (!urls.isEmpty()) {
330 mimeData->setUrls(urls);
331 } else {
332 // #378954: prevent itemDropEvent() drops if there isn't a source url.
333 mimeData->setData(blacklistItemDropEventMimeType(), QByteArrayLiteral("true"));
334 }
335 mimeData->setData(internalMimeType(), itemData);
336
337 return mimeData;
338 }
339
340 bool PlacesItemModel::supportsDropping(int index) const
341 {
342 return index >= 0 && index < count();
343 }
344
345 void PlacesItemModel::dropMimeDataBefore(int index, const QMimeData* mimeData)
346 {
347 if (mimeData->hasFormat(internalMimeType())) {
348 // The item has been moved inside the view
349 QByteArray itemData = mimeData->data(internalMimeType());
350 QDataStream stream(&itemData, QIODevice::ReadOnly);
351 int oldIndex;
352 stream >> oldIndex;
353
354 m_sourceModel->movePlace(oldIndex, index);
355 } else if (mimeData->hasFormat(QStringLiteral("text/uri-list"))) {
356 // One or more items must be added to the model
357 const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
358 for (int i = urls.count() - 1; i >= 0; --i) {
359 const QUrl& url = urls[i];
360
361 QString text = url.fileName();
362 if (text.isEmpty()) {
363 text = url.host();
364 }
365
366 if ((url.isLocalFile() && !QFileInfo(url.toLocalFile()).isDir())
367 || url.scheme() == QLatin1String("trash")) {
368 // Only directories outside the trash are allowed
369 continue;
370 }
371
372 createPlacesItem(text, url, KIO::iconNameForUrl(url), qMax(0, index - 1));
373 }
374 }
375 // will save bookmark alteration and fix sort if that is broken by the drag/drop operation
376 refresh();
377 }
378
379 void PlacesItemModel::addItemFromSourceModel(const QModelIndex &index)
380 {
381 if (!m_hiddenItemsShown && m_sourceModel->isHidden(index)) {
382 return;
383 }
384
385 const KBookmark bookmark = m_sourceModel->bookmarkForIndex(index);
386 Q_ASSERT(!bookmark.isNull());
387 PlacesItem *item = new PlacesItem(bookmark);
388 updateItem(item, index);
389 insertSortedItem(item);
390
391 if (m_sourceModel->isDevice(index)) {
392 connect(item->signalHandler(), &PlacesItemSignalHandler::tearDownExternallyRequested,
393 this, &PlacesItemModel::storageTearDownExternallyRequested);
394 }
395 }
396
397 void PlacesItemModel::removeItemByIndex(const QModelIndex &sourceIndex)
398 {
399 QString id = bookmarkId(m_sourceModel->bookmarkForIndex(sourceIndex));
400
401 for (int i = 0, iMax = count(); i < iMax; ++i) {
402 if (bookmarkId(placesItem(i)->bookmark()) == id) {
403 removeItem(i);
404 return;
405 }
406 }
407 }
408
409 QString PlacesItemModel::bookmarkId(const KBookmark &bookmark) const
410 {
411 QString id = bookmark.metaDataItem(QStringLiteral("UDI"));
412 if (id.isEmpty()) {
413 id = bookmark.metaDataItem(QStringLiteral("ID"));
414 }
415 return id;
416 }
417
418 void PlacesItemModel::initializeDefaultViewProperties() const
419 {
420 for(int i = 0, iMax = m_sourceModel->rowCount(); i < iMax; i++) {
421 const QModelIndex index = m_sourceModel->index(i, 0);
422 const PlacesItem *item = placesItem(mapFromSource(index));
423 if (!item) {
424 continue;
425 }
426
427 // Create default view-properties for all "Search For" and "Recently Saved" bookmarks
428 // in case the user has not already created custom view-properties for a corresponding
429 // query yet.
430 const bool createDefaultViewProperties = item->isSearchOrTimelineUrl() && !GeneralSettings::self()->globalViewProps();
431 if (createDefaultViewProperties) {
432 const QUrl itemUrl = item->url();
433 ViewProperties props(KFilePlacesModel::convertedUrl(itemUrl));
434 if (!props.exist()) {
435 const QString path = itemUrl.path();
436 if (path == QLatin1String("/documents")) {
437 props.setViewMode(DolphinView::DetailsView);
438 props.setPreviewsShown(false);
439 props.setVisibleRoles({"text", "path"});
440 } else if (path == QLatin1String("/images")) {
441 props.setViewMode(DolphinView::IconsView);
442 props.setPreviewsShown(true);
443 props.setVisibleRoles({"text", "height", "width"});
444 } else if (path == QLatin1String("/audio")) {
445 props.setViewMode(DolphinView::DetailsView);
446 props.setPreviewsShown(false);
447 props.setVisibleRoles({"text", "artist", "album"});
448 } else if (path == QLatin1String("/videos")) {
449 props.setViewMode(DolphinView::IconsView);
450 props.setPreviewsShown(true);
451 props.setVisibleRoles({"text"});
452 } else if (itemUrl.scheme() == QLatin1String("timeline")) {
453 props.setViewMode(DolphinView::DetailsView);
454 props.setVisibleRoles({"text", "modificationtime"});
455 }
456 props.save();
457 }
458 }
459 }
460 }
461
462 void PlacesItemModel::updateItem(PlacesItem *item, const QModelIndex &index)
463 {
464 item->setGroup(index.data(KFilePlacesModel::GroupRole).toString());
465 item->setIcon(index.data(KFilePlacesModel::IconNameRole).toString());
466 item->setGroupHidden(index.data(KFilePlacesModel::GroupHiddenRole).toBool());
467 }
468
469 void PlacesItemModel::slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData)
470 {
471 if (error && errorData.isValid()) {
472 emit errorMessage(errorData.toString());
473 }
474 disconnect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
475 this, &PlacesItemModel::slotStorageTearDownDone);
476 m_deviceToTearDown = nullptr;
477 }
478
479 void PlacesItemModel::slotStorageSetupDone(Solid::ErrorType error,
480 const QVariant& errorData,
481 const QString& udi)
482 {
483 Q_UNUSED(udi);
484
485 const int index = m_storageSetupInProgress.take(sender());
486 const PlacesItem* item = placesItem(index);
487 if (!item) {
488 return;
489 }
490
491 if (error != Solid::NoError) {
492 if (errorData.isValid()) {
493 emit errorMessage(i18nc("@info", "An error occurred while accessing '%1', the system responded: %2",
494 item->text(),
495 errorData.toString()));
496 } else {
497 emit errorMessage(i18nc("@info", "An error occurred while accessing '%1'",
498 item->text()));
499 }
500 emit storageSetupDone(index, false);
501 } else {
502 emit storageSetupDone(index, true);
503 }
504 }
505
506 void PlacesItemModel::onSourceModelRowsInserted(const QModelIndex &parent, int first, int last)
507 {
508 for (int i = first; i <= last; i++) {
509 const QModelIndex index = m_sourceModel->index(i, 0, parent);
510 addItemFromSourceModel(index);
511 }
512 }
513
514 void PlacesItemModel::onSourceModelRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
515 {
516 for(int r = first; r <= last; r++) {
517 const QModelIndex index = m_sourceModel->index(r, 0, parent);
518 int oldIndex = mapFromSource(index);
519 if (oldIndex != -1) {
520 removeItem(oldIndex);
521 }
522 }
523 }
524
525 void PlacesItemModel::onSourceModelRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
526 {
527 Q_UNUSED(destination);
528 Q_UNUSED(row);
529
530 for(int r = start; r <= end; r++) {
531 const QModelIndex sourceIndex = m_sourceModel->index(r, 0, parent);
532 // remove moved item
533 removeItem(mapFromSource(sourceIndex));
534 }
535 }
536
537 void PlacesItemModel::onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
538 {
539 Q_UNUSED(destination);
540 Q_UNUSED(parent);
541
542 const int blockSize = (end - start) + 1;
543
544 for (int r = start; r <= end; r++) {
545 // insert the moved item in the new position
546 const int targetRow = row + (start - r) - (r < row ? blockSize : 0);
547 const QModelIndex targetIndex = m_sourceModel->index(targetRow, 0, destination);
548
549 addItemFromSourceModel(targetIndex);
550 }
551 }
552
553 void PlacesItemModel::onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
554 {
555 Q_UNUSED(roles);
556
557 for (int r = topLeft.row(); r <= bottomRight.row(); r++) {
558 const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
559 const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
560 PlacesItem *placeItem = itemFromBookmark(bookmark);
561
562 if (placeItem && (!m_hiddenItemsShown && m_sourceModel->isHidden(sourceIndex))) {
563 //hide item if it became invisible
564 removeItem(index(placeItem));
565 return;
566 }
567
568 if (!placeItem && (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex))) {
569 //show item if it became visible
570 addItemFromSourceModel(sourceIndex);
571 return;
572 }
573
574 if (placeItem && !m_sourceModel->isDevice(sourceIndex)) {
575 placeItem->setText(bookmark.text());
576 placeItem->setIcon(sourceIndex.data(KFilePlacesModel::IconNameRole).toString());
577 placeItem->setUrl(m_sourceModel->url(sourceIndex));
578 placeItem->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"),
579 bookmark.metaDataItem(QStringLiteral("OnlyInApp")));
580 // must update the bookmark object
581 placeItem->setBookmark(bookmark);
582 }
583 }
584 }
585
586 void PlacesItemModel::onSourceModelGroupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden)
587 {
588 const auto groupIndexes = m_sourceModel->groupIndexes(group);
589 for (const QModelIndex &sourceIndex : groupIndexes) {
590 PlacesItem *item = placesItem(mapFromSource(sourceIndex));
591 if (item) {
592 item->setGroupHidden(hidden);
593 }
594 }
595 }
596
597 void PlacesItemModel::cleanupBookmarks()
598 {
599 // KIO model now provides support for baloo urls, and because of that we
600 // need to remove old URLs that were visible only in Dolphin to avoid duplication
601 int row = 0;
602 do {
603 const QModelIndex sourceIndex = m_sourceModel->index(row, 0);
604 const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
605 const QUrl url = bookmark.url();
606 const QString appName = bookmark.metaDataItem(QStringLiteral("OnlyInApp"));
607
608 if ((appName == KAboutData::applicationData().componentName() ||
609 appName == KAboutData::applicationData().componentName() + DolphinPlacesModelSingleton::applicationNameSuffix()) && balooURLs.contains(url)) {
610 qCDebug(DolphinDebug) << "Removing old baloo url:" << url;
611 m_sourceModel->removePlace(sourceIndex);
612 } else {
613 row++;
614 }
615 } while (row < m_sourceModel->rowCount());
616 }
617
618 void PlacesItemModel::loadBookmarks()
619 {
620 for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
621 const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
622 if (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex)) {
623 addItemFromSourceModel(sourceIndex);
624 }
625 }
626 }
627
628 void PlacesItemModel::clear() {
629 KStandardItemModel::clear();
630 }
631
632 void PlacesItemModel::proceedWithTearDown()
633 {
634 Q_ASSERT(m_deviceToTearDown);
635
636 connect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
637 this, &PlacesItemModel::slotStorageTearDownDone);
638 m_deviceToTearDown->teardown();
639 }
640
641 void PlacesItemModel::deleteItem(int index)
642 {
643 QModelIndex sourceIndex = mapToSource(index);
644 Q_ASSERT(sourceIndex.isValid());
645 m_sourceModel->removePlace(sourceIndex);
646 }
647
648 void PlacesItemModel::refresh()
649 {
650 m_sourceModel->refresh();
651 }
652
653 void PlacesItemModel::hideItem(int index)
654 {
655 PlacesItem* shownItem = placesItem(index);
656 if (!shownItem) {
657 return;
658 }
659
660 shownItem->setHidden(true);
661 }
662
663 QString PlacesItemModel::internalMimeType() const
664 {
665 return "application/x-dolphinplacesmodel-" +
666 QString::number((qptrdiff)this);
667 }
668
669 int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const
670 {
671 Q_ASSERT(item);
672
673 int dropIndex = index;
674 const QString group = item->group();
675
676 const int itemCount = count();
677 if (index < 0) {
678 dropIndex = itemCount;
679 }
680
681 // Search nearest previous item with the same group
682 int previousIndex = -1;
683 for (int i = dropIndex - 1; i >= 0; --i) {
684 if (placesItem(i)->group() == group) {
685 previousIndex = i;
686 break;
687 }
688 }
689
690 // Search nearest next item with the same group
691 int nextIndex = -1;
692 for (int i = dropIndex; i < count(); ++i) {
693 if (placesItem(i)->group() == group) {
694 nextIndex = i;
695 break;
696 }
697 }
698
699 // Adjust the drop-index to be inserted to the
700 // nearest item with the same group.
701 if (previousIndex >= 0 && nextIndex >= 0) {
702 dropIndex = (dropIndex - previousIndex < nextIndex - dropIndex) ?
703 previousIndex + 1 : nextIndex;
704 } else if (previousIndex >= 0) {
705 dropIndex = previousIndex + 1;
706 } else if (nextIndex >= 0) {
707 dropIndex = nextIndex;
708 }
709
710 return dropIndex;
711 }
712
713 bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2)
714 {
715 const QString udi1 = b1.metaDataItem(QStringLiteral("UDI"));
716 const QString udi2 = b2.metaDataItem(QStringLiteral("UDI"));
717 if (!udi1.isEmpty() && !udi2.isEmpty()) {
718 return udi1 == udi2;
719 } else {
720 return b1.metaDataItem(QStringLiteral("ID")) == b2.metaDataItem(QStringLiteral("ID"));
721 }
722 }
723
724 int PlacesItemModel::mapFromSource(const QModelIndex &index) const
725 {
726 if (!index.isValid()) {
727 return -1;
728 }
729
730 return m_indexMap.indexOf(index);
731 }
732
733 bool PlacesItemModel::isDir(int index) const
734 {
735 Q_UNUSED(index);
736 return true;
737 }
738
739 KFilePlacesModel::GroupType PlacesItemModel::groupType(int row) const
740 {
741 return m_sourceModel->groupType(mapToSource(row));
742 }
743
744 bool PlacesItemModel::isGroupHidden(KFilePlacesModel::GroupType type) const
745 {
746 return m_sourceModel->isGroupHidden(type);
747 }
748
749 void PlacesItemModel::setGroupHidden(KFilePlacesModel::GroupType type, bool hidden)
750 {
751 return m_sourceModel->setGroupHidden(type, hidden);
752 }
753
754 QModelIndex PlacesItemModel::mapToSource(int row) const
755 {
756 return m_indexMap.value(row);
757 }
758
759 PlacesItem *PlacesItemModel::itemFromBookmark(const KBookmark &bookmark) const
760 {
761 const QString id = bookmarkId(bookmark);
762 for (int i = 0, iMax = count(); i < iMax; i++) {
763 PlacesItem *item = placesItem(i);
764 const KBookmark itemBookmark = item->bookmark();
765 if (bookmarkId(itemBookmark) == id) {
766 return item;
767 }
768 }
769 return nullptr;
770 }
771