1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*! \class AbstractFindWidget
41
42 \brief A search bar that is commonly added below a searchable widget.
43
44 \internal
45
46 This widget implements a search bar which becomes visible when the user
47 wants to start searching. It is a modern replacement for the commonly used
48 search dialog. It is usually placed below the target widget using a QVBoxLayout.
49
50 The search is incremental and can be set to case sensitive or whole words
51 using buttons available on the search bar.
52 */
53
54#include "abstractfindwidget.h"
55
56#include <QtCore/QCoreApplication>
57#include <QtCore/QEvent>
58#include <QtCore/QFile>
59#include <QtCore/QTimer>
60
61#include <QtGui/QKeyEvent>
62
63#include <QtWidgets/QCheckBox>
64#include <QtWidgets/QAction>
65#include <QtWidgets/QLabel>
66#include <QtWidgets/QLayout>
67#include <QtWidgets/QLineEdit>
68#include <QtWidgets/QSpacerItem>
69#include <QtWidgets/QShortcut>
70#include <QtWidgets/QToolButton>
71
72QT_BEGIN_NAMESPACE
73
74static QIcon createIconSet(const QString &name)
75{
76 QStringList candidates = QStringList()
77 << (QString::fromUtf8(str: ":/qt-project.org/shared/images/") + name)
78#ifdef Q_OS_MAC
79 << (QString::fromUtf8(":/qt-project.org/shared/images/mac/") + name);
80#else
81 << (QString::fromUtf8(str: ":/qt-project.org/shared/images/win/") + name);
82#endif
83
84 for (const QString &f : qAsConst(t&: candidates)) {
85 if (QFile::exists(fileName: f))
86 return QIcon(f);
87 }
88
89 return QIcon();
90}
91
92/*!
93 Constructs an AbstractFindWidget.
94
95 \a flags can change the layout and turn off certain features.
96 \a parent is passed to the QWidget constructor.
97 */
98AbstractFindWidget::AbstractFindWidget(FindFlags flags, QWidget *parent)
99 : QWidget(parent)
100{
101 QBoxLayout *topLayOut;
102 QBoxLayout *layOut;
103 if (flags & NarrowLayout) {
104 topLayOut = new QVBoxLayout(this);
105 layOut = new QHBoxLayout;
106 topLayOut->addLayout(layout: layOut);
107 } else {
108 topLayOut = layOut = new QHBoxLayout(this);
109 }
110#ifndef Q_OS_MAC
111 topLayOut->setSpacing(6);
112 topLayOut->setContentsMargins(QMargins());
113#endif
114
115 m_toolClose = new QToolButton(this);
116 m_toolClose->setIcon(createIconSet(name: QLatin1String("closetab.png")));
117 m_toolClose->setAutoRaise(true);
118 layOut->addWidget(m_toolClose);
119 connect(asender: m_toolClose, SIGNAL(clicked()), SLOT(deactivate()));
120
121 m_editFind = new QLineEdit(this);
122 layOut->addWidget(m_editFind);
123 connect(asender: m_editFind, SIGNAL(returnPressed()), SLOT(findNext()));
124 connect(asender: m_editFind, SIGNAL(textChanged(QString)), SLOT(findCurrentText()));
125 connect(asender: m_editFind, SIGNAL(textChanged(QString)), SLOT(updateButtons()));
126
127 m_toolPrevious = new QToolButton(this);
128 m_toolPrevious->setAutoRaise(true);
129 m_toolPrevious->setText(tr(s: "&Previous"));
130 m_toolPrevious->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
131 m_toolPrevious->setIcon(createIconSet(name: QLatin1String("previous.png")));
132 layOut->addWidget(m_toolPrevious);
133 connect(asender: m_toolPrevious, SIGNAL(clicked()), SLOT(findPrevious()));
134
135 m_toolNext = new QToolButton(this);
136 m_toolNext->setAutoRaise(true);
137 m_toolNext->setText(tr(s: "&Next"));
138 m_toolNext->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
139 m_toolNext->setIcon(createIconSet(name: QLatin1String("next.png")));
140 layOut->addWidget(m_toolNext);
141 connect(asender: m_toolNext, SIGNAL(clicked()), SLOT(findNext()));
142
143 if (flags & NarrowLayout) {
144 QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Fixed);
145 m_toolPrevious->setSizePolicy(sp);
146 m_toolPrevious->setMinimumWidth(m_toolPrevious->minimumSizeHint().height());
147 m_toolNext->setSizePolicy(sp);
148 m_toolNext->setMinimumWidth(m_toolNext->minimumSizeHint().height());
149
150 QSpacerItem *spacerItem =
151 new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
152 layOut->addItem(spacerItem);
153
154 layOut = new QHBoxLayout;
155 topLayOut->addLayout(layout: layOut);
156 } else {
157 m_editFind->setMinimumWidth(150);
158 }
159
160 if (!(flags & NoCaseSensitive)) {
161 m_checkCase = new QCheckBox(tr(s: "&Case sensitive"), this);
162 layOut->addWidget(m_checkCase);
163 connect(asender: m_checkCase, SIGNAL(toggled(bool)), SLOT(findCurrentText()));
164 } else {
165 m_checkCase = 0;
166 }
167
168 if (!(flags & NoWholeWords)) {
169 m_checkWholeWords = new QCheckBox(tr(s: "Whole &words"), this);
170 layOut->addWidget(m_checkWholeWords);
171 connect(asender: m_checkWholeWords, SIGNAL(toggled(bool)), SLOT(findCurrentText()));
172 } else {
173 m_checkWholeWords = 0;
174 }
175
176 m_labelWrapped = new QLabel(this);
177 m_labelWrapped->setTextFormat(Qt::RichText);
178 m_labelWrapped->setAlignment(
179 Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
180 m_labelWrapped->setText(
181 tr(s: "<img src=\":/qt-project.org/shared/images/wrap.png\">"
182 "&nbsp;Search wrapped"));
183 m_labelWrapped->hide();
184 layOut->addWidget(m_labelWrapped);
185
186 QSpacerItem *spacerItem =
187 new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
188 layOut->addItem(spacerItem);
189
190 setMinimumWidth(minimumSizeHint().width());
191
192 updateButtons();
193 hide();
194}
195
196/*!
197 Destroys the AbstractFindWidget.
198 */
199AbstractFindWidget::~AbstractFindWidget() = default;
200
201/*!
202 Returns the icon set to be used for the action that initiates a search.
203 */
204QIcon AbstractFindWidget::findIconSet()
205{
206 return createIconSet(name: QLatin1String("searchfind.png"));
207}
208
209/*!
210 Creates an actions with standard icon and shortcut to activate the widget.
211 */
212QAction *AbstractFindWidget::createFindAction(QObject *parent)
213{
214
215 auto result = new QAction(AbstractFindWidget::findIconSet(),
216 tr(s: "&Find in Text..."), parent);
217 connect(sender: result, signal: &QAction::triggered, receiver: this, slot: &AbstractFindWidget::activate);
218 result->setShortcut(QKeySequence::Find);
219 return result;
220}
221
222/*!
223 Activates the find widget, making it visible and having focus on its input
224 field.
225 */
226void AbstractFindWidget::activate()
227{
228 show();
229 m_editFind->selectAll();
230 m_editFind->setFocus(Qt::ShortcutFocusReason);
231}
232
233/*!
234 Deactivates the find widget, making it invisible and handing focus to any
235 associated QTextEdit.
236 */
237void AbstractFindWidget::deactivate()
238{
239 hide();
240}
241
242void AbstractFindWidget::findNext()
243{
244 findInternal(textToFind: m_editFind->text(), skipCurrent: true, backward: false);
245}
246
247void AbstractFindWidget::findPrevious()
248{
249 findInternal(textToFind: m_editFind->text(), skipCurrent: true, backward: true);
250}
251
252void AbstractFindWidget::findCurrentText()
253{
254 findInternal(textToFind: m_editFind->text(), skipCurrent: false, backward: false);
255}
256
257void AbstractFindWidget::keyPressEvent(QKeyEvent *event)
258{
259 if (event->key() == Qt::Key_Escape) {
260 deactivate();
261 return;
262 }
263
264 QWidget::keyPressEvent(event);
265}
266
267void AbstractFindWidget::updateButtons()
268{
269 const bool en = !m_editFind->text().isEmpty();
270 m_toolPrevious->setEnabled(en);
271 m_toolNext->setEnabled(en);
272}
273
274void AbstractFindWidget::findInternal(const QString &ttf, bool skipCurrent, bool backward)
275{
276 bool found = false;
277 bool wrapped = false;
278 find(textToFind: ttf, skipCurrent, backward, found: &found, wrapped: &wrapped);
279 QPalette p;
280 p.setColor(acg: QPalette::Active, acr: QPalette::Base, acolor: found ? Qt::white : QColor(255, 102, 102));
281 m_editFind->setPalette(p);
282 m_labelWrapped->setVisible(wrapped);
283}
284
285bool AbstractFindWidget::caseSensitive() const
286{
287 return m_checkCase && m_checkCase->isChecked();
288}
289
290bool AbstractFindWidget::wholeWords() const
291{
292 return m_checkWholeWords && m_checkWholeWords->isChecked();
293}
294
295bool AbstractFindWidget::eventFilter(QObject *object, QEvent *e)
296{
297 if (isVisible() && e->type() == QEvent::KeyPress) {
298 QKeyEvent *ke = static_cast<QKeyEvent*>(e);
299 if (ke->key() == Qt::Key_Escape) {
300 hide();
301 return true;
302 }
303 }
304
305 return QWidget::eventFilter(watched: object, event: e);
306}
307
308QT_END_NAMESPACE
309

source code of qttools/src/shared/findwidget/abstractfindwidget.cpp