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