File indexing completed on 2025-04-27 03:58:40
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2007-11-25 0007 * Description : a bar used to search a string - version not based on database models 0008 * 0009 * SPDX-FileCopyrightText: 2007-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009-2010 by Johannes Wienke <languitar at semipol dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "searchtextbar.h" 0017 0018 // Qt includes 0019 0020 #include <QContextMenuEvent> 0021 #include <QMenu> 0022 #include <QColor> 0023 #include <QPalette> 0024 #include <QString> 0025 #include <QMap> 0026 #include <QTimer> 0027 0028 // KDE includes 0029 0030 #include <klocalizedstring.h> 0031 #include <kconfiggroup.h> 0032 0033 // Local includes 0034 0035 #include "digikam_debug.h" 0036 0037 namespace Digikam 0038 { 0039 0040 bool operator==(const SearchTextSettings& a, const SearchTextSettings& b) 0041 { 0042 return ((a.caseSensitive == b.caseSensitive) && (a.text == b.text)); 0043 } 0044 0045 class Q_DECL_HIDDEN SearchTextBar::Private 0046 { 0047 public: 0048 0049 explicit Private() 0050 : optionAutoCompletionModeEntry(QLatin1String("AutoCompletionMode")), 0051 optionCaseSensitiveEntry (QLatin1String("CaseSensitive")), 0052 textQueryCompletion (false), 0053 hasCaseSensitive (true), 0054 highlightOnResult (true), 0055 hasResultColor (140, 220, 140), 0056 hasNoResultColor (220, 140, 140), 0057 completer (nullptr), 0058 searchTimer (nullptr) 0059 { 0060 } 0061 0062 QString optionAutoCompletionModeEntry; 0063 QString optionCaseSensitiveEntry; 0064 0065 bool textQueryCompletion; 0066 bool hasCaseSensitive; 0067 bool highlightOnResult; 0068 0069 QColor hasResultColor; 0070 QColor hasNoResultColor; 0071 0072 ModelCompleter* completer; 0073 0074 SearchTextSettings settings; 0075 QTimer* searchTimer; ///< Timer to delay search processing and not signals bombarding at each key pressed. 0076 }; 0077 0078 SearchTextBar::SearchTextBar(QWidget* const parent, const QString& name, const QString& msg) 0079 : QLineEdit (parent), 0080 StateSavingObject(this), 0081 d (new Private) 0082 { 0083 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 0084 setObjectName(name + QLatin1String(" Search Text Tool")); 0085 setClearButtonEnabled(true); 0086 setPlaceholderText(msg.isNull() ? i18nc("@info: search text bar", "Search...") : msg); 0087 0088 d->completer = new ModelCompleter(this); 0089 setCompleter(d->completer); 0090 0091 d->searchTimer = new QTimer(this); 0092 d->searchTimer->setInterval(500); 0093 d->searchTimer->setSingleShot(true); 0094 0095 connect(d->searchTimer, SIGNAL(timeout()), 0096 this, SLOT(slotTextChanged())); 0097 0098 connect(this, SIGNAL(textChanged(QString)), 0099 d->searchTimer, SLOT(start())); 0100 0101 connect(d->completer, QOverload<>::of(&ModelCompleter::signalActivated), 0102 this, [=]() 0103 { 0104 Q_EMIT completerActivated(); 0105 } 0106 ); 0107 0108 connect(d->completer, QOverload<const int>::of(&ModelCompleter::signalHighlighted), 0109 this, [=](const int albumId) 0110 { 0111 Q_EMIT completerHighlighted(albumId); 0112 } 0113 ); 0114 0115 loadState(); 0116 } 0117 0118 SearchTextBar::~SearchTextBar() 0119 { 0120 saveState(); 0121 delete d; 0122 } 0123 0124 void SearchTextBar::doLoadState() 0125 { 0126 KConfigGroup group = getConfigGroup(); 0127 completer()->setCompletionMode((QCompleter::CompletionMode)group.readEntry(entryName(d->optionAutoCompletionModeEntry), 0128 (int)QCompleter::PopupCompletion)); 0129 d->settings.caseSensitive = (Qt::CaseSensitivity)group.readEntry(entryName(d->optionCaseSensitiveEntry), 0130 (int)Qt::CaseInsensitive); 0131 setIgnoreCase(d->settings.caseSensitive == Qt::CaseInsensitive); 0132 } 0133 0134 void SearchTextBar::doSaveState() 0135 { 0136 KConfigGroup group = getConfigGroup(); 0137 0138 if (completer()->completionMode() != QCompleter::PopupCompletion) 0139 { 0140 group.writeEntry(entryName(d->optionAutoCompletionModeEntry), (int)completer()->completionMode()); 0141 } 0142 else 0143 { 0144 group.deleteEntry(entryName(d->optionAutoCompletionModeEntry)); 0145 } 0146 0147 group.writeEntry(entryName(d->optionCaseSensitiveEntry), (int)d->settings.caseSensitive); 0148 group.sync(); 0149 } 0150 0151 void SearchTextBar::setTextQueryCompletion(bool b) 0152 { 0153 d->textQueryCompletion = b; 0154 } 0155 0156 bool SearchTextBar::hasTextQueryCompletion() const 0157 { 0158 return d->textQueryCompletion; 0159 } 0160 0161 void SearchTextBar::setHighlightOnResult(bool highlight) 0162 { 0163 d->highlightOnResult = highlight; 0164 0165 if (!highlight) 0166 { 0167 setPalette(QPalette()); 0168 } 0169 } 0170 0171 SearchTextBar::HighlightState SearchTextBar::getCurrentHighlightState() const 0172 { 0173 if (palette() == QPalette()) 0174 { 0175 return NEUTRAL; 0176 } 0177 else if (palette().color(QPalette::Active, QPalette::Base) == d->hasResultColor) 0178 { 0179 return HAS_RESULT; 0180 } 0181 else if (palette().color(QPalette::Active, QPalette::Base) == d->hasNoResultColor) 0182 { 0183 return NO_RESULT; 0184 } 0185 0186 qCDebug(DIGIKAM_WIDGETS_LOG) << "Impossible highlighting state"; 0187 0188 return NEUTRAL; 0189 } 0190 0191 void SearchTextBar::setCaseSensitive(bool b) 0192 { 0193 d->hasCaseSensitive = b; 0194 0195 // Reset settings if selecting case sensitivity is not allowed 0196 0197 if (!b) 0198 { 0199 d->settings.caseSensitive = Qt::CaseInsensitive; 0200 } 0201 0202 // Re-Q_EMIT signal with changed settings 0203 0204 if (!text().isEmpty()) 0205 { 0206 Q_EMIT signalSearchTextSettings(d->settings); 0207 } 0208 } 0209 0210 bool SearchTextBar::hasCaseSensitive() const 0211 { 0212 return d->hasCaseSensitive; 0213 } 0214 0215 void SearchTextBar::setSearchTextSettings(const SearchTextSettings& settings) 0216 { 0217 d->settings = settings; 0218 } 0219 0220 SearchTextSettings SearchTextBar::searchTextSettings() const 0221 { 0222 return d->settings; 0223 } 0224 0225 ModelCompleter* SearchTextBar::completerModel() const 0226 { 0227 return d->completer; 0228 } 0229 0230 void SearchTextBar::slotTextChanged() 0231 { 0232 QString txt = text(); 0233 0234 if (txt.isEmpty()) 0235 { 0236 setPalette(QPalette()); 0237 } 0238 0239 d->settings.text = txt; 0240 0241 Q_EMIT signalSearchTextSettings(d->settings); 0242 } 0243 0244 void SearchTextBar::slotSearchResult(bool match) 0245 { 0246 // only highlight if text is not empty or highlighting is disabled. 0247 0248 if (text().isEmpty() || !d->highlightOnResult) 0249 { 0250 setPalette(QPalette()); 0251 return; 0252 } 0253 0254 QPalette pal = palette(); 0255 pal.setColor(QPalette::Active, QPalette::Base, 0256 match ? d->hasResultColor 0257 : d->hasNoResultColor); 0258 pal.setColor(QPalette::Active, QPalette::Text, Qt::black); 0259 setPalette(pal); 0260 } 0261 0262 void SearchTextBar::contextMenuEvent(QContextMenuEvent* e) 0263 { 0264 QAction* cs = nullptr; 0265 QMenu* const menu = createStandardContextMenu(); 0266 0267 if (d->hasCaseSensitive) 0268 { 0269 cs = menu->addAction(i18nc("@info: search text bar", "Case sensitive")); 0270 cs->setCheckable(true); 0271 cs->setChecked(d->settings.caseSensitive == Qt::CaseInsensitive ? false : true); 0272 } 0273 0274 menu->exec(e->globalPos()); 0275 0276 if (d->hasCaseSensitive) 0277 { 0278 setIgnoreCase(!cs->isChecked()); 0279 } 0280 0281 delete menu; 0282 } 0283 0284 void SearchTextBar::setIgnoreCase(bool ignore) 0285 { 0286 if (hasCaseSensitive()) 0287 { 0288 if (ignore) 0289 { 0290 completer()->setCaseSensitivity(Qt::CaseInsensitive); 0291 d->settings.caseSensitive = Qt::CaseInsensitive; 0292 } 0293 else 0294 { 0295 completer()->setCaseSensitivity(Qt::CaseSensitive); 0296 d->settings.caseSensitive = Qt::CaseSensitive; 0297 } 0298 } 0299 else 0300 { 0301 completer()->setCaseSensitivity(Qt::CaseInsensitive); 0302 d->settings.caseSensitive = Qt::CaseInsensitive; 0303 } 0304 0305 // Re-Q_EMIT signal with changed settings 0306 0307 if (!text().isEmpty()) 0308 { 0309 Q_EMIT signalSearchTextSettings(d->settings); 0310 } 0311 } 0312 0313 } // namespace Digikam 0314 0315 #include "moc_searchtextbar.cpp"