]> git.lyx.org Git - features.git/blob - src/frontends/qt/GuiSearch.cpp
a9b93d8af0c9fa26cb94f0e206a99ede9dead2c2
[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 bool GuiSearchWidget::initialiseParams(std::string const & str)
95 {
96         if (!str.empty())
97                 findCO->lineEdit()->setText(toqstr(str));
98         return true;
99 }
100
101
102 void GuiSearchWidget::keyPressEvent(QKeyEvent * ev)
103 {
104         KeySymbol sym;
105         setKeySymbol(&sym, ev);
106
107         // catch Return and Shift-Return
108         if (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter) {
109                 doFind(ev->modifiers() == Qt::ShiftModifier);
110                 return;
111         }
112         if (ev->key() == Qt::Key_Escape) {
113                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, "findreplace"));
114                 return;
115         }
116
117         // we catch the key sequences for forward and backwards search
118         if (sym.isOK()) {
119                 KeyModifier mod = lyx::q_key_state(ev->modifiers());
120                 KeySequence keyseq(&theTopLevelKeymap(), &theTopLevelKeymap());
121                 FuncRequest fr = keyseq.addkey(sym, mod);
122                 if (fr == FuncRequest(LFUN_WORD_FIND_FORWARD)
123                     || fr == FuncRequest(LFUN_WORD_FIND)) {
124                         doFind();
125                         return;
126                 }
127                 if (fr == FuncRequest(LFUN_WORD_FIND_BACKWARD)) {
128                         doFind(true);
129                         return;
130                 }
131                 if (fr == FuncRequest(LFUN_DIALOG_TOGGLE, "findreplace")) {
132                         dispatch(fr);
133                         return;
134                 }
135         }
136         QWidget::keyPressEvent(ev);
137 }
138
139
140 void GuiSearchWidget::minimizeClicked(bool const toggle)
141 {
142         if (toggle)
143                 minimized_ = !minimized_;
144
145         replaceLA->setHidden(minimized_);
146         replaceCO->setHidden(minimized_);
147         replacePB->setHidden(minimized_);
148         replacePrevPB->setHidden(minimized_);
149         replaceallPB->setHidden(minimized_);
150         CBFrame->setHidden(minimized_);
151         if (minimized_) {
152                 minimizePB->setText(qt_("Ex&pand"));
153                 minimizePB->setToolTip(qt_("Show replace and option widgets"));
154         } else {
155                 minimizePB->setText(qt_("&Minimize"));
156                 minimizePB->setToolTip(qt_("Hide replace and option widgets"));
157         }
158
159         Q_EMIT needSizeUpdate();
160         Q_EMIT needTitleBarUpdate();
161 }
162
163
164 void GuiSearchWidget::showEvent(QShowEvent * e)
165 {
166         findChanged();
167         findPB->setFocus();
168         findCO->lineEdit()->selectAll();
169         QWidget::showEvent(e);
170 }
171
172
173 void GuiSearchWidget::findBufferChanged()
174 {
175         docstring search = theClipboard().getFindBuffer();
176         if (!search.empty()) {
177                 LYXERR(Debug::CLIPBOARD, "from findbuffer: " << search);
178 #if QT_VERSION > 0x050000
179                 findCO->setCurrentText(toqstr(search));
180 #else
181                 findCO->setEditText(toqstr(search));
182 #endif
183         }
184 }
185
186
187 void GuiSearchWidget::findChanged()
188 {
189         bool const emptytext = findCO->currentText().isEmpty();
190         findPB->setEnabled(!emptytext);
191         findPrevPB->setEnabled(!emptytext);
192         bool const replace = !emptytext && bv_ && !bv_->buffer().isReadonly();
193         replacePB->setEnabled(replace);
194         replacePrevPB->setEnabled(replace);
195         replaceallPB->setEnabled(replace);
196         if (instantSearchCB->isChecked() && !emptytext)
197                 doFind(false, true);
198 }
199
200
201 void GuiSearchWidget::findClicked()
202 {
203         doFind();
204 }
205
206
207 void GuiSearchWidget::findPrevClicked()
208 {
209         doFind(true);
210 }
211
212
213 void GuiSearchWidget::replaceClicked()
214 {
215         doReplace();
216 }
217
218
219 void GuiSearchWidget::replacePrevClicked()
220 {
221         doReplace(true);
222 }
223
224
225 void GuiSearchWidget::replaceallClicked()
226 {
227         replace(qstring_to_ucs4(findCO->currentText()),
228                 qstring_to_ucs4(replaceCO->currentText()),
229                 caseCB->isChecked(), wordsCB->isChecked(),
230                 true, true, true, selectionCB->isChecked());
231         uniqueInsert(findCO, findCO->currentText());
232         uniqueInsert(replaceCO, replaceCO->currentText());
233 }
234
235
236 void GuiSearchWidget::doFind(bool const backwards, bool const instant)
237 {
238         docstring const needle = qstring_to_ucs4(findCO->currentText());
239         find(needle, caseCB->isChecked(), wordsCB->isChecked(), !backwards,
240              instant, wrapCB->isChecked(), selectionCB->isChecked());
241         uniqueInsert(findCO, findCO->currentText());
242         if (!instant)
243                 findCO->lineEdit()->selectAll();
244 }
245
246
247 void GuiSearchWidget::find(docstring const & search, bool casesensitive,
248                            bool matchword, bool forward, bool instant,
249                            bool wrap, bool onlysel)
250 {
251         docstring const sdata =
252                 find2string(search, casesensitive, matchword,
253                             forward, wrap, instant, onlysel);
254
255         dispatch(FuncRequest(LFUN_WORD_FIND, sdata));
256 }
257
258
259 void GuiSearchWidget::doReplace(bool const backwards)
260 {
261         docstring const needle = qstring_to_ucs4(findCO->currentText());
262         docstring const repl = qstring_to_ucs4(replaceCO->currentText());
263         replace(needle, repl, caseCB->isChecked(), wordsCB->isChecked(),
264                 !backwards, false, wrapCB->isChecked(), selectionCB->isChecked());
265         uniqueInsert(findCO, findCO->currentText());
266         uniqueInsert(replaceCO, replaceCO->currentText());
267 }
268
269
270 void GuiSearchWidget::replace(docstring const & search, docstring const & replace,
271                             bool casesensitive, bool matchword,
272                             bool forward, bool all, bool wrap, bool onlysel)
273 {
274         docstring const sdata =
275                 replace2string(replace, search, casesensitive,
276                                matchword, all, forward, true, wrap, onlysel);
277
278         dispatch(FuncRequest(LFUN_WORD_REPLACE, sdata));
279 }
280
281 void GuiSearchWidget::saveSession(QSettings & settings, QString const & session_key) const
282 {
283         settings.setValue(session_key + "/casesensitive", caseCB->isChecked());
284         settings.setValue(session_key + "/words", wordsCB->isChecked());
285         settings.setValue(session_key + "/instant", instantSearchCB->isChecked());
286         settings.setValue(session_key + "/wrap", wrapCB->isChecked());
287         settings.setValue(session_key + "/selection", selectionCB->isChecked());
288         settings.setValue(session_key + "/minimized", minimized_);
289 }
290
291
292 void GuiSearchWidget::restoreSession(QString const & session_key)
293 {
294         QSettings settings;
295         caseCB->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
296         wordsCB->setChecked(settings.value(session_key + "/words", false).toBool());
297         instantSearchCB->setChecked(settings.value(session_key + "/instant", false).toBool());
298         wrapCB->setChecked(settings.value(session_key + "/wrap", false).toBool());
299         selectionCB->setChecked(settings.value(session_key + "/selection", false).toBool());
300         minimized_ = settings.value(session_key + "/minimized", false).toBool();
301         // initialize hidings
302         minimizeClicked(false);
303 }
304
305
306 GuiSearch::GuiSearch(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags)
307         : DockView(parent, "findreplace", qt_("Search and Replace"), area, flags),
308           widget_(new GuiSearchWidget(this))
309 {
310         setWidget(widget_);
311         widget_->setBufferView(bufferview());
312         setFocusProxy(widget_);
313
314         connect(widget_, SIGNAL(needTitleBarUpdate()), this, SLOT(updateTitle()));
315         connect(widget_, SIGNAL(needSizeUpdate()), this, SLOT(updateSize()));
316 }
317
318 void GuiSearch::onBufferViewChanged()
319 {
320         widget_->setEnabled(static_cast<bool>(bufferview()));
321         widget_->setBufferView(bufferview());
322 }
323
324
325 void GuiSearch::updateView()
326 {
327         updateTitle();
328         updateSize();
329 }
330
331
332 void GuiSearch::saveSession(QSettings & settings) const
333 {
334         Dialog::saveSession(settings);
335         widget_->saveSession(settings, sessionKey());
336 }
337
338
339 void GuiSearch::restoreSession()
340 {
341         DockView::restoreSession();
342         widget_->restoreSession(sessionKey());
343 }
344
345
346 void GuiSearch::updateTitle()
347 {
348         if (widget_->isMinimized()) {
349                 // remove title bar
350                 setTitleBarWidget(new QWidget());
351                 titleBarWidget()->hide();
352         } else
353                 // restore title bar
354                 setTitleBarWidget(nullptr);
355 }
356
357
358 void GuiSearch::updateSize()
359 {
360         widget_->setFixedHeight(widget_->sizeHint().height());
361         if (widget_->isMinimized())
362                 setFixedHeight(widget_->sizeHint().height());
363         else {
364                 // undo setFixedHeight
365                 setMaximumHeight(QWIDGETSIZE_MAX);
366                 setMinimumHeight(0);
367         }
368         update();
369 }
370
371
372 } // namespace frontend
373 } // namespace lyx
374
375
376 #include "moc_GuiSearch.cpp"