diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -306,9 +306,7 @@ 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 @@ } else { icon = KIO::iconNameForUrl(url); } - PlacesItem* item = model.createPlacesItem(container->placesText(), url, icon); - model.appendItemToGroup(item); - model.saveBookmarks(); + model.createPlacesItem(container->placesText(), url, icon); } } } diff --git a/src/panels/places/placesitem.cpp b/src/panels/places/placesitem.cpp --- a/src/panels/places/placesitem.cpp +++ b/src/panels/places/placesitem.cpp @@ -130,7 +130,6 @@ delete m_disc; delete m_mtp; - const QString udi = bookmark.metaDataItem(QStringLiteral("UDI")); if (udi.isEmpty()) { setIcon(bookmark.icon()); @@ -160,6 +159,9 @@ } setHidden(bookmark.metaDataItem(QStringLiteral("IsHidden")) == QLatin1String("true")); + if (type != DevicesType) { + setSystemItem(bookmark.metaDataItem(QStringLiteral("isSystemItem")) == QLatin1String("true")); + } } KBookmark PlacesItem::bookmark() const diff --git a/src/panels/places/placesitemmodel.h b/src/panels/places/placesitemmodel.h --- a/src/panels/places/placesitemmodel.h +++ b/src/panels/places/placesitemmodel.h @@ -33,9 +33,9 @@ class KBookmark; class KBookmarkManager; +class KFilePlacesModel; class PlacesItem; class QAction; -class QTimer; // #define PLACESITEMMODEL_DEBUG @@ -57,12 +57,19 @@ * @return A new instance of a places item with the given * attributes. */ - 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 hiden + */ + 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 +96,6 @@ */ 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 +124,19 @@ 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(); signals: void errorMessage(const QString& message); @@ -144,18 +150,14 @@ virtual void onItemChanged(int index, const QSet& changedRoles) Q_DECL_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 onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row); + void onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); private: struct SystemBookmarkData; @@ -171,37 +173,7 @@ * current application (e.g. bookmarks from other applications * will be ignored). */ - bool acceptBookmark(const KBookmark& bookmark, - const QSet& 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; @@ -237,6 +209,15 @@ */ static QUrl createSearchUrl(const QUrl& url); + /** + * 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 insertSortedItem(PlacesItem* item); + #ifdef HAVE_BALOO /** * Helper method for createSearchUrl() @@ -250,14 +231,19 @@ void showModelState(); #endif + int mapFromSource(const QModelIndex &index) const; + QModelIndex mapToSource(int row) const; + + PlacesItem *itemFromBookmark(const KBookmark &bookmark) const; + + void addItemFromSourceModel(const QModelIndex &index); + void removeItemByIndex(const QModelIndex &mapToSource); + + QString bookmarkId(const KBookmark &bookmark) const; + private: - bool m_fileIndexingEnabled; bool m_hiddenItemsShown; - QSet m_availableDevices; - Solid::Predicate m_predicate; - KBookmarkManager* m_bookmarkManager; - struct SystemBookmarkData { SystemBookmarkData(const QUrl& url, @@ -269,27 +255,11 @@ QString text; }; - QList m_systemBookmarks; - QHash 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 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 m_storageSetupInProgress; + + KFilePlacesModel *m_sourceModel; }; #endif diff --git a/src/panels/places/placesitemmodel.cpp b/src/panels/places/placesitemmodel.cpp --- a/src/panels/places/placesitemmodel.cpp +++ b/src/panels/places/placesitemmodel.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -69,53 +70,31 @@ PlacesItemModel::PlacesItemModel(QObject* parent) : KStandardItemModel(parent), - m_fileIndexingEnabled(false), m_hiddenItemsShown(false), - m_availableDevices(), - m_predicate(), - m_bookmarkManager(0), - m_systemBookmarks(), - m_systemBookmarksIndexes(), - m_bookmarkedItems(), - m_hiddenItemToRemove(-1), m_deviceToTearDown(0), - m_updateBookmarksTimer(0), - 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(); - 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(&QTimer::start)); + connect(m_sourceModel, &KFilePlacesModel::rowsInserted, this, &PlacesItemModel::onSourceModelRowsInserted); + connect(m_sourceModel, &KFilePlacesModel::rowsAboutToBeRemoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeRemoved); + connect(m_sourceModel, &KFilePlacesModel::dataChanged, this, &PlacesItemModel::onSourceModelDataChanged); + connect(m_sourceModel, &KFilePlacesModel::rowsMoved, this, &PlacesItemModel::onSourceModelRowsMoved); } PlacesItemModel::~PlacesItemModel() { - qDeleteAll(m_bookmarkedItems); - m_bookmarkedItems.clear(); + delete m_sourceModel; + m_sourceModel = nullptr; } -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 +104,7 @@ 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 +116,18 @@ m_hiddenItemsShown = show; if (show) { - // Move all items that are part of m_bookmarkedItems to the model. - QList itemsToInsert; - QList 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; r < m_sourceModel->rowCount(); r++) { + const QModelIndex index = m_sourceModel->index(r, 0); + if (!m_sourceModel->isHidden(index)) { + continue; } - ++modelIndex; - } - - // 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]); + addItemFromSourceModel(index); } - - 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; r < m_sourceModel->rowCount(); r++) { + const QModelIndex index = m_sourceModel->index(r, 0); + if (m_sourceModel->isHidden(index)) { + removeItemByIndex(index); } } } @@ -199,52 +145,66 @@ int PlacesItemModel::closestItem(const QUrl& url) const { - int foundIndex = -1; - int maxLength = 0; + return mapFromSource(m_sourceModel->closestItem(url)); +} - 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; +// look for the coorrect position for the item based on source model +void PlacesItemModel::insertSortedItem(PlacesItem* item) +{ + if (!item) { + return; + } + + const KBookmark iBookmark = item->bookmark(); + int pos = 0; + for(int r = 0; r < m_sourceModel->rowCount(); r++) { + const QModelIndex sIndex = m_sourceModel->index(r, 0); + + if (bookmarkId(m_sourceModel->bookmarkForIndex(sIndex)) == bookmarkId(iBookmark)) { break; - } else if (itemUrl.isParentOf(url)) { - const int length = itemUrl.path().length(); - if (length > maxLength) { - foundIndex = i; - maxLength = length; - } + } + + if (!m_sourceModel->isHidden(sIndex)) { + pos++; } } + insertItem(pos, item); +} - return foundIndex; +void PlacesItemModel::onItemInserted(int index) +{ + KStandardItemModel::onItemInserted(index); +#ifdef PLACESITEMMODEL_DEBUG + qCDebug(DolphinDebug) << "Inserted item" << index; + showModelState(); +#endif } -void PlacesItemModel::appendItemToGroup(PlacesItem* item) +void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem) { - if (!item) { - return; - } + KStandardItemModel::onItemRemoved(index, removedItem); +#ifdef PLACESITEMMODEL_DEBUG + qCDebug(DolphinDebug) << "Removed item" << index; + showModelState(); +#endif +} - int i = 0; - while (i < count() && placesItem(i)->group() != item->group()) { - ++i; - } +void PlacesItemModel::onItemChanged(int index, const QSet& changedRoles) +{ + const QModelIndex sIndex = mapToSource(index); + const PlacesItem *changedItem = placesItem(mapFromSource(sIndex)); - bool inserted = false; - while (!inserted && i < count()) { - if (placesItem(i)->group() != item->group()) { - insertItem(i, item); - inserted = true; - } - ++i; + if (!changedItem || !sIndex.isValid()) { + qWarning() << "invalid item changed signal"; + return; } - if (!inserted) { - appendItem(item); + if (changedRoles.contains("isHidden")) { + m_sourceModel->setPlaceHidden(sIndex, changedItem->isHidden()); } -} + KStandardItemModel::onItemChanged(index, changedRoles); +} QAction* PlacesItemModel::ejectAction(int index) const { @@ -440,9 +400,7 @@ continue; } - PlacesItem* newItem = createPlacesItem(text, url); - const int dropIndex = groupedDropIndex(index, newItem); - insertItem(dropIndex, newItem); + createPlacesItem(text, url, QString(), qMax(0, index - 1)); } } } @@ -459,131 +417,37 @@ return newUrl; } -void PlacesItemModel::onItemInserted(int index) +void PlacesItemModel::addItemFromSourceModel(const QModelIndex &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); + PlacesItem *item = new PlacesItem(bookmark); + insertSortedItem(item); -void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem) -{ - PlacesItem* placesItem = dynamic_cast(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& 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(); - } + QString id = bookmarkId(m_sourceModel->bookmarkForIndex(sourceIndex)); - m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark); - } - - if (changedRoles.contains("isHidden")) { - if (!m_hiddenItemsShown && changedItem->isHidden()) { - m_hiddenItemToRemove = index; - QTimer::singleShot(0, this, static_cast(&PlacesItemModel::hideItem)); + for (int i = 0; i < count(); ++i) { + if (bookmarkId(placesItem(i)->bookmark()) == id) { + removeItem(i); + return; } } } -void PlacesItemModel::slotDeviceAdded(const QString& udi) +QString PlacesItemModel::bookmarkId(const KBookmark &bookmark) const { - const Solid::Device device(udi); - - if (!m_predicate.matches(device)) { - return; - } - - 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) -{ - 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; - } + QString id = bookmark.metaDataItem(QStringLiteral("UDI")); + if (id.isEmpty()) { + id = bookmark.metaDataItem(QStringLiteral("ID")); } + return id; } void PlacesItemModel::slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData) @@ -622,199 +486,77 @@ } } -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); - } - } - } - } - - newBookmark = root.next(newBookmark); + for (int i = first; i <= last; i++) { + const QModelIndex sIndex = m_sourceModel->index(i, 0, parent); + removeItem(mapFromSource(sIndex)); } +} - // 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 == 0); - 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); - } +void PlacesItemModel::onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) +{ + Q_UNUSED(destination); - if (hasBeenRemoved) { - if (m_bookmarkedItems[i]) { - delete m_bookmarkedItems[i]; - m_bookmarkedItems.removeAt(i); - } else { - removeItem(modelIndex); - --modelIndex; - } - } + for(int r = start; r <= end; r++) { + const QModelIndex sIndex = m_sourceModel->index(row + r, 0, parent); + const KBookmark sourceBk = m_sourceModel->bookmarkForIndex(sIndex); + const PlacesItem *item = itemFromBookmark(sourceBk); - if (itemIsPartOfModel) { - ++modelIndex; + if (item) { + removeItem(index(item)); + addItemFromSourceModel(sIndex); } } } -void PlacesItemModel::saveBookmarks() -{ - m_bookmarkManager->emitChanged(m_bookmarkManager->root()); -} - -void PlacesItemModel::loadBookmarks() +void PlacesItemModel::onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { - KBookmarkGroup root = m_bookmarkManager->root(); - KBookmark bookmark = root.first(); - QSet devices = m_availableDevices; + Q_UNUSED(roles); - QSet missingSystemBookmarks; - foreach (const SystemBookmarkData& data, m_systemBookmarks) { - missingSystemBookmarks.insert(data.url); - } + for (int r = topLeft.row(); r <= bottomRight.row(); r++) { + const QModelIndex sIndex = m_sourceModel->index(r, 0); + const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sIndex); + PlacesItem *placeItem = itemFromBookmark(bookmark); - // 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 placesItems; - QList recentlySavedItems; - QList searchForItems; - QList devicesItems; - - 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); - } - - 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(sIndex))) { + //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(sIndex))) { + //show item if it became visible + addItemFromSourceModel(sIndex); + 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 (!m_sourceModel->isDevice(sIndex)) { + placeItem->setText(bookmark.text()); + placeItem->setIcon(bookmark.icon()); + placeItem->setUrl(m_sourceModel->url(sIndex)); + placeItem->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"), + bookmark.metaDataItem(QStringLiteral("OnlyInApp"))); + } } +} - QList 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; r < m_sourceModel->rowCount(); 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 +566,19 @@ #endif } -bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark, - const QSet& 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 +591,16 @@ 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& 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 +611,6 @@ } 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 @@ -1191,6 +754,58 @@ return query.toSearchUrl(); } + +int PlacesItemModel::mapFromSource(const QModelIndex &index) const +{ + if (!index.isValid()) { + return -1; + } + + if (m_hiddenItemsShown) { + return index.row(); + } + + const QString id = bookmarkId(m_sourceModel->bookmarkForIndex(index)); + for(int r = 0; r < count(); r++) { + if (bookmarkId(placesItem(r)->bookmark()) == id) { + return r; + } + } + + return -1; +} + +QModelIndex PlacesItemModel::mapToSource(int row) const +{ + const PlacesItem *item = placesItem(row); + if (!item) { + return QModelIndex(); + } + + const QString id = bookmarkId(item->bookmark()); + for (int r = 0; r < m_sourceModel->rowCount(); r++) { + const QModelIndex sIndex = m_sourceModel->index(r, 0); + if (bookmarkId(m_sourceModel->bookmarkForIndex(sIndex)) == id) { + return sIndex; + } + } + + return QModelIndex(); +} + +PlacesItem *PlacesItemModel::itemFromBookmark(const KBookmark &bookmark) const +{ + const QString id = bookmarkId(bookmark); + for (int i = 0; i < count(); i++) { + PlacesItem *item = placesItem(i); + const KBookmark itemBookmark = item->bookmark(); + if (bookmarkId(itemBookmark) == id) { + return item; + } + } + return nullptr; +} + #endif #ifdef PLACESITEMMODEL_DEBUG diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -238,11 +238,9 @@ 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); } else if (action == openInNewTabAction) { @@ -395,7 +393,6 @@ 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) @@ -455,9 +452,7 @@ 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; @@ -479,7 +474,7 @@ oldItem->setText(dialog->text()); oldItem->setUrl(dialog->url()); oldItem->setIcon(dialog->icon()); - m_model->saveBookmarks(); + m_model->refresh(); } } diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -310,11 +310,9 @@ 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")); } } diff --git a/src/tests/placesitemmodeltest.cpp b/src/tests/placesitemmodeltest.cpp --- a/src/tests/placesitemmodeltest.cpp +++ b/src/tests/placesitemmodeltest.cpp @@ -46,6 +46,16 @@ #define KDE_ROOT_PATH "/" #endif +// Avoid QHash randomization so that the order of the devices is stable +static void seedInit() +{ + qputenv("QT_HASH_SEED", "0"); + // This env var has no effect because this comes too late. qCpuFeatures() was already called by + // a Q_CONSTRUCTOR_FUNCTION inside QtGui (see image/qimage_conversions.cpp). Argh. QTBUG-47566. + qputenv("QT_NO_CPU_FEATURE", "sse4.2"); +} +Q_CONSTRUCTOR_FUNCTION(seedInit) + static QString bookmarksFile() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/user-places.xbel"; @@ -79,6 +89,7 @@ private: PlacesItemModel* m_model; + QSet m_tobeRemoved; QMap m_interfacesMap; void setBalooEnabled(bool enabled); @@ -88,18 +99,18 @@ QStringList placesUrls() const; QStringList initialUrls() const; void createPlaceItem(const QString &text, const QUrl &url, const QString &icon); + void removePlaceAfter(int index); + void cancelPlaceRemoval(int index); }; -#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) @@ -146,14 +157,33 @@ 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::init() @@ -166,6 +196,12 @@ 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; } @@ -194,38 +230,39 @@ 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; -} - void PlacesItemModelTest::testModelSort() { CHECK_PLACES_URLS(initialUrls()); } void PlacesItemModelTest::testModelMove() { QStringList urls = initialUrls(); + QSignalSpy rowRemovedSpy(m_model, &PlacesItemModel::itemsRemoved); 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); + QTRY_COMPARE(rowRemovedSpy.count(), 1); // 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); + rowRemovedSpy.clear(); + + //move it back + systemRoot = m_model->placesItem(2)->bookmark(); + KBookmark home = m_model->placesItem(0)->bookmark(); + root.moveBookmark(systemRoot,home); + bookmarkManager->emitChanged(root); + QTRY_COMPARE(rowRemovedSpy.count(), 1); + + urls.move(2, 1); + CHECK_PLACES_URLS(urls); } void PlacesItemModelTest::testGroups() @@ -359,6 +396,8 @@ 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() @@ -416,115 +455,128 @@ 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 args = itemsInsertedSpy.takeFirst(); KItemRangeList range = args.at(0).value(); - 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(); - 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 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 args = itemsChangedSply.takeFirst(); KItemRangeList range = args.at(0).value(); - QCOMPARE(range.first().index, 4); + QCOMPARE(range.first().index, 3); QCOMPARE(range.first().count, 1); QSet roles = args.at(1).value >(); 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(); - 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->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()); } QTEST_MAIN(PlacesItemModelTest)