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
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;
} else {
icon = KIO::iconNameForUrl(url);
}
- PlacesItem* item = model.createPlacesItem(container->placesText(), url, icon);
- model.appendItemToGroup(item);
- model.saveBookmarks();
+ model.createPlacesItem(container->placesText(), url, icon);
}
}
}
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()) {
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"));
}
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,
{
public:
- enum GroupType
- {
- PlacesType,
- SearchForType,
- RecentlySavedType,
- DevicesType
- };
-
explicit PlacesItem(const KBookmark& bookmark, PlacesItem* parent = nullptr);
~PlacesItem() override;
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;
#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";
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
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)
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);
}
}
}
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
{
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);
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)
}
}
-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);
}
}
#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();
}
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)
}
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
Q_ASSERT(item);
int dropIndex = index;
- const PlacesItem::GroupType type = item->groupType();
+ const QString group = item->group();
const int itemCount = count();
if (index < 0) {
// 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;
}
// 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;
}
}
}
-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
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()
#ifndef PLACESITEMMODEL_H
#define PLACESITEMMODEL_H
-#include <config-baloo.h>
-
#include <kitemviews/kstandarditemmodel.h>
#include <QUrl>
class KBookmark;
class KBookmarkManager;
+class KFilePlacesModel;
class PlacesItem;
class QAction;
-class QTimer;
// #define PLACESITEMMODEL_DEBUG
~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
*/
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;
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:
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.
* 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;
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
#include <KIO/DropJob>
#include <KIO/EmptyTrashJob>
#include <KIO/JobUiDelegate>
+#include <KFilePlacesModel>
#include <KJobWidgets>
#include <KLocalizedString>
#include <KIconLoader>
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.
}
const PlacesItem* destItem = m_model->placesItem(index);
- const PlacesItem::GroupType group = destItem->groupType();
- if (group == PlacesItem::SearchForType || group == PlacesItem::RecentlySavedType) {
+
+ if (destItem->isSearchOrTimelineUrl()) {
return;
}
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)
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;
oldItem->setText(dialog->text());
oldItem->setUrl(dialog->url());
oldItem->setIcon(dialog->icon());
- m_model->saveBookmarks();
+ m_model->refresh();
}
}
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));
}
}
}
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"));
}
}
#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"
#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
void cleanupTestCase();
void testModelSort();
- void testModelMove();
void testGroups();
+ void testDeletePlace();
void testPlaceItem_data();
void testPlaceItem();
void testTearDownDevice();
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)
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()
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());
}
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()
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()
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()
QFETCH(QUrl, url);
QFETCH(bool, expectedIsHidden);
QFETCH(bool, expectedIsSystemItem);
- QFETCH(PlacesItem::GroupType, expectedGroupType);
+ QFETCH(QString, expectedGroup);
QFETCH(bool, expectedStorageSetupNeeded);
const int index = indexOf(url);
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"));
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);
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()
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)