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