File indexing completed on 2024-05-26 04:46:49

0001 /*
0002    SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "texteditfindbarbase.h"
0008 #include "textcustomeditor_debug.h"
0009 #include "widgets/textfindreplacewidget.h"
0010 #include <KLocalizedString>
0011 #include <QEvent>
0012 #include <QHBoxLayout>
0013 #include <QIcon>
0014 #include <QKeyEvent>
0015 #include <QLineEdit>
0016 #include <QTimer>
0017 #include <QToolButton>
0018 #include <QVBoxLayout>
0019 
0020 using namespace TextCustomEditor;
0021 
0022 TextEditFindBarBase::TextEditFindBarBase(QWidget *parent)
0023     : QWidget(parent)
0024     , mFindWidget(new TextFindWidget(this))
0025     , mReplaceWidget(new TextReplaceWidget(this))
0026 {
0027     auto topLayout = new QVBoxLayout(this);
0028     auto lay = new QHBoxLayout;
0029     topLayout->addLayout(lay);
0030 
0031     auto closeBtn = new QToolButton(this);
0032     closeBtn->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
0033     closeBtn->setIconSize(QSize(16, 16));
0034     closeBtn->setToolTip(i18n("Close"));
0035 
0036 #ifndef QT_NO_ACCESSIBILITY
0037     closeBtn->setAccessibleName(i18n("Close"));
0038 #endif
0039 
0040     closeBtn->setAutoRaise(true);
0041     lay->addWidget(closeBtn);
0042 
0043     lay->addWidget(mFindWidget);
0044 
0045     topLayout->addWidget(mReplaceWidget);
0046     mReplaceWidget->hide();
0047 
0048     connect(closeBtn, &QToolButton::clicked, this, &TextEditFindBarBase::closeBar);
0049     connect(mFindWidget, &TextFindWidget::findNext, this, &TextEditFindBarBase::findNext);
0050     connect(mFindWidget, &TextFindWidget::findPrev, this, &TextEditFindBarBase::findPrev);
0051     connect(mFindWidget, &TextFindWidget::updateSearchOptions, this, &TextEditFindBarBase::slotUpdateSearchOptions);
0052     connect(mFindWidget, &TextFindWidget::autoSearch, this, &TextEditFindBarBase::autoSearch);
0053     connect(mFindWidget, &TextFindWidget::clearSearch, this, &TextEditFindBarBase::slotClearSearch);
0054     connect(mFindWidget, &TextFindWidget::searchStringEmpty, mReplaceWidget, &TextReplaceWidget::slotSearchStringEmpty);
0055     connect(mReplaceWidget, &TextReplaceWidget::replaceText, this, &TextEditFindBarBase::slotReplaceText);
0056     connect(mReplaceWidget, &TextReplaceWidget::replaceAllText, this, &TextEditFindBarBase::slotReplaceAllText);
0057     setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0058     hide();
0059 }
0060 
0061 TextEditFindBarBase::~TextEditFindBarBase() = default;
0062 
0063 void TextEditFindBarBase::showFind()
0064 {
0065     if (documentIsEmpty()) {
0066         return;
0067     }
0068     mReplaceWidget->slotSearchStringEmpty(mFindWidget->searchText().isEmpty());
0069     show();
0070     if (mReplaceWidget->isVisible()) {
0071         mReplaceWidget->hide();
0072         updateGeometry();
0073     }
0074 }
0075 
0076 void TextEditFindBarBase::setHideWhenClose(bool hide)
0077 {
0078     mHideWhenClose = hide;
0079 }
0080 
0081 void TextEditFindBarBase::showReplace()
0082 {
0083     if (viewIsReadOnly()) {
0084         return;
0085     }
0086     if (documentIsEmpty()) {
0087         return;
0088     }
0089     mReplaceWidget->slotSearchStringEmpty(mFindWidget->searchText().isEmpty());
0090     show();
0091     if (!mReplaceWidget->isVisible()) {
0092         mReplaceWidget->show();
0093         updateGeometry();
0094     }
0095 }
0096 
0097 void TextEditFindBarBase::setText(const QString &text)
0098 {
0099     mFindWidget->searchLineEdit()->setText(text);
0100 }
0101 
0102 QString TextEditFindBarBase::text() const
0103 {
0104     return mFindWidget->searchText();
0105 }
0106 
0107 void TextEditFindBarBase::focusAndSetCursor()
0108 {
0109     setFocus();
0110     mFindWidget->searchLineEdit()->selectAll();
0111     mFindWidget->searchLineEdit()->setFocus();
0112 }
0113 
0114 void TextEditFindBarBase::slotClearSearch()
0115 {
0116     clearSelections();
0117 }
0118 
0119 void TextEditFindBarBase::autoSearch(const QString &str)
0120 {
0121     const bool isNotEmpty = (!str.isEmpty());
0122     if (isNotEmpty) {
0123         QTimer::singleShot(0, this, [this]() {
0124             slotSearchText();
0125         });
0126     } else {
0127         clearSelections();
0128     }
0129 }
0130 
0131 void TextEditFindBarBase::messageInfo(bool backward, bool isAutoSearch, bool found)
0132 {
0133     Q_UNUSED(backward)
0134     if (!found && !isAutoSearch) {
0135         QString str = mLastSearchStr;
0136         if (str.length() > 39) {
0137             str.truncate(40);
0138             str += QLatin1String("...");
0139         }
0140         Q_EMIT displayMessageIndicator(i18n("End of message reached.\nPhrase '%1' could not be found.", str));
0141     }
0142 }
0143 
0144 bool TextEditFindBarBase::searchText(bool backward, bool isAutoSearch)
0145 {
0146     mLastSearchStr = mFindWidget->searchText();
0147     mLastSearchRegExp = mFindWidget->searchRegularExpression();
0148     TextEditFindBarBase::FindFlags searchOptions = mFindWidget->searchOptions();
0149     if (backward) {
0150         searchOptions |= TextEditFindBarBase::FindBackward;
0151     }
0152 
0153     if (isAutoSearch) {
0154         autoSearchMoveCursor();
0155     } else if (mFindWidget->isRegularExpression()) {
0156         // TODO
0157     } else if (!mLastSearchStr.contains(mFindWidget->searchText(), Qt::CaseSensitive)) {
0158         clearSelections();
0159     }
0160 
0161     bool found = false;
0162     if (mFindWidget->isRegularExpression()) {
0163         found = searchInDocument(mLastSearchRegExp, searchOptions);
0164     } else {
0165         found = searchInDocument(mLastSearchStr, searchOptions);
0166     }
0167     mFindWidget->setFoundMatch(found);
0168     messageInfo(backward, isAutoSearch, found);
0169     return found;
0170 }
0171 
0172 void TextEditFindBarBase::findNext()
0173 {
0174     searchText(false, false);
0175 }
0176 
0177 void TextEditFindBarBase::findPrev()
0178 {
0179     searchText(true, false);
0180 }
0181 
0182 void TextEditFindBarBase::slotUpdateSearchOptions()
0183 {
0184     const TextEditFindBarBase::FindFlags searchOptions = mFindWidget->searchOptions();
0185     mLastSearchStr = mFindWidget->searchText();
0186     mLastSearchRegExp = mFindWidget->searchRegularExpression();
0187     if (mFindWidget->isRegularExpression()) {
0188         searchInDocument(mLastSearchRegExp, searchOptions);
0189     } else {
0190         searchInDocument(mLastSearchStr, searchOptions);
0191     }
0192 }
0193 
0194 void TextEditFindBarBase::clearSelections()
0195 {
0196     mFindWidget->setFoundMatch(false);
0197 }
0198 
0199 void TextEditFindBarBase::closeBar()
0200 {
0201     // Make sure that all old searches are cleared
0202     mFindWidget->searchLineEdit()->setText(QString());
0203     mReplaceWidget->replaceLineEdit()->setText(QString());
0204     clearSelections();
0205     mReplaceWidget->hide();
0206     updateGeometry();
0207     if (mHideWhenClose) {
0208         hide();
0209     }
0210     Q_EMIT hideFindBar();
0211 }
0212 
0213 bool TextEditFindBarBase::event(QEvent *e)
0214 {
0215     // Close the bar when pressing Escape.
0216     // Not using a QShortcut for this because it could conflict with
0217     // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
0218     // With a shortcut override we can catch this before it gets to kactions.
0219     const bool shortCutOverride = (e->type() == QEvent::ShortcutOverride);
0220     if (shortCutOverride || e->type() == QEvent::KeyPress) {
0221         auto kev = static_cast<QKeyEvent *>(e);
0222         if (kev->key() == Qt::Key_Escape) {
0223             if (shortCutOverride) {
0224                 e->accept();
0225                 return true;
0226             }
0227             e->accept();
0228             closeBar();
0229             return true;
0230         } else if (kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return) {
0231             e->accept();
0232             if (shortCutOverride) {
0233                 return true;
0234             }
0235             if (mFindWidget->searchText().isEmpty()) {
0236                 return true;
0237             }
0238 
0239             if (kev->modifiers() & Qt::ShiftModifier) {
0240                 findPrev();
0241             } else if (kev->modifiers() == Qt::NoModifier) {
0242                 findNext();
0243             }
0244             return true;
0245         }
0246     }
0247     return QWidget::event(e);
0248 }
0249 
0250 #include "moc_texteditfindbarbase.cpp"