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