]> git.lyx.org Git - features.git/blob - src/frontends/qt/GuiSearch.cpp
GuiSearch: catch Esc
[features.git] / src / frontends / qt / GuiSearch.cpp
1 /**
2  * \file GuiSearch.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Edwin Leuven
8  * \author Angus Leeming
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiApplication.h"
16 #include "GuiSearch.h"
17
18 #include "lyxfind.h"
19 #include "qt_helpers.h"
20 #include "FuncRequest.h"
21 #include "LyX.h"
22 #include "BufferView.h"
23 #include "Buffer.h"
24 #include "Cursor.h"
25 #include "FuncRequest.h"
26 #include "KeyMap.h"
27 #include "GuiKeySymbol.h"
28 #include "GuiView.h"
29
30 #include "support/debug.h"
31 #include "support/gettext.h"
32 #include "frontends/alert.h"
33 #include "frontends/Clipboard.h"
34
35 #include <QClipboard>
36 #include <QLineEdit>
37 #include <QSettings>
38 #include <QShowEvent>
39 #include "QSizePolicy"
40
41 using namespace std;
42
43 using lyx::KeySymbol;
44
45 namespace lyx {
46 namespace frontend {
47
48 static void uniqueInsert(QComboBox * box, QString const & text)
49 {
50         for (int i = box->count(); --i >= 0; )
51                 if (box->itemText(i) == text)
52                         return;
53
54         box->insertItem(0, text);
55 }
56
57
58 GuiSearchWidget::GuiSearchWidget(QWidget * parent)
59         :       QWidget(parent)
60 {
61         setupUi(this);
62
63         // fix height to minimum
64         setFixedHeight(sizeHint().height());
65
66         // align items in grid on top
67         gridLayout->setAlignment(Qt::AlignTop);
68
69         connect(findPB, SIGNAL(clicked()), this, SLOT(findClicked()));
70         connect(findPrevPB, SIGNAL(clicked()), this, SLOT(findPrevClicked()));
71         connect(minimizePB, SIGNAL(clicked()), this, SLOT(minimizeClicked()));
72         connect(replacePB, SIGNAL(clicked()), this, SLOT(replaceClicked()));
73         connect(replacePrevPB, SIGNAL(clicked()), this, SLOT(replacePrevClicked()));
74         connect(replaceallPB, SIGNAL(clicked()), this, SLOT(replaceallClicked()));
75         connect(findCO, SIGNAL(editTextChanged(QString)),
76                 this, SLOT(findChanged()));
77         if(qApp->clipboard()->supportsFindBuffer()) {
78                 connect(qApp->clipboard(), SIGNAL(findBufferChanged()),
79                         this, SLOT(findBufferChanged()));
80                 findBufferChanged();
81         }
82
83         setFocusProxy(findCO);
84
85         findCO->setCompleter(nullptr);
86         replaceCO->setCompleter(nullptr);
87
88         replacePB->setEnabled(false);
89         replacePrevPB->setEnabled(false);
90         replaceallPB->setEnabled(false);
91 }
92
93
94 void GuiSearchWidget::keyPressEvent(QKeyEvent * ev)
95 {
96         KeySymbol sym;
97         setKeySymbol(&sym, ev);
98
99         // catch Return and Shift-Return
100         if (ev->key() == Qt::Key_Return) {
101                 findClicked(ev->modifiers() == Qt::ShiftModifier);
102                 return;
103         }
104         if (ev->key() == Qt::Key_Escape) {
105                 dispatch(FuncRequest(LFUN_DIALOG_TOGGLE, "findreplace"));
106                 return;
107         }
108
109         // we catch the key sequences for forward and backwards search
110         if (sym.isOK()) {
111                 KeyModifier mod = lyx::q_key_state(ev->modifiers());
112                 KeySequence keyseq(&theTopLevelKeymap(), &theTopLevelKeymap());
113                 FuncRequest fr = keyseq.addkey(sym, mod);
114                 if (fr == FuncRequest(LFUN_WORD_FIND_FORWARD) || fr == FuncRequest(LFUN_WORD_FIND)) {
115                         findClicked();
116                         return;
117                 }
118                 if (fr == FuncRequest(LFUN_WORD_FIND_BACKWARD)) {
119                         findClicked(true);
120                         return;
121                 }
122                 if (fr == FuncRequest(LFUN_DIALOG_TOGGLE, "findreplace")) {
123                         dispatch(fr);
124                         return;
125                 }
126         }
127         QWidget::keyPressEvent(ev);
128 }
129
130
131 void GuiSearchWidget::minimizeClicked(bool const toggle)
132 {
133         if (toggle)
134                 minimized_ = !minimized_;
135
136         replaceLA->setHidden(minimized_);
137         replaceCO->setHidden(minimized_);
138         replacePB->setHidden(minimized_);
139         replacePrevPB->setHidden(minimized_);
140         replaceallPB->setHidden(minimized_);
141         CBFrame->setHidden(minimized_);
142         if (minimized_) {
143                 minimizePB->setText(qt_("Ex&pand"));
144                 minimizePB->setToolTip(qt_("Show replace and option widgets"));
145         } else {
146                 minimizePB->setText(qt_("&Minimize"));
147                 minimizePB->setToolTip(qt_("Hide replace and option widgets"));
148         }
149
150         Q_EMIT needSizeUpdate();
151         Q_EMIT needTitleBarUpdate();
152 }
153
154
155 void GuiSearchWidget::showEvent(QShowEvent * e)
156 {
157         findChanged();
158         findPB->setFocus();
159         findCO->lineEdit()->selectAll();
160         QWidget::showEvent(e);
161 }
162
163
164 void GuiSearchWidget::findBufferChanged()
165 {
166         docstring search = theClipboard().getFindBuffer();
167         if (!search.empty()) {
168                 LYXERR(Debug::CLIPBOARD, "from findbuffer: " << search);
169 #if QT_VERSION > 0x050000
170                 findCO->setCurrentText(toqstr(search));
171 #else
172                 findCO->setEditText(toqstr(search));
173 #endif
174         }
175 }
176
177
178 void GuiSearchWidget::findChanged()
179 {
180         bool const emptytext = findCO->currentText().isEmpty();
181         findPB->setEnabled(!emptytext);
182         findPrevPB->setEnabled(!emptytext);
183         bool const replace = !emptytext && bv_ && !bv_->buffer().isReadonly();
184         replacePB->setEnabled(replace);
185         replacePrevPB->setEnabled(replace);
186         replaceallPB->setEnabled(replace);
187         if (instantSearchCB->isChecked() && !emptytext)
188                 findClicked();
189 }
190
191
192 void GuiSearchWidget::findClicked(bool const backwards)
193 {
194         docstring const needle = qstring_to_ucs4(findCO->currentText());
195         find(needle, caseCB->isChecked(), wordsCB->isChecked(), !backwards,
196              instantSearchCB->isChecked(), wrapCB->isChecked());
197         uniqueInsert(findCO, findCO->currentText());
198         if (!instantSearchCB->isChecked())
199                 findCO->lineEdit()->selectAll();
200 }
201
202
203 void GuiSearchWidget::findPrevClicked()
204 {
205         findClicked(true);
206 }
207
208
209 void GuiSearchWidget::replaceClicked(bool const backwards)
210 {
211         docstring const needle = qstring_to_ucs4(findCO->currentText());
212         docstring const repl = qstring_to_ucs4(replaceCO->currentText());
213         replace(needle, repl, caseCB->isChecked(), wordsCB->isChecked(),
214                 !backwards, false, wrapCB->isChecked());
215         uniqueInsert(findCO, findCO->currentText());
216         uniqueInsert(replaceCO, replaceCO->currentText());
217 }
218
219
220 void GuiSearchWidget::replacePrevClicked()
221 {
222         replaceClicked(true);
223 }
224
225
226 void GuiSearchWidget::replaceallClicked()
227 {
228         replace(qstring_to_ucs4(findCO->currentText()),
229                 qstring_to_ucs4(replaceCO->currentText()),
230                 caseCB->isChecked(), wordsCB->isChecked(), true, true, true);
231         uniqueInsert(findCO, findCO->currentText());
232         uniqueInsert(replaceCO, replaceCO->currentText());
233 }
234
235
236 void GuiSearchWidget::find(docstring const & search, bool casesensitive,
237                          bool matchword, bool forward, bool instant,
238                          bool wrap)
239 {
240         docstring const sdata =
241                 find2string(search, casesensitive, matchword,
242                             forward, wrap, instant);
243
244         dispatch(FuncRequest(LFUN_WORD_FIND, sdata));
245 }
246
247
248 void GuiSearchWidget::replace(docstring const & search, docstring const & replace,
249                             bool casesensitive, bool matchword,
250                             bool forward, bool all, bool wrap)
251 {
252         docstring const sdata =
253                 replace2string(replace, search, casesensitive,
254                                      matchword, all, forward, wrap);
255         dispatch(FuncRequest(LFUN_WORD_REPLACE, sdata));
256 }
257
258 void GuiSearchWidget::saveSession(QSettings & settings, QString const & session_key) const
259 {
260         settings.setValue(session_key + "/casesensitive", caseCB->isChecked());
261         settings.setValue(session_key + "/words", wordsCB->isChecked());
262         settings.setValue(session_key + "/instant", instantSearchCB->isChecked());
263         settings.setValue(session_key + "/wrap", wrapCB->isChecked());
264         settings.setValue(session_key + "/minimized", minimized_);
265 }
266
267
268 void GuiSearchWidget::restoreSession(QString const & session_key)
269 {
270         QSettings settings;
271         caseCB->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
272         wordsCB->setChecked(settings.value(session_key + "/words", false).toBool());
273         instantSearchCB->setChecked(settings.value(session_key + "/instant", false).toBool());
274         wrapCB->setChecked(settings.value(session_key + "/wrap", false).toBool());
275         minimized_ = settings.value(session_key + "/minimized", false).toBool();
276         // initialize hidings
277         minimizeClicked(false);
278 }
279
280
281 GuiSearch::GuiSearch(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags)
282         : DockView(parent, "findreplace", qt_("Search and Replace"), area, flags),
283           widget_(new GuiSearchWidget(this))
284 {
285         setWidget(widget_);
286         widget_->setBufferView(bufferview());
287         setFocusProxy(widget_->findCO);
288
289         connect(widget_, SIGNAL(needTitleBarUpdate()), this, SLOT(updateTitle()));
290         connect(widget_, SIGNAL(needSizeUpdate()), this, SLOT(updateSize()));
291 }
292
293 void GuiSearch::onBufferViewChanged()
294 {
295         widget_->setEnabled((bool)bufferview());
296         widget_->setBufferView(bufferview());
297 }
298
299
300 void GuiSearch::updateView()
301 {
302         updateTitle();
303         updateSize();
304 }
305
306
307 void GuiSearch::saveSession(QSettings & settings) const
308 {
309         Dialog::saveSession(settings);
310         widget_->saveSession(settings, sessionKey());
311 }
312
313
314 void GuiSearch::restoreSession()
315 {
316         DockView::restoreSession();
317         widget_->restoreSession(sessionKey());
318 }
319
320
321 void GuiSearch::updateTitle()
322 {
323         if (widget_->isMinimized()) {
324                 // remove title bar
325                 setTitleBarWidget(new QWidget());
326                 titleBarWidget()->hide();
327         } else
328                 // restore title bar
329                 setTitleBarWidget(nullptr);
330 }
331
332
333 void GuiSearch::updateSize()
334 {
335         widget_->setFixedHeight(widget_->sizeHint().height());
336         if (widget_->isMinimized())
337                 setFixedHeight(widget_->sizeHint().height());
338         else {
339                 // undo setFixedHeight
340                 setMaximumHeight(QWIDGETSIZE_MAX);
341                 setMinimumHeight(0);
342         }
343         update();
344 }
345
346
347 } // namespace frontend
348 } // namespace lyx
349
350
351 #include "moc_GuiSearch.cpp"