File indexing completed on 2024-05-05 05:13:11

0001 /*
0002     This file is part of Akregator.
0003 
0004     SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0007 */
0008 
0009 #include "searchbar.h"
0010 #include "akregatorconfig.h"
0011 
0012 #include "article.h"
0013 #include <TextUtils/ConvertText>
0014 
0015 #include <KLineEdit>
0016 #include <KLocalizedString>
0017 
0018 #include <QHBoxLayout>
0019 #include <QStandardPaths>
0020 #include <QString>
0021 #include <chrono>
0022 
0023 using namespace std::chrono_literals;
0024 
0025 using namespace Akregator;
0026 using namespace Akregator::Filters;
0027 
0028 SearchBar::SearchBar(QWidget *parent)
0029     : QWidget(parent)
0030     , m_searchLine(new StatusSearchLine(this))
0031 {
0032     m_delay = 400;
0033     auto layout = new QHBoxLayout(this);
0034 
0035     layout->setContentsMargins({});
0036     layout->setSpacing(5);
0037     setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
0038 
0039     m_searchLine->setClearButtonEnabled(true);
0040     m_searchLine->setPlaceholderText(i18n("Search articles..."));
0041     layout->addWidget(m_searchLine);
0042 
0043     connect(m_searchLine, &KLineEdit::textChanged, this, &SearchBar::slotSearchStringChanged);
0044     connect(m_searchLine, &StatusSearchLine::forceLostFocus, this, &SearchBar::forceLostFocus);
0045     connect(m_searchLine, &StatusSearchLine::statusChanged, this, &SearchBar::slotStatusChanged);
0046 
0047     connect(&m_timer, &QTimer::timeout, this, &SearchBar::slotActivateSearch);
0048     m_timer.setSingleShot(true);
0049 }
0050 
0051 SearchBar::~SearchBar() = default;
0052 
0053 QString SearchBar::text() const
0054 {
0055     return m_searchText;
0056 }
0057 
0058 StatusSearchLine::Status SearchBar::status() const
0059 {
0060     return m_searchLine->status();
0061 }
0062 
0063 void SearchBar::setDelay(int ms)
0064 {
0065     m_delay = ms;
0066 }
0067 
0068 int SearchBar::delay() const
0069 {
0070     return m_delay;
0071 }
0072 
0073 void SearchBar::setFocusSearchLine()
0074 {
0075     m_searchLine->setFocus();
0076 }
0077 
0078 void SearchBar::slotClearSearch()
0079 {
0080     if (status() != Akregator::StatusSearchLine::AllArticles || !m_searchLine->text().trimmed().isEmpty()) {
0081         m_searchLine->clear();
0082         m_searchLine->setStatus(Akregator::StatusSearchLine::AllArticles);
0083         m_timer.stop();
0084         slotStopActiveSearch();
0085     }
0086 }
0087 
0088 void SearchBar::slotSetStatus(int status)
0089 {
0090     m_searchLine->setStatus(static_cast<Akregator::StatusSearchLine::Status>(status));
0091     triggerTimer();
0092 }
0093 
0094 void SearchBar::slotSetText(const QString &text)
0095 {
0096     m_searchLine->setText(text);
0097     triggerTimer();
0098 }
0099 
0100 void SearchBar::slotStatusChanged(Akregator::StatusSearchLine::Status /*status*/)
0101 {
0102     triggerTimer();
0103 }
0104 
0105 std::vector<QSharedPointer<const AbstractMatcher>> SearchBar::matchers() const
0106 {
0107     return m_matchers;
0108 }
0109 
0110 void SearchBar::updateQuickSearchLineText(const QString &searchLineShortcut)
0111 {
0112     m_searchLine->setPlaceholderText(i18n("Search articles...<%1>", searchLineShortcut));
0113 }
0114 
0115 void SearchBar::slotSearchStringChanged(const QString &search)
0116 {
0117     m_searchText = search;
0118     triggerTimer();
0119 }
0120 
0121 void SearchBar::slotStopActiveSearch()
0122 {
0123     std::vector<QSharedPointer<const AbstractMatcher>> matchers;
0124     Settings::setStatusFilter(m_searchLine->status());
0125     Settings::setTextFilter(m_searchText);
0126     m_matchers = matchers;
0127     Q_EMIT signalSearch(matchers);
0128 }
0129 
0130 void SearchBar::slotActivateSearch()
0131 {
0132     QList<Criterion> textCriteria;
0133     QList<Criterion> statusCriteria;
0134 
0135     if (!m_searchText.isEmpty()) {
0136         const QString searchTextDiacritic(TextUtils::ConvertText::normalize(m_searchText));
0137         textCriteria.reserve(3);
0138         Criterion subjCrit(Criterion::Title, Criterion::Contains, searchTextDiacritic);
0139         textCriteria << subjCrit;
0140         Criterion crit1(Criterion::Description, Criterion::Contains, searchTextDiacritic);
0141         textCriteria << crit1;
0142         Criterion authCrit(Criterion::Author, Criterion::Contains, searchTextDiacritic);
0143         textCriteria << authCrit;
0144     }
0145 
0146     switch (m_searchLine->status()) {
0147     case StatusSearchLine::AllArticles:
0148         break;
0149     case StatusSearchLine::NewArticles: {
0150         Criterion crit(Criterion::Status, Criterion::Equals, New);
0151         statusCriteria << crit;
0152         break;
0153     }
0154     case StatusSearchLine::UnreadArticles: {
0155         Criterion crit1(Criterion::Status, Criterion::Equals, New);
0156         Criterion crit2(Criterion::Status, Criterion::Equals, Unread);
0157         statusCriteria << crit1;
0158         statusCriteria << crit2;
0159         break;
0160     }
0161     case StatusSearchLine::ReadArticles: {
0162         Criterion crit(Criterion::Status, Criterion::Equals, Read);
0163         statusCriteria << crit;
0164         break;
0165     }
0166     case StatusSearchLine::ImportantArticles: {
0167         Criterion crit(Criterion::KeepFlag, Criterion::Equals, true);
0168         statusCriteria << crit;
0169         break;
0170     }
0171     }
0172 
0173     std::vector<QSharedPointer<const AbstractMatcher>> matchers;
0174     if (!textCriteria.isEmpty()) {
0175         matchers.push_back(QSharedPointer<const AbstractMatcher>(new ArticleMatcher(textCriteria, ArticleMatcher::LogicalOr)));
0176     }
0177     if (!statusCriteria.isEmpty()) {
0178         matchers.push_back(QSharedPointer<const AbstractMatcher>(new ArticleMatcher(statusCriteria, ArticleMatcher::LogicalOr)));
0179     }
0180     Settings::setStatusFilter(m_searchLine->status());
0181     Settings::setTextFilter(m_searchText);
0182     m_matchers = matchers;
0183     Q_EMIT signalSearch(matchers);
0184 }
0185 
0186 void Akregator::SearchBar::triggerTimer()
0187 {
0188     if (m_timer.isActive()) {
0189         m_timer.stop();
0190     }
0191 
0192     m_timer.start(200ms);
0193 }
0194 
0195 #include "moc_searchbar.cpp"