File indexing completed on 2024-04-28 17:03:09

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
0003  *  SPDX-FileCopyrightText: 2019 Ismael Asensio <isma.af@mgmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "dolphinfacetswidget.h"
0009 
0010 #include <KLocalizedString>
0011 #include <KProtocolInfo>
0012 
0013 #include <QComboBox>
0014 #include <QDate>
0015 #include <QEvent>
0016 #include <QHBoxLayout>
0017 #include <QIcon>
0018 #include <QMenu>
0019 #include <QToolButton>
0020 
0021 DolphinFacetsWidget::DolphinFacetsWidget(QWidget *parent)
0022     : QWidget(parent)
0023     , m_typeSelector(nullptr)
0024     , m_dateSelector(nullptr)
0025     , m_ratingSelector(nullptr)
0026     , m_tagsSelector(nullptr)
0027 {
0028     m_typeSelector = new QComboBox(this);
0029     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString());
0030     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders"), QStringLiteral("Folder"));
0031     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents"), QStringLiteral("Document"));
0032     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images"), QStringLiteral("Image"));
0033     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio"));
0034     m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos"), QStringLiteral("Video"));
0035     initComboBox(m_typeSelector);
0036 
0037     const QDate currentDate = QDate::currentDate();
0038 
0039     m_dateSelector = new QComboBox(this);
0040     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar")), i18nc("@item:inlistbox", "Any Date"), QDate());
0041     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Today"), currentDate);
0042     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Yesterday"), currentDate.addDays(-1));
0043     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")),
0044                             i18nc("@item:inlistbox", "This Week"),
0045                             currentDate.addDays(1 - currentDate.dayOfWeek()));
0046     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")),
0047                             i18nc("@item:inlistbox", "This Month"),
0048                             currentDate.addDays(1 - currentDate.day()));
0049     m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-year")),
0050                             i18nc("@item:inlistbox", "This Year"),
0051                             currentDate.addDays(1 - currentDate.dayOfYear()));
0052     initComboBox(m_dateSelector);
0053 
0054     m_ratingSelector = new QComboBox(this);
0055     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("non-starred-symbolic")), i18nc("@item:inlistbox", "Any Rating"), 0);
0056     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "1 or more"), 1);
0057     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "2 or more"), 2);
0058     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "3 or more"), 3);
0059     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "4 or more"), 4);
0060     m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5);
0061     initComboBox(m_ratingSelector);
0062 
0063     m_clearTagsAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-all")), i18nc("@action:inmenu", "Clear Selection"), this);
0064     connect(m_clearTagsAction, &QAction::triggered, this, [this]() {
0065         resetSearchTags();
0066         Q_EMIT facetChanged();
0067     });
0068 
0069     m_tagsSelector = new QToolButton(this);
0070     m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag")));
0071     m_tagsSelector->setMenu(new QMenu(this));
0072     m_tagsSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0073     m_tagsSelector->setPopupMode(QToolButton::MenuButtonPopup);
0074     m_tagsSelector->setAutoRaise(true);
0075     updateTagsSelector();
0076 
0077     connect(m_tagsSelector, &QToolButton::clicked, m_tagsSelector, &QToolButton::showMenu);
0078     connect(m_tagsSelector->menu(), &QMenu::aboutToShow, this, &DolphinFacetsWidget::updateTagsMenu);
0079     connect(&m_tagsLister, &KCoreDirLister::itemsAdded, this, &DolphinFacetsWidget::updateTagsMenuItems);
0080     updateTagsMenu();
0081 
0082     QHBoxLayout *topLayout = new QHBoxLayout(this);
0083     topLayout->setContentsMargins(0, 0, 0, 0);
0084     topLayout->addWidget(m_typeSelector);
0085     topLayout->addWidget(m_dateSelector);
0086     topLayout->addWidget(m_ratingSelector);
0087     topLayout->addWidget(m_tagsSelector);
0088 
0089     resetSearchTerms();
0090 }
0091 
0092 DolphinFacetsWidget::~DolphinFacetsWidget()
0093 {
0094 }
0095 
0096 void DolphinFacetsWidget::changeEvent(QEvent *event)
0097 {
0098     if (event->type() == QEvent::EnabledChange) {
0099         if (isEnabled()) {
0100             updateTagsSelector();
0101         } else {
0102             resetSearchTerms();
0103         }
0104     }
0105 }
0106 
0107 QSize DolphinFacetsWidget::minimumSizeHint() const
0108 {
0109     return QSize(0, m_typeSelector->minimumHeight());
0110 }
0111 
0112 void DolphinFacetsWidget::resetSearchTerms()
0113 {
0114     m_typeSelector->setCurrentIndex(0);
0115     m_dateSelector->setCurrentIndex(0);
0116     m_ratingSelector->setCurrentIndex(0);
0117 
0118     resetSearchTags();
0119 }
0120 
0121 QStringList DolphinFacetsWidget::searchTerms() const
0122 {
0123     QStringList terms;
0124 
0125     if (m_ratingSelector->currentIndex() > 0) {
0126         const int rating = m_ratingSelector->currentData().toInt() * 2;
0127         terms << QStringLiteral("rating>=%1").arg(rating);
0128     }
0129 
0130     if (m_dateSelector->currentIndex() > 0) {
0131         const QDate date = m_dateSelector->currentData().toDate();
0132         terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate));
0133     }
0134 
0135     if (!m_searchTags.isEmpty()) {
0136         for (auto const &tag : m_searchTags) {
0137             if (tag.contains(QLatin1Char(' '))) {
0138                 terms << QStringLiteral("tag:\"%1\"").arg(tag);
0139             } else {
0140                 terms << QStringLiteral("tag:%1").arg(tag);
0141             }
0142         }
0143     }
0144 
0145     return terms;
0146 }
0147 
0148 QString DolphinFacetsWidget::facetType() const
0149 {
0150     return m_typeSelector->currentData().toString();
0151 }
0152 
0153 bool DolphinFacetsWidget::isSearchTerm(const QString &term) const
0154 {
0155     static const QLatin1String searchTokens[]{QLatin1String("modified>="), QLatin1String("rating>="), QLatin1String("tag:"), QLatin1String("tag=")};
0156 
0157     for (const auto &searchToken : searchTokens) {
0158         if (term.startsWith(searchToken)) {
0159             return true;
0160         }
0161     }
0162     return false;
0163 }
0164 
0165 void DolphinFacetsWidget::setSearchTerm(const QString &term)
0166 {
0167     if (term.startsWith(QLatin1String("modified>="))) {
0168         const QString value = term.mid(10);
0169         const QDate date = QDate::fromString(value, Qt::ISODate);
0170         setTimespan(date);
0171     } else if (term.startsWith(QLatin1String("rating>="))) {
0172         const QString value = term.mid(8);
0173         const int stars = value.toInt() / 2;
0174         setRating(stars);
0175     } else if (term.startsWith(QLatin1String("tag:")) || term.startsWith(QLatin1String("tag="))) {
0176         const QString value = term.mid(4);
0177         addSearchTag(value);
0178     }
0179 }
0180 
0181 void DolphinFacetsWidget::setFacetType(const QString &type)
0182 {
0183     for (int index = 0; index <= m_typeSelector->count(); index++) {
0184         if (type == m_typeSelector->itemData(index).toString()) {
0185             m_typeSelector->setCurrentIndex(index);
0186             break;
0187         }
0188     }
0189 }
0190 
0191 void DolphinFacetsWidget::setRating(const int stars)
0192 {
0193     if (stars < 0 || stars > 5) {
0194         return;
0195     }
0196     m_ratingSelector->setCurrentIndex(stars);
0197 }
0198 
0199 void DolphinFacetsWidget::setTimespan(const QDate &date)
0200 {
0201     if (!date.isValid()) {
0202         return;
0203     }
0204     m_dateSelector->setCurrentIndex(0);
0205     for (int index = 1; index <= m_dateSelector->count(); index++) {
0206         if (date >= m_dateSelector->itemData(index).toDate()) {
0207             m_dateSelector->setCurrentIndex(index);
0208             break;
0209         }
0210     }
0211 }
0212 
0213 void DolphinFacetsWidget::addSearchTag(const QString &tag)
0214 {
0215     if (tag.isEmpty() || m_searchTags.contains(tag)) {
0216         return;
0217     }
0218     m_searchTags.append(tag);
0219     m_searchTags.sort();
0220     updateTagsSelector();
0221 }
0222 
0223 void DolphinFacetsWidget::removeSearchTag(const QString &tag)
0224 {
0225     if (tag.isEmpty() || !m_searchTags.contains(tag)) {
0226         return;
0227     }
0228     m_searchTags.removeAll(tag);
0229     updateTagsSelector();
0230 }
0231 
0232 void DolphinFacetsWidget::resetSearchTags()
0233 {
0234     m_searchTags = QStringList();
0235     updateTagsSelector();
0236     updateTagsMenu();
0237 }
0238 
0239 void DolphinFacetsWidget::initComboBox(QComboBox *combo)
0240 {
0241     combo->setFrame(false);
0242     combo->setMinimumHeight(parentWidget()->height());
0243     combo->setCurrentIndex(0);
0244     connect(combo, &QComboBox::activated, this, &DolphinFacetsWidget::facetChanged);
0245 }
0246 
0247 void DolphinFacetsWidget::updateTagsSelector()
0248 {
0249     const bool hasListedTags = !m_tagsSelector->menu()->isEmpty();
0250     const bool hasSelectedTags = !m_searchTags.isEmpty();
0251 
0252     if (hasSelectedTags) {
0253         const QString tagsText = m_searchTags.join(i18nc("String list separator", ", "));
0254         m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags", "Tag: %2", "Tags: %2", m_searchTags.count(), tagsText));
0255     } else {
0256         m_tagsSelector->setText(i18nc("@action:button", "Add Tags"));
0257     }
0258 
0259     m_tagsSelector->setEnabled(isEnabled() && (hasListedTags || hasSelectedTags));
0260     m_clearTagsAction->setEnabled(hasSelectedTags);
0261 }
0262 
0263 void DolphinFacetsWidget::updateTagsMenu()
0264 {
0265     updateTagsMenuItems({}, {});
0266     if (KProtocolInfo::isKnownProtocol(QStringLiteral("tags"))) {
0267         m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload);
0268     }
0269 }
0270 
0271 void DolphinFacetsWidget::updateTagsMenuItems(const QUrl &, const KFileItemList &items)
0272 {
0273     QMenu *tagsMenu = m_tagsSelector->menu();
0274     tagsMenu->clear();
0275 
0276     QStringList allTags = QStringList(m_searchTags);
0277     for (const KFileItem &item : items) {
0278         allTags.append(item.name());
0279     }
0280     allTags.sort(Qt::CaseInsensitive);
0281     allTags.removeDuplicates();
0282 
0283     const bool onlyOneTag = allTags.count() == 1;
0284 
0285     for (const QString &tagName : std::as_const(allTags)) {
0286         QAction *action = tagsMenu->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName);
0287         action->setCheckable(true);
0288         action->setChecked(m_searchTags.contains(tagName));
0289 
0290         connect(action, &QAction::triggered, this, [this, tagName, onlyOneTag](bool isChecked) {
0291             if (isChecked) {
0292                 addSearchTag(tagName);
0293             } else {
0294                 removeSearchTag(tagName);
0295             }
0296             Q_EMIT facetChanged();
0297 
0298             if (!onlyOneTag) {
0299                 m_tagsSelector->menu()->show();
0300             }
0301         });
0302     }
0303 
0304     if (allTags.count() > 1) {
0305         tagsMenu->addSeparator();
0306         tagsMenu->addAction(m_clearTagsAction);
0307     }
0308 
0309     updateTagsSelector();
0310 }
0311 
0312 #include "moc_dolphinfacetswidget.cpp"