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