File indexing completed on 2024-12-01 03:41:41
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2003 Scott Wheeler <wheeler@kde.org> 0004 SPDX-FileCopyrightText: 2004 Gustavo Sverzut Barbieri <gsbarbieri@users.sourceforge.net> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "klistwidgetsearchline.h" 0010 0011 #include <QApplication> 0012 #include <QEvent> 0013 #include <QKeyEvent> 0014 #include <QListWidget> 0015 #include <QTimer> 0016 0017 class KListWidgetSearchLinePrivate 0018 { 0019 public: 0020 KListWidgetSearchLinePrivate(KListWidgetSearchLine *parent) 0021 : q(parent) 0022 { 0023 } 0024 0025 void _k_listWidgetDeleted(); 0026 void _k_queueSearch(const QString &); 0027 void _k_activateSearch(); 0028 void _k_rowsInserted(const QModelIndex &, int, int); 0029 void _k_dataChanged(const QModelIndex &, const QModelIndex &); 0030 0031 void init(QListWidget *listWidget = nullptr); 0032 void updateHiddenState(int start, int end); 0033 0034 KListWidgetSearchLine *const q; 0035 QListWidget *listWidget = nullptr; 0036 Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive; 0037 bool activeSearch = false; 0038 QString search; 0039 int queuedSearches = 0; 0040 }; 0041 0042 /****************************************************************************** 0043 * Public Methods * 0044 *****************************************************************************/ 0045 KListWidgetSearchLine::KListWidgetSearchLine(QWidget *parent, QListWidget *listWidget) 0046 : QLineEdit(parent) 0047 , d(new KListWidgetSearchLinePrivate(this)) 0048 0049 { 0050 d->init(listWidget); 0051 } 0052 0053 KListWidgetSearchLine::~KListWidgetSearchLine() 0054 { 0055 clear(); // returning items back to listWidget 0056 } 0057 0058 Qt::CaseSensitivity KListWidgetSearchLine::caseSensitive() const 0059 { 0060 return d->caseSensitivity; 0061 } 0062 0063 QListWidget *KListWidgetSearchLine::listWidget() const 0064 { 0065 return d->listWidget; 0066 } 0067 0068 /****************************************************************************** 0069 * Public Slots * 0070 *****************************************************************************/ 0071 void KListWidgetSearchLine::updateSearch(const QString &s) 0072 { 0073 d->search = s.isNull() ? text() : s; 0074 if (d->listWidget) { 0075 d->updateHiddenState(0, d->listWidget->count() - 1); 0076 } 0077 } 0078 0079 void KListWidgetSearchLine::clear() 0080 { 0081 // Show items back to QListWidget 0082 if (d->listWidget != nullptr) { 0083 for (int i = 0; i < d->listWidget->count(); ++i) { 0084 d->listWidget->item(i)->setHidden(false); 0085 } 0086 } 0087 0088 d->search = QString(); 0089 d->queuedSearches = 0; 0090 QLineEdit::clear(); 0091 } 0092 0093 void KListWidgetSearchLine::setCaseSensitivity(Qt::CaseSensitivity cs) 0094 { 0095 d->caseSensitivity = cs; 0096 } 0097 0098 void KListWidgetSearchLine::setListWidget(QListWidget *lw) 0099 { 0100 if (d->listWidget != nullptr) { 0101 disconnect(d->listWidget, SIGNAL(destroyed()), this, SLOT(_k_listWidgetDeleted())); 0102 d->listWidget->model()->disconnect(this); 0103 } 0104 0105 d->listWidget = lw; 0106 0107 if (lw != nullptr) { 0108 // clang-format off 0109 connect(d->listWidget, SIGNAL(destroyed()), this, SLOT(_k_listWidgetDeleted())); 0110 connect(d->listWidget->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(_k_rowsInserted(QModelIndex,int,int))); 0111 connect(d->listWidget->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(_k_dataChanged(QModelIndex,QModelIndex))); 0112 // clang-format on 0113 setEnabled(true); 0114 } else { 0115 setEnabled(false); 0116 } 0117 } 0118 0119 /****************************************************************************** 0120 * Protected Methods * 0121 *****************************************************************************/ 0122 bool KListWidgetSearchLine::itemMatches(const QListWidgetItem *item, const QString &s) const 0123 { 0124 if (s.isEmpty()) { 0125 return true; 0126 } 0127 0128 if (item == nullptr) { 0129 return false; 0130 } 0131 0132 return (item->text().indexOf(s, 0, caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0); 0133 } 0134 0135 void KListWidgetSearchLinePrivate::init(QListWidget *_listWidget) 0136 { 0137 listWidget = _listWidget; 0138 0139 QObject::connect(q, SIGNAL(textChanged(QString)), q, SLOT(_k_queueSearch(QString))); 0140 0141 if (listWidget != nullptr) { 0142 // clang-format off 0143 QObject::connect(listWidget, SIGNAL(destroyed()), q, SLOT(_k_listWidgetDeleted())); 0144 QObject::connect(listWidget->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_rowsInserted(QModelIndex,int,int))); 0145 QObject::connect(listWidget->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_dataChanged(QModelIndex,QModelIndex))); 0146 // clang-format on 0147 q->setEnabled(true); 0148 } else { 0149 q->setEnabled(false); 0150 } 0151 q->setClearButtonEnabled(true); 0152 } 0153 0154 void KListWidgetSearchLinePrivate::updateHiddenState(int start, int end) 0155 { 0156 if (!listWidget) { 0157 return; 0158 } 0159 0160 QListWidgetItem *currentItem = listWidget->currentItem(); 0161 0162 // Remove Non-Matching items 0163 for (int index = start; index <= end; ++index) { 0164 QListWidgetItem *item = listWidget->item(index); 0165 if (!q->itemMatches(item, search)) { 0166 item->setHidden(true); 0167 0168 if (item == currentItem) { 0169 currentItem = nullptr; // It's not in listWidget anymore. 0170 } 0171 } else if (item->isHidden()) { 0172 item->setHidden(false); 0173 } 0174 } 0175 0176 if (listWidget->isSortingEnabled()) { 0177 listWidget->sortItems(); 0178 } 0179 0180 if (currentItem != nullptr) { 0181 listWidget->scrollToItem(currentItem); 0182 } 0183 } 0184 0185 bool KListWidgetSearchLine::event(QEvent *event) 0186 { 0187 if (event->type() == QEvent::KeyPress) { 0188 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); 0189 if (keyEvent->matches(QKeySequence::MoveToNextLine) || keyEvent->matches(QKeySequence::SelectNextLine) 0190 || keyEvent->matches(QKeySequence::MoveToPreviousLine) || keyEvent->matches(QKeySequence::SelectPreviousLine) 0191 || keyEvent->matches(QKeySequence::MoveToNextPage) || keyEvent->matches(QKeySequence::SelectNextPage) 0192 || keyEvent->matches(QKeySequence::MoveToPreviousPage) || keyEvent->matches(QKeySequence::SelectPreviousPage)) { 0193 if (d->listWidget) { 0194 QApplication::sendEvent(d->listWidget, event); 0195 return true; 0196 } 0197 } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { 0198 if (d->listWidget) { 0199 QApplication::sendEvent(d->listWidget, event); 0200 return true; 0201 } 0202 } 0203 } 0204 return QLineEdit::event(event); 0205 } 0206 /****************************************************************************** 0207 * Protected Slots * 0208 *****************************************************************************/ 0209 void KListWidgetSearchLinePrivate::_k_queueSearch(const QString &s) 0210 { 0211 queuedSearches++; 0212 search = s; 0213 QTimer::singleShot(200, q, SLOT(_k_activateSearch())); 0214 } 0215 0216 void KListWidgetSearchLinePrivate::_k_activateSearch() 0217 { 0218 queuedSearches--; 0219 0220 if (queuedSearches <= 0) { 0221 q->updateSearch(search); 0222 queuedSearches = 0; 0223 } 0224 } 0225 0226 /****************************************************************************** 0227 * KListWidgetSearchLinePrivate Slots * 0228 *****************************************************************************/ 0229 void KListWidgetSearchLinePrivate::_k_listWidgetDeleted() 0230 { 0231 listWidget = nullptr; 0232 q->setEnabled(false); 0233 } 0234 0235 void KListWidgetSearchLinePrivate::_k_rowsInserted(const QModelIndex &parent, int start, int end) 0236 { 0237 if (parent.isValid()) { 0238 return; 0239 } 0240 0241 updateHiddenState(start, end); 0242 } 0243 0244 void KListWidgetSearchLinePrivate::_k_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0245 { 0246 if (topLeft.parent().isValid()) { 0247 return; 0248 } 0249 0250 updateHiddenState(topLeft.row(), bottomRight.row()); 0251 } 0252 0253 #include "moc_klistwidgetsearchline.cpp"