File indexing completed on 2024-04-28 15:51:57
0001 /* 0002 SPDX-FileCopyrightText: 2004 Enrico Ros <eros.kde@email.it> 0003 SPDX-FileCopyrightText: 2007, 2009-2010 Pino Toscano <pino@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "searchlineedit.h" 0009 0010 // local includes 0011 0012 // qt/kde includes 0013 #include <KBusyIndicatorWidget> 0014 #include <KColorScheme> 0015 #include <KLocalizedString> 0016 #include <KMessageBox> 0017 #include <QApplication> 0018 #include <QLayout> 0019 #include <QTimer> 0020 0021 SearchLineEdit::SearchLineEdit(QWidget *parent, Okular::Document *document) 0022 : KLineEdit(parent) 0023 , m_document(document) 0024 , m_minLength(0) 0025 , m_caseSensitivity(Qt::CaseInsensitive) 0026 , m_searchType(Okular::Document::AllDocument) 0027 , m_id(-1) 0028 , m_moveViewport(false) 0029 , m_changed(false) 0030 , m_fromStart(true) 0031 , m_findAsYouType(true) 0032 , m_searchRunning(false) 0033 { 0034 setObjectName(QStringLiteral("SearchLineEdit")); 0035 setClearButtonEnabled(true); 0036 0037 // a timer to ensure that we don't flood the document with requests to search 0038 m_inputDelayTimer = new QTimer(this); 0039 m_inputDelayTimer->setSingleShot(true); 0040 connect(m_inputDelayTimer, &QTimer::timeout, this, &SearchLineEdit::startSearch); 0041 0042 connect(this, &SearchLineEdit::textChanged, this, &SearchLineEdit::slotTextChanged); 0043 connect(document, &Okular::Document::searchFinished, this, &SearchLineEdit::searchFinished); 0044 } 0045 0046 void SearchLineEdit::clearText() 0047 { 0048 clear(); 0049 } 0050 0051 void SearchLineEdit::setSearchCaseSensitivity(Qt::CaseSensitivity cs) 0052 { 0053 m_caseSensitivity = cs; 0054 m_changed = true; 0055 } 0056 0057 void SearchLineEdit::setSearchMinimumLength(int length) 0058 { 0059 m_minLength = length; 0060 m_changed = true; 0061 } 0062 0063 void SearchLineEdit::setSearchType(Okular::Document::SearchType type) 0064 { 0065 if (type == m_searchType) { 0066 return; 0067 } 0068 0069 disconnect(this, &SearchLineEdit::returnKeyPressed, this, &SearchLineEdit::slotReturnPressed); 0070 0071 m_searchType = type; 0072 0073 // Only connect Enter for next/prev searches, the rest of searches are document global so 0074 // next/prev search does not make sense for them 0075 if (m_searchType == Okular::Document::NextMatch || m_searchType == Okular::Document::PreviousMatch) { 0076 connect(this, &SearchLineEdit::returnKeyPressed, this, &SearchLineEdit::slotReturnPressed); 0077 } 0078 0079 if (!m_changed) { 0080 m_changed = (m_searchType != Okular::Document::NextMatch && m_searchType != Okular::Document::PreviousMatch); 0081 } 0082 } 0083 0084 void SearchLineEdit::setSearchId(int id) 0085 { 0086 m_id = id; 0087 m_changed = true; 0088 } 0089 0090 void SearchLineEdit::setSearchColor(const QColor &color) 0091 { 0092 m_color = color; 0093 m_changed = true; 0094 } 0095 0096 void SearchLineEdit::setSearchMoveViewport(bool move) 0097 { 0098 m_moveViewport = move; 0099 } 0100 0101 void SearchLineEdit::setSearchFromStart(bool fromStart) 0102 { 0103 m_fromStart = fromStart; 0104 } 0105 0106 void SearchLineEdit::setFindAsYouType(bool findAsYouType) 0107 { 0108 m_findAsYouType = findAsYouType; 0109 } 0110 0111 void SearchLineEdit::resetSearch() 0112 { 0113 // Stop the currently running search, if any 0114 stopSearch(); 0115 0116 // Clear highlights 0117 if (m_id != -1) { 0118 m_document->resetSearch(m_id); 0119 } 0120 0121 // Make sure that the search will be reset at the next one 0122 m_changed = true; 0123 0124 // Reset input box color 0125 prepareLineEditForSearch(); 0126 } 0127 0128 bool SearchLineEdit::isSearchRunning() const 0129 { 0130 return m_searchRunning; 0131 } 0132 0133 void SearchLineEdit::restartSearch() 0134 { 0135 m_inputDelayTimer->stop(); 0136 m_inputDelayTimer->start(700); 0137 m_changed = true; 0138 } 0139 0140 void SearchLineEdit::stopSearch() 0141 { 0142 if (m_id == -1 || !m_searchRunning) { 0143 return; 0144 } 0145 0146 m_inputDelayTimer->stop(); 0147 // ### this should just cancel the search with id m_id, not all of them 0148 m_document->cancelSearch(); 0149 // flagging as "changed" so the search will be reset at the next one 0150 m_changed = true; 0151 } 0152 0153 void SearchLineEdit::findNext() 0154 { 0155 if (m_id == -1 || m_searchType != Okular::Document::NextMatch) { 0156 return; 0157 } 0158 0159 if (!m_changed) { 0160 Q_EMIT searchStarted(); 0161 m_searchRunning = true; 0162 m_document->continueSearch(m_id, m_searchType); 0163 } else { 0164 startSearch(); 0165 } 0166 } 0167 0168 void SearchLineEdit::findPrev() 0169 { 0170 if (m_id == -1 || m_searchType != Okular::Document::PreviousMatch) { 0171 return; 0172 } 0173 0174 if (!m_changed) { 0175 Q_EMIT searchStarted(); 0176 m_searchRunning = true; 0177 m_document->continueSearch(m_id, m_searchType); 0178 } else { 0179 startSearch(); 0180 } 0181 } 0182 0183 void SearchLineEdit::slotTextChanged(const QString &text) 0184 { 0185 Q_UNUSED(text); 0186 0187 prepareLineEditForSearch(); 0188 0189 if (m_findAsYouType) { 0190 restartSearch(); 0191 } else { 0192 m_changed = true; 0193 } 0194 } 0195 0196 void SearchLineEdit::prepareLineEditForSearch() 0197 { 0198 QPalette pal = palette(); 0199 const int textLength = text().length(); 0200 if (textLength > 0 && textLength < m_minLength) { 0201 const KColorScheme scheme(QPalette::Active, KColorScheme::View); 0202 pal.setBrush(QPalette::Base, scheme.background(KColorScheme::NegativeBackground)); 0203 pal.setBrush(QPalette::Text, scheme.foreground(KColorScheme::NegativeText)); 0204 } else { 0205 const QPalette qAppPalette = QApplication::palette(); 0206 pal.setColor(QPalette::Base, qAppPalette.color(QPalette::Base)); 0207 pal.setColor(QPalette::Text, qAppPalette.color(QPalette::Text)); 0208 } 0209 setPalette(pal); 0210 } 0211 0212 void SearchLineEdit::slotReturnPressed(const QString &text) 0213 { 0214 Q_UNUSED(text); 0215 0216 m_inputDelayTimer->stop(); 0217 prepareLineEditForSearch(); 0218 if (QApplication::keyboardModifiers() == Qt::ShiftModifier) { 0219 m_searchType = Okular::Document::PreviousMatch; 0220 findPrev(); 0221 } else { 0222 m_searchType = Okular::Document::NextMatch; 0223 findNext(); 0224 } 0225 } 0226 0227 void SearchLineEdit::startSearch() 0228 { 0229 if (m_id == -1 || !m_color.isValid()) { 0230 return; 0231 } 0232 0233 if (m_changed && (m_searchType == Okular::Document::NextMatch || m_searchType == Okular::Document::PreviousMatch)) { 0234 m_document->resetSearch(m_id); 0235 } 0236 m_changed = false; 0237 // search text if have more than 3 chars or else clear search 0238 QString thistext = text(); 0239 if (thistext.length() >= qMax(m_minLength, 1)) { 0240 Q_EMIT searchStarted(); 0241 m_searchRunning = true; 0242 m_document->searchText(m_id, thistext, m_fromStart, m_caseSensitivity, m_searchType, m_moveViewport, m_color); 0243 } else { 0244 m_document->resetSearch(m_id); 0245 } 0246 } 0247 0248 void SearchLineEdit::searchFinished(int id, Okular::Document::SearchStatus endStatus) 0249 { 0250 // ignore the searches not started by this search edit 0251 if (id != m_id) { 0252 return; 0253 } 0254 0255 // if not found, use warning colors 0256 if (endStatus == Okular::Document::NoMatchFound) { 0257 QPalette pal = palette(); 0258 const KColorScheme scheme(QPalette::Active, KColorScheme::View); 0259 pal.setBrush(QPalette::Base, scheme.background(KColorScheme::NegativeBackground)); 0260 pal.setBrush(QPalette::Text, scheme.foreground(KColorScheme::NegativeText)); 0261 setPalette(pal); 0262 } else { 0263 QPalette pal = palette(); 0264 const QPalette qAppPalette = QApplication::palette(); 0265 pal.setColor(QPalette::Base, qAppPalette.color(QPalette::Base)); 0266 pal.setColor(QPalette::Text, qAppPalette.color(QPalette::Text)); 0267 setPalette(pal); 0268 } 0269 0270 m_searchRunning = false; 0271 Q_EMIT searchStopped(); 0272 } 0273 0274 SearchLineWidget::SearchLineWidget(QWidget *parent, Okular::Document *document) 0275 : QWidget(parent) 0276 { 0277 QHBoxLayout *layout = new QHBoxLayout(this); 0278 layout->setContentsMargins(0, 0, 0, 0); 0279 0280 m_edit = new SearchLineEdit(this, document); 0281 layout->addWidget(m_edit); 0282 0283 m_anim = new KBusyIndicatorWidget(this); 0284 m_anim->setFixedSize(22, 22); 0285 layout->addWidget(m_anim); 0286 m_anim->hide(); 0287 0288 m_timer = new QTimer(this); 0289 m_timer->setSingleShot(true); 0290 connect(m_timer, &QTimer::timeout, this, &SearchLineWidget::slotTimedout); 0291 0292 connect(m_edit, &SearchLineEdit::searchStarted, this, &SearchLineWidget::slotSearchStarted); 0293 connect(m_edit, &SearchLineEdit::searchStopped, this, &SearchLineWidget::slotSearchStopped); 0294 } 0295 0296 SearchLineEdit *SearchLineWidget::lineEdit() const 0297 { 0298 return m_edit; 0299 } 0300 0301 void SearchLineWidget::slotSearchStarted() 0302 { 0303 m_timer->start(100); 0304 } 0305 0306 void SearchLineWidget::slotSearchStopped() 0307 { 0308 m_timer->stop(); 0309 m_anim->hide(); 0310 } 0311 0312 void SearchLineWidget::slotTimedout() 0313 { 0314 m_anim->show(); 0315 } 0316 0317 #include "moc_searchlineedit.cpp"