X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/6bcdb624291f98dbbdb0a30477689bdb89dd70e8..7e10da6e2e0171fa30a1162e5767be2b95937c21:/src/search/dolphinfacetswidget.cpp diff --git a/src/search/dolphinfacetswidget.cpp b/src/search/dolphinfacetswidget.cpp index ae05509e7..da36caa36 100644 --- a/src/search/dolphinfacetswidget.cpp +++ b/src/search/dolphinfacetswidget.cpp @@ -1,57 +1,54 @@ -/*************************************************************************** -* Copyright (C) 2012 by Peter Penz * -* Copyright (C) 2019 by Ismael Asensio * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the * -* Free Software Foundation, Inc., * -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * -* **************************************************************************/ +/* + * SPDX-FileCopyrightText: 2012 Peter Penz + * SPDX-FileCopyrightText: 2019 Ismael Asensio + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "dolphinfacetswidget.h" #include +#include #include #include #include #include #include +#include +#include -DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : - QWidget(parent), - m_typeSelector(nullptr), - m_dateSelector(nullptr), - m_ratingSelector(nullptr) +DolphinFacetsWidget::DolphinFacetsWidget(QWidget *parent) + : QWidget(parent) + , m_typeSelector(nullptr) + , m_dateSelector(nullptr) + , m_ratingSelector(nullptr) + , m_tagsSelector(nullptr) { m_typeSelector = new QComboBox(this); m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString()); - m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders") , QStringLiteral("Folder")); - m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents") , QStringLiteral("Document")); - m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images") , QStringLiteral("Image")); + m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders"), QStringLiteral("Folder")); + m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents"), QStringLiteral("Document")); + m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images"), QStringLiteral("Image")); m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio")); - m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos") , QStringLiteral("Video")); + m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos"), QStringLiteral("Video")); initComboBox(m_typeSelector); const QDate currentDate = QDate::currentDate(); m_dateSelector = new QComboBox(this); m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar")), i18nc("@item:inlistbox", "Any Date"), QDate()); - m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Today") , currentDate); - m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Yesterday") , currentDate.addDays(-1)); - m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")), i18nc("@item:inlistbox", "This Week") , currentDate.addDays(1 - currentDate.dayOfWeek())); - m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")), i18nc("@item:inlistbox", "This Month"), currentDate.addDays(1 - currentDate.day())); - m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-year")), i18nc("@item:inlistbox", "This Year") , currentDate.addDays(1 - currentDate.dayOfYear())); + m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Today"), currentDate); + m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Yesterday"), currentDate.addDays(-1)); + m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")), + i18nc("@item:inlistbox", "This Week"), + currentDate.addDays(1 - currentDate.dayOfWeek())); + m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")), + i18nc("@item:inlistbox", "This Month"), + currentDate.addDays(1 - currentDate.day())); + m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-year")), + i18nc("@item:inlistbox", "This Year"), + currentDate.addDays(1 - currentDate.dayOfYear())); initComboBox(m_dateSelector); m_ratingSelector = new QComboBox(this); @@ -63,13 +60,33 @@ DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5); initComboBox(m_ratingSelector); - QHBoxLayout* topLayout = new QHBoxLayout(this); + m_clearTagsAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-all")), i18nc("@action:inmenu", "Clear Selection"), this); + connect(m_clearTagsAction, &QAction::triggered, this, [this]() { + resetSearchTags(); + Q_EMIT facetChanged(); + }); + + m_tagsSelector = new QToolButton(this); + m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag"))); + m_tagsSelector->setMenu(new QMenu(this)); + m_tagsSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_tagsSelector->setPopupMode(QToolButton::MenuButtonPopup); + m_tagsSelector->setAutoRaise(true); + updateTagsSelector(); + + connect(m_tagsSelector, &QToolButton::clicked, m_tagsSelector, &QToolButton::showMenu); + connect(m_tagsSelector->menu(), &QMenu::aboutToShow, this, &DolphinFacetsWidget::updateTagsMenu); + connect(&m_tagsLister, &KCoreDirLister::itemsAdded, this, &DolphinFacetsWidget::updateTagsMenuItems); + updateTagsMenu(); + + QHBoxLayout *topLayout = new QHBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); topLayout->addWidget(m_typeSelector); topLayout->addWidget(m_dateSelector); topLayout->addWidget(m_ratingSelector); + topLayout->addWidget(m_tagsSelector); - resetOptions(); + resetSearchTerms(); } DolphinFacetsWidget::~DolphinFacetsWidget() @@ -78,19 +95,30 @@ DolphinFacetsWidget::~DolphinFacetsWidget() void DolphinFacetsWidget::changeEvent(QEvent *event) { - if (event->type() == QEvent::EnabledChange && !isEnabled()) { - resetOptions(); + if (event->type() == QEvent::EnabledChange) { + if (isEnabled()) { + updateTagsSelector(); + } else { + resetSearchTerms(); + } } } -void DolphinFacetsWidget::resetOptions() +QSize DolphinFacetsWidget::minimumSizeHint() const +{ + return QSize(0, m_typeSelector->minimumHeight()); +} + +void DolphinFacetsWidget::resetSearchTerms() { m_typeSelector->setCurrentIndex(0); m_dateSelector->setCurrentIndex(0); m_ratingSelector->setCurrentIndex(0); + + resetSearchTags(); } -QString DolphinFacetsWidget::ratingTerm() const +QStringList DolphinFacetsWidget::searchTerms() const { QStringList terms; @@ -104,7 +132,17 @@ QString DolphinFacetsWidget::ratingTerm() const terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate)); } - return terms.join(QLatin1String(" AND ")); + if (!m_searchTags.isEmpty()) { + for (auto const &tag : m_searchTags) { + if (tag.contains(QLatin1Char(' '))) { + terms << QStringLiteral("tag:\"%1\"").arg(tag); + } else { + terms << QStringLiteral("tag:%1").arg(tag); + } + } + } + + return terms; } QString DolphinFacetsWidget::facetType() const @@ -112,46 +150,35 @@ QString DolphinFacetsWidget::facetType() const return m_typeSelector->currentData().toString(); } -bool DolphinFacetsWidget::isRatingTerm(const QString& term) const +bool DolphinFacetsWidget::isSearchTerm(const QString &term) const { - const QStringList subTerms = term.split(' ', QString::SkipEmptyParts); - - // If term has sub terms, then sone of the sub terms are always "rating" and "modified" terms. - bool containsRating = false; - bool containsModified = false; + static const QLatin1String searchTokens[]{QLatin1String("modified>="), QLatin1String("rating>="), QLatin1String("tag:"), QLatin1String("tag=")}; - foreach (const QString& subTerm, subTerms) { - if (subTerm.startsWith(QLatin1String("rating>="))) { - containsRating = true; - } else if (subTerm.startsWith(QLatin1String("modified>="))) { - containsModified = true; + for (const auto &searchToken : searchTokens) { + if (term.startsWith(searchToken)) { + return true; } } - - return containsModified || containsRating; + return false; } -void DolphinFacetsWidget::setRatingTerm(const QString& term) +void DolphinFacetsWidget::setSearchTerm(const QString &term) { - // If term has sub terms, then the sub terms are always "rating" and "modified" terms. - // If term has no sub terms, then the term itself is either a "rating" term or a "modified" - // term. To avoid code duplication we add term to subTerms list, if the list is empty. - QStringList subTerms = term.split(' ', QString::SkipEmptyParts); - - foreach (const QString& subTerm, subTerms) { - if (subTerm.startsWith(QLatin1String("modified>="))) { - const QString value = subTerm.mid(10); - const QDate date = QDate::fromString(value, Qt::ISODate); - setTimespan(date); - } else if (subTerm.startsWith(QLatin1String("rating>="))) { - const QString value = subTerm.mid(8); - const int stars = value.toInt() / 2; - setRating(stars); - } + if (term.startsWith(QLatin1String("modified>="))) { + const QString value = term.mid(10); + const QDate date = QDate::fromString(value, Qt::ISODate); + setTimespan(date); + } else if (term.startsWith(QLatin1String("rating>="))) { + const QString value = term.mid(8); + const int stars = value.toInt() / 2; + setRating(stars); + } else if (term.startsWith(QLatin1String("tag:")) || term.startsWith(QLatin1String("tag="))) { + const QString value = term.mid(4); + addSearchTag(value); } } -void DolphinFacetsWidget::setFacetType(const QString& type) +void DolphinFacetsWidget::setFacetType(const QString &type) { for (int index = 0; index <= m_typeSelector->count(); index++) { if (type == m_typeSelector->itemData(index).toString()) { @@ -169,7 +196,7 @@ void DolphinFacetsWidget::setRating(const int stars) m_ratingSelector->setCurrentIndex(stars); } -void DolphinFacetsWidget::setTimespan(const QDate& date) +void DolphinFacetsWidget::setTimespan(const QDate &date) { if (!date.isValid()) { return; @@ -183,11 +210,103 @@ void DolphinFacetsWidget::setTimespan(const QDate& date) } } -void DolphinFacetsWidget::initComboBox(QComboBox* combo) +void DolphinFacetsWidget::addSearchTag(const QString &tag) +{ + if (tag.isEmpty() || m_searchTags.contains(tag)) { + return; + } + m_searchTags.append(tag); + m_searchTags.sort(); + updateTagsSelector(); +} + +void DolphinFacetsWidget::removeSearchTag(const QString &tag) +{ + if (tag.isEmpty() || !m_searchTags.contains(tag)) { + return; + } + m_searchTags.removeAll(tag); + updateTagsSelector(); +} + +void DolphinFacetsWidget::resetSearchTags() +{ + m_searchTags = QStringList(); + updateTagsSelector(); + updateTagsMenu(); +} + +void DolphinFacetsWidget::initComboBox(QComboBox *combo) { combo->setFrame(false); combo->setMinimumHeight(parentWidget()->height()); combo->setCurrentIndex(0); - connect(combo, QOverload::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged); + connect(combo, &QComboBox::activated, this, &DolphinFacetsWidget::facetChanged); +} + +void DolphinFacetsWidget::updateTagsSelector() +{ + const bool hasListedTags = !m_tagsSelector->menu()->isEmpty(); + const bool hasSelectedTags = !m_searchTags.isEmpty(); + + if (hasSelectedTags) { + const QString tagsText = m_searchTags.join(i18nc("String list separator", ", ")); + m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags", "Tag: %2", "Tags: %2", m_searchTags.count(), tagsText)); + } else { + m_tagsSelector->setText(i18nc("@action:button", "Add Tags")); + } + + m_tagsSelector->setEnabled(isEnabled() && (hasListedTags || hasSelectedTags)); + m_clearTagsAction->setEnabled(hasSelectedTags); +} + +void DolphinFacetsWidget::updateTagsMenu() +{ + updateTagsMenuItems({}, {}); + if (KProtocolInfo::isKnownProtocol(QStringLiteral("tags"))) { + m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload); + } +} + +void DolphinFacetsWidget::updateTagsMenuItems(const QUrl &, const KFileItemList &items) +{ + QMenu *tagsMenu = m_tagsSelector->menu(); + tagsMenu->clear(); + + QStringList allTags = QStringList(m_searchTags); + for (const KFileItem &item : items) { + allTags.append(item.name()); + } + allTags.sort(Qt::CaseInsensitive); + allTags.removeDuplicates(); + + const bool onlyOneTag = allTags.count() == 1; + + for (const QString &tagName : std::as_const(allTags)) { + QAction *action = tagsMenu->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName); + action->setCheckable(true); + action->setChecked(m_searchTags.contains(tagName)); + + connect(action, &QAction::triggered, this, [this, tagName, onlyOneTag](bool isChecked) { + if (isChecked) { + addSearchTag(tagName); + } else { + removeSearchTag(tagName); + } + Q_EMIT facetChanged(); + + if (!onlyOneTag) { + m_tagsSelector->menu()->show(); + } + }); + } + + if (allTags.count() > 1) { + tagsMenu->addSeparator(); + tagsMenu->addAction(m_clearTagsAction); + } + + updateTagsSelector(); } +#include "moc_dolphinfacetswidget.cpp"