]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placesitemmodel.cpp
Implement bookmark synchronization
[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 <KBookmark>
27 #include <KBookmarkGroup>
28 #include <KBookmarkManager>
29 #include <KComponentData>
30 #include <KDebug>
31 #include <KIcon>
32 #include <KLocale>
33 #include <KStandardDirs>
34 #include <KUser>
35 #include "placesitem.h"
36 #include <QAction>
37 #include <QDate>
38 #include <QTimer>
39
40 #include <Solid/Device>
41 #include <Solid/DeviceNotifier>
42 #include <Solid/OpticalDisc>
43 #include <Solid/OpticalDrive>
44 #include <Solid/StorageAccess>
45 #include <Solid/StorageDrive>
46
47 #ifdef HAVE_NEPOMUK
48 #include <Nepomuk/ResourceManager>
49 #endif
50
51 namespace {
52 // As long as KFilePlacesView from kdelibs is available in parallel, the
53 // system-bookmarks for "Recently Accessed" and "Search For" should be
54 // shown only inside the Places Panel. This is necessary as the stored
55 // URLs needs to get translated to a Nepomuk-search-URL on-the-fly to
56 // be independent from changes in the Nepomuk-search-URL-syntax.
57 // Hence a prefix to the application-name of the stored bookmarks is
58 // added, which is only read by PlacesItemModel.
59 const char* AppNamePrefix = "-places-panel";
60 }
61
62 PlacesItemModel::PlacesItemModel(QObject* parent) :
63 KStandardItemModel(parent),
64 m_nepomukRunning(false),
65 m_hiddenItemsShown(false),
66 m_availableDevices(),
67 m_predicate(),
68 m_bookmarkManager(0),
69 m_systemBookmarks(),
70 m_systemBookmarksIndexes(),
71 m_bookmarkedItems(),
72 m_hiddenItemToRemove(-1),
73 m_saveBookmarksTimer(0),
74 m_updateBookmarksTimer(0)
75 {
76 #ifdef HAVE_NEPOMUK
77 m_nepomukRunning = (Nepomuk::ResourceManager::instance()->initialized());
78 #endif
79 const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
80 m_bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
81
82 createSystemBookmarks();
83 initializeAvailableDevices();
84 loadBookmarks();
85
86 const int syncBookmarksTimeout = 1000;
87
88 m_saveBookmarksTimer = new QTimer(this);
89 m_saveBookmarksTimer->setInterval(syncBookmarksTimeout);
90 m_saveBookmarksTimer->setSingleShot(true);
91 connect(m_saveBookmarksTimer, SIGNAL(timeout()), this, SLOT(saveBookmarks()));
92
93 m_updateBookmarksTimer = new QTimer(this);
94 m_updateBookmarksTimer->setInterval(syncBookmarksTimeout);
95 m_updateBookmarksTimer->setSingleShot(true);
96 connect(m_updateBookmarksTimer, SIGNAL(timeout()), this, SLOT(updateBookmarks()));
97
98 connect(m_bookmarkManager, SIGNAL(changed(QString,QString)),
99 m_updateBookmarksTimer, SLOT(start()));
100 connect(m_bookmarkManager, SIGNAL(bookmarksChanged(QString)),
101 m_updateBookmarksTimer, SLOT(start()));
102 }
103
104 PlacesItemModel::~PlacesItemModel()
105 {
106 saveBookmarks();
107 qDeleteAll(m_bookmarkedItems);
108 m_bookmarkedItems.clear();
109 }
110
111 PlacesItem* PlacesItemModel::createPlacesItem(const QString& text,
112 const KUrl& url,
113 const QString& iconName)
114 {
115 const KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, text, url, iconName);
116 return new PlacesItem(bookmark);
117 }
118
119 PlacesItem* PlacesItemModel::placesItem(int index) const
120 {
121 return dynamic_cast<PlacesItem*>(item(index));
122 }
123
124 int PlacesItemModel::hiddenCount() const
125 {
126 int modelIndex = 0;
127 int hiddenItemCount = 0;
128 foreach (const PlacesItem* item, m_bookmarkedItems) {
129 if (item) {
130 ++hiddenItemCount;
131 } else {
132 if (placesItem(modelIndex)->isHidden()) {
133 ++hiddenItemCount;
134 }
135 ++modelIndex;
136 }
137 }
138
139 return hiddenItemCount;
140 }
141
142 void PlacesItemModel::setHiddenItemsShown(bool show)
143 {
144 if (m_hiddenItemsShown == show) {
145 return;
146 }
147
148 m_hiddenItemsShown = show;
149
150 if (show) {
151 // Move all items that are part of m_bookmarkedItems to the model.
152 int modelIndex = 0;
153 for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
154 if (m_bookmarkedItems[i]) {
155 PlacesItem* visibleItem = new PlacesItem(*m_bookmarkedItems[i]);
156 delete m_bookmarkedItems[i];
157 m_bookmarkedItems.removeAt(i);
158 insertItem(modelIndex, visibleItem);
159 Q_ASSERT(!m_bookmarkedItems[i]);
160 }
161 ++modelIndex;
162 }
163 } else {
164 // Move all items of the model, where the "isHidden" property is true, to
165 // m_allItems.
166 Q_ASSERT(m_bookmarkedItems.count() == count());
167 for (int i = count() - 1; i >= 0; --i) {
168 const PlacesItem* visibleItem = placesItem(i);
169 if (visibleItem->isHidden()) {
170 hideItem(i);
171 }
172 }
173 }
174 #ifdef PLACESITEMMODEL_DEBUG
175 kDebug() << "Changed visibility of hidden items";
176 showModelState();
177 #endif
178 }
179
180 bool PlacesItemModel::hiddenItemsShown() const
181 {
182 return m_hiddenItemsShown;
183 }
184
185 int PlacesItemModel::closestItem(const KUrl& url) const
186 {
187 int foundIndex = -1;
188 int maxLength = 0;
189
190 for (int i = 0; i < count(); ++i) {
191 const KUrl itemUrl = placesItem(i)->url();
192 if (itemUrl.isParentOf(url)) {
193 const int length = itemUrl.prettyUrl().length();
194 if (length > maxLength) {
195 foundIndex = i;
196 maxLength = length;
197 }
198 }
199 }
200
201 return foundIndex;
202 }
203
204 QAction* PlacesItemModel::ejectAction(int index) const
205 {
206 const PlacesItem* item = placesItem(index);
207 if (item && item->device().is<Solid::OpticalDisc>()) {
208 return new QAction(KIcon("media-eject"), i18nc("@item", "Eject '%1'", item->text()), 0);
209 }
210
211 return 0;
212 }
213
214 QAction* PlacesItemModel::teardownAction(int index) const
215 {
216 const PlacesItem* item = placesItem(index);
217 if (!item) {
218 return 0;
219 }
220
221 Solid::Device device = item->device();
222 const bool providesTearDown = device.is<Solid::StorageAccess>() &&
223 device.as<Solid::StorageAccess>()->isAccessible();
224 if (!providesTearDown) {
225 return 0;
226 }
227
228 Solid::StorageDrive* drive = device.as<Solid::StorageDrive>();
229 if (!drive) {
230 drive = device.parent().as<Solid::StorageDrive>();
231 }
232
233 bool hotPluggable = false;
234 bool removable = false;
235 if (drive) {
236 hotPluggable = drive->isHotpluggable();
237 removable = drive->isRemovable();
238 }
239
240 QString iconName;
241 QString text;
242 const QString label = item->text();
243 if (device.is<Solid::OpticalDisc>()) {
244 text = i18nc("@item", "Release '%1'", label);
245 } else if (removable || hotPluggable) {
246 text = i18nc("@item", "Safely Remove '%1'", label);
247 iconName = "media-eject";
248 } else {
249 text = i18nc("@item", "Unmount '%1'", label);
250 iconName = "media-eject";
251 }
252
253 if (iconName.isEmpty()) {
254 return new QAction(text, 0);
255 }
256
257 return new QAction(KIcon(iconName), text, 0);
258 }
259
260 void PlacesItemModel::requestEject(int index)
261 {
262 const PlacesItem* item = placesItem(index);
263 if (item) {
264 Solid::OpticalDrive* drive = item->device().parent().as<Solid::OpticalDrive>();
265 if (drive) {
266 connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)),
267 this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)));
268 drive->eject();
269 } else {
270
271 }
272 }
273 }
274
275 void PlacesItemModel::requestTeardown(int index)
276 {
277 const PlacesItem* item = placesItem(index);
278 if (item) {
279 Solid::StorageAccess* access = item->device().as<Solid::StorageAccess>();
280 if (access) {
281 connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)),
282 this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)));
283 access->teardown();
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::onItemInserted(int index)
293 {
294 const PlacesItem* insertedItem = placesItem(index);
295 if (insertedItem) {
296 // Take care to apply the PlacesItemModel-order of the inserted item
297 // also to the bookmark-manager.
298 const KBookmark insertedBookmark = insertedItem->bookmark();
299
300 const PlacesItem* previousItem = placesItem(index - 1);
301 KBookmark previousBookmark;
302 if (previousItem) {
303 previousBookmark = previousItem->bookmark();
304 }
305
306 m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark);
307 }
308
309 if (index == count() - 1) {
310 // The item has been appended as last item to the list. In this
311 // case assure that it is also appended after the hidden items and
312 // not before (like done otherwise).
313 m_bookmarkedItems.append(0);
314 return;
315 }
316
317 int modelIndex = -1;
318 int bookmarkIndex = 0;
319 while (bookmarkIndex < m_bookmarkedItems.count()) {
320 if (!m_bookmarkedItems[bookmarkIndex]) {
321 ++modelIndex;
322 if (modelIndex + 1 == index) {
323 break;
324 }
325 }
326 ++bookmarkIndex;
327 }
328 m_bookmarkedItems.insert(bookmarkIndex, 0);
329
330 m_saveBookmarksTimer->start();
331
332 #ifdef PLACESITEMMODEL_DEBUG
333 kDebug() << "Inserted item" << index;
334 showModelState();
335 #endif
336 }
337
338 void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem)
339 {
340 PlacesItem* placesItem = dynamic_cast<PlacesItem*>(removedItem);
341 if (placesItem) {
342 const KBookmark bookmark = placesItem->bookmark();
343 m_bookmarkManager->root().deleteBookmark(bookmark);
344 }
345
346 const int boomarkIndex = bookmarkIndex(index);
347 Q_ASSERT(!m_bookmarkedItems[boomarkIndex]);
348 m_bookmarkedItems.removeAt(boomarkIndex);
349
350 m_saveBookmarksTimer->start();
351
352 #ifdef PLACESITEMMODEL_DEBUG
353 kDebug() << "Removed item" << index;
354 showModelState();
355 #endif
356 }
357
358 void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
359 {
360 const PlacesItem* changedItem = placesItem(index);
361 if (changedItem) {
362 // Take care to apply the PlacesItemModel-order of the inserted item
363 // also to the bookmark-manager.
364 const KBookmark insertedBookmark = changedItem->bookmark();
365
366 const PlacesItem* previousItem = placesItem(index - 1);
367 KBookmark previousBookmark;
368 if (previousItem) {
369 previousBookmark = previousItem->bookmark();
370 }
371
372 m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark);
373 }
374
375 if (changedRoles.contains("isHidden")) {
376 const PlacesItem* shownItem = placesItem(index);
377 Q_ASSERT(shownItem);
378 if (!m_hiddenItemsShown && shownItem->isHidden()) {
379 m_hiddenItemToRemove = index;
380 QTimer::singleShot(0, this, SLOT(hideItem()));
381 }
382 }
383
384 m_saveBookmarksTimer->start();
385 }
386
387 void PlacesItemModel::slotDeviceAdded(const QString& udi)
388 {
389 const Solid::Device device(udi);
390 if (m_predicate.matches(device)) {
391 m_availableDevices << udi;
392 const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
393 appendItem(new PlacesItem(bookmark));
394 }
395 }
396
397 void PlacesItemModel::slotDeviceRemoved(const QString& udi)
398 {
399 if (!m_availableDevices.contains(udi)) {
400 return;
401 }
402
403 for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
404 PlacesItem* item = m_bookmarkedItems[i];
405 if (item && item->udi() == udi) {
406 m_bookmarkedItems.removeAt(i);
407 delete item;
408 return;
409 }
410 }
411
412 for (int i = 0; i < count(); ++i) {
413 if (placesItem(i)->udi() == udi) {
414 removeItem(i);
415 return;
416 }
417 }
418 }
419
420 void PlacesItemModel::slotStorageTeardownDone(Solid::ErrorType error, const QVariant& errorData)
421 {
422 if (error && errorData.isValid()) {
423 emit errorMessage(errorData.toString());
424 }
425 }
426
427 void PlacesItemModel::hideItem()
428 {
429 hideItem(m_hiddenItemToRemove);
430 m_hiddenItemToRemove = -1;
431 }
432
433 void PlacesItemModel::updateBookmarks()
434 {
435 // Verify whether new bookmarks have been added or existing
436 // bookmarks have been changed.
437 KBookmarkGroup root = m_bookmarkManager->root();
438 KBookmark newBookmark = root.first();
439 while (!newBookmark.isNull()) {
440 if (acceptBookmark(newBookmark)) {
441 bool found = false;
442 int modelIndex = 0;
443 for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
444 PlacesItem* item = m_bookmarkedItems[i];
445 if (!item) {
446 item = placesItem(modelIndex);
447 ++modelIndex;
448 }
449
450 const KBookmark oldBookmark = item->bookmark();
451 if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) {
452 // The bookmark has been found in the model or as
453 // a hidden item. The content of the bookmark might
454 // have been changed, so an update is done.
455 found = true;
456 if (newBookmark.metaDataItem("UDI").isEmpty()) {
457 item->setBookmark(newBookmark);
458 }
459 break;
460 }
461 }
462
463 if (!found) {
464 PlacesItem* item = new PlacesItem(newBookmark);
465 if (item->isHidden() && !m_hiddenItemsShown) {
466 m_bookmarkedItems.append(item);
467 } else {
468 appendItem(item);
469 }
470 }
471 }
472
473 newBookmark = root.next(newBookmark);
474 }
475
476 // Remove items that are not part of the bookmark-manager anymore
477 int modelIndex = 0;
478 for (int i = m_bookmarkedItems.count() - 1; i >= 0; --i) {
479 PlacesItem* item = m_bookmarkedItems[i];
480 const bool itemIsPartOfModel = (item == 0);
481 if (itemIsPartOfModel) {
482 item = placesItem(modelIndex);
483 }
484
485 bool hasBeenRemoved = true;
486 const KBookmark oldBookmark = item->bookmark();
487 KBookmark newBookmark = root.first();
488 while (!newBookmark.isNull()) {
489 if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) {
490 hasBeenRemoved = false;
491 break;
492 }
493 newBookmark = root.next(newBookmark);
494 }
495
496 if (hasBeenRemoved) {
497 if (m_bookmarkedItems[i]) {
498 delete m_bookmarkedItems[i];
499 m_bookmarkedItems.removeAt(i);
500 } else {
501 removeItem(modelIndex);
502 --modelIndex;
503 }
504 }
505
506 if (itemIsPartOfModel) {
507 ++modelIndex;
508 }
509 }
510 }
511
512 void PlacesItemModel::saveBookmarks()
513 {
514 m_bookmarkManager->emitChanged(m_bookmarkManager->root());
515 }
516
517 void PlacesItemModel::loadBookmarks()
518 {
519 KBookmarkGroup root = m_bookmarkManager->root();
520 KBookmark bookmark = root.first();
521 QSet<QString> devices = m_availableDevices;
522
523 QSet<KUrl> missingSystemBookmarks;
524 foreach (const SystemBookmarkData& data, m_systemBookmarks) {
525 missingSystemBookmarks.insert(data.url);
526 }
527
528 // The bookmarks might have a mixed order of places, devices and search-groups due
529 // to the compatibility with the KFilePlacesPanel. In Dolphin's places panel the
530 // items should always be collected in one group so the items are collected first
531 // in separate lists before inserting them.
532 QList<PlacesItem*> placesItems;
533 QList<PlacesItem*> recentlyAccessedItems;
534 QList<PlacesItem*> searchForItems;
535 QList<PlacesItem*> devicesItems;
536
537 while (!bookmark.isNull()) {
538 const bool deviceAvailable = devices.remove(bookmark.metaDataItem("UDI"));
539 if (acceptBookmark(bookmark)) {
540 PlacesItem* item = new PlacesItem(bookmark);
541 if (deviceAvailable) {
542 devicesItems.append(item);
543 } else {
544 const KUrl url = bookmark.url();
545 if (missingSystemBookmarks.contains(url)) {
546 missingSystemBookmarks.remove(url);
547
548 // Apply the translated text to the system bookmarks, otherwise an outdated
549 // translation might be shown.
550 const int index = m_systemBookmarksIndexes.value(url);
551 item->setText(m_systemBookmarks[index].text);
552 item->setSystemItem(true);
553 }
554
555 switch (item->groupType()) {
556 case PlacesItem::PlacesType: placesItems.append(item); break;
557 case PlacesItem::RecentlyAccessedType: recentlyAccessedItems.append(item); break;
558 case PlacesItem::SearchForType: searchForItems.append(item); break;
559 case PlacesItem::DevicesType:
560 default: Q_ASSERT(false); break;
561 }
562 }
563 }
564
565 bookmark = root.next(bookmark);
566 }
567
568 if (!missingSystemBookmarks.isEmpty()) {
569 // The current bookmarks don't contain all system-bookmarks. Add the missing
570 // bookmarks.
571 foreach (const SystemBookmarkData& data, m_systemBookmarks) {
572 if (missingSystemBookmarks.contains(data.url)) {
573 KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager,
574 data.text,
575 data.url,
576 data.icon);
577
578 const QString protocol = data.url.protocol();
579 if (protocol == QLatin1String("timeline") || protocol == QLatin1String("search")) {
580 // As long as the KFilePlacesView from kdelibs is available, the system-bookmarks
581 // for "Recently Accessed" and "Search For" should be a setting available only
582 // in the Places Panel (see description of AppNamePrefix for more details).
583 const QString appName = KGlobal::mainComponent().componentName() + AppNamePrefix;
584 bookmark.setMetaDataItem("OnlyInApp", appName);
585 }
586
587 PlacesItem* item = new PlacesItem(bookmark);
588 item->setSystemItem(true);
589
590 switch (item->groupType()) {
591 case PlacesItem::PlacesType: placesItems.append(item); break;
592 case PlacesItem::RecentlyAccessedType: recentlyAccessedItems.append(item); break;
593 case PlacesItem::SearchForType: searchForItems.append(item); break;
594 case PlacesItem::DevicesType:
595 default: Q_ASSERT(false); break;
596 }
597 }
598 }
599 }
600
601 // Create items for devices that have not been stored as bookmark yet
602 foreach (const QString& udi, devices) {
603 const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
604 devicesItems.append(new PlacesItem(bookmark));
605 }
606
607 QList<PlacesItem*> items;
608 items.append(placesItems);
609 items.append(recentlyAccessedItems);
610 items.append(searchForItems);
611 items.append(devicesItems);
612
613 foreach (PlacesItem* item, items) {
614 if (!m_hiddenItemsShown && item->isHidden()) {
615 m_bookmarkedItems.append(item);
616 } else {
617 appendItem(item);
618 }
619 }
620
621 #ifdef PLACESITEMMODEL_DEBUG
622 kDebug() << "Loaded bookmarks";
623 showModelState();
624 #endif
625 }
626
627 bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark) const
628 {
629 const QString udi = bookmark.metaDataItem("UDI");
630 const KUrl url = bookmark.url();
631 const QString appName = bookmark.metaDataItem("OnlyInApp");
632 const bool deviceAvailable = m_availableDevices.contains(udi);
633
634 const bool allowedHere = (appName.isEmpty()
635 || appName == KGlobal::mainComponent().componentName()
636 || appName == KGlobal::mainComponent().componentName() + AppNamePrefix)
637 && (m_nepomukRunning || (url.protocol() != QLatin1String("timeline") &&
638 url.protocol() != QLatin1String("search")));
639
640 return (udi.isEmpty() && allowedHere) || deviceAvailable;
641 }
642
643 void PlacesItemModel::createSystemBookmarks()
644 {
645 Q_ASSERT(m_systemBookmarks.isEmpty());
646 Q_ASSERT(m_systemBookmarksIndexes.isEmpty());
647
648 const QString timeLineIcon = "package_utility_time"; // TODO: Ask the Oxygen team to create
649 // a custom icon for the timeline-protocol
650
651 m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()),
652 "user-home",
653 i18nc("@item", "Home")));
654 m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"),
655 "network-workgroup",
656 i18nc("@item", "Network")));
657 m_systemBookmarks.append(SystemBookmarkData(KUrl("/"),
658 "folder-red",
659 i18nc("@item", "Root")));
660 m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"),
661 "user-trash",
662 i18nc("@item", "Trash")));
663
664 if (m_nepomukRunning) {
665 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/today"),
666 timeLineIcon,
667 i18nc("@item Recently Accessed", "Today")));
668 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/yesterday"),
669 timeLineIcon,
670 i18nc("@item Recently Accessed", "Yesterday")));
671 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/thismonth"),
672 timeLineIcon,
673 i18nc("@item Recently Accessed", "This Month")));
674 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/lastmonth"),
675 timeLineIcon,
676 i18nc("@item Recently Accessed", "Last Month")));
677 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/documents"),
678 "folder-txt",
679 i18nc("@item Commonly Accessed", "Documents")));
680 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/images"),
681 "folder-image",
682 i18nc("@item Commonly Accessed", "Images")));
683 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/audio"),
684 "folder-sound",
685 i18nc("@item Commonly Accessed", "Audio Files")));
686 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/videos"),
687 "folder-video",
688 i18nc("@item Commonly Accessed", "Videos")));
689 }
690
691 for (int i = 0; i < m_systemBookmarks.count(); ++i) {
692 m_systemBookmarksIndexes.insert(m_systemBookmarks[i].url, i);
693 }
694 }
695
696 void PlacesItemModel::initializeAvailableDevices()
697 {
698 m_predicate = Solid::Predicate::fromString(
699 "[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]"
700 " OR "
701 "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]"
702 " OR "
703 "OpticalDisc.availableContent & 'Audio' ]"
704 " OR "
705 "StorageAccess.ignored == false ]");
706 Q_ASSERT(m_predicate.isValid());
707
708 Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance();
709 connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(slotDeviceAdded(QString)));
710 connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString)));
711
712 const QList<Solid::Device>& deviceList = Solid::Device::listFromQuery(m_predicate);
713 foreach(const Solid::Device& device, deviceList) {
714 m_availableDevices << device.udi();
715 }
716 }
717
718 int PlacesItemModel::bookmarkIndex(int index) const
719 {
720 int bookmarkIndex = 0;
721 int modelIndex = 0;
722 while (bookmarkIndex < m_bookmarkedItems.count()) {
723 if (!m_bookmarkedItems[bookmarkIndex]) {
724 if (modelIndex == index) {
725 break;
726 }
727 ++modelIndex;
728 }
729 ++bookmarkIndex;
730 }
731
732 return bookmarkIndex >= m_bookmarkedItems.count() ? -1 : bookmarkIndex;
733 }
734
735 void PlacesItemModel::hideItem(int index)
736 {
737 PlacesItem* shownItem = placesItem(index);
738 if (!shownItem) {
739 return;
740 }
741
742 shownItem->setHidden(true);
743 if (m_hiddenItemsShown) {
744 // Removing items from the model is not allowed if all hidden
745 // items should be shown.
746 return;
747 }
748
749 const int newIndex = bookmarkIndex(index);
750 if (newIndex >= 0) {
751 PlacesItem* hiddenItem = new PlacesItem(*shownItem);
752 const KBookmark hiddenBookmark = hiddenItem->bookmark();
753
754 const PlacesItem* previousItem = placesItem(index - 1);
755 KBookmark previousBookmark;
756 if (previousItem) {
757 previousBookmark = previousItem->bookmark();
758 }
759
760 const bool updateBookmark = (m_bookmarkManager->root().indexOf(hiddenBookmark) >= 0);
761 removeItem(index);
762
763 if (updateBookmark) {
764 // removeItem() also removed the bookmark from m_bookmarkManager in
765 // PlacesItemModel::onItemRemoved(). However for hidden items the
766 // bookmark should still be remembered, so readd it again:
767 m_bookmarkManager->root().addBookmark(hiddenBookmark);
768 m_bookmarkManager->root().moveBookmark(hiddenBookmark, previousBookmark);
769 m_saveBookmarksTimer->start();
770 }
771
772 m_bookmarkedItems.insert(newIndex, hiddenItem);
773 }
774 }
775
776 bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2)
777 {
778 const QString udi1 = b1.metaDataItem("UDI");
779 const QString udi2 = b2.metaDataItem("UDI");
780 if (!udi1.isEmpty() && !udi2.isEmpty()) {
781 return udi1 == udi2;
782 } else {
783 return b1.metaDataItem("ID") == b2.metaDataItem("ID");
784 }
785 }
786
787 #ifdef PLACESITEMMODEL_DEBUG
788 void PlacesItemModel::showModelState()
789 {
790 kDebug() << "=================================";
791 kDebug() << "Model:";
792 kDebug() << "hidden-index model-index text";
793 int modelIndex = 0;
794 for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
795 if (m_bookmarkedItems[i]) {
796 kDebug() << i << "(Hidden) " << " " << m_bookmarkedItems[i]->dataValue("text").toString();
797 } else {
798 if (item(modelIndex)) {
799 kDebug() << i << " " << modelIndex << " " << item(modelIndex)->dataValue("text").toString();
800 } else {
801 kDebug() << i << " " << modelIndex << " " << "(not available yet)";
802 }
803 ++modelIndex;
804 }
805 }
806
807 kDebug();
808 kDebug() << "Bookmarks:";
809
810 int bookmarkIndex = 0;
811 KBookmarkGroup root = m_bookmarkManager->root();
812 KBookmark bookmark = root.first();
813 while (!bookmark.isNull()) {
814 const QString udi = bookmark.metaDataItem("UDI");
815 const QString text = udi.isEmpty() ? bookmark.text() : udi;
816 if (bookmark.metaDataItem("IsHidden") == QLatin1String("true")) {
817 kDebug() << bookmarkIndex << "(Hidden)" << text;
818 } else {
819 kDebug() << bookmarkIndex << " " << text;
820 }
821
822 bookmark = root.next(bookmark);
823 ++bookmarkIndex;
824 }
825 }
826 #endif
827
828 #include "placesitemmodel.moc"