KItemViews

klistwidgetsearchline.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2003 Scott Wheeler <wheeler@kde.org>
4 SPDX-FileCopyrightText: 2004 Gustavo Sverzut Barbieri <gsbarbieri@users.sourceforge.net>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "klistwidgetsearchline.h"
10
11#include <QApplication>
12#include <QEvent>
13#include <QKeyEvent>
14#include <QListWidget>
15#include <QTimer>
16
17class KListWidgetSearchLinePrivate
18{
19public:
20 KListWidgetSearchLinePrivate(KListWidgetSearchLine *parent)
21 : q(parent)
22 {
23 }
24
25 void _k_listWidgetDeleted();
26 void _k_queueSearch(const QString &);
27 void _k_activateSearch();
28 void _k_rowsInserted(const QModelIndex &, int, int);
29 void _k_dataChanged(const QModelIndex &, const QModelIndex &);
30
31 void init(QListWidget *listWidget = nullptr);
32 void updateHiddenState(int start, int end);
33
34 KListWidgetSearchLine *const q;
35 QListWidget *listWidget = nullptr;
37 bool activeSearch = false;
38 QString search;
39 int queuedSearches = 0;
40};
41
42/******************************************************************************
43 * Public Methods *
44 *****************************************************************************/
46 : QLineEdit(parent)
47 , d(new KListWidgetSearchLinePrivate(this))
48
49{
50 d->init(listWidget);
51}
52
54{
55 clear(); // returning items back to listWidget
56}
57
59{
60 return d->caseSensitivity;
61}
62
64{
65 return d->listWidget;
66}
67
68/******************************************************************************
69 * Public Slots *
70 *****************************************************************************/
72{
73 d->search = s.isNull() ? text() : s;
74 if (d->listWidget) {
75 d->updateHiddenState(0, d->listWidget->count() - 1);
76 }
77}
78
80{
81 // Show items back to QListWidget
82 if (d->listWidget != nullptr) {
83 for (int i = 0; i < d->listWidget->count(); ++i) {
84 d->listWidget->item(i)->setHidden(false);
85 }
86 }
87
88 d->search = QString();
89 d->queuedSearches = 0;
91}
92
94{
95 d->caseSensitivity = cs;
96}
97
99{
100 if (d->listWidget != nullptr) {
101 disconnect(d->listWidget, SIGNAL(destroyed()), this, SLOT(_k_listWidgetDeleted()));
102 d->listWidget->model()->disconnect(this);
103 }
104
105 d->listWidget = lw;
106
107 if (lw != nullptr) {
108 // clang-format off
109 connect(d->listWidget, SIGNAL(destroyed()), this, SLOT(_k_listWidgetDeleted()));
110 connect(d->listWidget->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(_k_rowsInserted(QModelIndex,int,int)));
111 connect(d->listWidget->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(_k_dataChanged(QModelIndex,QModelIndex)));
112 // clang-format on
113 setEnabled(true);
114 } else {
115 setEnabled(false);
116 }
117}
118
119/******************************************************************************
120 * Protected Methods *
121 *****************************************************************************/
123{
124 if (s.isEmpty()) {
125 return true;
126 }
127
128 if (item == nullptr) {
129 return false;
130 }
131
132 return (item->text().indexOf(s, 0, caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0);
133}
134
135void KListWidgetSearchLinePrivate::init(QListWidget *_listWidget)
136{
137 listWidget = _listWidget;
138
139 QObject::connect(q, SIGNAL(textChanged(QString)), q, SLOT(_k_queueSearch(QString)));
140
141 if (listWidget != nullptr) {
142 // clang-format off
143 QObject::connect(listWidget, SIGNAL(destroyed()), q, SLOT(_k_listWidgetDeleted()));
144 QObject::connect(listWidget->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_rowsInserted(QModelIndex,int,int)));
145 QObject::connect(listWidget->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_dataChanged(QModelIndex,QModelIndex)));
146 // clang-format on
147 q->setEnabled(true);
148 } else {
149 q->setEnabled(false);
150 }
151 q->setClearButtonEnabled(true);
152}
153
154void KListWidgetSearchLinePrivate::updateHiddenState(int start, int end)
155{
156 if (!listWidget) {
157 return;
158 }
159
160 QListWidgetItem *currentItem = listWidget->currentItem();
161
162 // Remove Non-Matching items
163 for (int index = start; index <= end; ++index) {
164 QListWidgetItem *item = listWidget->item(index);
165 if (!q->itemMatches(item, search)) {
166 item->setHidden(true);
167
168 if (item == currentItem) {
169 currentItem = nullptr; // It's not in listWidget anymore.
170 }
171 } else if (item->isHidden()) {
172 item->setHidden(false);
173 }
174 }
175
176 if (listWidget->isSortingEnabled()) {
177 listWidget->sortItems();
178 }
179
180 if (currentItem != nullptr) {
181 listWidget->scrollToItem(currentItem);
182 }
183}
184
186{
187 if (event->type() == QEvent::KeyPress) {
188 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
189 if (keyEvent->matches(QKeySequence::MoveToNextLine) || keyEvent->matches(QKeySequence::SelectNextLine)
190 || keyEvent->matches(QKeySequence::MoveToPreviousLine) || keyEvent->matches(QKeySequence::SelectPreviousLine)
191 || keyEvent->matches(QKeySequence::MoveToNextPage) || keyEvent->matches(QKeySequence::SelectNextPage)
192 || keyEvent->matches(QKeySequence::MoveToPreviousPage) || keyEvent->matches(QKeySequence::SelectPreviousPage)) {
193 if (d->listWidget) {
194 QApplication::sendEvent(d->listWidget, event);
195 return true;
196 }
197 } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {
198 if (d->listWidget) {
199 QApplication::sendEvent(d->listWidget, event);
200 return true;
201 }
202 }
203 }
204 return QLineEdit::event(event);
205}
206/******************************************************************************
207 * Protected Slots *
208 *****************************************************************************/
209void KListWidgetSearchLinePrivate::_k_queueSearch(const QString &s)
210{
211 queuedSearches++;
212 search = s;
213 QTimer::singleShot(200, q, SLOT(_k_activateSearch()));
214}
215
216void KListWidgetSearchLinePrivate::_k_activateSearch()
217{
218 queuedSearches--;
219
220 if (queuedSearches <= 0) {
221 q->updateSearch(search);
222 queuedSearches = 0;
223 }
224}
225
226/******************************************************************************
227 * KListWidgetSearchLinePrivate Slots *
228 *****************************************************************************/
229void KListWidgetSearchLinePrivate::_k_listWidgetDeleted()
230{
231 listWidget = nullptr;
232 q->setEnabled(false);
233}
234
235void KListWidgetSearchLinePrivate::_k_rowsInserted(const QModelIndex &parent, int start, int end)
236{
237 if (parent.isValid()) {
238 return;
239 }
240
241 updateHiddenState(start, end);
242}
243
244void KListWidgetSearchLinePrivate::_k_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
245{
246 if (topLeft.parent().isValid()) {
247 return;
248 }
249
250 updateHiddenState(topLeft.row(), bottomRight.row());
251}
252
253#include "moc_klistwidgetsearchline.cpp"
This class makes it easy to add a search line for filtering the items in a listwidget based on a simp...
bool event(QEvent *event) override
Re-implemented for internal reasons.
~KListWidgetSearchLine() override
Destroys the KListWidgetSearchLine.
void clear()
Clear line edit and empty hiddenItems, returning elements to listWidget.
virtual void updateSearch(const QString &s=QString())
Updates search to only make visible the items that match s.
void setCaseSensitivity(Qt::CaseSensitivity cs)
Make the search case sensitive or case insensitive.
KListWidgetSearchLine(QWidget *parent=nullptr, QListWidget *listWidget=nullptr)
Constructs a KListWidgetSearchLine with listWidget being the QListWidget to be filtered.
QListWidget * listWidget() const
Returns the listWidget that is currently filtered by the search.
Qt::CaseSensitivity caseSensitive() const
Returns if the search is case sensitive.
virtual bool itemMatches(const QListWidgetItem *item, const QString &s) const
Returns true if item matches the search s.
void setListWidget(QListWidget *lv)
Sets the QListWidget that is filtered by this search line.
Q_SCRIPTABLE Q_NOREPLY void start()
const QList< QKeySequence > & end()
QAbstractItemModel * model() const const
bool sendEvent(QObject *receiver, QEvent *event)
void clear()
void setClearButtonEnabled(bool enable)
virtual bool event(QEvent *e) override
QListWidgetItem * currentItem() const const
QListWidgetItem * item(int row) const const
void scrollToItem(const QListWidgetItem *item, QAbstractItemView::ScrollHint hint)
void sortItems(Qt::SortOrder order)
bool isSortingEnabled() const const
bool isHidden() const const
void setHidden(bool hide)
QString text() const const
bool isValid() const const
QModelIndex parent() const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
CaseSensitivity
Key_Enter
void setEnabled(bool)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:32 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.