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());
229 tpixmap.fill(Qt::transparent);
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;
267 if (guiApp && guiApp->isInDarkMode()) {
268 QImage img = tpixmap.toImage();
270 tpixmap.convertFromImage(img);
272 findLE_->setButtonPixmap(FancyLineEdit::Right, tpixmap);
274 tip = qt_("Click here to change search options");
275 if (guiApp && guiApp->isInDarkMode()) {
276 QImage img = bpixmap.toImage();
278 bpixmap.convertFromImage(img);
280 findLE_->setButtonPixmap(FancyLineEdit::Right, bpixmap);
283 findLE_->setButtonToolTip(FancyLineEdit::Right, tip);
287 void GuiSearchWidget::caseSenseActTriggered()
289 caseCB->setChecked(act_casesense_->isChecked());
294 void GuiSearchWidget::wholeWordsActTriggered()
296 wordsCB->setChecked(act_wholewords_->isChecked());
301 void GuiSearchWidget::searchSelActTriggered()
303 selectionCB->setChecked(act_selection_->isChecked());
308 void GuiSearchWidget::immediateActTriggered()
310 instantSearchCB->setChecked(act_immediate_->isChecked());
315 void GuiSearchWidget::wrapActTriggered()
317 wrapCB->setChecked(act_wrap_->isChecked());
322 void GuiSearchWidget::showEvent(QShowEvent * e)
326 findCO->lineEdit()->selectAll();
327 QWidget::showEvent(e);
331 void GuiSearchWidget::findBufferChanged()
333 docstring search = theClipboard().getFindBuffer();
334 if (!search.empty()) {
335 LYXERR(Debug::CLIPBOARD, "from findbuffer: " << search);
336 findCO->lineEdit()->selectAll();
337 findCO->lineEdit()->insert(toqstr(search));
338 findCO->lineEdit()->selectAll();
343 void GuiSearchWidget::findChanged()
345 bool const emptytext = findCO->currentText().isEmpty();
346 findPB->setEnabled(!emptytext);
347 findPrevPB->setEnabled(!emptytext);
348 bool const replace = !emptytext && bv_ && !bv_->buffer().isReadonly();
349 replacePB->setEnabled(replace);
350 replacePrevPB->setEnabled(replace);
351 replaceallPB->setEnabled(replace);
352 if (instantSearchCB->isChecked())
357 void GuiSearchWidget::findClicked()
363 void GuiSearchWidget::findPrevClicked()
369 void GuiSearchWidget::replaceClicked()
375 void GuiSearchWidget::replacePrevClicked()
381 void GuiSearchWidget::replaceallClicked()
383 replace(qstring_to_ucs4(findCO->currentText()),
384 qstring_to_ucs4(replaceCO->currentText()),
385 caseCB->isChecked(), wordsCB->isChecked(),
386 true, true, true, selectionCB->isChecked());
387 uniqueInsert(findCO, findCO->currentText());
388 uniqueInsert(replaceCO, replaceCO->currentText());
392 void GuiSearchWidget::doFind(bool const backwards, bool const instant)
394 docstring const needle = qstring_to_ucs4(findCO->currentText());
395 find(needle, caseCB->isChecked(), wordsCB->isChecked(), !backwards,
396 instant, wrapCB->isChecked(), selectionCB->isChecked());
397 uniqueInsert(findCO, findCO->currentText());
399 findCO->lineEdit()->selectAll();
403 void GuiSearchWidget::find(docstring const & search, bool casesensitive,
404 bool matchword, bool forward, bool instant,
405 bool wrap, bool onlysel)
407 docstring const sdata =
408 find2string(search, casesensitive, matchword,
409 forward, wrap, instant, onlysel);
411 dispatch(FuncRequest(LFUN_WORD_FIND, sdata));
415 void GuiSearchWidget::doReplace(bool const backwards)
417 docstring const needle = qstring_to_ucs4(findCO->currentText());
418 docstring const repl = qstring_to_ucs4(replaceCO->currentText());
419 replace(needle, repl, caseCB->isChecked(), wordsCB->isChecked(),
420 !backwards, false, wrapCB->isChecked(), selectionCB->isChecked());
421 uniqueInsert(findCO, findCO->currentText());
422 uniqueInsert(replaceCO, replaceCO->currentText());
426 void GuiSearchWidget::replace(docstring const & search, docstring const & replace,
427 bool casesensitive, bool matchword,
428 bool forward, bool all, bool wrap, bool onlysel)
430 docstring const sdata =
431 replace2string(replace, search, casesensitive,
432 matchword, all, forward, true, wrap, onlysel);
434 dispatch(FuncRequest(LFUN_WORD_REPLACE, sdata));
437 void GuiSearchWidget::saveSession(QSettings & settings, QString const & session_key) const
439 settings.setValue(session_key + "/casesensitive", caseCB->isChecked());
440 settings.setValue(session_key + "/words", wordsCB->isChecked());
441 settings.setValue(session_key + "/instant", instantSearchCB->isChecked());
442 settings.setValue(session_key + "/wrap", wrapCB->isChecked());
443 settings.setValue(session_key + "/selection", selectionCB->isChecked());
444 settings.setValue(session_key + "/minimized", minimized_);
448 void GuiSearchWidget::restoreSession(QString const & session_key)
451 caseCB->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
452 act_casesense_->setChecked(settings.value(session_key + "/casesensitive", false).toBool());
453 wordsCB->setChecked(settings.value(session_key + "/words", false).toBool());
454 act_wholewords_->setChecked(settings.value(session_key + "/words", false).toBool());
455 instantSearchCB->setChecked(settings.value(session_key + "/instant", false).toBool());
456 act_immediate_->setChecked(settings.value(session_key + "/instant", false).toBool());
457 wrapCB->setChecked(settings.value(session_key + "/wrap", false).toBool());
458 act_wrap_->setChecked(settings.value(session_key + "/wrap", false).toBool());
459 selectionCB->setChecked(settings.value(session_key + "/selection", false).toBool());
460 act_selection_->setChecked(settings.value(session_key + "/selection", false).toBool());
461 minimized_ = settings.value(session_key + "/minimized", false).toBool();
462 // initialize hidings
463 minimizeClicked(false);
467 GuiSearch::GuiSearch(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags)
468 : DockView(parent, "findreplace", qt_("Search and Replace"), area, flags),
469 widget_(new GuiSearchWidget(this))
472 widget_->setBufferView(bufferview());
473 setFocusProxy(widget_);
475 connect(widget_, SIGNAL(needTitleBarUpdate()), this, SLOT(updateTitle()));
476 connect(widget_, SIGNAL(needSizeUpdate()), this, SLOT(updateSize()));
479 void GuiSearch::onBufferViewChanged()
481 widget_->setEnabled(static_cast<bool>(bufferview()));
482 widget_->setBufferView(bufferview());
486 void GuiSearch::updateView()
493 void GuiSearch::saveSession(QSettings & settings) const
495 Dialog::saveSession(settings);
496 widget_->saveSession(settings, sessionKey());
500 void GuiSearch::restoreSession()
502 DockView::restoreSession();
503 widget_->restoreSession(sessionKey());
507 void GuiSearch::updateTitle()
509 if (widget_->isMinimized()) {
511 setTitleBarWidget(new QWidget());
512 titleBarWidget()->hide();
515 setTitleBarWidget(nullptr);
519 void GuiSearch::updateSize()
521 widget_->setFixedHeight(widget_->sizeHint().height());
522 if (widget_->isMinimized())
523 setFixedHeight(widget_->sizeHint().height());
525 // undo setFixedHeight
526 setMaximumHeight(QWIDGETSIZE_MAX);
533 } // namespace frontend
537 #include "moc_GuiSearch.cpp"