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