File indexing completed on 2025-02-16 04:56:04

0001 /* SPDX-FileCopyrightText: 2010 Torgny Nyblom <nyblom@kde.org>
0002  * SPDX-FileCopyrightText: 2010-2024 Laurent Montel <montel@kde.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "findbarbase.h"
0008 #include <KStatefulBrush>
0009 
0010 #include <PimCommon/LineEditWithCompleterNg>
0011 
0012 #include <KColorScheme>
0013 #include <KLocalizedString>
0014 #include <QIcon>
0015 
0016 #include <QEvent>
0017 #include <QHBoxLayout>
0018 #include <QKeyEvent>
0019 #include <QLabel>
0020 #include <QMenu>
0021 #include <QPushButton>
0022 #include <QTimer>
0023 #include <QToolButton>
0024 
0025 using namespace KSieveUi;
0026 
0027 FindBarBase::FindBarBase(QWidget *parent)
0028     : QWidget(parent)
0029 {
0030     auto lay = new QHBoxLayout(this);
0031     lay->setContentsMargins(2, 2, 2, 2);
0032 
0033     auto closeBtn = new QToolButton(this);
0034     closeBtn->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
0035     closeBtn->setObjectName(QLatin1StringView("close"));
0036     closeBtn->setIconSize(QSize(16, 16));
0037     closeBtn->setToolTip(i18n("Close"));
0038 
0039 #ifndef QT_NO_ACCESSIBILITY
0040     closeBtn->setAccessibleName(i18n("Close"));
0041 #endif
0042 
0043     closeBtn->setAutoRaise(true);
0044     lay->addWidget(closeBtn);
0045 
0046     auto label = new QLabel(i18nc("Find text", "F&ind:"), this);
0047     lay->addWidget(label);
0048 
0049     mSearch = new PimCommon::LineEditWithCompleterNg(this);
0050     mSearch->setObjectName(QLatin1StringView("searchline"));
0051     mSearch->setToolTip(i18n("Text to search for"));
0052     mSearch->setClearButtonEnabled(true);
0053     label->setBuddy(mSearch);
0054     lay->addWidget(mSearch);
0055 
0056     mFindNextBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("go-down-search")), i18nc("Find and go to the next search match", "Next"), this);
0057     mFindNextBtn->setToolTip(i18n("Jump to next match"));
0058     mFindNextBtn->setObjectName(QLatin1StringView("findnext"));
0059     lay->addWidget(mFindNextBtn);
0060     mFindNextBtn->setEnabled(false);
0061 
0062     mFindPrevBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("go-up-search")), i18nc("Find and go to the previous search match", "Previous"), this);
0063     mFindPrevBtn->setToolTip(i18n("Jump to previous match"));
0064     mFindPrevBtn->setObjectName(QLatin1StringView("findprevious"));
0065     lay->addWidget(mFindPrevBtn);
0066     mFindPrevBtn->setEnabled(false);
0067 
0068     auto optionsBtn = new QPushButton(this);
0069     optionsBtn->setText(i18n("Options"));
0070     optionsBtn->setToolTip(i18n("Modify search behavior"));
0071     mOptionsMenu = new QMenu(optionsBtn);
0072     mCaseSensitiveAct = mOptionsMenu->addAction(i18n("Case sensitive"));
0073     mCaseSensitiveAct->setCheckable(true);
0074 
0075     optionsBtn->setMenu(mOptionsMenu);
0076     lay->addWidget(optionsBtn);
0077 
0078     connect(closeBtn, &QToolButton::clicked, this, &FindBarBase::closeBar);
0079     connect(mFindNextBtn, &QPushButton::clicked, this, &FindBarBase::findNext);
0080     connect(mFindPrevBtn, &QPushButton::clicked, this, &FindBarBase::findPrev);
0081     connect(mCaseSensitiveAct, &QAction::toggled, this, &FindBarBase::caseSensitivityChanged);
0082     connect(mSearch, &QLineEdit::textChanged, this, &FindBarBase::autoSearch);
0083 
0084     mStatus = new QLabel(this);
0085     mStatus->setObjectName(QLatin1StringView("status"));
0086     mStatus->setTextFormat(Qt::PlainText);
0087     QFontMetrics fm(mStatus->font());
0088     mNotFoundString = i18n("Phrase not found");
0089     mStatus->setFixedWidth(fm.boundingRect(mNotFoundString).width());
0090     lay->addWidget(mStatus);
0091 
0092     setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0093     // lay->addStretch();
0094     hide();
0095 }
0096 
0097 FindBarBase::~FindBarBase() = default;
0098 
0099 QMenu *FindBarBase::optionsMenu()
0100 {
0101     return mOptionsMenu;
0102 }
0103 
0104 QString FindBarBase::text() const
0105 {
0106     return mSearch->text();
0107 }
0108 
0109 void FindBarBase::setText(const QString &text)
0110 {
0111     mSearch->setText(text);
0112 }
0113 
0114 void FindBarBase::focusAndSetCursor()
0115 {
0116     setFocus();
0117     mStatus->clear();
0118     mSearch->selectAll();
0119     mSearch->setFocus();
0120 }
0121 
0122 void FindBarBase::autoSearch(const QString &str)
0123 {
0124     const bool isNotEmpty = (!str.isEmpty());
0125     mFindPrevBtn->setEnabled(isNotEmpty);
0126     mFindNextBtn->setEnabled(isNotEmpty);
0127     if (isNotEmpty) {
0128         QTimer::singleShot(0, this, [this]() {
0129             slotSearchText();
0130         });
0131     } else {
0132         clearSelections();
0133     }
0134 }
0135 
0136 void FindBarBase::slotSearchText(bool backward, bool isAutoSearch)
0137 {
0138     searchText(backward, isAutoSearch);
0139 }
0140 
0141 void FindBarBase::setFoundMatch(bool match)
0142 {
0143 #ifndef QT_NO_STYLE_STYLESHEET
0144     QString styleSheet;
0145 
0146     if (!mSearch->text().isEmpty()) {
0147         if (mNegativeBackground.isEmpty()) {
0148             KStatefulBrush bgBrush(KColorScheme::View, KColorScheme::PositiveBackground);
0149             mPositiveBackground = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(mSearch->palette()).color().name());
0150             bgBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeBackground);
0151             mNegativeBackground = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(mSearch->palette()).color().name());
0152         }
0153         if (match) {
0154             styleSheet = mPositiveBackground;
0155             mStatus->clear();
0156         } else {
0157             styleSheet = mNegativeBackground;
0158             mStatus->setText(mNotFoundString);
0159         }
0160     }
0161     mSearch->setStyleSheet(styleSheet);
0162 #endif
0163 }
0164 
0165 void FindBarBase::searchText(bool backward, bool isAutoSearch)
0166 {
0167     Q_UNUSED(backward)
0168     Q_UNUSED(isAutoSearch)
0169 }
0170 
0171 void FindBarBase::addToCompletion(const QString &text)
0172 {
0173     mSearch->addCompletionItem(text);
0174 }
0175 
0176 void FindBarBase::findNext()
0177 {
0178     searchText(false, false);
0179     addToCompletion(mLastSearchStr);
0180 }
0181 
0182 void FindBarBase::findPrev()
0183 {
0184     searchText(true, false);
0185     addToCompletion(mLastSearchStr);
0186 }
0187 
0188 void FindBarBase::caseSensitivityChanged(bool b)
0189 {
0190     updateSensitivity(b);
0191 }
0192 
0193 void FindBarBase::updateSensitivity(bool)
0194 {
0195 }
0196 
0197 void FindBarBase::slotHighlightAllChanged(bool b)
0198 {
0199     updateHighLight(b);
0200 }
0201 
0202 void FindBarBase::updateHighLight(bool)
0203 {
0204 }
0205 
0206 void FindBarBase::clearSelections()
0207 {
0208     setFoundMatch(false);
0209 }
0210 
0211 void FindBarBase::closeBar()
0212 {
0213     // Make sure that all old searches are cleared
0214     mSearch->clear();
0215     clearSelections();
0216     mSearch->clearFocus();
0217     Q_EMIT hideFindBar();
0218 }
0219 
0220 bool FindBarBase::event(QEvent *e)
0221 {
0222     // Close the bar when pressing Escape.
0223     // Not using a QShortcut for this because it could conflict with
0224     // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
0225     // With a shortcut override we can catch this before it gets to kactions.
0226     const bool shortCutOverride = (e->type() == QEvent::ShortcutOverride);
0227     if (shortCutOverride || e->type() == QEvent::KeyPress) {
0228         auto kev = static_cast<QKeyEvent *>(e);
0229         if (kev->key() == Qt::Key_Escape) {
0230             if (shortCutOverride) {
0231                 e->accept();
0232                 return true;
0233             }
0234             e->accept();
0235             closeBar();
0236             return true;
0237         } else if (kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return) {
0238             e->accept();
0239             if (shortCutOverride) {
0240                 return true;
0241             }
0242             if (mSearch->text().isEmpty()) {
0243                 return true;
0244             }
0245             if (kev->modifiers() & Qt::ShiftModifier) {
0246                 findPrev();
0247             } else if (kev->modifiers() == Qt::NoModifier) {
0248                 findNext();
0249             }
0250             return true;
0251         }
0252     }
0253     return QWidget::event(e);
0254 }
0255 
0256 #include "moc_findbarbase.cpp"