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"