3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * \author Angus Leeming
10 * Full author contact details are available in file CREDITS.
15 #include "GuiApplication.h"
16 #include "GuiSearch.h"
19 #include "qt_helpers.h"
20 #include "FuncRequest.h"
22 #include "BufferView.h"
25 #include "FuncRequest.h"
27 #include "GuiKeySymbol.h"
30 #include "support/debug.h"
31 #include "support/gettext.h"
32 #include "frontends/alert.h"
33 #include "frontends/Clipboard.h"
40 #include "QSizePolicy"
49 static void uniqueInsert(QComboBox * box, QString const & text)
51 for (int i = box->count(); --i >= 0; )
52 if (box->itemText(i) == text)
55 box->insertItem(0, text);
59 GuiSearchWidget::GuiSearchWidget(QWidget * parent)
64 // fix height to minimum
65 setFixedHeight(sizeHint().height());
67 // align items in grid on top
68 gridLayout->setAlignment(Qt::AlignTop);
70 connect(findPB, SIGNAL(clicked()), this, SLOT(findClicked()));
71 connect(findPrevPB, SIGNAL(clicked()), this, SLOT(findPrevClicked()));
72 connect(minimizePB, SIGNAL(clicked()), this, SLOT(minimizeClicked()));
73 connect(replacePB, SIGNAL(clicked()), this, SLOT(replaceClicked()));
74 connect(replacePrevPB, SIGNAL(clicked()), this, SLOT(replacePrevClicked()));
75 connect(replaceallPB, SIGNAL(clicked()), this, SLOT(replaceallClicked()));
76 connect(findCO, SIGNAL(editTextChanged(QString)),
77 this, SLOT(findChanged()));
78 if(qApp->clipboard()->supportsFindBuffer()) {
79 connect(qApp->clipboard(), SIGNAL(findBufferChanged()),
80 this, SLOT(findBufferChanged()));
84 setFocusProxy(findCO);
86 // Use a FancyLineEdit due to the indicator icons
87 findLE_ = new FancyLineEdit(this);
88 findCO->setLineEdit(findLE_);
90 // And a menu in minimal mode
92 act_casesense_ = new QAction(qt_("&Case sensitive[[search]]"), this);
93 act_casesense_->setCheckable(true);
94 act_wholewords_ = new QAction(qt_("Wh&ole words"), this);
95 act_wholewords_->setCheckable(true);
96 act_selection_ = new QAction(qt_("Selection onl&y"), this);
97 act_selection_->setCheckable(true);
98 act_immediate_ = new QAction(qt_("Search as yo&u type"), this);
99 act_immediate_->setCheckable(true);
100 act_wrap_ = new QAction(qt_("&Wrap"), this);
101 act_wrap_->setCheckable(true);
103 menu_->addAction(act_casesense_);
104 menu_->addAction(act_wholewords_);
105 menu_->addAction(act_selection_);
106 menu_->addAction(act_immediate_);
107 menu_->addAction(act_wrap_);
108 findLE_->setButtonMenu(FancyLineEdit::Right, menu_);
110 connect(act_casesense_, SIGNAL(triggered()), this, SLOT(caseSenseActTriggered()));
111 connect(act_wholewords_, SIGNAL(triggered()), this, SLOT(wholeWordsActTriggered()));
112 connect(act_selection_, SIGNAL(triggered()), this, SLOT(searchSelActTriggered()));
113 connect(act_immediate_, SIGNAL(triggered()), this, SLOT(immediateActTriggered()));
114 connect(act_wrap_, SIGNAL(triggered()), this, SLOT(wrapActTriggered()));
116 findCO->setCompleter(nullptr);
117 replaceCO->setCompleter(nullptr);
119 replacePB->setEnabled(false);
120 replacePrevPB->setEnabled(false);
121 replaceallPB->setEnabled(false);
125 bool GuiSearchWidget::initialiseParams(std::string const & str)
128 findCO->lineEdit()->setText(toqstr(str));
133 void GuiSearchWidget::keyPressEvent(QKeyEvent * ev)
136 setKeySymbol(&sym, ev);
138 // catch Return and Shift-Return
139 if (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter) {
140 doFind(ev->modifiers() == Qt::ShiftModifier);
143 if (ev->key() == Qt::Key_Escape) {
144 dispatch(FuncRequest(LFUN_DIALOG_HIDE, "findreplace"));
148 // we catch the key sequences for forward and backwards search
150 KeyModifier mod = lyx::q_key_state(ev->modifiers());
151 KeySequence keyseq(&theTopLevelKeymap(), &theTopLevelKeymap());
152 FuncRequest fr = keyseq.addkey(sym, mod);
153 if (fr == FuncRequest(LFUN_WORD_FIND_FORWARD)
154 || fr == FuncRequest(LFUN_WORD_FIND)) {
158 if (fr == FuncRequest(LFUN_WORD_FIND_BACKWARD)) {
162 if (fr == FuncRequest(LFUN_DIALOG_TOGGLE, "findreplace")) {
167 QWidget::keyPressEvent(ev);
171 void GuiSearchWidget::minimizeClicked(bool const toggle)
174 minimized_ = !minimized_;
176 replaceLA->setHidden(minimized_);
177 replaceCO->setHidden(minimized_);
178 replacePB->setHidden(minimized_);
179 replacePrevPB->setHidden(minimized_);
180 replaceallPB->setHidden(minimized_);
181 CBFrame->setHidden(minimized_);
184 minimizePB->setText(qt_("Ex&pand"));
185 minimizePB->setToolTip(qt_("Show replace and option widgets"));
188 act_casesense_->setChecked(caseCB->isChecked());
189 act_immediate_->setChecked(instantSearchCB->isChecked());
190 act_selection_->setChecked(selectionCB->isChecked());
191 act_wholewords_->setChecked(wordsCB->isChecked());
192 act_wrap_->setChecked(wrapCB->isChecked());
195 minimizePB->setText(qt_("&Minimize"));
196 minimizePB->setToolTip(qt_("Hide replace and option widgets"));
199 Q_EMIT needSizeUpdate();
200 Q_EMIT needTitleBarUpdate();
205 void GuiSearchWidget::handleIndicators()
207 findLE_->setButtonVisible(FancyLineEdit::Right, minimized_);
213 if (caseCB->isChecked())
215 if (wordsCB->isChecked())
217 if (selectionCB->isChecked())
219 if (instantSearchCB->isChecked())
221 if (wrapCB->isChecked())
224 QPixmap bpixmap = getPixmap("images/", "search-options", "svgz,png");
228 QPixmap tpixmap(pms * (bpixmap.width() + gap), bpixmap.height());
230 QPainter painter(&tpixmap);
233 tip = qt_("Active options:");
235 if (caseCB->isChecked()) {
236 tip += "<li>" + qt_("Case sensitive search");
237 QPixmap spixmap = getPixmap("images/", "search-case-sensitive", "svgz,png");
238 painter.drawPixmap(x, 0, spixmap);
239 x += spixmap.width() + gap;
241 if (wordsCB->isChecked()) {
242 tip += "<li>" + qt_("Whole words only");
243 QPixmap spixmap = getPixmap("images/", "search-whole-words", "svgz,png");
244 painter.drawPixmap(x, 0, spixmap);
245 x += spixmap.width() + gap;
247 if (selectionCB->isChecked()) {
248 tip += "<li>" + qt_("Search only in selection");
249 QPixmap spixmap = getPixmap("images/", "search-selection", "svgz,png");
250 painter.drawPixmap(x, 0, spixmap);
251 x += spixmap.width() + gap;
253 if (instantSearchCB->isChecked()) {
254 tip += "<li>" + qt_("Search as you type");
255 QPixmap spixmap = getPixmap("images/", "search-instant", "svgz,png");
256 painter.drawPixmap(x, 0, spixmap);
257 x += spixmap.width() + gap;
259 if (wrapCB->isChecked()) {
260 tip += "<li>" + qt_("Wrap search");
261 QPixmap spixmap = getPixmap("images/", "search-wrap", "svgz,png");
262 painter.drawPixmap(x, 0, spixmap);
263 x += spixmap.width() + gap;
266 findLE_->setButtonPixmap(FancyLineEdit::Right, tpixmap);
268 tip = qt_("Click here to change search options");
269 findLE_->setButtonPixmap(FancyLineEdit::Right, bpixmap);
272 findLE_->setButtonToolTip(FancyLineEdit::Right, tip);
276 void GuiSearchWidget::caseSenseActTriggered()
278 caseCB->setChecked(act_casesense_->isChecked());
283 void GuiSearchWidget::wholeWordsActTriggered()
285 wordsCB->setChecked(act_wholewords_->isChecked());
290 void GuiSearchWidget::searchSelActTriggered()
292 selectionCB->setChecked(act_selection_->isChecked());
297 void GuiSearchWidget::immediateActTriggered()
299 instantSearchCB->setChecked(act_immediate_->isChecked());
304 void GuiSearchWidget::wrapActTriggered()
306 wrapCB->setChecked(act_wrap_->isChecked());
311 void GuiSearchWidget::showEvent(QShowEvent * e)
315 findCO->lineEdit()->selectAll();
316 QWidget::showEvent(e);
320 void GuiSearchWidget::findBufferChanged()
322 docstring search = theClipboard().getFindBuffer();
323 if (!search.empty()) {
324 LYXERR(Debug::CLIPBOARD, "from findbuffer: " << search);
325 findCO->lineEdit()->selectAll();
326 findCO->lineEdit()->insert(toqstr(search));
331 void GuiSearchWidget::findChanged()
333 bool const emptytext = findCO->currentText().isEmpty();
334 findPB->setEnabled(!emptytext);
335 findPrevPB->setEnabled(!emptytext);
336 bool const replace = !emptytext && bv_ && !bv_->buffer().isReadonly();
337 replacePB->setEnabled(replace);
338 replacePrevPB->setEnabled(replace);
339 replaceallPB->setEnabled(replace);
340 if (instantSearchCB->isChecked())
345 void GuiSearchWidget::findClicked()
351 void GuiSearchWidget::findPrevClicked()
357 void GuiSearchWidget::replaceClicked()
363 void GuiSearchWidget::replacePrevClicked()
369 void GuiSearchWidget::replaceallClicked()
371 replace(qstring_to_ucs4(findCO->currentText()),
372 qstring_to_ucs4(replaceCO->currentText()),
373 caseCB->isChecked(), wordsCB->isChecked(),
374 true, true, true, selectionCB->isChecked());
375 uniqueInsert(findCO, findCO->currentText());
376 uniqueInsert(replaceCO, replaceCO->currentText());
380 void GuiSearchWidget::doFind(bool const backwards, bool const instant)
382 docstring const needle = qstring_to_ucs4(findCO->currentText());
383 find(needle, caseCB->isChecked(), wordsCB->isChecked(), !backwards,
384 instant, wrapCB->isChecked(), selectionCB->isChecked());
385 uniqueInsert(findCO, findCO->currentText());
387 findCO->lineEdit()->selectAll();
391 void GuiSearchWidget::find(docstring const & search, bool casesensitive,
392 bool matchword, bool forward, bool instant,
393 bool wrap, bool onlysel)
395 docstring const sdata =
396 find2string(search, casesensitive, matchword,
397 forward, wrap, instant, onlysel);
399 dispatch(FuncRequest(LFUN_WORD_FIND, sdata));
403 void GuiSearchWidget::doReplace(bool const backwards)
405 docstring const needle = qstring_to_ucs4(findCO->currentText());
406 docstring const repl = qstring_to_ucs4(replaceCO->currentText());
407 replace(needle, repl, caseCB->isChecked(), wordsCB->isChecked(),
408 !backwards, false, wrapCB->isChecked(), selectionCB->isChecked());
409 uniqueInsert(findCO, findCO->currentText());
410 uniqueInsert(replaceCO, replaceCO->currentText());
414 void GuiSearchWidget::replace(docstring const & search, docstring const & replace,
415 bool casesensitive, bool matchword,
416 bool forward, bool all, bool wrap, bool onlysel)
418 docstring const sdata =
419 replace2string(replace, search, casesensitive,
420 matchword, all, forward, true, wrap, onlysel);
422 dispatch(FuncRequest(LFUN_WORD_REPLACE, sdata));
425 void GuiSearchWidget::saveSession(QSettings & settings, QString const & session_key) const
427 settings.setValue(session_key + "/casesensitive", caseCB->isChecked());
428 settings.setValue(session_key + "/words", wordsCB->isChecked());
429 settings.setValue(session_key + "/instant", instantSearchCB->isChecked());
430 settings.setValue(session_key + "/wrap", wrapCB->isChecked());
431 settings.setValue(session_key + "/selection", selectionCB->isChecked());
432 settings.setValue(session_key + "/minimized", minimized_);
436 void GuiSearchWidget::restoreSession(QString const & session_key)
439 caseCB->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
440 act_casesense_->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
441 wordsCB->setChecked(settings.value(session_key + "/words", false).toBool());
442 act_wholewords_->setChecked(settings.value(session_key + "/words", false).toBool());
443 instantSearchCB->setChecked(settings.value(session_key + "/instant", false).toBool());
444 act_immediate_->setChecked(settings.value(session_key + "/instant", false).toBool());
445 wrapCB->setChecked(settings.value(session_key + "/wrap", false).toBool());
446 act_wrap_->setChecked(settings.value(session_key + "/wrap", false).toBool());
447 selectionCB->setChecked(settings.value(session_key + "/selection", false).toBool());
448 act_selection_->setChecked(settings.value(session_key + "/selection", false).toBool());
449 minimized_ = settings.value(session_key + "/minimized", false).toBool();
450 // initialize hidings
451 minimizeClicked(false);
455 GuiSearch::GuiSearch(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags)
456 : DockView(parent, "findreplace", qt_("Search and Replace"), area, flags),
457 widget_(new GuiSearchWidget(this))
460 widget_->setBufferView(bufferview());
461 setFocusProxy(widget_);
463 connect(widget_, SIGNAL(needTitleBarUpdate()), this, SLOT(updateTitle()));
464 connect(widget_, SIGNAL(needSizeUpdate()), this, SLOT(updateSize()));
467 void GuiSearch::onBufferViewChanged()
469 widget_->setEnabled(static_cast<bool>(bufferview()));
470 widget_->setBufferView(bufferview());
474 void GuiSearch::updateView()
481 void GuiSearch::saveSession(QSettings & settings) const
483 Dialog::saveSession(settings);
484 widget_->saveSession(settings, sessionKey());
488 void GuiSearch::restoreSession()
490 DockView::restoreSession();
491 widget_->restoreSession(sessionKey());
495 void GuiSearch::updateTitle()
497 if (widget_->isMinimized()) {
499 setTitleBarWidget(new QWidget());
500 titleBarWidget()->hide();
503 setTitleBarWidget(nullptr);
507 void GuiSearch::updateSize()
509 widget_->setFixedHeight(widget_->sizeHint().height());
510 if (widget_->isMinimized())
511 setFixedHeight(widget_->sizeHint().height());
513 // undo setFixedHeight
514 setMaximumHeight(QWIDGETSIZE_MAX);
521 } // namespace frontend
525 #include "moc_GuiSearch.cpp"