]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Use Kio::KPlacesModel as source model for PlacesItemModel
authorRenato Araujo Oliveira Filho <renato.araujo@kdab.com>
Thu, 16 Nov 2017 15:32:49 +0000 (12:32 -0300)
committerRenato Araujo Oliveira Filho <renato.araujo@kdab.com>
Thu, 14 Dec 2017 12:40:34 +0000 (09:40 -0300)
Summary:
Use Kio::KPlacesModel as source model for PlacesItemModel avoiding
duplicated code.

Depends on D8862
Depends on D8332
Depends on D8434
Depends on D8348
Depends on D8630

Test Plan: Unit test created

Reviewers: elvisangelaccio, emmanuelp, mlaurent, mwolff

Reviewed By: elvisangelaccio, mlaurent, mwolff

Subscribers: mwolff, mlaurent, anthonyfieroni, nicolasfella, ngraham, #dolphin

Differential Revision: https://phabricator.kde.org/D8855

CMakeLists.txt
src/dolphincontextmenu.cpp
src/kitemviews/kfileitemmodel.cpp
src/panels/places/placesitem.cpp
src/panels/places/placesitem.h
src/panels/places/placesitemmodel.cpp
src/panels/places/placesitemmodel.h
src/panels/places/placespanel.cpp
src/search/dolphinsearchbox.cpp
src/tests/placesitemmodeltest.cpp

index 558e676134766a51518ef2204b725e244a145b76..1cbeb0795c19b3260aaf42b97e331a73242a2ff9 100644 (file)
@@ -8,7 +8,7 @@ set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATI
 project(Dolphin VERSION ${KDE_APPLICATIONS_VERSION})
 
 set(QT_MIN_VERSION "5.5.0")
-set(KF5_MIN_VERSION "5.40.0")
+set(KF5_MIN_VERSION "5.41.0")
 set(ECM_MIN_VERSION "1.6.0")
 
 # ECM setup
index af3878e1fcf5e2fb20804eda3a9cd15c39b7492c..de40d7eaa0356df55495d295774ffc818ea548f5 100644 (file)
@@ -306,9 +306,7 @@ void DolphinContextMenu::openItemContextMenu()
             if (selectedUrl.isValid()) {
                 PlacesItemModel model;
                 const QString text = selectedUrl.fileName();
-                PlacesItem* item = model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl));
-                model.appendItemToGroup(item);
-                model.saveBookmarks();
+                model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl));
             }
         } else if (activatedAction == openParentAction) {
             m_command = OpenParentFolder;
@@ -378,9 +376,7 @@ void DolphinContextMenu::openViewportContextMenu()
             } else {
                 icon = KIO::iconNameForUrl(url);
             }
-            PlacesItem* item = model.createPlacesItem(container->placesText(), url, icon);
-            model.appendItemToGroup(item);
-            model.saveBookmarks();
+            model.createPlacesItem(container->placesText(), url, icon);
         }
     }
 }
index 08520c9f62ec95b4eaf6368b4a158c0fcf579b39..634e7039961d2f7b13020bf541476bb550d19497 100644 (file)
@@ -2373,7 +2373,7 @@ bool KFileItemModel::isConsistent() const
         return false;
     }
 
-    for (int i = 0; i < count(); ++i) {
+    for (int i = 0, iMax = count(); i < iMax; ++i) {
         // Check if m_items and m_itemData are consistent.
         const KFileItem item = fileItem(i);
         if (item.isNull()) {
index 297cf95d6acf21f344114d6713a2b824d9628b09..c473856565f4961107d70cc1673d32053399938c 100644 (file)
@@ -130,35 +130,16 @@ void PlacesItem::setBookmark(const KBookmark& bookmark)
     delete m_disc;
     delete m_mtp;
 
-
     const QString udi = bookmark.metaDataItem(QStringLiteral("UDI"));
     if (udi.isEmpty()) {
         setIcon(bookmark.icon());
         setText(i18nc("KFile System Bookmarks", bookmark.text().toUtf8().constData()));
         setUrl(bookmark.url());
+        setSystemItem(bookmark.metaDataItem(QStringLiteral("isSystemItem")) == QLatin1String("true"));
     } else {
         initializeDevice(udi);
     }
 
-    const GroupType type = groupType();
-    if (icon().isEmpty()) {
-        switch (type) {
-        case RecentlySavedType: setIcon(QStringLiteral("chronometer")); break;
-        case SearchForType:     setIcon(QStringLiteral("system-search")); break;
-        case PlacesType:
-        default:                setIcon(QStringLiteral("folder"));
-        }
-
-    }
-
-    switch (type) {
-    case PlacesType:        setGroup(i18nc("@item", "Places")); break;
-    case RecentlySavedType: setGroup(i18nc("@item", "Recently Saved")); break;
-    case SearchForType:     setGroup(i18nc("@item", "Search For")); break;
-    case DevicesType:       setGroup(i18nc("@item", "Devices")); break;
-    default:                Q_ASSERT(false); break;
-    }
-
     setHidden(bookmark.metaDataItem(QStringLiteral("IsHidden")) == QLatin1String("true"));
 }
 
@@ -167,62 +148,15 @@ KBookmark PlacesItem::bookmark() const
     return m_bookmark;
 }
 
-PlacesItem::GroupType PlacesItem::groupType() const
-{
-    if (udi().isEmpty()) {
-        const QString protocol = url().scheme();
-        if (protocol == QLatin1String("timeline")) {
-            return RecentlySavedType;
-        }
-
-        if (protocol.contains(QLatin1String("search"))) {
-            return SearchForType;
-        }
-
-        if (protocol == QLatin1String("bluetooth") || protocol == QLatin1String("obexftp") || protocol == QLatin1String("kdeconnect")) {
-            return DevicesType;
-        }
-
-        return PlacesType;
-    }
-
-    return DevicesType;
-}
-
 bool PlacesItem::storageSetupNeeded() const
 {
     return m_access ? !m_access->isAccessible() : false;
 }
 
-KBookmark PlacesItem::createBookmark(KBookmarkManager* manager,
-                                     const QString& text,
-                                     const QUrl& url,
-                                     const QString& iconName)
+bool PlacesItem::isSearchOrTimelineUrl() const
 {
-    KBookmarkGroup root = manager->root();
-    if (root.isNull()) {
-        return KBookmark();
-    }
-
-    KBookmark bookmark = root.addBookmark(text, url, iconName);
-    bookmark.setFullText(text);
-    bookmark.setMetaDataItem(QStringLiteral("ID"), generateNewId());
-
-    return bookmark;
-}
-
-KBookmark PlacesItem::createDeviceBookmark(KBookmarkManager* manager,
-                                           const QString& udi)
-{
-    KBookmarkGroup root = manager->root();
-    if (root.isNull()) {
-        return KBookmark();
-    }
-
-    KBookmark bookmark = root.createNewSeparator();
-    bookmark.setMetaDataItem(QStringLiteral("UDI"), udi);
-    bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), QStringLiteral("true"));
-    return bookmark;
+    const QString urlScheme = url().scheme();
+    return (urlScheme.contains("search") || urlScheme.contains("timeline"));
 }
 
 void PlacesItem::onDataValueChanged(const QByteArray& role,
index b7cd8e79f514e5e7643087b22c1755a6ceb73582..d32c616db860393e98df2ed1ee7baaca3c260507 100644 (file)
@@ -40,14 +40,6 @@ class PlacesItem : public KStandardItem
 {
 
 public:
-    enum GroupType
-    {
-        PlacesType,
-        SearchForType,
-        RecentlySavedType,
-        DevicesType
-    };
-
     explicit PlacesItem(const KBookmark& bookmark, PlacesItem* parent = nullptr);
     ~PlacesItem() override;
 
@@ -68,16 +60,9 @@ public:
     void setBookmark(const KBookmark& bookmark);
     KBookmark bookmark() const;
 
-    GroupType groupType() const;
-
     bool storageSetupNeeded() const;
 
-    static KBookmark createBookmark(KBookmarkManager* manager,
-                                    const QString& text,
-                                    const QUrl& url,
-                                    const QString& iconName);
-    static KBookmark createDeviceBookmark(KBookmarkManager* manager,
-                                          const QString& udi);
+    bool isSearchOrTimelineUrl() const;
 
     PlacesItemSignalHandler* signalHandler() const;
 
index 371bcb892bfb7148b092899c10fa03db51414c20..074b7ab452689dce4b19c14b8da8ea5af40f9fdb 100644 (file)
@@ -40,6 +40,7 @@
 #include <QMimeData>
 #include <QTimer>
 #include <KUrlMimeData>
+#include <KFilePlacesModel>
 
 #include <Solid/Device>
 #include <Solid/DeviceNotifier>
 #include <views/dolphinview.h>
 #include <views/viewproperties.h>
 
-#ifdef HAVE_BALOO
-    #include <Baloo/Query>
-    #include <Baloo/IndexerConfig>
-#endif
-
 namespace {
-    // As long as KFilePlacesView from kdelibs is available in parallel, the
-    // system-bookmarks for "Recently Saved" and "Search For" should be
-    // shown only inside the Places Panel. This is necessary as the stored
-    // URLs needs to get translated to a Baloo-search-URL on-the-fly to
-    // be independent from changes in the Baloo-search-URL-syntax.
     // Hence a prefix to the application-name of the stored bookmarks is
     // added, which is only read by PlacesItemModel.
     const char AppNamePrefix[] = "-places-panel";
@@ -69,53 +60,31 @@ namespace {
 
 PlacesItemModel::PlacesItemModel(QObject* parent) :
     KStandardItemModel(parent),
-    m_fileIndexingEnabled(false),
     m_hiddenItemsShown(false),
-    m_availableDevices(),
-    m_predicate(),
-    m_bookmarkManager(nullptr),
-    m_systemBookmarks(),
-    m_systemBookmarksIndexes(),
-    m_bookmarkedItems(),
-    m_hiddenItemToRemove(-1),
     m_deviceToTearDown(nullptr),
-    m_updateBookmarksTimer(nullptr),
-    m_storageSetupInProgress()
+    m_storageSetupInProgress(),
+    m_sourceModel(new KFilePlacesModel(this))
 {
-#ifdef HAVE_BALOO
-    Baloo::IndexerConfig config;
-    m_fileIndexingEnabled = config.fileIndexingEnabled();
-#endif
-    const QString file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/user-places.xbel";
-    m_bookmarkManager = KBookmarkManager::managerForExternalFile(file);
-
-    createSystemBookmarks();
-    initializeAvailableDevices();
     loadBookmarks();
+    initializeDefaultViewProperties();
 
-    const int syncBookmarksTimeout = 100;
-
-    m_updateBookmarksTimer = new QTimer(this);
-    m_updateBookmarksTimer->setInterval(syncBookmarksTimeout);
-    m_updateBookmarksTimer->setSingleShot(true);
-    connect(m_updateBookmarksTimer, &QTimer::timeout, this, &PlacesItemModel::updateBookmarks);
-
-    connect(m_bookmarkManager, &KBookmarkManager::changed,
-            m_updateBookmarksTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
+    connect(m_sourceModel.data(), &KFilePlacesModel::rowsInserted, this, &PlacesItemModel::onSourceModelRowsInserted);
+    connect(m_sourceModel.data(), &KFilePlacesModel::rowsAboutToBeRemoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeRemoved);
+    connect(m_sourceModel.data(), &KFilePlacesModel::dataChanged, this, &PlacesItemModel::onSourceModelDataChanged);
+    connect(m_sourceModel.data(), &KFilePlacesModel::rowsAboutToBeMoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeMoved);
+    connect(m_sourceModel.data(), &KFilePlacesModel::rowsMoved, this, &PlacesItemModel::onSourceModelRowsMoved);
 }
 
 PlacesItemModel::~PlacesItemModel()
 {
-    qDeleteAll(m_bookmarkedItems);
-    m_bookmarkedItems.clear();
 }
 
-PlacesItem* PlacesItemModel::createPlacesItem(const QString& text,
-                                              const QUrl& url,
-                                              const QString& iconName)
+void PlacesItemModel::createPlacesItem(const QString& text,
+                                       const QUrl& url,
+                                       const QString& iconName,
+                                       int after)
 {
-    const KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, text, url, iconName);
-    return new PlacesItem(bookmark);
+    m_sourceModel->addPlace(text, url, iconName, {}, mapToSource(after));
 }
 
 PlacesItem* PlacesItemModel::placesItem(int index) const
@@ -125,20 +94,7 @@ PlacesItem* PlacesItemModel::placesItem(int index) const
 
 int PlacesItemModel::hiddenCount() const
 {
-    int modelIndex = 0;
-    int hiddenItemCount = 0;
-    foreach (const PlacesItem* item, m_bookmarkedItems) {
-        if (item) {
-            ++hiddenItemCount;
-        } else {
-            if (placesItem(modelIndex)->isHidden()) {
-                ++hiddenItemCount;
-            }
-            ++modelIndex;
-        }
-    }
-
-    return hiddenItemCount;
+    return m_sourceModel->hiddenCount();
 }
 
 void PlacesItemModel::setHiddenItemsShown(bool show)
@@ -150,38 +106,18 @@ void PlacesItemModel::setHiddenItemsShown(bool show)
     m_hiddenItemsShown = show;
 
     if (show) {
-        // Move all items that are part of m_bookmarkedItems to the model.
-        QList<PlacesItem*> itemsToInsert;
-        QList<int> insertPos;
-        int modelIndex = 0;
-        for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
-            if (m_bookmarkedItems[i]) {
-                itemsToInsert.append(m_bookmarkedItems[i]);
-                m_bookmarkedItems[i] = 0;
-                insertPos.append(modelIndex);
+        for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
+            const QModelIndex index = m_sourceModel->index(r, 0);
+            if (!m_sourceModel->isHidden(index)) {
+                continue;
             }
-            ++modelIndex;
+            addItemFromSourceModel(index);
         }
-
-        // Inserting the items will automatically insert an item
-        // to m_bookmarkedItems in PlacesItemModel::onItemsInserted().
-        // The items are temporary saved in itemsToInsert, so
-        // m_bookmarkedItems can be shrinked now.
-        m_bookmarkedItems.erase(m_bookmarkedItems.begin(),
-                                m_bookmarkedItems.begin() + itemsToInsert.count());
-
-        for (int i = 0; i < itemsToInsert.count(); ++i) {
-            insertItem(insertPos[i], itemsToInsert[i]);
-        }
-
-        Q_ASSERT(m_bookmarkedItems.count() == count());
     } else {
-        // Move all items of the model, where the "isHidden" property is true, to
-        // m_bookmarkedItems.
-        Q_ASSERT(m_bookmarkedItems.count() == count());
-        for (int i = count() - 1; i >= 0; --i) {
-            if (placesItem(i)->isHidden()) {
-                hideItem(i);
+        for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
+            const QModelIndex index = m_sourceModel->index(r, 0);
+            if (m_sourceModel->isHidden(index)) {
+                removeItemByIndex(index);
             }
         }
     }
@@ -199,52 +135,73 @@ bool PlacesItemModel::hiddenItemsShown() const
 
 int PlacesItemModel::closestItem(const QUrl& url) const
 {
-    int foundIndex = -1;
-    int maxLength = 0;
-
-    for (int i = 0; i < count(); ++i) {
-        const QUrl itemUrl = placesItem(i)->url();
-        if (url == itemUrl) {
-            // We can't find a closer one, so stop here.
-            foundIndex = i;
-            break;
-        } else if (itemUrl.isParentOf(url)) {
-            const int length = itemUrl.path().length();
-            if (length > maxLength) {
-                foundIndex = i;
-                maxLength = length;
-            }
-        }
-    }
-
-    return foundIndex;
+    return mapFromSource(m_sourceModel->closestItem(url));
 }
 
-void PlacesItemModel::appendItemToGroup(PlacesItem* item)
+// look for the correct position for the item based on source model
+void PlacesItemModel::insertSortedItem(PlacesItem* item)
 {
     if (!item) {
         return;
     }
 
-    int i = 0;
-    while (i < count() && placesItem(i)->group() != item->group()) {
-        ++i;
-    }
+    const KBookmark iBookmark = item->bookmark();
+    const QString iBookmarkId = bookmarkId(iBookmark);
+    QModelIndex sourceIndex;
+    int pos = 0;
 
-    bool inserted = false;
-    while (!inserted && i < count()) {
-        if (placesItem(i)->group() != item->group()) {
-            insertItem(i, item);
-            inserted = true;
+    for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
+        sourceIndex = m_sourceModel->index(r, 0);
+
+        if (bookmarkId(m_sourceModel->bookmarkForIndex(sourceIndex)) == iBookmarkId) {
+            break;
         }
-        ++i;
-    }
 
-    if (!inserted) {
-        appendItem(item);
+        if (!m_sourceModel->isHidden(sourceIndex)) {
+            pos++;
+        }
     }
+
+    m_indexMap.insert(pos, sourceIndex);
+    insertItem(pos, item);
 }
 
+void PlacesItemModel::onItemInserted(int index)
+{
+    KStandardItemModel::onItemInserted(index);
+#ifdef PLACESITEMMODEL_DEBUG
+    qCDebug(DolphinDebug) << "Inserted item" << index;
+    showModelState();
+#endif
+}
+
+void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem)
+{
+    m_indexMap.removeAt(index);
+
+    KStandardItemModel::onItemRemoved(index, removedItem);
+#ifdef PLACESITEMMODEL_DEBUG
+    qCDebug(DolphinDebug) << "Removed item" << index;
+    showModelState();
+#endif
+}
+
+void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
+{
+    const QModelIndex sourceIndex = mapToSource(index);
+    const PlacesItem *changedItem = placesItem(mapFromSource(sourceIndex));
+
+    if (!changedItem || !sourceIndex.isValid()) {
+        qWarning() << "invalid item changed signal";
+        return;
+    }
+
+    if (changedRoles.contains("isHidden")) {
+        m_sourceModel->setPlaceHidden(sourceIndex, changedItem->isHidden());
+    }
+
+    KStandardItemModel::onItemChanged(index, changedRoles);
+}
 
 QAction* PlacesItemModel::ejectAction(int index) const
 {
@@ -404,25 +361,8 @@ void PlacesItemModel::dropMimeDataBefore(int index, const QMimeData* mimeData)
         QDataStream stream(&itemData, QIODevice::ReadOnly);
         int oldIndex;
         stream >> oldIndex;
-        if (oldIndex == index || oldIndex == index - 1) {
-            // No moving has been done
-            return;
-        }
 
-        PlacesItem* oldItem = placesItem(oldIndex);
-        if (!oldItem) {
-            return;
-        }
-
-        PlacesItem* newItem = new PlacesItem(oldItem->bookmark());
-        removeItem(oldIndex);
-
-        if (oldIndex < index) {
-            --index;
-        }
-
-        const int dropIndex = groupedDropIndex(index, newItem);
-        insertItem(dropIndex, newItem);
+        m_sourceModel->movePlace(oldIndex, index);
     } else if (mimeData->hasFormat(QStringLiteral("text/uri-list"))) {
         // One or more items must be added to the model
         const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
@@ -440,150 +380,96 @@ void PlacesItemModel::dropMimeDataBefore(int index, const QMimeData* mimeData)
                 continue;
             }
 
-            PlacesItem* newItem = createPlacesItem(text, url);
-            const int dropIndex = groupedDropIndex(index, newItem);
-            insertItem(dropIndex, newItem);
+            createPlacesItem(text, url, QString(), qMax(0, index - 1));
         }
     }
+    // will save bookmark alteration and fix sort if that is broken by the drag/drop operation
+    refresh();
 }
 
-QUrl PlacesItemModel::convertedUrl(const QUrl& url)
+void PlacesItemModel::addItemFromSourceModel(const QModelIndex &index)
 {
-    QUrl newUrl = url;
-    if (url.scheme() == QLatin1String("timeline")) {
-        newUrl = createTimelineUrl(url);
-    } else if (url.scheme() == QLatin1String("search")) {
-        newUrl = createSearchUrl(url);
-    }
-
-    return newUrl;
-}
-
-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_bookmarkedItems.append(0);
-    } else {
-
-        int modelIndex = -1;
-        int bookmarkIndex = 0;
-        while (bookmarkIndex < m_bookmarkedItems.count()) {
-            if (!m_bookmarkedItems[bookmarkIndex]) {
-                ++modelIndex;
-                if (modelIndex + 1 == index) {
-                    break;
-                }
-            }
-            ++bookmarkIndex;
-        }
-        m_bookmarkedItems.insert(bookmarkIndex, 0);
-    }
-
-#ifdef PLACESITEMMODEL_DEBUG
-    qCDebug(DolphinDebug) << "Inserted item" << index;
-    showModelState();
-#endif
-}
+    const KBookmark bookmark = m_sourceModel->bookmarkForIndex(index);
+    Q_ASSERT(!bookmark.isNull());
+    PlacesItem *item = new PlacesItem(bookmark);
+    updateItem(item, index);
+    insertSortedItem(item);
 
-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);
+    if (m_sourceModel->isDevice(index)) {
+        connect(item->signalHandler(), &PlacesItemSignalHandler::tearDownExternallyRequested,
+                this, &PlacesItemModel::storageTearDownExternallyRequested);
     }
-
-    const int boomarkIndex = bookmarkIndex(index);
-    Q_ASSERT(!m_bookmarkedItems[boomarkIndex]);
-    m_bookmarkedItems.removeAt(boomarkIndex);
-
-#ifdef PLACESITEMMODEL_DEBUG
-    qCDebug(DolphinDebug) << "Removed item" << index;
-    showModelState();
-#endif
 }
 
-void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
+void PlacesItemModel::removeItemByIndex(const QModelIndex &sourceIndex)
 {
-    const PlacesItem* changedItem = placesItem(index);
-    if (changedItem) {
-        // Take care to apply the PlacesItemModel-order of the changed 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);
-    }
+    QString id = bookmarkId(m_sourceModel->bookmarkForIndex(sourceIndex));
 
-    if (changedRoles.contains("isHidden")) {
-        if (!m_hiddenItemsShown && changedItem->isHidden()) {
-            m_hiddenItemToRemove = index;
-            QTimer::singleShot(0, this, static_cast<void (PlacesItemModel::*)()>(&PlacesItemModel::hideItem));
+    for (int i = 0, iMax = count(); i < iMax; ++i) {
+        if (bookmarkId(placesItem(i)->bookmark()) == id) {
+            removeItem(i);
+            return;
         }
     }
 }
 
-void PlacesItemModel::slotDeviceAdded(const QString& udi)
-{
-    const Solid::Device device(udi);
-
-    if (!m_predicate.matches(device)) {
-        return;
+QString PlacesItemModel::bookmarkId(const KBookmark &bookmark) const
+{
+    QString id = bookmark.metaDataItem(QStringLiteral("UDI"));
+    if (id.isEmpty()) {
+        id = bookmark.metaDataItem(QStringLiteral("ID"));
+    }
+    return id;
+}
+
+void PlacesItemModel::initializeDefaultViewProperties() const
+{
+    for(int i = 0, iMax = m_sourceModel->rowCount(); i < iMax; i++) {
+        const QModelIndex index = m_sourceModel->index(i, 0);
+        const PlacesItem *item = placesItem(mapFromSource(index));
+        if (!item) {
+            continue;
+        }
+
+        // Create default view-properties for all "Search For" and "Recently Saved" bookmarks
+        // in case the user has not already created custom view-properties for a corresponding
+        // query yet.
+        const bool createDefaultViewProperties = item->isSearchOrTimelineUrl() && !GeneralSettings::self()->globalViewProps();
+        if (createDefaultViewProperties) {
+            const QUrl itemUrl = item->url();
+            ViewProperties props(KFilePlacesModel::convertedUrl(itemUrl));
+            if (!props.exist()) {
+                const QString path = itemUrl.path();
+                if (path == QLatin1String("/documents")) {
+                    props.setViewMode(DolphinView::DetailsView);
+                    props.setPreviewsShown(false);
+                    props.setVisibleRoles({"text", "path"});
+                } else if (path == QLatin1String("/images")) {
+                    props.setViewMode(DolphinView::IconsView);
+                    props.setPreviewsShown(true);
+                    props.setVisibleRoles({"text", "imageSize"});
+                } else if (path == QLatin1String("/audio")) {
+                    props.setViewMode(DolphinView::DetailsView);
+                    props.setPreviewsShown(false);
+                    props.setVisibleRoles({"text", "artist", "album"});
+                } else if (path == QLatin1String("/videos")) {
+                    props.setViewMode(DolphinView::IconsView);
+                    props.setPreviewsShown(true);
+                    props.setVisibleRoles({"text"});
+                } else if (itemUrl.scheme() == QLatin1String("timeline")) {
+                    props.setViewMode(DolphinView::DetailsView);
+                    props.setVisibleRoles({"text", "modificationtime"});
+                }
+                props.save();
+            }
+        }
     }
-
-    m_availableDevices << udi;
-    const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
-
-    PlacesItem *item = new PlacesItem(bookmark);
-    appendItem(item);
-    connect(item->signalHandler(), &PlacesItemSignalHandler::tearDownExternallyRequested,
-            this, &PlacesItemModel::storageTearDownExternallyRequested);
 }
 
-void PlacesItemModel::slotDeviceRemoved(const QString& udi)
+void PlacesItemModel::updateItem(PlacesItem *item, const QModelIndex &index)
 {
-    if (!m_availableDevices.contains(udi)) {
-        return;
-    }
-
-    for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
-        PlacesItem* item = m_bookmarkedItems[i];
-        if (item && item->udi() == udi) {
-            m_bookmarkedItems.removeAt(i);
-            delete item;
-            return;
-        }
-    }
-
-    for (int i = 0; i < count(); ++i) {
-        if (placesItem(i)->udi() == udi) {
-            removeItem(i);
-            return;
-        }
-    }
+    item->setGroup(index.data(KFilePlacesModel::GroupRole).toString());
+    item->setIcon(index.data(KFilePlacesModel::IconNameRole).toString());
 }
 
 void PlacesItemModel::slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData)
@@ -622,199 +508,96 @@ void PlacesItemModel::slotStorageSetupDone(Solid::ErrorType error,
     }
 }
 
-void PlacesItemModel::hideItem()
+void PlacesItemModel::onSourceModelRowsInserted(const QModelIndex &parent, int first, int last)
 {
-    hideItem(m_hiddenItemToRemove);
-    m_hiddenItemToRemove = -1;
+    for (int i = first; i <= last; i++) {
+        const QModelIndex index = m_sourceModel->index(i, 0, parent);
+        addItemFromSourceModel(index);
+    }
 }
 
-void PlacesItemModel::updateBookmarks()
+void PlacesItemModel::onSourceModelRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
 {
-    // Verify whether new bookmarks have been added or existing
-    // bookmarks have been changed.
-    KBookmarkGroup root = m_bookmarkManager->root();
-    KBookmark newBookmark = root.first();
-    while (!newBookmark.isNull()) {
-        if (acceptBookmark(newBookmark, m_availableDevices)) {
-            bool found = false;
-            int modelIndex = 0;
-            for (int i = 0; i < m_bookmarkedItems.count(); ++i) {
-                PlacesItem* item = m_bookmarkedItems[i];
-                if (!item) {
-                    item = placesItem(modelIndex);
-                    ++modelIndex;
-                }
-
-                const KBookmark oldBookmark = item->bookmark();
-                if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) {
-                    // The bookmark has been found in the model or as
-                    // a hidden item. The content of the bookmark might
-                    // have been changed, so an update is done.
-                    found = true;
-                    if (newBookmark.metaDataItem(QStringLiteral("UDI")).isEmpty()) {
-                        item->setBookmark(newBookmark);
-                        item->setText(i18nc("KFile System Bookmarks", newBookmark.text().toUtf8().constData()));
-                    }
-                    break;
-                }
-            }
-
-            if (!found) {
-                const QString udi = newBookmark.metaDataItem(QStringLiteral("UDI"));
-
-                /*
-                 * See Bug 304878
-                 * Only add a new places item, if the item text is not empty
-                 * and if the device is available. Fixes the strange behaviour -
-                 * add a places item without text in the Places section - when you
-                 * remove a device (e.g. a usb stick) without unmounting.
-                 */
-                if (udi.isEmpty() || Solid::Device(udi).isValid()) {
-                    PlacesItem* item = new PlacesItem(newBookmark);
-                    if (item->isHidden() && !m_hiddenItemsShown) {
-                        m_bookmarkedItems.append(item);
-                    } else {
-                        appendItemToGroup(item);
-                    }
-                }
-            }
+    for(int r = first; r <= last; r++) {
+        const QModelIndex index = m_sourceModel->index(r, 0, parent);
+        int oldIndex = mapFromSource(index);
+        if (oldIndex != -1) {
+            removeItem(oldIndex);
         }
-
-        newBookmark = root.next(newBookmark);
     }
+}
 
-    // Remove items that are not part of the bookmark-manager anymore
-    int modelIndex = 0;
-    for (int i = m_bookmarkedItems.count() - 1; i >= 0; --i) {
-        PlacesItem* item = m_bookmarkedItems[i];
-        const bool itemIsPartOfModel = (item == nullptr);
-        if (itemIsPartOfModel) {
-            item = placesItem(modelIndex);
-        }
-
-        bool hasBeenRemoved = true;
-        const KBookmark oldBookmark = item->bookmark();
-        KBookmark newBookmark = root.first();
-        while (!newBookmark.isNull()) {
-            if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) {
-                hasBeenRemoved = false;
-                break;
-            }
-            newBookmark = root.next(newBookmark);
-        }
-
-        if (hasBeenRemoved) {
-            if (m_bookmarkedItems[i]) {
-                delete m_bookmarkedItems[i];
-                m_bookmarkedItems.removeAt(i);
-            } else {
-                removeItem(modelIndex);
-                --modelIndex;
-            }
-        }
+void PlacesItemModel::onSourceModelRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
+{
+    Q_UNUSED(destination);
+    Q_UNUSED(row);
 
-        if (itemIsPartOfModel) {
-            ++modelIndex;
-        }
+    for(int r = start; r <= end; r++) {
+        const QModelIndex sourceIndex = m_sourceModel->index(r, 0, parent);
+        // remove moved item
+        removeItem(mapFromSource(sourceIndex));
     }
 }
 
-void PlacesItemModel::saveBookmarks()
+void PlacesItemModel::onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
 {
-    m_bookmarkManager->emitChanged(m_bookmarkManager->root());
-}
+    Q_UNUSED(destination);
+    Q_UNUSED(parent);
 
-void PlacesItemModel::loadBookmarks()
-{
-    KBookmarkGroup root = m_bookmarkManager->root();
-    KBookmark bookmark = root.first();
-    QSet<QString> devices = m_availableDevices;
+    const int blockSize = (end - start) + 1;
+
+    for (int r = start; r <= end; r++) {
+        // insert the moved item in the new position
+        const int targetRow = row + (start - r) - (r < row ? blockSize : 0);
+        const QModelIndex targetIndex = m_sourceModel->index(targetRow, 0, destination);
+
+        const KBookmark bookmark = m_sourceModel->bookmarkForIndex(targetIndex);
+        PlacesItem *item = new PlacesItem(bookmark);
+        updateItem(item, targetIndex);
 
-    QSet<QUrl> missingSystemBookmarks;
-    foreach (const SystemBookmarkData& data, m_systemBookmarks) {
-        missingSystemBookmarks.insert(data.url);
+        insertSortedItem(item);
     }
+}
 
-    // The bookmarks might have a mixed order of places, devices and search-groups due
-    // to the compatibility with the KFilePlacesPanel. In Dolphin's places panel the
-    // items should always be collected in one group so the items are collected first
-    // in separate lists before inserting them.
-    QList<PlacesItem*> placesItems;
-    QList<PlacesItem*> recentlySavedItems;
-    QList<PlacesItem*> searchForItems;
-    QList<PlacesItem*> devicesItems;
+void PlacesItemModel::onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
+{
+    Q_UNUSED(roles);
 
-    while (!bookmark.isNull()) {
-        if (acceptBookmark(bookmark, devices)) {
-            PlacesItem* item = new PlacesItem(bookmark);
-            if (item->groupType() == PlacesItem::DevicesType) {
-                devices.remove(item->udi());
-                devicesItems.append(item);
-            } else {
-                const QUrl url = bookmark.url();
-                if (missingSystemBookmarks.contains(url)) {
-                    missingSystemBookmarks.remove(url);
-
-                    // Try to retranslate the text of system bookmarks to have translated
-                    // items when changing the language. In case if the user has applied a custom
-                    // text, the retranslation will fail and the users custom text is still used.
-                    // It is important to use "KFile System Bookmarks" as context (see
-                    // createSystemBookmarks()).
-                    item->setText(i18nc("KFile System Bookmarks", bookmark.text().toUtf8().constData()));
-                    item->setSystemItem(true);
-                }
+    for (int r = topLeft.row(); r <= bottomRight.row(); r++) {
+        const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
+        const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
+        PlacesItem *placeItem = itemFromBookmark(bookmark);
 
-                switch (item->groupType()) {
-                case PlacesItem::PlacesType:        placesItems.append(item); break;
-                case PlacesItem::RecentlySavedType: recentlySavedItems.append(item); break;
-                case PlacesItem::SearchForType:     searchForItems.append(item); break;
-                case PlacesItem::DevicesType:
-                default:                            Q_ASSERT(false); break;
-                }
-            }
+        if (placeItem && (!m_hiddenItemsShown && m_sourceModel->isHidden(sourceIndex))) {
+            //hide item if it became invisible
+            removeItem(index(placeItem));
+            return;
         }
 
-        bookmark = root.next(bookmark);
-    }
-
-    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)) {
-                PlacesItem* item = createSystemPlacesItem(data);
-                switch (item->groupType()) {
-                case PlacesItem::PlacesType:        placesItems.append(item); break;
-                case PlacesItem::RecentlySavedType: recentlySavedItems.append(item); break;
-                case PlacesItem::SearchForType:     searchForItems.append(item); break;
-                case PlacesItem::DevicesType:
-                default:                            Q_ASSERT(false); break;
-                }
-            }
+        if (!placeItem && (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex))) {
+            //show item if it became visible
+            addItemFromSourceModel(sourceIndex);
+            return;
         }
-    }
 
-    // Create items for devices that have not been stored as bookmark yet
-    devicesItems.reserve(devicesItems.count() + devices.count());
-    foreach (const QString& udi, devices) {
-        const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi);
-        PlacesItem *item = new PlacesItem(bookmark);
-        devicesItems.append(item);
-        connect(item->signalHandler(), &PlacesItemSignalHandler::tearDownExternallyRequested,
-                this, &PlacesItemModel::storageTearDownExternallyRequested);
+        if (placeItem && !m_sourceModel->isDevice(sourceIndex)) {
+            placeItem->setText(bookmark.text());
+            placeItem->setIcon(sourceIndex.data(KFilePlacesModel::IconNameRole).toString());
+            placeItem->setUrl(m_sourceModel->url(sourceIndex));
+            placeItem->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"),
+                                                  bookmark.metaDataItem(QStringLiteral("OnlyInApp")));
+        }
     }
+}
 
-    QList<PlacesItem*> items;
-    items.append(placesItems);
-    items.append(recentlySavedItems);
-    items.append(searchForItems);
-    items.append(devicesItems);
-
-    foreach (PlacesItem* item, items) {
-        if (!m_hiddenItemsShown && item->isHidden()) {
-            m_bookmarkedItems.append(item);
-        } else {
-            appendItem(item);
+void PlacesItemModel::loadBookmarks()
+{
+    for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
+        const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
+        KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
+        if (acceptBookmark(bookmark) &&
+                (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex))) {
+            addItemFromSourceModel(sourceIndex);
         }
     }
 
@@ -824,134 +607,19 @@ void PlacesItemModel::loadBookmarks()
 #endif
 }
 
-bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark,
-                                     const QSet<QString>& availableDevices) const
+bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark) const
 {
     const QString udi = bookmark.metaDataItem(QStringLiteral("UDI"));
     const QUrl url = bookmark.url();
     const QString appName = bookmark.metaDataItem(QStringLiteral("OnlyInApp"));
-    const bool deviceAvailable = availableDevices.contains(udi);
-
     const bool allowedHere = (appName.isEmpty()
                               || appName == KAboutData::applicationData().componentName()
-                              || appName == KAboutData::applicationData().componentName() + AppNamePrefix)
-                             && (m_fileIndexingEnabled || (url.scheme() != QLatin1String("timeline") &&
-                                                           url.scheme() != QLatin1String("search")));
-
-    return (udi.isEmpty() && allowedHere) || deviceAvailable;
-}
-
-PlacesItem* PlacesItemModel::createSystemPlacesItem(const SystemBookmarkData& data)
-{
-    KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager,
-                                                    data.text,
-                                                    data.url,
-                                                    data.icon);
-
-    const QString protocol = data.url.scheme();
-    if (protocol == QLatin1String("timeline") || protocol == QLatin1String("search")) {
-        // As long as the KFilePlacesView from kdelibs is available, the system-bookmarks
-        // for "Recently Saved" and "Search For" should be a setting available only
-        // in the Places Panel (see description of AppNamePrefix for more details).
-        const QString appName = KAboutData::applicationData().componentName() + AppNamePrefix;
-        bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), appName);
-    }
-
-    PlacesItem* item = new PlacesItem(bookmark);
-    item->setSystemItem(true);
-
-    // Create default view-properties for all "Search For" and "Recently Saved" bookmarks
-    // in case if the user has not already created custom view-properties for a corresponding
-    // query yet.
-    const bool createDefaultViewProperties = (item->groupType() == PlacesItem::SearchForType ||
-                                              item->groupType() == PlacesItem::RecentlySavedType) &&
-                                              !GeneralSettings::self()->globalViewProps();
-    if (createDefaultViewProperties) {
-        ViewProperties props(convertedUrl(data.url));
-        if (!props.exist()) {
-            const QString path = data.url.path();
-            if (path == QLatin1String("/documents")) {
-                props.setViewMode(DolphinView::DetailsView);
-                props.setPreviewsShown(false);
-                props.setVisibleRoles({"text", "path"});
-            } else if (path == QLatin1String("/images")) {
-                props.setViewMode(DolphinView::IconsView);
-                props.setPreviewsShown(true);
-                props.setVisibleRoles({"text", "imageSize"});
-            } else if (path == QLatin1String("/audio")) {
-                props.setViewMode(DolphinView::DetailsView);
-                props.setPreviewsShown(false);
-                props.setVisibleRoles({"text", "artist", "album"});
-            } else if (path == QLatin1String("/videos")) {
-                props.setViewMode(DolphinView::IconsView);
-                props.setPreviewsShown(true);
-                props.setVisibleRoles({"text"});
-            } else if (data.url.scheme() == QLatin1String("timeline")) {
-                props.setViewMode(DolphinView::DetailsView);
-                props.setVisibleRoles({"text", "modificationtime"});
-            }
-        }
-    }
-
-    return item;
-}
-
-void PlacesItemModel::createSystemBookmarks()
-{
-    Q_ASSERT(m_systemBookmarks.isEmpty());
-    Q_ASSERT(m_systemBookmarksIndexes.isEmpty());
-
-    // Note: The context of the I18N_NOOP2 must be "KFile System Bookmarks". The real
-    // i18nc call is done after reading the bookmark. The reason why the i18nc call is not
-    // done here is because otherwise switching the language would not result in retranslating the
-    // bookmarks.
-    m_systemBookmarks.append(SystemBookmarkData(QUrl::fromLocalFile(QDir::homePath()),
-                                                QStringLiteral("user-home"),
-                                                I18N_NOOP2("KFile System Bookmarks", "Home")));
-    m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("remote:/")),
-                                                QStringLiteral("network-workgroup"),
-                                                I18N_NOOP2("KFile System Bookmarks", "Network")));
-    m_systemBookmarks.append(SystemBookmarkData(QUrl::fromLocalFile(QStringLiteral("/")),
-                                                QStringLiteral("folder-red"),
-                                                I18N_NOOP2("KFile System Bookmarks", "Root")));
-    m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("trash:/")),
-                                                QStringLiteral("user-trash"),
-                                                I18N_NOOP2("KFile System Bookmarks", "Trash")));
-
-    if (m_fileIndexingEnabled) {
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("timeline:/today")),
-                                                    QStringLiteral("go-jump-today"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Today")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("timeline:/yesterday")),
-                                                    QStringLiteral("view-calendar-day"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Yesterday")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("timeline:/thismonth")),
-                                                    QStringLiteral("view-calendar-month"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "This Month")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("timeline:/lastmonth")),
-                                                    QStringLiteral("view-calendar-month"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Last Month")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("search:/documents")),
-                                                    QStringLiteral("folder-text"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Documents")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("search:/images")),
-                                                    QStringLiteral("folder-images"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Images")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("search:/audio")),
-                                                    QStringLiteral("folder-sound"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Audio Files")));
-        m_systemBookmarks.append(SystemBookmarkData(QUrl(QStringLiteral("search:/videos")),
-                                                    QStringLiteral("folder-videos"),
-                                                    I18N_NOOP2("KFile System Bookmarks", "Videos")));
-    }
+                              || appName == KAboutData::applicationData().componentName() + AppNamePrefix);
 
-    for (int i = 0; i < m_systemBookmarks.count(); ++i) {
-        m_systemBookmarksIndexes.insert(m_systemBookmarks[i].url, i);
-    }
+    return (udi.isEmpty() && allowedHere);
 }
 
 void PlacesItemModel::clear() {
-    m_bookmarkedItems.clear();
     KStandardItemModel::clear();
 }
 
@@ -964,50 +632,16 @@ void PlacesItemModel::proceedWithTearDown()
     m_deviceToTearDown->teardown();
 }
 
-void PlacesItemModel::initializeAvailableDevices()
+void PlacesItemModel::deleteItem(int index)
 {
-    QString predicate(QStringLiteral("[[[[ 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 ]"));
-
-
-    if (KProtocolInfo::isKnownProtocol(QStringLiteral("mtp"))) {
-        predicate.prepend("[");
-        predicate.append(" OR PortableMediaPlayer.supportedProtocols == 'mtp']");
-    }
-
-    m_predicate = Solid::Predicate::fromString(predicate);
-    Q_ASSERT(m_predicate.isValid());
-
-    Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance();
-    connect(notifier, &Solid::DeviceNotifier::deviceAdded,   this, &PlacesItemModel::slotDeviceAdded);
-    connect(notifier, &Solid::DeviceNotifier::deviceRemoved, this, &PlacesItemModel::slotDeviceRemoved);
-
-    const QList<Solid::Device>& deviceList = Solid::Device::listFromQuery(m_predicate);
-    foreach (const Solid::Device& device, deviceList) {
-        m_availableDevices << device.udi();
-    }
+    QModelIndex sourceIndex = mapToSource(index);
+    Q_ASSERT(sourceIndex.isValid());
+    m_sourceModel->removePlace(sourceIndex);
 }
 
-int PlacesItemModel::bookmarkIndex(int index) const
+void PlacesItemModel::refresh()
 {
-    int bookmarkIndex = 0;
-    int modelIndex = 0;
-    while (bookmarkIndex < m_bookmarkedItems.count()) {
-        if (!m_bookmarkedItems[bookmarkIndex]) {
-            if (modelIndex == index) {
-                break;
-            }
-            ++modelIndex;
-        }
-        ++bookmarkIndex;
-    }
-
-    return bookmarkIndex >= m_bookmarkedItems.count() ? -1 : bookmarkIndex;
+    m_sourceModel->refresh();
 }
 
 void PlacesItemModel::hideItem(int index)
@@ -1018,36 +652,6 @@ void PlacesItemModel::hideItem(int index)
     }
 
     shownItem->setHidden(true);
-    if (m_hiddenItemsShown) {
-        // Removing items from the model is not allowed if all hidden
-        // items should be shown.
-        return;
-    }
-
-    const int newIndex = bookmarkIndex(index);
-    if (newIndex >= 0) {
-        const KBookmark hiddenBookmark = shownItem->bookmark();
-        PlacesItem* hiddenItem = new PlacesItem(hiddenBookmark);
-
-        const PlacesItem* previousItem = placesItem(index - 1);
-        KBookmark previousBookmark;
-        if (previousItem) {
-            previousBookmark = previousItem->bookmark();
-        }
-
-        const bool updateBookmark = (m_bookmarkManager->root().indexOf(hiddenBookmark) >= 0);
-        removeItem(index);
-
-        if (updateBookmark) {
-            // removeItem() also removed the bookmark from m_bookmarkManager in
-            // PlacesItemModel::onItemRemoved(). However for hidden items the
-            // bookmark should still be remembered, so readd it again:
-            m_bookmarkManager->root().addBookmark(hiddenBookmark);
-            m_bookmarkManager->root().moveBookmark(hiddenBookmark, previousBookmark);
-        }
-
-        m_bookmarkedItems.insert(newIndex, hiddenItem);
-    }
 }
 
 QString PlacesItemModel::internalMimeType() const
@@ -1061,7 +665,7 @@ int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const
     Q_ASSERT(item);
 
     int dropIndex = index;
-    const PlacesItem::GroupType type = item->groupType();
+    const QString group = item->group();
 
     const int itemCount = count();
     if (index < 0) {
@@ -1071,7 +675,7 @@ int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const
     // Search nearest previous item with the same group
     int previousIndex = -1;
     for (int i = dropIndex - 1; i >= 0; --i) {
-        if (placesItem(i)->groupType() == type) {
+        if (placesItem(i)->group() == group) {
             previousIndex = i;
             break;
         }
@@ -1080,7 +684,7 @@ int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const
     // Search nearest next item with the same group
     int nextIndex = -1;
     for (int i = dropIndex; i < count(); ++i) {
-        if (placesItem(i)->groupType() == type) {
+        if (placesItem(i)->group() == group) {
             nextIndex = i;
             break;
         }
@@ -1111,52 +715,13 @@ bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookm
     }
 }
 
-QUrl PlacesItemModel::createTimelineUrl(const QUrl& url)
+int PlacesItemModel::mapFromSource(const QModelIndex &index) const
 {
-    // TODO: Clarify with the Baloo-team whether it makes sense
-    // provide default-timeline-URLs like 'yesterday', 'this month'
-    // and 'last month'.
-    QUrl timelineUrl;
-
-    const QString path = url.toDisplayString(QUrl::PreferLocalFile);
-    if (path.endsWith(QLatin1String("yesterday"))) {
-        const QDate date = QDate::currentDate().addDays(-1);
-        const int year = date.year();
-        const int month = date.month();
-        const int day = date.day();
-        timelineUrl = QUrl("timeline:/" + timelineDateString(year, month) +
-              '/' + timelineDateString(year, month, day));
-    } else if (path.endsWith(QLatin1String("thismonth"))) {
-        const QDate date = QDate::currentDate();
-        timelineUrl = QUrl("timeline:/" + timelineDateString(date.year(), date.month()));
-    } else if (path.endsWith(QLatin1String("lastmonth"))) {
-        const QDate date = QDate::currentDate().addMonths(-1);
-        timelineUrl = QUrl("timeline:/" + timelineDateString(date.year(), date.month()));
-    } else {
-        Q_ASSERT(path.endsWith(QLatin1String("today")));
-        timelineUrl = url;
+    if (!index.isValid()) {
+        return -1;
     }
 
-    return timelineUrl;
-}
-
-QString PlacesItemModel::timelineDateString(int year, int month, int day)
-{
-    QString date = QString::number(year) + '-';
-    if (month < 10) {
-        date += '0';
-    }
-    date += QString::number(month);
-
-    if (day >= 1) {
-        date += '-';
-        if (day < 10) {
-            date += '0';
-        }
-        date += QString::number(day);
-    }
-
-    return date;
+    return m_indexMap.indexOf(index);
 }
 
 bool PlacesItemModel::isDir(int index) const
@@ -1165,39 +730,23 @@ bool PlacesItemModel::isDir(int index) const
     return true;
 }
 
-QUrl PlacesItemModel::createSearchUrl(const QUrl& url)
+QModelIndex PlacesItemModel::mapToSource(int row) const
 {
-    QUrl searchUrl;
-
-#ifdef HAVE_BALOO
-    const QString path = url.toDisplayString(QUrl::PreferLocalFile);
-    if (path.endsWith(QLatin1String("documents"))) {
-        searchUrl = searchUrlForType(QStringLiteral("Document"));
-    } else if (path.endsWith(QLatin1String("images"))) {
-        searchUrl = searchUrlForType(QStringLiteral("Image"));
-    } else if (path.endsWith(QLatin1String("audio"))) {
-        searchUrl = searchUrlForType(QStringLiteral("Audio"));
-    } else if (path.endsWith(QLatin1String("videos"))) {
-        searchUrl = searchUrlForType(QStringLiteral("Video"));
-    } else {
-        Q_ASSERT(false);
-    }
-#else
-    Q_UNUSED(url);
-#endif
-
-    return searchUrl;
+    return m_indexMap.value(row);
 }
 
-#ifdef HAVE_BALOO
-QUrl PlacesItemModel::searchUrlForType(const QString& type)
+PlacesItem *PlacesItemModel::itemFromBookmark(const KBookmark &bookmark) const
 {
-    Baloo::Query query;
-    query.addType(type);
-
-    return query.toSearchUrl();
+    const QString id = bookmarkId(bookmark);
+    for (int i = 0, iMax = count(); i < iMax; i++) {
+        PlacesItem *item = placesItem(i);
+        const KBookmark itemBookmark = item->bookmark();
+        if (bookmarkId(itemBookmark) == id) {
+            return item;
+        }
+    }
+    return nullptr;
 }
-#endif
 
 #ifdef PLACESITEMMODEL_DEBUG
 void PlacesItemModel::showModelState()
index 3b9307fe6d301f6414e6887da8f9ae629b36cc54..b701c8ea9b43fcabb235eb2ddeeb3f2e70a05d4a 100644 (file)
@@ -20,8 +20,6 @@
 #ifndef PLACESITEMMODEL_H
 #define PLACESITEMMODEL_H
 
-#include <config-baloo.h>
-
 #include <kitemviews/kstandarditemmodel.h>
 
 #include <QUrl>
@@ -33,9 +31,9 @@
 
 class KBookmark;
 class KBookmarkManager;
+class KFilePlacesModel;
 class PlacesItem;
 class QAction;
-class QTimer;
 
 // #define PLACESITEMMODEL_DEBUG
 
@@ -54,15 +52,22 @@ public:
     ~PlacesItemModel() override;
 
     /**
-     * @return A new instance of a places item with the given
-     *         attributes.
+     * @brief Create a new place entry in the bookmark file
+     * and add it to the model
      */
-    PlacesItem* createPlacesItem(const QString& text,
-                                 const QUrl& url,
-                                 const QString& iconName = QString());
+    void createPlacesItem(const QString& text,
+                          const QUrl& url,
+                          const QString& iconName = QString(),
+                          int after = -1);
 
     PlacesItem* placesItem(int index) const;
 
+    /**
+     * @brief Mark an item as hiden
+     * @param index of the item to be hidden
+     */
+    void hideItem(int index);
+
     /**
      * If set to true, all items that are marked as hidden
      * will be shown in the view. The items will
@@ -89,15 +94,6 @@ public:
      */
     int closestItem(const QUrl& url) const;
 
-    /**
-     * Appends the item \a item as last element of the group
-     * the item belongs to. If no item with the same group is
-     * present, the item gets appended as last element of the
-     * model. PlacesItemModel takes the ownership
-     * of the item.
-     */
-    void appendItemToGroup(PlacesItem* item);
-
     QAction* ejectAction(int index) const;
     QAction* teardownAction(int index) const;
 
@@ -126,11 +122,19 @@ public:
     void proceedWithTearDown();
 
     /**
-     * Saves the bookmarks and indicates to other applications that the
-     * state of the bookmarks has been changed. Is only called by the
-     * timeout of m_saveBookmarksTimer to prevent unnecessary savings.
+     * @brief Remove item from bookmark
+     *
+     * This function remove the index from bookmark file permanently
+     *
+     * @param index - the item to be removed
      */
-    void saveBookmarks();
+    void deleteItem(int index);
+
+    /**
+    * Force a sync on the bookmarks and indicates to other applications that the
+    * state of the bookmarks has been changed.
+    */
+    void refresh();
 
     bool isDir(int index) const override;
 signals:
@@ -145,22 +149,17 @@ protected:
     void onItemChanged(int index, const QSet<QByteArray>& changedRoles) override;
 
 private slots:
-    void slotDeviceAdded(const QString& udi);
-    void slotDeviceRemoved(const QString& udi);
     void slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData);
     void slotStorageSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString& udi);
-    void hideItem();
 
-    /**
-     * Updates the bookmarks from the model corresponding to the changed
-     * bookmarks stored by the bookmark-manager. Is called whenever the bookmarks
-     * have been changed by another application.
-     */
-    void updateBookmarks();
+    // source model control
+    void onSourceModelRowsInserted(const QModelIndex &parent, int first, int last);
+    void onSourceModelRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
+    void onSourceModelRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
+    void onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
+    void onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
 
 private:
-    struct SystemBookmarkData;
-
     /**
      * Loads the bookmarks from the bookmark-manager and creates items for
      * the model or moves hidden items to m_bookmarkedItems.
@@ -172,37 +171,7 @@ private:
      *         current application (e.g. bookmarks from other applications
      *         will be ignored).
      */
-    bool acceptBookmark(const KBookmark& bookmark,
-                        const QSet<QString>& availableDevices) const;
-
-    /**
-     * Creates a PlacesItem for a system-bookmark:
-     * - PlacesItem::isSystemItem() will return true
-     * - Default view-properties will be created for "Search For" items
-     * The item is not inserted to the model yet.
-     */
-    PlacesItem* createSystemPlacesItem(const SystemBookmarkData& data);
-
-    /**
-     * Creates system bookmarks that are shown per default and can
-     * only be hidden but not removed. The result will be stored
-     * in m_systemBookmarks.
-     */
-    void createSystemBookmarks();
-
-    void initializeAvailableDevices();
-
-    /**
-     * @param index Item index related to the model.
-     * @return      Corresponding index related to m_bookmarkedItems.
-     */
-    int bookmarkIndex(int index) const;
-
-    /**
-     * Marks the item with the index \a index as hidden and
-     * removes it from the model so that it gets invisible.
-     */
-    void hideItem(int index);
+    bool acceptBookmark(const KBookmark& bookmark) const;
 
     QString internalMimeType() const;
 
@@ -220,77 +189,41 @@ private:
     static bool equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2);
 
     /**
-     * @return URL using the timeline-protocol for searching (see convertedUrl()).
-     */
-    static QUrl createTimelineUrl(const QUrl& url);
-
-    /**
-     * Helper method for createTimelineUrl().
-     * @return String that represents a date-path in the format that
-     *         the timeline-protocol expects.
-     */
-    static QString timelineDateString(int year, int month, int day = 0);
-
-    /**
-     * @return URL that can be listed by KIO and results in searching
-     *         for a given term. The URL \a url represents a places-internal
-     *         URL like e.g. "search:/documents" (see convertedUrl()).
-     */
-    static QUrl createSearchUrl(const QUrl& url);
-
-#ifdef HAVE_BALOO
-    /**
-     * Helper method for createSearchUrl()
-     * @return URL that can be listed by KIO and results in searching
-     *         for the given type
+     * Appends the item \a item as last element of the group
+     * the item belongs to. If no item with the same group is
+     * present, the item gets appended as last element of the
+     * model. PlacesItemModel takes the ownership
+     * of the item.
      */
-    static QUrl searchUrlForType(const QString& type);
-#endif
+    void insertSortedItem(PlacesItem* item);
 
 #ifdef PLACESITEMMODEL_DEBUG
     void showModelState();
 #endif
 
+    PlacesItem *itemFromBookmark(const KBookmark &bookmark) const;
+
+    void addItemFromSourceModel(const QModelIndex &index);
+    void removeItemByIndex(const QModelIndex &mapToSource);
+
+    QString bookmarkId(const KBookmark &bookmark) const;
+    void initializeDefaultViewProperties() const;
+
+    int mapFromSource(const QModelIndex &index) const;
+    QModelIndex mapToSource(int row) const;
+
+    static void updateItem(PlacesItem *item, const QModelIndex &index);
+
 private:
-    bool m_fileIndexingEnabled;
     bool m_hiddenItemsShown;
 
-    QSet<QString> m_availableDevices;
-    Solid::Predicate m_predicate;
-    KBookmarkManager* m_bookmarkManager;
-
-    struct SystemBookmarkData
-    {
-        SystemBookmarkData(const QUrl& url,
-                           const QString& icon,
-                           const QString& text) :
-            url(url), icon(icon), text(text) {}
-        QUrl url;
-        QString icon;
-        QString text;
-    };
-
-    QList<SystemBookmarkData> m_systemBookmarks;
-    QHash<QUrl, int> m_systemBookmarksIndexes;
-
-    // Contains hidden and unhidden items that are stored as
-    // bookmark (the model itself only contains items that
-    // are shown in the view). If an entry is 0, then the
-    // places-item is part of the model. If an entry is not
-    // 0, the item is hidden and not part of the model.
-    QList<PlacesItem*> m_bookmarkedItems;
-
-    // Index of the hidden item that should be removed in
-    // removeHiddenItem(). The removing must be done
-    // asynchronously as in the scope of onItemChanged()
-    // removing an item is not allowed.
-    int m_hiddenItemToRemove;
-
     Solid::StorageAccess *m_deviceToTearDown;
 
-    QTimer* m_updateBookmarksTimer;
-
     QHash<QObject*, int> m_storageSetupInProgress;
+
+    QScopedPointer<KFilePlacesModel> m_sourceModel;
+
+    QVector<QPersistentModelIndex> m_indexMap;
 };
 
 #endif
index 9888290d9feb82d96b970cda2432b5c2aa2da588..d8eab7dd928a443a078d710c4f333e995bba394a 100644 (file)
@@ -34,6 +34,7 @@
 #include <KIO/DropJob>
 #include <KIO/EmptyTrashJob>
 #include <KIO/JobUiDelegate>
+#include <KFilePlacesModel>
 #include <KJobWidgets>
 #include <KLocalizedString>
 #include <KIconLoader>
@@ -239,13 +240,11 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
             if (action == editAction) {
                 editEntry(index);
             } else if (action == removeAction) {
-                m_model->removeItem(index);
-                m_model->saveBookmarks();
+                m_model->deleteItem(index);
             } else if (action == hideAction) {
                 item->setHidden(hideAction->isChecked());
-                m_model->saveBookmarks();
             } else if (action == openInNewWindowAction) {
-                Dolphin::openNewWindow({PlacesItemModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this);
+                Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this);
             } else if (action == openInNewTabAction) {
                 // TriggerItem does set up the storage first and then it will
                 // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton.
@@ -334,8 +333,8 @@ void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* even
     }
 
     const PlacesItem* destItem = m_model->placesItem(index);
-    const PlacesItem::GroupType group = destItem->groupType();
-    if (group == PlacesItem::SearchForType || group == PlacesItem::RecentlySavedType) {
+
+    if (destItem->isSearchOrTimelineUrl()) {
         return;
     }
 
@@ -396,7 +395,6 @@ void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success)
 void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
 {
     m_model->dropMimeDataBefore(index, event->mimeData());
-    m_model->saveBookmarks();
 }
 
 void PlacesPanel::slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent)
@@ -456,9 +454,7 @@ void PlacesPanel::addEntry()
     dialog->setAllowGlobal(true);
     dialog->setUrl(url);
     if (dialog->exec() == QDialog::Accepted) {
-        PlacesItem* item = m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon());
-        m_model->appendItemToGroup(item);
-        m_model->saveBookmarks();
+        m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon());
     }
 
     delete dialog;
@@ -480,7 +476,7 @@ void PlacesPanel::editEntry(int index)
             oldItem->setText(dialog->text());
             oldItem->setUrl(dialog->url());
             oldItem->setIcon(dialog->icon());
-            m_model->saveBookmarks();
+            m_model->refresh();
         }
     }
 
@@ -517,9 +513,9 @@ void PlacesPanel::triggerItem(int index, Qt::MouseButton button)
         const QUrl url = m_model->data(index).value("url").toUrl();
         if (!url.isEmpty()) {
             if (button == Qt::MiddleButton) {
-                emit placeMiddleClicked(PlacesItemModel::convertedUrl(url));
+                emit placeMiddleClicked(KFilePlacesModel::convertedUrl(url));
             } else {
-                emit placeActivated(PlacesItemModel::convertedUrl(url));
+                emit placeActivated(KFilePlacesModel::convertedUrl(url));
             }
         }
     }
index 60678eb45bff9f233e7dc5ae55d5a2d50e55929f..44de97aa02ed14b99bd9e699a0cf0a0fcd713d3e 100644 (file)
@@ -310,11 +310,9 @@ void DolphinSearchBox::slotSearchSaved()
     if (searchURL.isValid()) {
         PlacesItemModel model;
         const QString label = i18n("Search for %1 in %2", text(), searchPath().fileName());
-        PlacesItem* item = model.createPlacesItem(label,
-                                                  searchURL,
-                                                  QStringLiteral("folder-saved-search-symbolic"));
-        model.appendItemToGroup(item);
-        model.saveBookmarks();
+        model.createPlacesItem(label,
+                               searchURL,
+                               QStringLiteral("folder-saved-search-symbolic"));
     }
 }
 
index 5eb65e076dd914e48a6e59fb6073f1894f027658..7c4cf308ba6a637cffed3a24388d692cb3e2eb0d 100644 (file)
 #include <QStandardPaths>
 #include <QAction>
 #include <QDBusInterface>
+#include <QUrlQuery>
 
 #include <KBookmarkManager>
 #include <KConfig>
 #include <KConfigGroup>
 #include <KAboutData>
+#include <KFilePlacesModel>
 
 #include "panels/places/placesitemmodel.h"
 #include "panels/places/placesitem.h"
@@ -37,7 +39,7 @@
 #include "kitemviews/kitemrange.h"
 
 Q_DECLARE_METATYPE(KItemRangeList)
-Q_DECLARE_METATYPE(PlacesItem::GroupType)
+Q_DECLARE_METATYPE(KItemRange)
 
 #ifdef Q_OS_WIN
 //c:\ as root for windows
@@ -63,8 +65,8 @@ private slots:
     void cleanupTestCase();
 
     void testModelSort();
-    void testModelMove();
     void testGroups();
+    void testDeletePlace();
     void testPlaceItem_data();
     void testPlaceItem();
     void testTearDownDevice();
@@ -76,30 +78,37 @@ private slots:
     void testEditBookmark();
     void testEditAfterCreation();
     void testEditMetadata();
+    void testRefresh();
+    void testIcons_data();
+    void testIcons();
+    void testDragAndDrop();
 
 private:
     PlacesItemModel* m_model;
+    QSet<int> m_tobeRemoved;
     QMap<QString, QDBusInterface *> m_interfacesMap;
 
     void setBalooEnabled(bool enabled);
     int indexOf(const QUrl &url);
     QDBusInterface *fakeManager();
     QDBusInterface *fakeDevice(const QString &udi);
-    QStringList placesUrls() const;
+    QStringList placesUrls(PlacesItemModel *model = nullptr) const;
     QStringList initialUrls() const;
     void createPlaceItem(const QString &text, const QUrl &url, const QString &icon);
+    void removePlaceAfter(int index);
+    void cancelPlaceRemoval(int index);
+    void removeTestUserData();
+    QMimeData *createMimeData(const QList<int> &indexes) const;
 };
 
-#define CHECK_PLACES_URLS(urls)                                              \
-    QStringList tmp(urls);                                                   \
-    QStringList places = placesUrls();                                       \
-    while(!places.isEmpty()) {                                               \
-        tmp.removeOne(places.takeFirst());                                   \
-    }                                                                        \
-    if (!tmp.isEmpty()) {                                                    \
-        qWarning() << "Expected:" << urls;                                   \
-        qWarning() << "Got:" << places;                                      \
-        QCOMPARE(places, urls);                                              \
+#define CHECK_PLACES_URLS(urls)                                             \
+    {                                                                       \
+        QStringList places = placesUrls();                                  \
+        if (places != urls) {                                               \
+            qWarning() << "Expected:" << urls;                              \
+            qWarning() << "Got:" << places;                                 \
+            QCOMPARE(places, urls);                                         \
+        }                                                                   \
     }
 
 void PlacesItemModelTest::setBalooEnabled(bool enabled)
@@ -137,23 +146,78 @@ QDBusInterface *PlacesItemModelTest::fakeDevice(const QString &udi)
     return iface;
 }
 
-QStringList PlacesItemModelTest::placesUrls() const
+QStringList PlacesItemModelTest::placesUrls(PlacesItemModel *model) const
 {
     QStringList urls;
-    for (int row = 0; row < m_model->count(); ++row) {
-        urls << m_model->placesItem(row)->url().toDisplayString(QUrl::PreferLocalFile);
+    if (!model) {
+        model = m_model;
+    }
+
+    for (int row = 0; row < model->count(); ++row) {
+        urls << model->placesItem(row)->url().toDisplayString(QUrl::PreferLocalFile);
+    }
+    return urls;
+}
+
+QStringList PlacesItemModelTest::initialUrls() const
+{
+    static QStringList urls;
+    if (urls.isEmpty()) {
+        urls << QDir::homePath() << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/")
+             << QStringLiteral("remote:/")
+             << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("timeline:/thismonth") << QStringLiteral("timeline:/lastmonth")
+             << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
+             << QStringLiteral("/media/nfs") << QStringLiteral("/foreign")
+             << QStringLiteral("/media/floppy0") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/cdrom");
     }
     return urls;
 }
 
 void PlacesItemModelTest::createPlaceItem(const QString &text, const QUrl &url, const QString &icon)
 {
-    PlacesItem *item = m_model->createPlacesItem(text,
-                                                 url,
-                                                 icon);
-    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-    m_model->appendItemToGroup(item);
-    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    m_model->createPlacesItem(text, url, icon);
+}
+
+void PlacesItemModelTest::removePlaceAfter(int index)
+{
+    m_tobeRemoved.insert(index);
+}
+
+void PlacesItemModelTest::cancelPlaceRemoval(int index)
+{
+    m_tobeRemoved.remove(index);
+}
+
+void PlacesItemModelTest::removeTestUserData()
+{
+    // user hardcoded path to avoid removal of any user personal data
+    QDir dir(QStringLiteral("/home/renato/.qttest/share/placesitemmodeltest"));
+    if (dir.exists()) {
+        QVERIFY(dir.removeRecursively());
+    }
+}
+
+QMimeData *PlacesItemModelTest::createMimeData(const QList<int> &indexes) const
+{
+    QByteArray itemData;
+    QDataStream stream(&itemData, QIODevice::WriteOnly);
+    QList<QUrl> urls;
+
+    for (int index : indexes) {
+        const QUrl itemUrl = m_model->placesItem(index)->url();
+        if (itemUrl.isValid()) {
+            urls << itemUrl;
+        }
+        stream << index;
+    }
+
+    QMimeData* mimeData = new QMimeData();
+    mimeData->setUrls(urls);
+    // copied from PlacesItemModel::internalMimeType()
+    const QString internalMimeType = "application/x-dolphinplacesmodel-" +
+            QString::number((qptrdiff)m_model);
+    mimeData->setData(internalMimeType, itemData);
+    return mimeData;
 }
 
 void PlacesItemModelTest::init()
@@ -166,13 +230,22 @@ void PlacesItemModelTest::init()
 
 void PlacesItemModelTest::cleanup()
 {
+    for (int i : m_tobeRemoved) {
+        int before = m_model->count();
+        m_model->deleteItem(i);
+        QTRY_COMPARE(m_model->count(), before - 1);
+    }
+    m_tobeRemoved.clear();
     delete m_model;
     m_model = nullptr;
+    removeTestUserData();
 }
 
 void PlacesItemModelTest::initTestCase()
 {
     QStandardPaths::setTestModeEnabled(true);
+    // remove test user data
+    removeTestUserData();
 
     const QString fakeHw = QFINDTESTDATA("data/fakecomputer.xml");
     QVERIFY(!fakeHw.isEmpty());
@@ -186,24 +259,16 @@ void PlacesItemModelTest::initTestCase()
     }
 
     qRegisterMetaType<KItemRangeList>();
+    qRegisterMetaType<KItemRange>();
 }
 
 void PlacesItemModelTest::cleanupTestCase()
 {
     qDeleteAll(m_interfacesMap);
     QFile::remove(bookmarksFile());
-}
-
-QStringList PlacesItemModelTest::initialUrls() const
-{
-    QStringList urls;
 
-    urls << QDir::homePath() << QStringLiteral("remote:/") << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/")
-            << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("timeline:/thismonth") << QStringLiteral("timeline:/lastmonth")
-            << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
-            << QStringLiteral("/media/cdrom") << QStringLiteral("/foreign") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/nfs") << QStringLiteral("/media/floppy0");
-
-    return urls;
+    // Remove any previous properties file
+    removeTestUserData();
 }
 
 void PlacesItemModelTest::testModelSort()
@@ -211,36 +276,29 @@ void PlacesItemModelTest::testModelSort()
     CHECK_PLACES_URLS(initialUrls());
 }
 
-void PlacesItemModelTest::testModelMove()
-{
-    QStringList urls = initialUrls();
-    KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile(), QStringLiteral("kfilePlaces"));
-    KBookmarkGroup root = bookmarkManager->root();
-    KBookmark systemRoot = m_model->placesItem(1)->bookmark();
-    KBookmark last = m_model->placesItem(m_model->count() - 1)->bookmark();
-
-    // try to move the "root" path to the end of the list
-    root.moveBookmark(systemRoot, last);
-    bookmarkManager->emitChanged(root);
-
-    // make sure that the items still grouped and the "root" item was moved to the end of places group instead
-    urls.move(1, 2);
-    CHECK_PLACES_URLS(urls);
-}
-
 void PlacesItemModelTest::testGroups()
 {
     const auto groups = m_model->groups();
 
-    QCOMPARE(groups.size(), 4);
+    QCOMPARE(groups.size(), 6);
+
     QCOMPARE(groups.at(0).first, 0);
     QCOMPARE(groups.at(0).second.toString(), QStringLiteral("Places"));
-    QCOMPARE(groups.at(1).first, 4);
-    QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Recently Saved"));
-    QCOMPARE(groups.at(2).first, 8);
-    QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Search For"));
-    QCOMPARE(groups.at(3).first, 12);
-    QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Devices"));
+
+    QCOMPARE(groups.at(1).first, 3);
+    QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Remote"));
+
+    QCOMPARE(groups.at(2).first, 4);
+    QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Recently Saved"));
+
+    QCOMPARE(groups.at(3).first, 8);
+    QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Search For"));
+
+    QCOMPARE(groups.at(4).first, 12);
+    QCOMPARE(groups.at(4).second.toString(), QStringLiteral("Devices"));
+
+    QCOMPARE(groups.at(5).first, 14);
+    QCOMPARE(groups.at(5).second.toString(), QStringLiteral("Removable Devices"));
 }
 
 void PlacesItemModelTest::testPlaceItem_data()
@@ -248,20 +306,20 @@ void PlacesItemModelTest::testPlaceItem_data()
     QTest::addColumn<QUrl>("url");
     QTest::addColumn<bool>("expectedIsHidden");
     QTest::addColumn<bool>("expectedIsSystemItem");
-    QTest::addColumn<PlacesItem::GroupType>("expectedGroupType");
+    QTest::addColumn<QString>("expectedGroup");
     QTest::addColumn<bool>("expectedStorageSetupNeeded");
 
     // places
-    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << PlacesItem::PlacesType << false;
+    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << QStringLiteral("Places") << false;
 
     // baloo -search
-    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << PlacesItem::SearchForType << false;
+    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << QStringLiteral("Search For") << false;
 
     // baloo - timeline
-    QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << false << true << PlacesItem::RecentlySavedType << false;
+    QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << false << true << QStringLiteral("Recently Saved") << false;
 
     // devices
-    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << PlacesItem::DevicesType << false;
+    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << QStringLiteral("Removable Devices") << false;
 }
 
 void PlacesItemModelTest::testPlaceItem()
@@ -269,7 +327,7 @@ void PlacesItemModelTest::testPlaceItem()
     QFETCH(QUrl, url);
     QFETCH(bool, expectedIsHidden);
     QFETCH(bool, expectedIsSystemItem);
-    QFETCH(PlacesItem::GroupType, expectedGroupType);
+    QFETCH(QString, expectedGroup);
     QFETCH(bool, expectedStorageSetupNeeded);
 
     const int index = indexOf(url);
@@ -277,10 +335,38 @@ void PlacesItemModelTest::testPlaceItem()
     QCOMPARE(item->url(), url);
     QCOMPARE(item->isHidden(), expectedIsHidden);
     QCOMPARE(item->isSystemItem(), expectedIsSystemItem);
-    QCOMPARE(item->groupType(), expectedGroupType);
+    QCOMPARE(item->group(), expectedGroup);
     QCOMPARE(item->storageSetupNeeded(), expectedStorageSetupNeeded);
 }
 
+void PlacesItemModelTest::testDeletePlace()
+{
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QStringList urls = initialUrls();
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
+
+    PlacesItemModel *model = new PlacesItemModel();
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    urls.insert(3, tempUrl.toLocalFile());
+
+    // check if the new entry was created
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    CHECK_PLACES_URLS(urls);
+    QTRY_COMPARE(model->count(), m_model->count());
+
+    // delete item
+    m_model->deleteItem(3);
+
+    // make sure that the new item is removed
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+    QTRY_COMPARE(m_model->count(), 17);
+    CHECK_PLACES_URLS(initialUrls());
+    QTRY_COMPARE(model->count(), m_model->count());
+}
+
 void PlacesItemModelTest::testTearDownDevice()
 {
     const QUrl mediaUrl = QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
@@ -351,7 +437,7 @@ void PlacesItemModelTest::testDefaultViewProperties()
     QFETCH(bool, expectedPreviewShow);
     QFETCH(QList<QByteArray>, expectedVisibleRole);
 
-    ViewProperties properties(m_model->convertedUrl(url));
+    ViewProperties properties(KFilePlacesModel::convertedUrl(url));
     QCOMPARE(properties.viewMode(), expectedViewMode);
     QCOMPARE(properties.previewsShown(), expectedPreviewShow);
     QCOMPARE(properties.visibleRoles(), expectedVisibleRole);
@@ -363,6 +449,8 @@ void PlacesItemModelTest::testClear()
     m_model->clear();
     QCOMPARE(m_model->count(), 0);
     QCOMPARE(m_model->hiddenCount(), 0);
+    m_model->refresh();
+    QTRY_COMPARE(m_model->count(), 17);
 }
 
 void PlacesItemModelTest::testHideItem()
@@ -420,115 +508,254 @@ void PlacesItemModelTest::testSystemItems()
         QCOMPARE(m_model->placesItem(r)->isSystemItem(), !m_model->placesItem(r)->device().isValid());
     }
 
-    // create a new entry (non system item)
-    PlacesItem *item = m_model->createPlacesItem(QStringLiteral("Temporary Dir"),
-                                                 QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)),
-                                                 QString());
-
     QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-    m_model->appendItemToGroup(item);
+
+    // create a new entry (non system item)
+    createPlaceItem(QStringLiteral("Temporary Dir"),  QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
 
     // check if the new entry was created
     QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+
+    // make sure the new place get removed
+    removePlaceAfter(3);
+
     QList<QVariant> args = itemsInsertedSpy.takeFirst();
     KItemRangeList range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 3);
     QCOMPARE(range.first().count, 1);
-    QVERIFY(!m_model->placesItem(4)->isSystemItem());
+    QVERIFY(!m_model->placesItem(3)->isSystemItem());
     QCOMPARE(m_model->count(), 18);
 
-    // remove new entry
+    QTest::qWait(300);
+    // check if the removal signal is correct
     QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
+    m_model->deleteItem(3);
     QTRY_COMPARE(itemsRemovedSpy.count(), 1);
     args = itemsRemovedSpy.takeFirst();
     range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 3);
     QCOMPARE(range.first().count, 1);
     QTRY_COMPARE(m_model->count(), 17);
+
+    //cancel removal (it was removed above)
+    cancelPlaceRemoval(3);
 }
 
 void PlacesItemModelTest::testEditBookmark()
 {
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
     QScopedPointer<PlacesItemModel> other(new PlacesItemModel());
 
     createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
 
+    // make sure that the new item will be removed later
+    removePlaceAfter(3);
+
     QSignalSpy itemsChangedSply(m_model, &PlacesItemModel::itemsChanged);
-    m_model->item(4)->setText(QStringLiteral("Renamed place"));
-    m_model->saveBookmarks();
+
+    // modify place text
+    m_model->item(3)->setText(QStringLiteral("Renamed place"));
+    m_model->refresh();
+
+    // check if the correct signal was fired
     QTRY_COMPARE(itemsChangedSply.count(), 1);
     QList<QVariant> args = itemsChangedSply.takeFirst();
     KItemRangeList range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 3);
     QCOMPARE(range.first().count, 1);
     QSet<QByteArray> roles = args.at(1).value<QSet<QByteArray> >();
     QCOMPARE(roles.size(), 1);
     QCOMPARE(*roles.begin(), QByteArrayLiteral("text"));
-    QCOMPARE(m_model->item(4)->text(), QStringLiteral("Renamed place"));
+    QCOMPARE(m_model->item(3)->text(), QStringLiteral("Renamed place"));
 
     // check if the item was updated in the other model
-    QTRY_COMPARE(other->item(4)->text(), QStringLiteral("Renamed place"));
-
-    // remove new entry
-    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
-    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
-    args = itemsRemovedSpy.takeFirst();
-    range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
-    QCOMPARE(range.first().count, 1);
-    QTRY_COMPARE(m_model->count(), 17);
+    QTRY_COMPARE(other->item(3)->text(), QStringLiteral("Renamed place"));
 }
 
 void PlacesItemModelTest::testEditAfterCreation()
 {
-    createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
 
     PlacesItemModel *model = new PlacesItemModel();
     QTRY_COMPARE(model->count(), m_model->count());
 
-    PlacesItem *item = m_model->placesItem(4);
+    // make sure that the new item will be removed later
+    removePlaceAfter(3);
+
+    // modify place text
+    PlacesItem *item = m_model->placesItem(3);
     item->setText(QStringLiteral("Renamed place"));
-    m_model->saveBookmarks();
+    m_model->refresh();
 
+    // check if the second model got the changes
     QTRY_COMPARE(model->count(), m_model->count());
-    QTRY_COMPARE(model->placesItem(4)->text(), m_model->placesItem(4)->text());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
-                 m_model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
-    QTRY_COMPARE(model->placesItem(4)->icon(), m_model->placesItem(4)->icon());
-    QTRY_COMPARE(model->placesItem(4)->url(), m_model->placesItem(4)->url());
-
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
-    QTRY_COMPARE(model->count(), m_model->count());
+    QTRY_COMPARE(model->placesItem(3)->text(), m_model->placesItem(3)->text());
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+                 m_model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
+    QTRY_COMPARE(model->placesItem(3)->icon(), m_model->placesItem(3)->icon());
+    QTRY_COMPARE(model->placesItem(3)->url(), m_model->placesItem(3)->url());
 }
 
 void PlacesItemModelTest::testEditMetadata()
 {
-    createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
 
+    // check if the new entry was created
     PlacesItemModel *model = new PlacesItemModel();
     QTRY_COMPARE(model->count(), m_model->count());
 
-    PlacesItem *item = m_model->placesItem(4);
+    // make sure that the new item will be removed later
+    removePlaceAfter(3);
+
+    // modify place metadata
+    PlacesItem *item = m_model->placesItem(3);
     item->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
-    m_model->saveBookmarks();
+    m_model->refresh();
 
-    QTRY_COMPARE(model->count(), m_model->count());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+    // check if the place was modified in both models
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
                  KAboutData::applicationData().componentName());
-    QTRY_COMPARE(model->placesItem(4)->text(), m_model->placesItem(4)->text());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
-                 m_model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
-    QTRY_COMPARE(model->placesItem(4)->icon(), m_model->placesItem(4)->icon());
-    QTRY_COMPARE(model->placesItem(4)->url(), m_model->placesItem(4)->url());
-
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
+    QTRY_COMPARE(model->placesItem(3)->text(), m_model->placesItem(3)->text());
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+                 m_model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
+    QTRY_COMPARE(model->placesItem(3)->icon(), m_model->placesItem(3)->icon());
+    QTRY_COMPARE(model->placesItem(3)->url(), m_model->placesItem(3)->url());
+}
+
+void PlacesItemModelTest::testRefresh()
+{
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+
+    PlacesItemModel *model = new PlacesItemModel();
     QTRY_COMPARE(model->count(), m_model->count());
+
+    // make sure that the new item will be removed later
+    removePlaceAfter(3);
+
+    PlacesItem *item = m_model->placesItem(3);
+    PlacesItem *sameItem = model->placesItem(3);
+    QCOMPARE(item->text(), sameItem->text());
+
+    // modify place text
+    item->setText(QStringLiteral("Renamed place"));
+
+    // item from another model is not affected at the moment
+    QVERIFY(item->text() != sameItem->text());
+
+    // propagate change
+    m_model->refresh();
+
+    // item must be equal
+    QTRY_COMPARE(item->text(), sameItem->text());
+}
+
+void PlacesItemModelTest::testIcons_data()
+{
+    QTest::addColumn<QUrl>("url");
+    QTest::addColumn<QString>("expectedIconName");
+
+    // places
+    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << QStringLiteral("user-home");
+
+    // baloo -search
+    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << QStringLiteral("folder-text");
+
+    // baloo - timeline
+    QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << QStringLiteral("view-calendar-month");
+
+    // devices
+    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << QStringLiteral("blockdevice");
+}
+
+void PlacesItemModelTest::testIcons()
+{
+    QFETCH(QUrl, url);
+    QFETCH(QString, expectedIconName);
+
+    PlacesItem *item = m_model->placesItem(indexOf(url));
+    QCOMPARE(item->icon(), expectedIconName);
+
+    for (int r = 0; r < m_model->count(); r++) {
+        QVERIFY(!m_model->placesItem(r)->icon().isEmpty());
+    }
+}
+
+void PlacesItemModelTest::testDragAndDrop()
+{
+    QList<QVariant> args;
+    KItemRangeList range;
+    QStringList urls = initialUrls();
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
+
+    CHECK_PLACES_URLS(initialUrls());
+    // Move the KDE_ROOT_PATH at the end of the places list will case it to be moved to the end of the places group
+    QMimeData *dropData = createMimeData(QList<int>() << 1);
+    m_model->dropMimeDataBefore(m_model->count() - 1, dropData);
+    urls.move(1, 2);
+    delete dropData;
+
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+
+    // remove item from actual position
+    args = itemsRemovedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 1);
+
+    // insert intem in his group
+    args = itemsInsertedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 2);
+
+    CHECK_PLACES_URLS(urls);
+
+    itemsInsertedSpy.clear();
+    itemsRemovedSpy.clear();
+
+    // Move the KDE_ROOT_PATH to his original position
+    dropData = createMimeData(QList<int>() << 2);
+    m_model->dropMimeDataBefore(1, dropData);
+    urls.move(2, 1);
+    delete dropData;
+
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+
+    // remove item from actual position
+    args = itemsRemovedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 2);
+
+    // insert intem in the requested position
+    args = itemsInsertedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 1);
+
+    CHECK_PLACES_URLS(urls);
 }
 
 QTEST_MAIN(PlacesItemModelTest)