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