]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/panels/places/placesitemmodel.cpp
Fix several bookmark synchronization issues
[dolphin.git] / src / panels / places / placesitemmodel.cpp
index f3162e7b24f53f4360ae5d6976323155049e4023..681d9d17ef7572c465e44f02e66865481a46310f 100644 (file)
 
 #include "placesitemmodel.h"
 
-#ifdef HAVE_NEPOMUK
-    #include <Nepomuk/ResourceManager>
-    #include <Nepomuk/Query/ComparisonTerm>
-    #include <Nepomuk/Query/LiteralTerm>
-    #include <Nepomuk/Query/Query>
-    #include <Nepomuk/Query/ResourceTypeTerm>
-    #include <Nepomuk/Vocabulary/NFO>
-    #include <Nepomuk/Vocabulary/NIE>
-#endif
-
 #include <KBookmark>
 #include <KBookmarkGroup>
 #include <KBookmarkManager>
 #include <KComponentData>
+#include <KDebug>
 #include <KIcon>
-#include <kitemviews/kstandarditem.h>
 #include <KLocale>
 #include <KStandardDirs>
 #include <KUser>
+#include "placesitem.h"
+#include <QAction>
 #include <QDate>
+#include <QTimer>
+
+#include <Solid/Device>
+#include <Solid/DeviceNotifier>
+#include <Solid/OpticalDisc>
+#include <Solid/OpticalDrive>
+#include <Solid/StorageAccess>
+#include <Solid/StorageDrive>
+
+#ifdef HAVE_NEPOMUK
+    #include <Nepomuk/ResourceManager>
+#endif
 
 PlacesItemModel::PlacesItemModel(QObject* parent) :
     KStandardItemModel(parent),
     m_nepomukRunning(false),
+    m_hiddenItemsShown(false),
     m_availableDevices(),
+    m_predicate(),
     m_bookmarkManager(0),
-    m_defaultBookmarks(),
-    m_defaultBookmarksIndexes()
+    m_systemBookmarks(),
+    m_systemBookmarksIndexes(),
+    m_hiddenItems(),
+    m_hiddenItemToRemove(-1),
+    m_saveBookmarksTimer(0)
 {
 #ifdef HAVE_NEPOMUK
     m_nepomukRunning = (Nepomuk::ResourceManager::instance()->initialized());
@@ -58,29 +67,375 @@ PlacesItemModel::PlacesItemModel(QObject* parent) :
     const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
     m_bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
 
-    createDefaultBookmarks();
+    createSystemBookmarks();
+    initializeAvailableDevices();
     loadBookmarks();
+
+    m_saveBookmarksTimer = new QTimer(this);
+    m_saveBookmarksTimer->setInterval(100);
+    m_saveBookmarksTimer->setSingleShot(true);
+    connect(m_saveBookmarksTimer, SIGNAL(timeout()), this, SLOT(saveBookmarks()));
 }
 
 PlacesItemModel::~PlacesItemModel()
 {
+    saveBookmarks();
+    qDeleteAll(m_hiddenItems);
+    m_hiddenItems.clear();
+}
+
+PlacesItem* PlacesItemModel::createPlacesItem(const QString& text,
+                                              const KUrl& url,
+                                              const QString& iconName)
+{
+    const KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, text, url, iconName);
+    PlacesItem* item = new PlacesItem(bookmark);
+    item->setGroup(groupName(url));
+    return item;
+}
+
+PlacesItem* PlacesItemModel::placesItem(int index) const
+{
+    return dynamic_cast<PlacesItem*>(item(index));
 }
 
 int PlacesItemModel::hiddenCount() const
 {
-    return 0;
+    int modelIndex = 0;
+    int itemCount = 0;
+    foreach (const PlacesItem* hiddenItem, m_hiddenItems) {
+        if (hiddenItem) {
+            ++itemCount;
+        } else {
+            if (placesItem(modelIndex)->isHidden()) {
+                ++itemCount;
+            }
+            ++modelIndex;
+        }
+    }
+
+    return itemCount;
+}
+
+void PlacesItemModel::setHiddenItemsShown(bool show)
+{
+    if (m_hiddenItemsShown == show) {
+        return;
+    }
+
+    m_hiddenItemsShown = show;
+
+    if (show) {
+        // Move all items that are part of m_hiddenItems to the model.
+        int modelIndex = 0;
+        for (int hiddenIndex = 0; hiddenIndex < m_hiddenItems.count(); ++hiddenIndex) {
+            if (m_hiddenItems[hiddenIndex]) {
+                PlacesItem* visibleItem = new PlacesItem(*m_hiddenItems[hiddenIndex]);
+                delete m_hiddenItems[hiddenIndex];
+                m_hiddenItems.removeAt(hiddenIndex);
+                insertItem(modelIndex, visibleItem);
+                Q_ASSERT(!m_hiddenItems[hiddenIndex]);
+            }
+            ++modelIndex;
+        }
+    } else {
+        // Move all items of the model, where the "isHidden" property is true, to
+        // m_hiddenItems.
+        Q_ASSERT(m_hiddenItems.count() == count());
+        for (int i = count() - 1; i >= 0; --i) {
+            PlacesItem* visibleItem = placesItem(i);
+            if (visibleItem->isHidden()) {
+                PlacesItem* hiddenItem = new PlacesItem(*visibleItem);
+                removeItem(i);
+                m_hiddenItems.insert(i, hiddenItem);
+            }
+        }
+    }
+#ifdef PLACESITEMMODEL_DEBUG
+        kDebug() << "Changed visibility of hidden items";
+        showModelState();
+#endif
+}
+
+bool PlacesItemModel::hiddenItemsShown() const
+{
+    return m_hiddenItemsShown;
+}
+
+int PlacesItemModel::closestItem(const KUrl& url) const
+{
+    int foundIndex = -1;
+    int maxLength = 0;
+
+    for (int i = 0; i < count(); ++i) {
+        const KUrl itemUrl = placesItem(i)->url();
+        if (itemUrl.isParentOf(url)) {
+            const int length = itemUrl.prettyUrl().length();
+            if (length > maxLength) {
+                foundIndex = i;
+                maxLength = length;
+            }
+        }
+    }
+
+    return foundIndex;
+}
+
+QString PlacesItemModel::groupName(const KUrl &url) const
+{
+    const QString protocol = url.protocol();
+
+    if (protocol.contains(QLatin1String("search"))) {
+        return searchForGroupName();
+    }
+
+    if (protocol == QLatin1String("timeline")) {
+        return recentlyAccessedGroupName();
+    }
+
+    return placesGroupName();
 }
 
 QAction* PlacesItemModel::ejectAction(int index) const
 {
-    Q_UNUSED(index);
+    const PlacesItem* item = placesItem(index);
+    if (item && item->device().is<Solid::OpticalDisc>()) {
+        return new QAction(KIcon("media-eject"), i18nc("@item", "Eject '%1'", item->text()), 0);
+    }
+
     return 0;
 }
 
-QAction* PlacesItemModel::tearDownAction(int index) const
+QAction* PlacesItemModel::teardownAction(int index) const
 {
-    Q_UNUSED(index);
-    return 0;
+    const PlacesItem* item = placesItem(index);
+    if (!item) {
+        return 0;
+    }
+
+    Solid::Device device = item->device();
+    const bool providesTearDown = device.is<Solid::StorageAccess>() &&
+                                  device.as<Solid::StorageAccess>()->isAccessible();
+    if (!providesTearDown) {
+        return 0;
+    }
+
+    Solid::StorageDrive* drive = device.as<Solid::StorageDrive>();
+    if (!drive) {
+        drive = device.parent().as<Solid::StorageDrive>();
+    }
+
+    bool hotPluggable = false;
+    bool removable = false;
+    if (drive) {
+        hotPluggable = drive->isHotpluggable();
+        removable = drive->isRemovable();
+    }
+
+    QString iconName;
+    QString text;
+    const QString label = item->text();
+    if (device.is<Solid::OpticalDisc>()) {
+        text = i18nc("@item", "Release '%1'", label);
+    } else if (removable || hotPluggable) {
+        text = i18nc("@item", "Safely Remove '%1'", label);
+        iconName = "media-eject";
+    } else {
+        text = i18nc("@item", "Unmount '%1'", label);
+        iconName = "media-eject";
+    }
+
+    if (iconName.isEmpty()) {
+        return new QAction(text, 0);
+    }
+
+    return new QAction(KIcon(iconName), text, 0);
+}
+
+void PlacesItemModel::requestEject(int index)
+{
+    const PlacesItem* item = placesItem(index);
+    if (item) {
+        Solid::OpticalDrive* drive = item->device().parent().as<Solid::OpticalDrive>();
+        if (drive) {
+            connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)),
+                    this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)));
+            drive->eject();
+        } else {
+
+        }
+    }
+}
+
+void PlacesItemModel::requestTeardown(int index)
+{
+    const PlacesItem* item = placesItem(index);
+    if (item) {
+        Solid::StorageAccess* access = item->device().as<Solid::StorageAccess>();
+        if (access) {
+            connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)),
+                    this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant)));
+            access->teardown();
+        } else {
+            const QString label = item->text();
+            const QString message = i18nc("@info", "The device '%1' is not a disk and cannot be ejected.", label);
+            emit errorMessage(message);
+        }
+    }
+}
+
+void PlacesItemModel::onItemInserted(int index)
+{
+    const PlacesItem* insertedItem = placesItem(index);
+    if (insertedItem) {
+        // Take care to apply the PlacesItemModel-order of the inserted item
+        // also to the bookmark-manager.
+        const KBookmark insertedBookmark = insertedItem->bookmark();
+
+        const PlacesItem* previousItem = placesItem(index - 1);
+        KBookmark previousBookmark;
+        if (previousItem) {
+            previousBookmark = previousItem->bookmark();
+        }
+
+        m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark);
+    }
+
+    if (index == count() - 1) {
+        // The item has been appended as last item to the list. In this
+        // case assure that it is also appended after the hidden items and
+        // not before (like done otherwise).
+        m_hiddenItems.append(0);
+        return;
+    }
+
+    int modelIndex = -1;
+    int hiddenIndex = 0;
+    while (hiddenIndex < m_hiddenItems.count()) {
+        if (!m_hiddenItems[hiddenIndex]) {
+            ++modelIndex;
+            if (modelIndex + 1 == index) {
+                break;
+            }
+        }
+        ++hiddenIndex;
+    }
+    m_hiddenItems.insert(hiddenIndex, 0);
+
+    m_saveBookmarksTimer->start();
+
+#ifdef PLACESITEMMODEL_DEBUG
+    kDebug() << "Inserted item" << index;
+    showModelState();
+#endif
+}
+
+void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem)
+{
+    PlacesItem* placesItem = dynamic_cast<PlacesItem*>(removedItem);
+    if (placesItem) {
+        const KBookmark bookmark = placesItem->bookmark();
+        m_bookmarkManager->root().deleteBookmark(bookmark);
+    }
+
+    const int removeIndex = hiddenIndex(index);
+    Q_ASSERT(!m_hiddenItems[removeIndex]);
+    m_hiddenItems.removeAt(removeIndex);
+
+    m_saveBookmarksTimer->start();
+
+#ifdef PLACESITEMMODEL_DEBUG
+    kDebug() << "Removed item" << index;
+    showModelState();
+#endif
+}
+
+void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
+{
+    const PlacesItem* changedItem = placesItem(index);
+    if (changedItem) {
+        // Take care to apply the PlacesItemModel-order of the inserted item
+        // also to the bookmark-manager.
+        const KBookmark insertedBookmark = changedItem->bookmark();
+
+        const PlacesItem* previousItem = placesItem(index - 1);
+        KBookmark previousBookmark;
+        if (previousItem) {
+            previousBookmark = previousItem->bookmark();
+        }
+
+        m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark);
+    }
+
+    if (changedRoles.contains("isHidden")) {
+        const PlacesItem* shownItem = placesItem(index);
+        Q_ASSERT(shownItem);
+        if (!m_hiddenItemsShown && shownItem->isHidden()) {
+            m_hiddenItemToRemove = index;
+            QTimer::singleShot(0, this, SLOT(removeHiddenItem()));
+        }
+    }
+    m_saveBookmarksTimer->start();
+}
+
+void PlacesItemModel::slotDeviceAdded(const QString& udi)
+{
+    const Solid::Device device(udi);
+    if (m_predicate.matches(device)) {
+        m_availableDevices << udi;
+        const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
+        appendItem(new PlacesItem(bookmark));
+    }
+}
+
+void PlacesItemModel::slotDeviceRemoved(const QString& udi)
+{
+    if (!m_availableDevices.contains(udi)) {
+        return;
+    }
+
+    for (int i = 0; i < m_hiddenItems.count(); ++i) {
+        PlacesItem* item = m_hiddenItems[i];
+        if (item && item->udi() == udi) {
+            m_hiddenItems.removeAt(i);
+            delete item;
+            return;
+        }
+     }
+
+     for (int i = 0; i < count(); ++i) {
+         if (placesItem(i)->udi() == udi) {
+             removeItem(i);
+             return;
+         }
+     }
+}
+
+void PlacesItemModel::slotStorageTeardownDone(Solid::ErrorType error, const QVariant& errorData)
+{
+    if (error && errorData.isValid()) {
+        emit errorMessage(errorData.toString());
+    }
+}
+
+void PlacesItemModel::removeHiddenItem()
+{
+    const PlacesItem* shownItem = placesItem(m_hiddenItemToRemove);
+    const int newIndex = hiddenIndex(m_hiddenItemToRemove);
+    if (shownItem && newIndex >= 0) {
+        PlacesItem* hiddenItem = new PlacesItem(*shownItem);
+        removeItem(m_hiddenItemToRemove);
+        m_hiddenItems.insert(newIndex, hiddenItem);
+        m_saveBookmarksTimer->start();
+    }
+    m_hiddenItemToRemove = -1;
+}
+
+
+void PlacesItemModel::saveBookmarks()
+{
+    // TODO: Temporary deactivated until 100 % backward compatibility is provided
+    // m_bookmarkManager->emitChanged(m_bookmarkManager->root());
 }
 
 void PlacesItemModel::loadBookmarks()
@@ -89,11 +444,17 @@ void PlacesItemModel::loadBookmarks()
     KBookmark bookmark = root.first();
     QSet<QString> devices = m_availableDevices;
 
-    QSet<KUrl> missingDefaultBookmarks;
-    foreach (const DefaultBookmarkData& data, m_defaultBookmarks) {
-        missingDefaultBookmarks.insert(data.url);
+    QSet<KUrl> missingSystemBookmarks;
+    foreach (const SystemBookmarkData& data, m_systemBookmarks) {
+        missingSystemBookmarks.insert(data.url);
     }
 
+    // The bookmarks might have a mixed order of "places" and "devices". In
+    // Dolphin's places panel the devices should always be appended as last
+    // group, so they are collected as separate lists.
+    QList<PlacesItem*> placesItems;
+    QList<PlacesItem*> devicesItems;
+
     while (!bookmark.isNull()) {
         const QString udi = bookmark.metaDataItem("UDI");
         const KUrl url = bookmark.url();
@@ -101,215 +462,223 @@ void PlacesItemModel::loadBookmarks()
         const bool deviceAvailable = devices.remove(udi);
 
         const bool allowedHere = (appName.isEmpty() || appName == KGlobal::mainComponent().componentName())
-                                 && (m_nepomukRunning || url.protocol() != QLatin1String("timeline"));
+                                 && (m_nepomukRunning || (url.protocol() != QLatin1String("timeline") &&
+                                                          url.protocol() != QLatin1String("search")));
 
         if ((udi.isEmpty() && allowedHere) || deviceAvailable) {
-            KStandardItem* item = new KStandardItem();
-            item->setIcon(KIcon(bookmark.icon()));
-            item->setDataValue("address", bookmark.address());
-            item->setDataValue("url", url);
-
-            if (missingDefaultBookmarks.contains(url)) {
-                missingDefaultBookmarks.remove(url);
-                // Apply the translated text to the default bookmarks, otherwise an outdated
-                // translation might be shown.
-                const int index = m_defaultBookmarksIndexes.value(url);
-                item->setText(m_defaultBookmarks[index].text);
-
-                // The default bookmarks don't contain "real" queries stored as URLs, so
-                // they must be translated first.
-                item->setDataValue("url", translatedDefaultBookmarkUrl(url));
-            } else {
-                item->setText(bookmark.text());
-            }
-
+            PlacesItem* item = new PlacesItem(bookmark);
             if (deviceAvailable) {
-                item->setDataValue("udi", udi);
-                item->setGroup(i18nc("@item", "Devices"));
+                devicesItems.append(item);
             } else {
-                item->setGroup(i18nc("@item", "Places"));
+                placesItems.append(item);
+
+                if (missingSystemBookmarks.contains(url)) {
+                    missingSystemBookmarks.remove(url);
+
+                    // Apply the translated text to the system bookmarks, otherwise an outdated
+                    // translation might be shown.
+                    const int index = m_systemBookmarksIndexes.value(url);
+                    item->setText(m_systemBookmarks[index].text);
+                    item->setSystemItem(true);
+                    item->setGroup(m_systemBookmarks[index].group);
+                }
             }
-
-            appendItem(item);
         }
 
         bookmark = root.next(bookmark);
     }
 
-    if (!missingDefaultBookmarks.isEmpty()) {
-        foreach (const DefaultBookmarkData& data, m_defaultBookmarks) {
-            if (missingDefaultBookmarks.contains(data.url)) {
-                KStandardItem* item = new KStandardItem();
-                item->setIcon(KIcon(data.icon));
-                item->setText(data.text);
-                item->setDataValue("url", translatedDefaultBookmarkUrl(data.url));
+    addItems(placesItems);
+
+    if (!missingSystemBookmarks.isEmpty()) {
+        // The current bookmarks don't contain all system-bookmarks. Add the missing
+        // bookmarks.
+        foreach (const SystemBookmarkData& data, m_systemBookmarks) {
+            if (missingSystemBookmarks.contains(data.url)) {
+                KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager,
+                                                                data.text,
+                                                                data.url,
+                                                                data.icon);
+
+                const QString protocol = data.url.protocol();
+                if (protocol == QLatin1String("timeline") || protocol == QLatin1String("search")) {
+                    // As long as the KFilePlacesView from kdelibs is available, the system-bookmarks
+                    // for timeline and search should be a Dolphin-specific setting.
+                    bookmark.setMetaDataItem("OnlyInApp", KGlobal::mainComponent().componentName());
+                }
+
+                PlacesItem* item = new PlacesItem(bookmark);
+                item->setSystemItem(true);
                 item->setGroup(data.group);
                 appendItem(item);
             }
         }
     }
+
+    // Create items for devices that have not stored as bookmark yet
+    foreach (const QString& udi, devices) {
+        const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
+        devicesItems.append(new PlacesItem(bookmark));
+    }
+
+    addItems(devicesItems);
+
+#ifdef PLACESITEMMODEL_DEBUG
+    kDebug() << "Loaded bookmarks";
+    showModelState();
+#endif
+}
+
+void PlacesItemModel::addItems(const QList<PlacesItem*>& items)
+{
+    foreach (PlacesItem* item, items) {
+        if (!m_hiddenItemsShown && item->isHidden()) {
+            m_hiddenItems.append(item);
+        } else {
+            appendItem(item);
+        }
+    }
 }
 
-void PlacesItemModel::createDefaultBookmarks()
+void PlacesItemModel::createSystemBookmarks()
 {
-    Q_ASSERT(m_defaultBookmarks.isEmpty());
-    Q_ASSERT(m_defaultBookmarksIndexes.isEmpty());
+    Q_ASSERT(m_systemBookmarks.isEmpty());
+    Q_ASSERT(m_systemBookmarksIndexes.isEmpty());
 
-    const QString placesGroup = i18nc("@item", "Places");
-    const QString recentlyAccessedGroup = i18nc("@item", "Recently Accessed");
-    const QString searchForGroup = i18nc("@item", "Search For");
+    const QString placesGroup = placesGroupName();
+    const QString recentlyAccessedGroup = recentlyAccessedGroupName();
+    const QString searchForGroup = searchForGroupName();
     const QString timeLineIcon = "package_utility_time"; // TODO: Ask the Oxygen team to create
                                                          // a custom icon for the timeline-protocol
 
-    m_defaultBookmarks.append(DefaultBookmarkData(KUrl(KUser().homeDir()),
-                                                  "user-home",
-                                                  i18nc("@item", "Home"),
-                                                  placesGroup));
-    m_defaultBookmarks.append(DefaultBookmarkData(KUrl("remote:/"),
-                                                  "network-workgroup",
-                                                  i18nc("@item", "Network"),
-                                                  placesGroup));
-    m_defaultBookmarks.append(DefaultBookmarkData(KUrl("/"),
-                                                  "folder-red",
-                                                  i18nc("@item", "Root"),
-                                                  placesGroup));
-    m_defaultBookmarks.append(DefaultBookmarkData(KUrl("trash:/"),
-                                                  "user-trash",
-                                                  i18nc("@item", "Trash"),
-                                                  placesGroup));
+    m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()),
+                                                "user-home",
+                                                i18nc("@item", "Home"),
+                                                placesGroup));
+    m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"),
+                                                "network-workgroup",
+                                                i18nc("@item", "Network"),
+                                                placesGroup));
+    m_systemBookmarks.append(SystemBookmarkData(KUrl("/"),
+                                                "folder-red",
+                                                i18nc("@item", "Root"),
+                                                placesGroup));
+    m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"),
+                                                "user-trash",
+                                                i18nc("@item", "Trash"),
+                                                placesGroup));
 
     if (m_nepomukRunning) {
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/today"),
-                                                      timeLineIcon,
-                                                      i18nc("@item Recently Accessed", "Today"),
-                                                      recentlyAccessedGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/yesterday"),
-                                                      timeLineIcon,
-                                                      i18nc("@item Recently Accessed", "Yesterday"),
-                                                      recentlyAccessedGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/thismonth"),
-                                                      timeLineIcon,
-                                                      i18nc("@item Recently Accessed", "This Month"),
-                                                      recentlyAccessedGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/lastmonth"),
-                                                      timeLineIcon,
-                                                      i18nc("@item Recently Accessed", "Last Month"),
-                                                      recentlyAccessedGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/documents"),
-                                                      "folder-txt",
-                                                      i18nc("@item Commonly Accessed", "Documents"),
-                                                      searchForGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/images"),
-                                                      "folder-image",
-                                                      i18nc("@item Commonly Accessed", "Images"),
-                                                      searchForGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/audio"),
-                                                      "folder-sound",
-                                                      i18nc("@item Commonly Accessed", "Audio"),
-                                                      searchForGroup));
-        m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/videos"),
-                                                      "folder-video",
-                                                      i18nc("@item Commonly Accessed", "Videos"),
-                                                      searchForGroup));
-    }
-
-    for (int i = 0; i < m_defaultBookmarks.count(); ++i) {
-        m_defaultBookmarksIndexes.insert(m_defaultBookmarks[i].url, i);
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/today"),
+                                                    timeLineIcon,
+                                                    i18nc("@item Recently Accessed", "Today"),
+                                                    recentlyAccessedGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/yesterday"),
+                                                    timeLineIcon,
+                                                    i18nc("@item Recently Accessed", "Yesterday"),
+                                                    recentlyAccessedGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/thismonth"),
+                                                    timeLineIcon,
+                                                    i18nc("@item Recently Accessed", "This Month"),
+                                                    recentlyAccessedGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/lastmonth"),
+                                                    timeLineIcon,
+                                                    i18nc("@item Recently Accessed", "Last Month"),
+                                                    recentlyAccessedGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/documents"),
+                                                    "folder-txt",
+                                                    i18nc("@item Commonly Accessed", "Documents"),
+                                                    searchForGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/images"),
+                                                    "folder-image",
+                                                    i18nc("@item Commonly Accessed", "Images"),
+                                                    searchForGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/audio"),
+                                                    "folder-sound",
+                                                    i18nc("@item Commonly Accessed", "Audio Files"),
+                                                    searchForGroup));
+        m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/videos"),
+                                                    "folder-video",
+                                                    i18nc("@item Commonly Accessed", "Videos"),
+                                                    searchForGroup));
     }
-}
 
-
-KUrl PlacesItemModel::translatedDefaultBookmarkUrl(const KUrl& url) const
-{
-    KUrl translatedUrl = url;
-    if (url.protocol() == QLatin1String("timeline")) {
-        translatedUrl = createTimelineUrl(url);
-    } else if (url.protocol() == QLatin1String("search")) {
-        translatedUrl = createSearchUrl(url);
+    for (int i = 0; i < m_systemBookmarks.count(); ++i) {
+        m_systemBookmarksIndexes.insert(m_systemBookmarks[i].url, i);
     }
-
-    return translatedUrl;
 }
 
-KUrl PlacesItemModel::createTimelineUrl(const KUrl& url)
+void PlacesItemModel::initializeAvailableDevices()
 {
-    // TODO: Clarify with the Nepomuk-team whether it makes sense
-    // provide default-timeline-URLs like 'yesterday', 'this month'
-    // and 'last month'.
-    KUrl timelineUrl;
-
-    const QString path = url.pathOrUrl();
-    if (path.endsWith("yesterday")) {
-        const QDate date = QDate::currentDate().addDays(-1);
-        const int year = date.year();
-        const int month = date.month();
-        const int day = date.day();
-        timelineUrl = "timeline:/" + timelineDateString(year, month) +
-              '/' + timelineDateString(year, month, day);
-    } else if (path.endsWith("thismonth")) {
-        const QDate date = QDate::currentDate();
-        timelineUrl = "timeline:/" + timelineDateString(date.year(), date.month());
-    } else if (path.endsWith("lastmonth")) {
-        const QDate date = QDate::currentDate().addMonths(-1);
-        timelineUrl = "timeline:/" + timelineDateString(date.year(), date.month());
-    } else {
-        Q_ASSERT(path.endsWith("today"));
-        timelineUrl= url;
+    m_predicate = Solid::Predicate::fromString(
+        "[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]"
+        " OR "
+        "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]"
+        " OR "
+        "OpticalDisc.availableContent & 'Audio' ]"
+        " OR "
+        "StorageAccess.ignored == false ]");
+    Q_ASSERT(m_predicate.isValid());
+
+    Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance();
+    connect(notifier, SIGNAL(deviceAdded(QString)),   this, SLOT(slotDeviceAdded(QString)));
+    connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString)));
+
+    const QList<Solid::Device>& deviceList = Solid::Device::listFromQuery(m_predicate);
+    foreach(const Solid::Device& device, deviceList) {
+        m_availableDevices << device.udi();
     }
-
-    return timelineUrl;
 }
 
-QString PlacesItemModel::timelineDateString(int year, int month, int day)
+int PlacesItemModel::hiddenIndex(int index) const
 {
-    QString date = QString::number(year) + '-';
-    if (month < 10) {
-        date += '0';
-    }
-    date += QString::number(month);
-
-    if (day >= 1) {
-        date += '-';
-        if (day < 10) {
-            date += '0';
+    int hiddenIndex = 0;
+    int visibleItemIndex = 0;
+    while (hiddenIndex < m_hiddenItems.count()) {
+        if (!m_hiddenItems[hiddenIndex]) {
+            if (visibleItemIndex == index) {
+                break;
+            }
+            ++visibleItemIndex;
         }
-        date += QString::number(day);
+        ++hiddenIndex;
     }
 
-    return date;
+    return hiddenIndex >= m_hiddenItems.count() ? -1 : hiddenIndex;
 }
 
-KUrl PlacesItemModel::createSearchUrl(const KUrl& url)
+QString PlacesItemModel::placesGroupName()
 {
-    KUrl searchUrl;
+    return i18nc("@item", "Places");
+}
 
-#ifdef HAVE_NEPOMUK
-    const QString path = url.pathOrUrl();
-    if (path.endsWith("documents")) {
-        searchUrl = searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Document()));
-    } else if (path.endsWith("images")) {
-        searchUrl = searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Image()));
-    } else if (path.endsWith("audio")) {
-        searchUrl = searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
-                                                                    Nepomuk::Query::LiteralTerm("audio")));
-    } else if (path.endsWith("videos")) {
-        searchUrl = searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
-                                                                    Nepomuk::Query::LiteralTerm("video")));
-    } else {
-        Q_ASSERT(false);
-    }
-#else
-    Q_UNUSED(url);
-#endif
+QString PlacesItemModel::recentlyAccessedGroupName()
+{
+    return i18nc("@item", "Recently Accessed");
+}
 
-    return searchUrl;
+QString PlacesItemModel::searchForGroupName()
+{
+    return i18nc("@item", "Search For");
 }
 
-#ifdef HAVE_NEPOMUK
-KUrl PlacesItemModel::searchUrlForTerm(const Nepomuk::Query::Term& term)
+#ifdef PLACESITEMMODEL_DEBUG
+void PlacesItemModel::showModelState()
 {
-    const Nepomuk::Query::Query query(term);
-    return query.toSearchUrl();
+    kDebug() << "hidden-index   model-index   text";
+    int j = 0;
+    for (int i = 0; i < m_hiddenItems.count(); ++i) {
+        if (m_hiddenItems[i]) {
+            kDebug() <<  i << "(Hidden)    " << "             " << m_hiddenItems[i]->dataValue("text").toString();
+        } else {
+            if (item(j)) {
+                kDebug() <<  i << "            " << j << "           " << item(j)->dataValue("text").toString();
+            } else {
+                kDebug() <<  i << "            " << j << "           " << "(not available yet)";
+            }
+            ++j;
+        }
+    }
 }
 #endif