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