diff --git a/src/filewidgets/kfileplacesitem.cpp b/src/filewidgets/kfileplacesitem.cpp --- a/src/filewidgets/kfileplacesitem.cpp +++ b/src/filewidgets/kfileplacesitem.cpp @@ -121,6 +121,9 @@ case KFilePlacesModel::RemovableDevicesType: m_groupName = i18nc("@item", "Removable Devices"); break; + case KFilePlacesModel::TagsType: + m_groupName = i18nc("@item", "Tags"); + break; default: Q_UNREACHABLE(); break; @@ -162,6 +165,10 @@ return KFilePlacesModel::DevicesType; } + if (protocol == QLatin1String("tags")) { + return KFilePlacesModel::TagsType; + } + if (protocol == QLatin1String("remote") || KProtocolInfo::protocolClass(protocol) != QLatin1String(":local")) { return KFilePlacesModel::RemoteType; @@ -338,6 +345,16 @@ return bookmark; } +KBookmark KFilePlacesItem::createTagBookmark(KBookmarkManager *manager, + const QString &tag) +{ + KBookmark bookmark = createSystemBookmark(manager, tag, tag, QUrl(QStringLiteral("tags:/") + tag), QStringLiteral("tag")); + bookmark.setMetaDataItem(QStringLiteral("tag"), tag); + bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), QStringLiteral("true")); + + return bookmark; +} + QString KFilePlacesItem::generateNewId() { static int count = 0; diff --git a/src/filewidgets/kfileplacesitem_p.h b/src/filewidgets/kfileplacesitem_p.h --- a/src/filewidgets/kfileplacesitem_p.h +++ b/src/filewidgets/kfileplacesitem_p.h @@ -51,7 +51,8 @@ RecentlySavedType, SearchForType, DevicesType, - RemovableDevicesType + RemovableDevicesType, + TagsType }; KFilePlacesItem(KBookmarkManager *manager, @@ -82,6 +83,8 @@ const QString &iconName); static KBookmark createDeviceBookmark(KBookmarkManager *manager, const QString &udi); + static KBookmark createTagBookmark(KBookmarkManager *manager, + const QString &tag); Q_SIGNALS: void itemChanged(const QString &id); diff --git a/src/filewidgets/kfileplacesmodel.h b/src/filewidgets/kfileplacesmodel.h --- a/src/filewidgets/kfileplacesmodel.h +++ b/src/filewidgets/kfileplacesmodel.h @@ -62,7 +62,9 @@ SearchForType, DevicesType, RemovableDevicesType, - UnknownType + UnknownType, + /// @since 5.54 + TagsType }; explicit KFilePlacesModel(QObject *parent = nullptr); diff --git a/src/filewidgets/kfileplacesmodel.cpp b/src/filewidgets/kfileplacesmodel.cpp --- a/src/filewidgets/kfileplacesmodel.cpp +++ b/src/filewidgets/kfileplacesmodel.cpp @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -80,6 +81,8 @@ return QStringLiteral("GroupState-Devices-IsHidden"); case KFilePlacesModel::RemovableDevicesType: return QStringLiteral("GroupState-RemovableDevices-IsHidden"); + case KFilePlacesModel::TagsType: + return QStringLiteral("GroupState-Tags-IsHidden"); default: Q_UNREACHABLE(); } @@ -164,8 +167,48 @@ explicit Private(KFilePlacesModel *self) : q(self), bookmarkManager(nullptr), - fileIndexingEnabled(isFileIndexingEnabled()) + fileIndexingEnabled(isFileIndexingEnabled()), + tags(), + tagsLister(new KCoreDirLister()) { + if (KProtocolInfo::isKnownProtocol(QStringLiteral("tags"))) { + connect(tagsLister, &KCoreDirLister::itemsAdded, q, [this](const QUrl&, const KFileItemList& items) { + + if(tags.isEmpty()) { + QList existingBookmarks; + + KBookmarkGroup root = bookmarkManager->root(); + KBookmark bookmark = root.first(); + + while (!bookmark.isNull()) { + existingBookmarks.append(bookmark.url()); + bookmark = root.next(bookmark); + } + + if (!existingBookmarks.contains(QUrl(tagsUrlBase))) { + KBookmark alltags = KFilePlacesItem::createSystemBookmark(bookmarkManager, QStringLiteral("All tags"), i18n("All tags"), QUrl(tagsUrlBase), QStringLiteral("tag")); + } + } + + for (const KFileItem &item: items) { + const QString name = item.name(); + + if (!tags.contains(name)) { + tags.append(name); + } + } + _k_reloadBookmarks(); + }); + + connect(tagsLister, &KCoreDirLister::itemsDeleted, q, [this](const KFileItemList& items) { + for (const KFileItem &item: items) { + tags.removeAll(item.name()); + } + _k_reloadBookmarks(); + }); + + tagsLister->openUrl(QUrl(tagsUrlBase), KCoreDirLister::OpenUrlFlag::Reload); + } } ~Private() @@ -191,6 +234,10 @@ QList loadBookmarkList(); int findNearestPosition(int source, int target); + QVector tags; + const QString tagsUrlBase = QStringLiteral("tags:/"); + KCoreDirLister* tagsLister; + void _k_initDeviceList(); void _k_deviceAdded(const QString &udi); void _k_deviceRemoved(const QString &udi); @@ -270,6 +317,7 @@ setDefaultMetadataItemForGroup(RemoteType); setDefaultMetadataItemForGroup(DevicesType); setDefaultMetadataItemForGroup(RemovableDevicesType); + setDefaultMetadataItemForGroup(TagsType); // Force bookmarks to be saved. If on open/save dialog and the bookmarks are not saved, QFile::exists // will always return false, which opening/closing all the time the open/save dialog would case the @@ -659,38 +707,55 @@ KBookmarkGroup root = bookmarkManager->root(); KBookmark bookmark = root.first(); QVector devices = availableDevices; + QVector tagsList = tags; while (!bookmark.isNull()) { const QString udi = bookmark.metaDataItem(QStringLiteral("UDI")); const QUrl url = bookmark.url(); + const QString tag = bookmark.metaDataItem(QStringLiteral("tag")); if (!udi.isEmpty() || url.isValid()) { QString appName = bookmark.metaDataItem(QStringLiteral("OnlyInApp")); - auto it = std::find(devices.begin(), devices.end(), udi); - bool deviceAvailable = (it != devices.end()); - if (deviceAvailable) { - devices.erase(it); - } - - bool allowedHere = appName.isEmpty() || - ((appName == QCoreApplication::instance()->applicationName()) || - (appName == alternativeApplicationName)); - bool isSupportedUrl = isBalooUrl(url) ? fileIndexingEnabled : true; - bool isSupportedScheme = supportedSchemes.isEmpty() || supportedSchemes.contains(url.scheme()); - if (isSupportedScheme && ((isSupportedUrl && udi.isEmpty() && allowedHere) || deviceAvailable)) { - - KFilePlacesItem *item; + // If it's not a tag it's a device + if (tag.isEmpty()) { + auto it = std::find(devices.begin(), devices.end(), udi); + bool deviceAvailable = (it != devices.end()); if (deviceAvailable) { - item = new KFilePlacesItem(bookmarkManager, bookmark.address(), udi); - // TODO: Update bookmark internal element - } else { - item = new KFilePlacesItem(bookmarkManager, bookmark.address()); + devices.erase(it); + } + + bool allowedHere = appName.isEmpty() || + ((appName == QCoreApplication::instance()->applicationName()) || + (appName == alternativeApplicationName)); + bool isSupportedUrl = isBalooUrl(url) ? fileIndexingEnabled : true; + bool isSupportedScheme = supportedSchemes.isEmpty() || supportedSchemes.contains(url.scheme()); + + if (isSupportedScheme && ((isSupportedUrl && udi.isEmpty() && allowedHere) || deviceAvailable)) { + + KFilePlacesItem *item; + if (deviceAvailable) { + item = new KFilePlacesItem(bookmarkManager, bookmark.address(), udi); + // TODO: Update bookmark internal element + } else { + item = new KFilePlacesItem(bookmarkManager, bookmark.address()); + } + connect(item, SIGNAL(itemChanged(QString)), + q, SLOT(_k_itemChanged(QString))); + + items << item; + } + } else { + auto it = std::find(tagsList.begin(), tagsList.end(), tag); + if (it != tagsList.end()) { + tagsList.removeAll(tag); + KFilePlacesItem *item = new KFilePlacesItem(bookmarkManager, bookmark.address()); + items << item; + connect(item, SIGNAL(itemChanged(QString)), + q, SLOT(_k_itemChanged(QString))); } - connect(item, SIGNAL(itemChanged(QString)), - q, SLOT(_k_itemChanged(QString))); - items << item; } } + bookmark = root.next(bookmark); } @@ -707,6 +772,17 @@ } } + for (const QString& tag: tagsList) { + bookmark = KFilePlacesItem::createTagBookmark(bookmarkManager, tag); + if (!bookmark.isNull()) { + KFilePlacesItem *item = new KFilePlacesItem(bookmarkManager, + bookmark.address(), tag); + connect(item, SIGNAL(itemChanged(QString)), + q, SLOT(_k_itemChanged(QString))); + items << item; + } + } + // return a sorted list based on groups std::stable_sort(items.begin(), items.end(), [](KFilePlacesItem *itemA, KFilePlacesItem *itemB) { @@ -1054,7 +1130,7 @@ if (isGroupHidden(type) == hidden) return; - d->bookmarkManager->root().setMetaDataItem(stateNameForGroupType(type), (hidden ? QStringLiteral("true") : QStringLiteral("false"))); + d->bookmarkManager->root().setMetaDataItem(stateNameForGroupType(type), (hidden ? QStringLiteral("true") : QStringLiteral("false"))); d->reloadAndSignal(); emit groupHiddenChanged(type, hidden); }