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