#include <QEvent>
#include <QHBoxLayout>
#include <QIcon>
+#include <QMenu>
+#include <QToolButton>
DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) :
QWidget(parent),
m_typeSelector(nullptr),
m_dateSelector(nullptr),
- m_ratingSelector(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_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5);
initComboBox(m_ratingSelector);
+ 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()
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()
+void DolphinFacetsWidget::resetSearchTerms()
{
m_typeSelector->setCurrentIndex(0);
m_dateSelector->setCurrentIndex(0);
m_ratingSelector->setCurrentIndex(0);
+
+ m_searchTags = QStringList();
+ updateTagsSelector();
+ updateTagsMenu();
}
-QString DolphinFacetsWidget::ratingTerm() const
+QStringList DolphinFacetsWidget::searchTerms() const
{
QStringList terms;
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
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::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::initComboBox(QComboBox* combo)
{
combo->setFrame(false);
connect(combo, QOverload<int>::of(&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));
+}
+
+void DolphinFacetsWidget::updateTagsMenu()
+{
+ updateTagsMenuItems({}, {});
+ m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload);
+}
+
+void DolphinFacetsWidget::updateTagsMenuItems(const QUrl&, const KFileItemList& items)
+{
+ m_tagsSelector->menu()->clear();
+
+ QStringList allTags = QStringList(m_searchTags);
+ for (const KFileItem &item: items) {
+ allTags.append(item.name());
+ }
+ allTags.sort(Qt::CaseInsensitive);
+ allTags.removeDuplicates();
+
+ for (const QString& tagName : qAsConst(allTags)) {
+ QAction* action = m_tagsSelector->menu()->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName);
+ action->setCheckable(true);
+ action->setChecked(m_searchTags.contains(tagName));
+
+ connect(action, &QAction::triggered, this, [this, tagName](bool isChecked) {
+ if (isChecked) {
+ addSearchTag(tagName);
+ } else {
+ removeSearchTag(tagName);
+ }
+ emit facetChanged();
+
+ m_tagsSelector->menu()->show();
+ });
+ }
+
+ updateTagsSelector();
+}