X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/321f747ddf9cf71ed3c8fa4de287d131cd22c2d8..073f8cae13f2637c0bf2f5611295e103418d52ff:/src/panels/places/placesitemmodel.cpp diff --git a/src/panels/places/placesitemmodel.cpp b/src/panels/places/placesitemmodel.cpp index 48f54b276..6723391db 100644 --- a/src/panels/places/placesitemmodel.cpp +++ b/src/panels/places/placesitemmodel.cpp @@ -23,15 +23,7 @@ #include "placesitemmodel.h" -#ifdef HAVE_NEPOMUK - #include - #include - #include - #include - #include - #include - #include -#endif +#include "dolphin_generalsettings.h" #include #include @@ -45,9 +37,39 @@ #include "placesitem.h" #include #include +#include +#include #include #include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_NEPOMUK + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace { + // As long as KFilePlacesView from kdelibs is available in parallel, the + // system-bookmarks for "Recently Accessed" and "Search For" should be + // shown only inside the Places Panel. This is necessary as the stored + // URLs needs to get translated to a Nepomuk-search-URL on-the-fly to + // be independent from changes in the Nepomuk-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), @@ -58,7 +80,10 @@ PlacesItemModel::PlacesItemModel(QObject* parent) : m_bookmarkManager(0), m_systemBookmarks(), m_systemBookmarksIndexes(), - m_hiddenItems() + m_bookmarkedItems(), + m_hiddenItemToRemove(-1), + m_saveBookmarksTimer(0), + m_updateBookmarksTimer(0) { #ifdef HAVE_NEPOMUK m_nepomukRunning = (Nepomuk::ResourceManager::instance()->initialized()); @@ -69,12 +94,38 @@ PlacesItemModel::PlacesItemModel(QObject* parent) : createSystemBookmarks(); initializeAvailableDevices(); loadBookmarks(); + + const int syncBookmarksTimeout = 1000; + + m_saveBookmarksTimer = new QTimer(this); + m_saveBookmarksTimer->setInterval(syncBookmarksTimeout); + m_saveBookmarksTimer->setSingleShot(true); + connect(m_saveBookmarksTimer, SIGNAL(timeout()), this, SLOT(saveBookmarks())); + + m_updateBookmarksTimer = new QTimer(this); + m_updateBookmarksTimer->setInterval(syncBookmarksTimeout); + m_updateBookmarksTimer->setSingleShot(true); + connect(m_updateBookmarksTimer, SIGNAL(timeout()), this, SLOT(updateBookmarks())); + + connect(m_bookmarkManager, SIGNAL(changed(QString,QString)), + m_updateBookmarksTimer, SLOT(start())); + connect(m_bookmarkManager, SIGNAL(bookmarksChanged(QString)), + m_updateBookmarksTimer, SLOT(start())); } PlacesItemModel::~PlacesItemModel() { - qDeleteAll(m_hiddenItems); - m_hiddenItems.clear(); + saveBookmarks(); + qDeleteAll(m_bookmarkedItems); + m_bookmarkedItems.clear(); +} + +PlacesItem* PlacesItemModel::createPlacesItem(const QString& text, + const KUrl& url, + const QString& iconName) +{ + const KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, text, url, iconName); + return new PlacesItem(bookmark); } PlacesItem* PlacesItemModel::placesItem(int index) const @@ -85,42 +136,19 @@ PlacesItem* PlacesItemModel::placesItem(int index) const int PlacesItemModel::hiddenCount() const { int modelIndex = 0; - int itemCount = 0; - foreach (const PlacesItem* hiddenItem, m_hiddenItems) { - if (hiddenItem) { - ++itemCount; + int hiddenItemCount = 0; + foreach (const PlacesItem* item, m_bookmarkedItems) { + if (item) { + ++hiddenItemCount; } else { if (placesItem(modelIndex)->isHidden()) { - ++itemCount; + ++hiddenItemCount; } ++modelIndex; } } - return itemCount; -} - -void PlacesItemModel::setItemHidden(int index, bool hide) -{ - if (index >= 0 && index < count()) { - PlacesItem* shownItem = placesItem(index); - shownItem->setHidden(true); - if (!m_hiddenItemsShown && hide) { - const int newIndex = hiddenIndex(index); - PlacesItem* hiddenItem = new PlacesItem(*shownItem); - removeItem(index); - m_hiddenItems.insert(newIndex, hiddenItem); - } -#ifdef PLACESITEMMODEL_DEBUG - kDebug() << "Changed hide-state from" << index << "to" << hide; - showModelState(); -#endif - } -} - -bool PlacesItemModel::isItemHidden(int index) const -{ - return (index >= 0 && index < count()) ? m_hiddenItems[index] != 0 : false; + return hiddenItemCount; } void PlacesItemModel::setHiddenItemsShown(bool show) @@ -132,31 +160,42 @@ void PlacesItemModel::setHiddenItemsShown(bool show) m_hiddenItemsShown = show; if (show) { - // Move all items that are part of m_hiddenItems to the model. + // Move all items that are part of m_bookmarkedItems to the model. + QList itemsToInsert; + QList insertPos; int modelIndex = 0; - for (int hiddenIndex = 0; hiddenIndex < m_hiddenItems.count(); ++hiddenIndex) { - if (m_hiddenItems[hiddenIndex]) { - PlacesItem* visibleItem = new PlacesItem(*m_hiddenItems[hiddenIndex]); - delete m_hiddenItems[hiddenIndex]; - m_hiddenItems.removeAt(hiddenIndex); - insertItem(modelIndex, visibleItem); - Q_ASSERT(!m_hiddenItems[hiddenIndex]); + 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); } ++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]); + } + + Q_ASSERT(m_bookmarkedItems.count() == count()); } else { // Move all items of the model, where the "isHidden" property is true, to - // m_hiddenItems. - Q_ASSERT(m_hiddenItems.count() == count()); + // m_bookmarkedItems. + Q_ASSERT(m_bookmarkedItems.count() == count()); for (int i = count() - 1; i >= 0; --i) { - PlacesItem* visibleItem = placesItem(i); - if (visibleItem->isHidden()) { - PlacesItem* hiddenItem = new PlacesItem(*visibleItem); - removeItem(i); - m_hiddenItems.insert(i, hiddenItem); + if (placesItem(i)->isHidden()) { + hideItem(i); } } } + #ifdef PLACESITEMMODEL_DEBUG kDebug() << "Changed visibility of hidden items"; showModelState(); @@ -168,15 +207,6 @@ bool PlacesItemModel::hiddenItemsShown() const return m_hiddenItemsShown; } -bool PlacesItemModel::isSystemItem(int index) const -{ - if (index >= 0 && index < count()) { - const KUrl url = placesItem(index)->url(); - return m_systemBookmarksIndexes.contains(url); - } - return false; -} - int PlacesItemModel::closestItem(const KUrl& url) const { int foundIndex = -1; @@ -196,21 +226,6 @@ int PlacesItemModel::closestItem(const KUrl& url) const return foundIndex; } -QString PlacesItemModel::groupName(const KUrl &url) const -{ - const QString protocol = url.protocol(); - - if (protocol.contains(QLatin1String("search"))) { - return searchForGroupName(); - } - - if (protocol == QLatin1String("timeline")) { - return recentlyAccessedGroupName(); - } - - return placesGroupName(); -} - QAction* PlacesItemModel::ejectAction(int index) const { const PlacesItem* item = placesItem(index); @@ -221,48 +236,167 @@ QAction* PlacesItemModel::ejectAction(int index) const return 0; } -QAction* PlacesItemModel::tearDownAction(int index) const +QAction* PlacesItemModel::teardownAction(int index) const { - // TODO: This is a dummy-implementation to have at least all - // translation-strings as part of the code before the freeze + const PlacesItem* item = placesItem(index); + if (!item) { + return 0; + } + + Solid::Device device = item->device(); + const bool providesTearDown = device.is() && + device.as()->isAccessible(); + if (!providesTearDown) { + return 0; + } + + Solid::StorageDrive* drive = device.as(); + if (!drive) { + drive = device.parent().as(); + } + + bool hotPluggable = false; + bool removable = false; + if (drive) { + hotPluggable = drive->isHotpluggable(); + removable = drive->isRemovable(); + } + QString iconName; QString text; - QString label; - switch (index) { - case 0: + const QString label = item->text(); + if (device.is()) { text = i18nc("@item", "Release '%1'", label); - break; - case 1: + } else if (removable || hotPluggable) { text = i18nc("@item", "Safely Remove '%1'", label); iconName = "media-eject"; - break; - case 2: + } else { text = i18nc("@item", "Unmount '%1'", label); iconName = "media-eject"; - break; - default: - break; } - //return new QAction(KIcon(iconName), text, 0); - return 0; + if (iconName.isEmpty()) { + return new QAction(text, 0); + } + + return new QAction(KIcon(iconName), text, 0); +} + +void PlacesItemModel::requestEject(int index) +{ + const PlacesItem* item = placesItem(index); + if (item) { + Solid::OpticalDrive* drive = item->device().parent().as(); + if (drive) { + connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + drive->eject(); + } else { + const QString label = item->text(); + const QString message = i18nc("@info", "The device '%1' is not a disk and cannot be ejected.", label); + emit errorMessage(message); + } + } +} + +void PlacesItemModel::requestTeardown(int index) +{ + const PlacesItem* item = placesItem(index); + if (item) { + Solid::StorageAccess* access = item->device().as(); + if (access) { + connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + access->teardown(); + } + } +} + +QMimeData* PlacesItemModel::createMimeData(const QSet& indexes) const +{ + KUrl::List urls; + QByteArray itemData; + + QDataStream stream(&itemData, QIODevice::WriteOnly); + + foreach (int index, indexes) { + const KUrl itemUrl = placesItem(index)->url(); + if (itemUrl.isValid()) { + urls << itemUrl; + } + stream << index; + } + + QMimeData* mimeData = new QMimeData(); + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + mimeData->setData(internalMimeType(), itemData); + + return mimeData; +} + +void PlacesItemModel::dropMimeData(int index, const QMimeData* mimeData) +{ + Q_UNUSED(index); // TODO + if (mimeData->hasFormat(internalMimeType())) { + // TODO + } else if (mimeData->hasFormat("text/uri-list")) { + // TODO + } +} + +KUrl PlacesItemModel::convertedUrl(const KUrl& url) +{ + KUrl newUrl = url; + if (url.protocol() == QLatin1String("timeline")) { + newUrl = createTimelineUrl(url); + } else if (url.protocol() == QLatin1String("search")) { + newUrl = createSearchUrl(url); + } + + return newUrl; } void PlacesItemModel::onItemInserted(int index) { - int modelIndex = 0; - int hiddenIndex = 0; - while (hiddenIndex < m_hiddenItems.count()) { - if (!m_hiddenItems[hiddenIndex]) { - ++modelIndex; - if (modelIndex + 1 == index) { - ++hiddenIndex; - break; + 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; } - ++hiddenIndex; + m_bookmarkedItems.insert(bookmarkIndex, 0); } - m_hiddenItems.insert(hiddenIndex, 0); + + triggerBookmarksSaving(); #ifdef PLACESITEMMODEL_DEBUG kDebug() << "Inserted item" << index; @@ -270,25 +404,181 @@ void PlacesItemModel::onItemInserted(int index) #endif } -void PlacesItemModel::onItemRemoved(int index) +void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem) { - const int removeIndex = hiddenIndex(index); - Q_ASSERT(!m_hiddenItems[removeIndex]); - m_hiddenItems.removeAt(removeIndex); + PlacesItem* placesItem = dynamic_cast(removedItem); + if (placesItem) { + const KBookmark bookmark = placesItem->bookmark(); + m_bookmarkManager->root().deleteBookmark(bookmark); + } + + const int boomarkIndex = bookmarkIndex(index); + Q_ASSERT(!m_bookmarkedItems[boomarkIndex]); + m_bookmarkedItems.removeAt(boomarkIndex); + + triggerBookmarksSaving(); + #ifdef PLACESITEMMODEL_DEBUG kDebug() << "Removed item" << index; showModelState(); #endif } +void PlacesItemModel::onItemChanged(int index, const QSet& changedRoles) +{ + 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); + } + + if (changedRoles.contains("isHidden")) { + if (!m_hiddenItemsShown && changedItem->isHidden()) { + m_hiddenItemToRemove = index; + QTimer::singleShot(0, this, SLOT(hideItem())); + } + } + + triggerBookmarksSaving(); +} + void PlacesItemModel::slotDeviceAdded(const QString& udi) { - Q_UNUSED(udi); + const Solid::Device device(udi); + if (m_predicate.matches(device)) { + m_availableDevices << udi; + const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi); + appendItem(new PlacesItem(bookmark)); + } } void PlacesItemModel::slotDeviceRemoved(const QString& udi) { - Q_UNUSED(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; + } + } +} + +void PlacesItemModel::slotStorageTeardownDone(Solid::ErrorType error, const QVariant& errorData) +{ + if (error && errorData.isValid()) { + emit errorMessage(errorData.toString()); + } +} + +void PlacesItemModel::hideItem() +{ + hideItem(m_hiddenItemToRemove); + m_hiddenItemToRemove = -1; +} + +void PlacesItemModel::updateBookmarks() +{ + // 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)) { + 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("UDI").isEmpty()) { + item->setBookmark(newBookmark); + } + break; + } + } + + if (!found) { + PlacesItem* item = new PlacesItem(newBookmark); + if (item->isHidden() && !m_hiddenItemsShown) { + m_bookmarkedItems.append(item); + } else { + appendItem(item); + } + } + } + + 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 == 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); + } + + if (hasBeenRemoved) { + if (m_bookmarkedItems[i]) { + delete m_bookmarkedItems[i]; + m_bookmarkedItems.removeAt(i); + } else { + removeItem(modelIndex); + --modelIndex; + } + } + + if (itemIsPartOfModel) { + ++modelIndex; + } + } +} + +void PlacesItemModel::saveBookmarks() +{ + m_bookmarkManager->emitChanged(m_bookmarkManager->root()); } void PlacesItemModel::loadBookmarks() @@ -302,28 +592,23 @@ void PlacesItemModel::loadBookmarks() missingSystemBookmarks.insert(data.url); } - // The bookmarks might have a mixed order of "places" and "devices". In - // Dolphin's places panel the devices should always be appended as last - // group. + // 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 recentlyAccessedItems; + QList searchForItems; QList devicesItems; while (!bookmark.isNull()) { - const QString udi = bookmark.metaDataItem("UDI"); - const KUrl url = bookmark.url(); - const QString appName = bookmark.metaDataItem("OnlyInApp"); - const bool deviceAvailable = devices.remove(udi); - - const bool allowedHere = (appName.isEmpty() || appName == KGlobal::mainComponent().componentName()) - && (m_nepomukRunning || url.protocol() != QLatin1String("timeline")); - - if ((udi.isEmpty() && allowedHere) || deviceAvailable) { + const bool deviceAvailable = devices.remove(bookmark.metaDataItem("UDI")); + if (acceptBookmark(bookmark)) { PlacesItem* item = new PlacesItem(bookmark); if (deviceAvailable) { devicesItems.append(item); } else { - placesItems.append(item); - + const KUrl url = bookmark.url(); if (missingSystemBookmarks.contains(url)) { missingSystemBookmarks.remove(url); @@ -331,10 +616,15 @@ void PlacesItemModel::loadBookmarks() // translation might be shown. const int index = m_systemBookmarksIndexes.value(url); item->setText(m_systemBookmarks[index].text); + item->setSystemItem(true); + } - // The system bookmarks don't contain "real" queries stored as URLs, so - // they must be translated first. - item->setUrl(translatedSystemBookmarkUrl(url)); + switch (item->groupType()) { + case PlacesItem::PlacesType: placesItems.append(item); break; + case PlacesItem::RecentlyAccessedType: recentlyAccessedItems.append(item); break; + case PlacesItem::SearchForType: searchForItems.append(item); break; + case PlacesItem::DevicesType: + default: Q_ASSERT(false); break; } } } @@ -342,28 +632,42 @@ void PlacesItemModel::loadBookmarks() bookmark = root.next(bookmark); } - addItems(placesItems); - if (!missingSystemBookmarks.isEmpty()) { + // The current bookmarks don't contain all system-bookmarks. Add the missing + // bookmarks. foreach (const SystemBookmarkData& data, m_systemBookmarks) { if (missingSystemBookmarks.contains(data.url)) { - PlacesItem* item = new PlacesItem(); - item->setIcon(data.icon); - item->setText(data.text); - item->setUrl(translatedSystemBookmarkUrl(data.url)); - item->setGroup(data.group); - appendItem(item); + PlacesItem* item = createSystemPlacesItem(data); + switch (item->groupType()) { + case PlacesItem::PlacesType: placesItems.append(item); break; + case PlacesItem::RecentlyAccessedType: recentlyAccessedItems.append(item); break; + case PlacesItem::SearchForType: searchForItems.append(item); break; + case PlacesItem::DevicesType: + default: Q_ASSERT(false); break; + } } } } - // Create items for devices that have not stored as bookmark yet + // Create items for devices that have not been stored as bookmark yet foreach (const QString& udi, devices) { - PlacesItem* item = new PlacesItem(udi); - devicesItems.append(item); + const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi); + devicesItems.append(new PlacesItem(bookmark)); } - addItems(devicesItems); + QList items; + items.append(placesItems); + items.append(recentlyAccessedItems); + items.append(searchForItems); + items.append(devicesItems); + + foreach (PlacesItem* item, items) { + if (!m_hiddenItemsShown && item->isHidden()) { + m_bookmarkedItems.append(item); + } else { + appendItem(item); + } + } #ifdef PLACESITEMMODEL_DEBUG kDebug() << "Loaded bookmarks"; @@ -371,15 +675,75 @@ void PlacesItemModel::loadBookmarks() #endif } -void PlacesItemModel::addItems(const QList& items) +bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark) const { - foreach (PlacesItem* item, items) { - if (item->isHidden()) { - m_hiddenItems.append(item); - } else { - appendItem(item); + const QString udi = bookmark.metaDataItem("UDI"); + const KUrl url = bookmark.url(); + const QString appName = bookmark.metaDataItem("OnlyInApp"); + const bool deviceAvailable = m_availableDevices.contains(udi); + + const bool allowedHere = (appName.isEmpty() + || appName == KGlobal::mainComponent().componentName() + || appName == KGlobal::mainComponent().componentName() + AppNamePrefix) + && (m_nepomukRunning || (url.protocol() != QLatin1String("timeline") && + url.protocol() != 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.protocol(); + if (protocol == QLatin1String("timeline") || protocol == QLatin1String("search")) { + // As long as the KFilePlacesView from kdelibs is available, the system-bookmarks + // for "Recently Accessed" and "Search For" should be a setting available only + // in the Places Panel (see description of AppNamePrefix for more details). + const QString appName = KGlobal::mainComponent().componentName() + AppNamePrefix; + bookmark.setMetaDataItem("OnlyInApp", appName); + } + + PlacesItem* item = new PlacesItem(bookmark); + item->setSystemItem(true); + + // Create default view-properties for all "Search For" and "Recently Accessed" 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::RecentlyAccessedType) && + !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(QList() << "text" << "path"); + } else if (path == QLatin1String("/images")) { + props.setViewMode(DolphinView::IconsView); + props.setPreviewsShown(true); + props.setVisibleRoles(QList() << "text" << "imageSize"); + } else if (path == QLatin1String("/audio")) { + props.setViewMode(DolphinView::DetailsView); + props.setPreviewsShown(false); + props.setVisibleRoles(QList() << "text" << "artist" << "album"); + } else if (path == QLatin1String("/videos")) { + props.setViewMode(DolphinView::IconsView); + props.setPreviewsShown(true); + props.setVisibleRoles(QList() << "text"); + } else if (data.url.protocol() == "timeline") { + props.setViewMode(DolphinView::DetailsView); + props.setVisibleRoles(QList() << "text" << "date"); + } } } + + return item; } void PlacesItemModel::createSystemBookmarks() @@ -387,67 +751,51 @@ void PlacesItemModel::createSystemBookmarks() Q_ASSERT(m_systemBookmarks.isEmpty()); Q_ASSERT(m_systemBookmarksIndexes.isEmpty()); - const QString placesGroup = placesGroupName(); - const QString recentlyAccessedGroup = recentlyAccessedGroupName(); - const QString searchForGroup = searchForGroupName(); const QString timeLineIcon = "package_utility_time"; // TODO: Ask the Oxygen team to create // a custom icon for the timeline-protocol m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()), "user-home", - i18nc("@item", "Home"), - placesGroup)); + i18nc("@item", "Home"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"), "network-workgroup", - i18nc("@item", "Network"), - placesGroup)); + i18nc("@item", "Network"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("/"), "folder-red", - i18nc("@item", "Root"), - placesGroup)); + i18nc("@item", "Root"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"), "user-trash", - i18nc("@item", "Trash"), - placesGroup)); + i18nc("@item", "Trash"))); if (m_nepomukRunning) { m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/today"), timeLineIcon, - i18nc("@item Recently Accessed", "Today"), - recentlyAccessedGroup)); + i18nc("@item Recently Accessed", "Today"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/yesterday"), timeLineIcon, - i18nc("@item Recently Accessed", "Yesterday"), - recentlyAccessedGroup)); + i18nc("@item Recently Accessed", "Yesterday"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/thismonth"), timeLineIcon, - i18nc("@item Recently Accessed", "This Month"), - recentlyAccessedGroup)); + i18nc("@item Recently Accessed", "This Month"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/lastmonth"), timeLineIcon, - i18nc("@item Recently Accessed", "Last Month"), - recentlyAccessedGroup)); + i18nc("@item Recently Accessed", "Last Month"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/documents"), "folder-txt", - i18nc("@item Commonly Accessed", "Documents"), - searchForGroup)); + i18nc("@item Commonly Accessed", "Documents"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/images"), "folder-image", - i18nc("@item Commonly Accessed", "Images"), - searchForGroup)); + i18nc("@item Commonly Accessed", "Images"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/audio"), "folder-sound", - i18nc("@item Commonly Accessed", "Audio"), - searchForGroup)); + i18nc("@item Commonly Accessed", "Audio Files"))); m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/videos"), "folder-video", - i18nc("@item Commonly Accessed", "Videos"), - searchForGroup)); + i18nc("@item Commonly Accessed", "Videos"))); } for (int i = 0; i < m_systemBookmarks.count(); ++i) { - const KUrl url = translatedSystemBookmarkUrl(m_systemBookmarks[i].url); - m_systemBookmarksIndexes.insert(url, i); + m_systemBookmarksIndexes.insert(m_systemBookmarks[i].url, i); } } @@ -473,48 +821,86 @@ void PlacesItemModel::initializeAvailableDevices() } } -int PlacesItemModel::hiddenIndex(int index) const +int PlacesItemModel::bookmarkIndex(int index) const { - int hiddenIndex = 0; - int visibleItemIndex = 0; - while (hiddenIndex < m_hiddenItems.count()) { - if (!m_hiddenItems[hiddenIndex]) { - if (visibleItemIndex == index) { + int bookmarkIndex = 0; + int modelIndex = 0; + while (bookmarkIndex < m_bookmarkedItems.count()) { + if (!m_bookmarkedItems[bookmarkIndex]) { + if (modelIndex == index) { break; } - ++visibleItemIndex; + ++modelIndex; } - ++hiddenIndex; + ++bookmarkIndex; } - return hiddenIndex >= m_hiddenItems.count() ? -1 : hiddenIndex; + return bookmarkIndex >= m_bookmarkedItems.count() ? -1 : bookmarkIndex; } -QString PlacesItemModel::placesGroupName() +void PlacesItemModel::hideItem(int index) { - return i18nc("@item", "Places"); + PlacesItem* shownItem = placesItem(index); + if (!shownItem) { + return; + } + + 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); + triggerBookmarksSaving(); + } + + m_bookmarkedItems.insert(newIndex, hiddenItem); + } } -QString PlacesItemModel::recentlyAccessedGroupName() +void PlacesItemModel::triggerBookmarksSaving() { - return i18nc("@item", "Recently Accessed"); + if (m_saveBookmarksTimer) { + m_saveBookmarksTimer->start(); + } } -QString PlacesItemModel::searchForGroupName() +QString PlacesItemModel::internalMimeType() const { - return i18nc("@item", "Search For"); + return "application/x-dolphinplacesmodel-" + + QString::number((qptrdiff)this); } -KUrl PlacesItemModel::translatedSystemBookmarkUrl(const KUrl& url) +bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2) { - KUrl translatedUrl = url; - if (url.protocol() == QLatin1String("timeline")) { - translatedUrl = createTimelineUrl(url); - } else if (url.protocol() == QLatin1String("search")) { - translatedUrl = createSearchUrl(url); + const QString udi1 = b1.metaDataItem("UDI"); + const QString udi2 = b2.metaDataItem("UDI"); + if (!udi1.isEmpty() && !udi2.isEmpty()) { + return udi1 == udi2; + } else { + return b1.metaDataItem("ID") == b2.metaDataItem("ID"); } - - return translatedUrl; } KUrl PlacesItemModel::createTimelineUrl(const KUrl& url) @@ -602,15 +988,40 @@ KUrl PlacesItemModel::searchUrlForTerm(const Nepomuk::Query::Term& term) #ifdef PLACESITEMMODEL_DEBUG void PlacesItemModel::showModelState() { - kDebug() << "hidden-index model-index text"; - int j = 0; - for (int i = 0; i < m_hiddenItems.count(); ++i) { - if (m_hiddenItems[i]) { - kDebug() << i << "(Hidden) " << " " << m_hiddenItems[i]->dataValue("text").toString(); + kDebug() << "================================="; + kDebug() << "Model:"; + kDebug() << "hidden-index model-index text"; + int modelIndex = 0; + for (int i = 0; i < m_bookmarkedItems.count(); ++i) { + if (m_bookmarkedItems[i]) { + kDebug() << i << "(Hidden) " << " " << m_bookmarkedItems[i]->dataValue("text").toString(); } else { - kDebug() << i << " " << j << " " << item(j)->dataValue("text").toString(); - ++j; + if (item(modelIndex)) { + kDebug() << i << " " << modelIndex << " " << item(modelIndex)->dataValue("text").toString(); + } else { + kDebug() << i << " " << modelIndex << " " << "(not available yet)"; + } + ++modelIndex; + } + } + + kDebug(); + kDebug() << "Bookmarks:"; + + int bookmarkIndex = 0; + KBookmarkGroup root = m_bookmarkManager->root(); + KBookmark bookmark = root.first(); + while (!bookmark.isNull()) { + const QString udi = bookmark.metaDataItem("UDI"); + const QString text = udi.isEmpty() ? bookmark.text() : udi; + if (bookmark.metaDataItem("IsHidden") == QLatin1String("true")) { + kDebug() << bookmarkIndex << "(Hidden)" << text; + } else { + kDebug() << bookmarkIndex << " " << text; } + + bookmark = root.next(bookmark); + ++bookmarkIndex; } } #endif