]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/FindAndReplace.cpp
No need (any longer?) to create a new view for lyxfiles-open
[lyx.git] / src / frontends / qt / FindAndReplace.cpp
1 /**
2  * \file FindAndReplace.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Tommaso Cucinotta
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "FindAndReplace.h"
14
15 #include "GuiApplication.h"
16 #include "GuiView.h"
17
18 #include "Buffer.h"
19 #include "BufferList.h"
20 #include "BufferParams.h"
21 #include "BufferView.h"
22 #include "Cursor.h"
23 #include "FuncRequest.h"
24 #include "FuncStatus.h"
25 #include "KeyMap.h"
26 #include "KeySequence.h"
27 #include "Language.h"
28 #include "LyX.h"
29 #include "lyxfind.h"
30 #include "Text.h"
31
32 #include "frontends/alert.h"
33
34 #include "support/debug.h"
35 #include "support/docstream.h"
36 #include "support/filetools.h"
37 #include "support/FileName.h"
38 #include "support/gettext.h"
39 #include "support/lassert.h"
40 #include "support/lstrings.h"
41
42 #include <QCloseEvent>
43 #include <QLineEdit>
44 #include <QMenu>
45
46 using namespace std;
47 using namespace lyx::support;
48
49 namespace lyx {
50 namespace frontend {
51
52
53 FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view)
54         : QTabWidget(&view), view_(view)
55 {
56         setupUi(this);
57         find_work_area_->setGuiView(view_);
58         find_work_area_->init();
59         find_work_area_->setFrameStyle(QFrame::StyledPanel);
60
61         setFocusProxy(find_work_area_);
62         replace_work_area_->setGuiView(view_);
63         replace_work_area_->init();
64         replace_work_area_->setFrameStyle(QFrame::StyledPanel);
65
66         // We don't want two cursors blinking.
67         find_work_area_->stopBlinkingCaret();
68         replace_work_area_->stopBlinkingCaret();
69         old_buffer_ = view_.documentBufferView() ?
70             &(view_.documentBufferView()->buffer()) : 0;
71
72         // align items on top
73         cbVerticalLayout->setAlignment(Qt::AlignTop);
74         pbVerticalLayout->setAlignment(Qt::AlignTop);
75 }
76
77
78 void FindAndReplaceWidget::dockLocationChanged(Qt::DockWidgetArea area)
79 {
80         if (area == Qt::RightDockWidgetArea || area == Qt::LeftDockWidgetArea) {
81                 dynamicLayoutBasic_->setDirection(QBoxLayout::TopToBottom);
82                 dynamicLayoutAdvanced_->setDirection(QBoxLayout::TopToBottom);
83         } else {
84                 dynamicLayoutBasic_->setDirection(QBoxLayout::LeftToRight);
85                 dynamicLayoutAdvanced_->setDirection(QBoxLayout::LeftToRight);
86         }
87 }
88
89
90 bool FindAndReplaceWidget::eventFilter(QObject * obj, QEvent * event)
91 {
92         updateButtons();
93         if (event->type() != QEvent::KeyPress
94                   || (obj != find_work_area_ && obj != replace_work_area_))
95                 return QWidget::eventFilter(obj, event);
96
97         QKeyEvent * e = static_cast<QKeyEvent *> (event);
98         switch (e->key()) {
99         case Qt::Key_Escape:
100                 if (e->modifiers() == Qt::NoModifier) {
101                         hideDialog();
102                         return true;
103                 }
104                 break;
105
106         case Qt::Key_Enter:
107         case Qt::Key_Return: {
108                 bool const searchback = (e->modifiers() == Qt::ShiftModifier);
109                 bool const replace = (obj == replace_work_area_);
110                 findAndReplace(searchback, replace);
111                 if (replace)
112                         replace_work_area_->setFocus();
113                 else
114                         find_work_area_->setFocus();
115                 return true;
116         }
117
118         case Qt::Key_Tab:
119                 if (e->modifiers() == Qt::NoModifier && obj == find_work_area_){
120                         KeySequence seq;
121                         seq.parse("Tab");
122                         FuncRequest func = theTopLevelKeymap().getBinding(seq);
123                         if (!getStatus(func).enabled()) {
124                                 LYXERR(Debug::FINDVERBOSE, "Focusing replace WA");
125                                 replace_work_area_->setFocus();
126                                 LYXERR(Debug::FINDVERBOSE, "Selecting entire replace buffer");
127                                 dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
128                                 dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
129                                 return true;
130                         }
131                 }
132                 break;
133
134         case Qt::Key_Backtab:
135                 if (obj == replace_work_area_) {
136                         KeySequence seq;
137                         seq.parse("~S-BackTab");
138                         FuncRequest func = theTopLevelKeymap().getBinding(seq);
139                         if (!getStatus(func).enabled()) {
140                                 LYXERR(Debug::FINDVERBOSE, "Focusing find WA");
141                                 find_work_area_->setFocus();
142                                 LYXERR(Debug::FINDVERBOSE, "Selecting entire find buffer");
143                                 dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
144                                 dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
145                                 return true;
146                         }
147                 }
148                 break;
149
150         default:
151                 break;
152         }
153         // standard event processing
154         return QWidget::eventFilter(obj, event);
155 }
156
157
158 static vector<string> const & allManualsFiles()
159 {
160         static const char * files[] = {
161                 "Intro", "UserGuide", "Tutorial", "Additional",
162                 "EmbeddedObjects", "Math", "Customization", "Shortcuts",
163                 "LFUNs", "LaTeXConfig"
164         };
165
166         static vector<string> v;
167         if (v.empty()) {
168                 FileName fname;
169                 for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); ++i) {
170                         fname = i18nLibFileSearch("doc", files[i], "lyx");
171                         v.push_back(fname.absFileName());
172                 }
173         }
174
175         return v;
176 }
177
178
179 /** Switch buf to point to next document buffer.
180  **
181  ** Return true if restarted from master-document buffer.
182  **/
183 static bool nextDocumentBuffer(Buffer * & buf)
184 {
185         ListOfBuffers const children = buf->allRelatives();
186         LYXERR(Debug::FINDVERBOSE, "children.size()=" << children.size());
187         ListOfBuffers::const_iterator it =
188                 find(children.begin(), children.end(), buf);
189         LASSERT(it != children.end(), return false);
190         ++it;
191         if (it == children.end()) {
192                 buf = *children.begin();
193                 return true;
194         }
195         buf = *it;
196         return false;
197 }
198
199
200 /** Switch p_buf to point to previous document buffer.
201  **
202  ** Return true if restarted from last child buffer.
203  **/
204 static bool prevDocumentBuffer(Buffer * & buf)
205 {
206         ListOfBuffers const children = buf->allRelatives();
207         LYXERR(Debug::FINDVERBOSE, "children.size()=" << children.size());
208         ListOfBuffers::const_iterator it =
209                 find(children.begin(), children.end(), buf);
210         LASSERT(it != children.end(), return false)
211         if (it == children.begin()) {
212                 it = children.end();
213                 --it;
214                 buf = *it;
215                 return true;
216         }
217         --it;
218         buf = *it;
219         return false;
220 }
221
222
223 /** Switch buf to point to next or previous buffer in search scope.
224  **
225  ** Return true if restarted from scratch.
226  **/
227 static bool nextPrevBuffer(Buffer * & buf,
228                              FindAndReplaceOptions const & opt)
229 {
230         bool restarted = false;
231         switch (opt.scope) {
232         case FindAndReplaceOptions::S_BUFFER:
233                 restarted = true;
234                 break;
235         case FindAndReplaceOptions::S_DOCUMENT:
236                 if (opt.forward)
237                         restarted = nextDocumentBuffer(buf);
238                 else
239                         restarted = prevDocumentBuffer(buf);
240                 break;
241         case FindAndReplaceOptions::S_OPEN_BUFFERS:
242                 if (opt.forward) {
243                         buf = theBufferList().next(buf);
244                         restarted = (buf == *theBufferList().begin());
245                 } else {
246                         buf = theBufferList().previous(buf);
247                         restarted = (buf == *(theBufferList().end() - 1));
248                 }
249                 break;
250         case FindAndReplaceOptions::S_ALL_MANUALS:
251                 vector<string> const & manuals = allManualsFiles();
252                 vector<string>::const_iterator it =
253                         find(manuals.begin(), manuals.end(), buf->absFileName());
254                 if (it == manuals.end())
255                         it = manuals.begin();
256                 else if (opt.forward) {
257                         ++it;
258                         if (it == manuals.end()) {
259                                 it = manuals.begin();
260                                 restarted = true;
261                         }
262                 } else {
263                         if (it == manuals.begin()) {
264                                 it = manuals.end();
265                                 restarted = true;
266                         }
267                         --it;
268                 }
269                 FileName const & fname = FileName(*it);
270                 if (!theBufferList().exists(fname)) {
271                         guiApp->currentView()->setBusy(false);
272                         guiApp->currentView()->loadDocument(fname, false);
273                         guiApp->currentView()->setBusy(true);
274                 }
275                 buf = theBufferList().getBuffer(fname);
276                 break;
277         }
278         return restarted;
279 }
280
281
282 /** Find the finest question message to post to the user */
283 docstring getQuestionString(FindAndReplaceOptions const & opt)
284 {
285         docstring scope;
286         switch (opt.scope) {
287         case FindAndReplaceOptions::S_BUFFER:
288                 scope = _("File");
289                 break;
290         case FindAndReplaceOptions::S_DOCUMENT:
291                 scope = _("Master document");
292                 break;
293         case FindAndReplaceOptions::S_OPEN_BUFFERS:
294                 scope = _("Open files");
295                 break;
296         case FindAndReplaceOptions::S_ALL_MANUALS:
297                 scope = _("Manuals");
298                 break;
299         }
300         docstring message = opt.forward ?
301                 bformat(_("%1$s: the end was reached while searching forward.\n"
302                           "Continue searching from the beginning?"),
303                         scope) :
304                 bformat(_("%1$s: the beginning was reached while searching backward.\n"
305                           "Continue searching from the end?"),
306                         scope);
307
308         return message;
309 }
310
311
312 /// Return true if a match was found
313 bool FindAndReplaceWidget::findAndReplaceScope(FindAndReplaceOptions & opt, bool replace_all)
314 {
315         BufferView * bv = view_.documentBufferView();
316         if (!bv)
317                 return false;
318         Buffer * buf = &bv->buffer();
319         Buffer * buf_orig = &bv->buffer();
320         DocIterator cur_orig(bv->cursor());
321         int wrap_answer = -1;
322         opt.replace_all = replace_all;
323         ostringstream oss;
324         oss << opt;
325         FuncRequest cmd(LFUN_WORD_FINDADV, from_utf8(oss.str()));
326
327         view_.message(_("Advanced search in progress (press ESC to cancel) . . ."));
328         theApp()->startLongOperation();
329         view_.setBusy(true);
330         if (opt.scope == FindAndReplaceOptions::S_ALL_MANUALS) {
331                 vector<string> const & v = allManualsFiles();
332                 if (std::find(v.begin(), v.end(), buf->absFileName()) == v.end()) {
333                         FileName const & fname = FileName(*v.begin());
334                         if (!theBufferList().exists(fname)) {
335                                 guiApp->currentView()->setBusy(false);
336                                 theApp()->stopLongOperation();
337                                 guiApp->currentView()->loadDocument(fname, false);
338                                 theApp()->startLongOperation();
339                                 guiApp->currentView()->setBusy(true);
340                         }
341                         buf = theBufferList().getBuffer(fname);
342                         if (!buf) {
343                                 view_.setBusy(false);
344                                 return false;
345                         }
346
347                         lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
348                                                   buf->absFileName()));
349                         bv = view_.documentBufferView();
350                         bv->cursor().clear();
351                         bv->cursor().push_back(CursorSlice(buf->inset()));
352                 }
353         }
354
355         UndoGroupHelper helper(buf);
356
357         do {
358                 LYXERR(Debug::FINDVERBOSE, "Dispatching LFUN_WORD_FINDADV");
359                 dispatch(cmd);
360                 LYXERR(Debug::FINDVERBOSE, "dispatched");
361                 if (bv->cursor().result().dispatched()) {
362                         // New match found and selected (old selection replaced if needed)
363                         if (replace_all)
364                                 continue;
365                         view_.setBusy(false);
366                         theApp()->stopLongOperation();
367                         return true;
368                 } else if (replace_all)
369                         bv->clearSelection();
370
371                 if (theApp()->longOperationCancelled()) {
372                         // Search aborted by user
373                         view_.message(_("Advanced search cancelled by user"));
374                         view_.setBusy(false);
375                         theApp()->stopLongOperation();
376                         return false;
377                 }
378
379                 // No match found in current buffer (however old selection might have been replaced)
380                 // select next buffer in scope, if any
381                 bool const prompt = nextPrevBuffer(buf, opt);
382                 if (!buf)
383                         break;
384                 if (prompt) {
385                         if (wrap_answer != -1)
386                                 break;
387                         docstring q = getQuestionString(opt);
388                         view_.setBusy(false);
389                         theApp()->stopLongOperation();
390                         wrap_answer = frontend::Alert::prompt(
391                                 _("Wrap search?"), q,
392                                 0, 1, _("&Yes"), _("&No"));
393                         theApp()->startLongOperation();
394                         view_.setBusy(true);
395                         if (wrap_answer == 1)
396                                 break;
397                 }
398                 if (buf != &view_.documentBufferView()->buffer())
399                         lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
400                                                   buf->absFileName()));
401
402                 helper.resetBuffer(buf);
403
404                 bv = view_.documentBufferView();
405                 if (opt.forward) {
406                         bv->cursor().clear();
407                         bv->cursor().push_back(CursorSlice(buf->inset()));
408                 } else {
409                         //lyx::dispatch(FuncRequest(LFUN_BUFFER_END));
410                         bv->cursor().setCursor(doc_iterator_end(buf));
411                         bv->cursor().backwardPos();
412                         LYXERR(Debug::FINDVERBOSE, "findBackAdv5: cur: "
413                                 << bv->cursor());
414                 }
415                 bv->clearSelection();
416         } while (wrap_answer != 1);
417
418         if (buf_orig != &view_.documentBufferView()->buffer())
419                 lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
420                                           buf_orig->absFileName()));
421         bv = view_.documentBufferView();
422         // This may happen after a replace occurred
423         if (cur_orig.pos() > cur_orig.lastpos())
424                 cur_orig.pos() = cur_orig.lastpos();
425         bv->cursor().setCursor(cur_orig);
426         view_.setBusy(false);
427         theApp()->stopLongOperation();
428         return false;
429 }
430
431
432 /// Return true if a match was found
433 bool FindAndReplaceWidget::findAndReplace(
434         bool casesensitive, bool matchword, bool backwards,
435         bool expandmacros, bool adhereformat, bool replace,
436         bool keep_case, bool replace_all)
437 {
438         Buffer & find_buf = find_work_area_->bufferView().buffer();
439         docstring const & find_buf_name = find_buf.fileName().absoluteFilePath();
440
441         if (find_buf.text().empty()) {
442                 view_.message(_("Nothing to search"));
443                 return false;
444         }
445
446         Buffer & repl_buf = replace_work_area_->bufferView().buffer();
447         docstring const & repl_buf_name = replace ?
448                 repl_buf.fileName().absoluteFilePath() : docstring();
449
450         FindAndReplaceOptions::SearchScope scope =
451                 FindAndReplaceOptions::S_BUFFER;
452         if (CurrentDocument->isChecked())
453                 scope = FindAndReplaceOptions::S_BUFFER;
454         else if (MasterDocument->isChecked())
455                 scope = FindAndReplaceOptions::S_DOCUMENT;
456         else if (OpenDocuments->isChecked())
457                 scope = FindAndReplaceOptions::S_OPEN_BUFFERS;
458         else if (AllManualsRB->isChecked())
459                 scope = FindAndReplaceOptions::S_ALL_MANUALS;
460         else
461                 LATTEST(false);
462
463         FindAndReplaceOptions::SearchRestriction restr =
464                 OnlyMaths->isChecked()
465                         ? FindAndReplaceOptions::R_ONLY_MATHS
466                         : FindAndReplaceOptions::R_EVERYTHING;
467
468         LYXERR(Debug::FINDVERBOSE, "FindAndReplaceOptions: "
469                << "find_buf_name=" << find_buf_name
470                << ", casesensitiv=" << casesensitive
471                << ", matchword=" << matchword
472                << ", backwards=" << backwards
473                << ", expandmacros=" << expandmacros
474                << ", adhereformat=" << adhereformat
475                << ", repl_buf_name" << repl_buf_name
476                << ", keep_case=" << keep_case
477                << ", scope=" << scope
478                << ", restr=" << restr);
479
480         FindAndReplaceOptions opt(find_buf_name, casesensitive, matchword,
481                                   !backwards, expandmacros, !adhereformat,
482                                   repl_buf_name, keep_case, scope, restr);
483         
484         if (adhereformat) {
485                 // Formats to adhere
486                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("language",
487                                           !adhereLanguageCB->isChecked())));
488                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("color",
489                                           !adhereColorCB->isChecked())));
490                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("family",
491                                           !adhereFFamilyCB->isChecked())));
492                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("series",
493                                           !adhereFSeriesCB->isChecked())));
494                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("shape",
495                                           !adhereFShapeCB->isChecked())));
496                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("size",
497                                           !adhereFSizeCB->isChecked())));
498                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("markup",
499                                           !adhereMarkupCB->isChecked())));
500                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("underline",
501                                           !adhereUnderlineCB->isChecked())));
502                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("strike",
503                                           !adhereStrikeCB->isChecked())));
504                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("deleted",
505                                           !adhereDeletedCB->isChecked())));
506                 lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("sectioning",
507                                           !adhereSectioningCB->isChecked())));
508         }
509         lyx::dispatch(FuncRequest(LFUN_SEARCH_IGNORE, checkState("non-output-content",
510                                   ignoreNonOutputCB->isChecked())));
511         
512         return findAndReplaceScope(opt, replace_all);
513 }
514
515
516 docstring const FindAndReplaceWidget::checkState(string const s, bool const b)
517 {
518         docstring res = from_ascii(s) + from_ascii(" ");
519         if (b)
520                 res += from_ascii("true");
521         else
522                 res += from_ascii("false");
523         return res;
524 }
525
526
527 bool FindAndReplaceWidget::findAndReplace(bool backwards, bool replace, bool replace_all)
528 {
529         if (! view_.currentMainWorkArea()) {
530                 view_.message(_("No open document(s) in which to search"));
531                 return false;
532         }
533         // Finalize macros that are being typed, both in main document and in search or replacement WAs
534         if (view_.currentWorkArea()->bufferView().cursor().macroModeClose())
535                 view_.currentWorkArea()->bufferView().processUpdateFlags(Update::Force);
536         if (view_.currentMainWorkArea()->bufferView().cursor().macroModeClose())
537                 view_.currentMainWorkArea()->bufferView().processUpdateFlags(Update::Force);
538
539         // FIXME: create a Dialog::returnFocus()
540         // or something instead of this:
541         view_.setCurrentWorkArea(view_.currentMainWorkArea());
542         return findAndReplace(caseCB->isChecked(),
543                 wordsCB->isChecked(),
544                 backwards,
545                 expandMacrosCB->isChecked(),
546                 adhereFormatGB->isChecked(),
547                 replace,
548                 keepCaseCB->isChecked(),
549                 replace_all);
550 }
551
552
553 void FindAndReplaceWidget::hideDialog()
554 {
555         dispatch(FuncRequest(LFUN_DIALOG_TOGGLE, "findreplaceadv"));
556 }
557
558
559 void FindAndReplaceWidget::on_findNextPB_clicked()
560 {
561         findAndReplace(searchbackCB->isChecked(), false);
562         find_work_area_->setFocus();
563 }
564
565
566 void FindAndReplaceWidget::on_replacePB_clicked()
567 {
568         findAndReplace(searchbackCB->isChecked(), true);
569         replace_work_area_->setFocus();
570 }
571
572
573 void FindAndReplaceWidget::on_replaceallPB_clicked()
574 {
575         findAndReplace(searchbackCB->isChecked(), true, true);
576         replace_work_area_->setFocus();
577 }
578
579
580 void FindAndReplaceWidget::on_searchbackCB_clicked()
581 {
582         updateButtons();
583 }
584
585
586 void FindAndReplaceWidget::setFormatIgnores(bool const b)
587 {
588         adhereLanguageCB->setChecked(b);
589         adhereColorCB->setChecked(b);
590         adhereFFamilyCB->setChecked(b);
591         adhereFSeriesCB->setChecked(b);
592         adhereFShapeCB->setChecked(b);
593         adhereFSizeCB->setChecked(b);
594         adhereMarkupCB->setChecked(b);
595         adhereUnderlineCB->setChecked(b);
596         adhereStrikeCB->setChecked(b);
597         adhereDeletedCB->setChecked(b);
598         adhereSectioningCB->setChecked(b);
599 }
600
601
602 void FindAndReplaceWidget::on_selectAllPB_clicked()
603 {
604         setFormatIgnores(true);
605 }
606
607
608 void FindAndReplaceWidget::on_deselectAllPB_clicked()
609 {
610         setFormatIgnores(false);
611 }
612
613
614 // Copy selected elements from bv's BufferParams to the dest_bv's
615 static void copy_params(BufferView const & bv, BufferView & dest_bv) {
616         Buffer const & doc_buf = bv.buffer();
617         BufferParams const & doc_bp = doc_buf.params();
618         Buffer & dest_buf = dest_bv.buffer();
619         dest_buf.params().copyForAdvFR(doc_bp);
620         dest_bv.makeDocumentClass();
621         dest_bv.cursor().current_font.setLanguage(doc_bp.language);
622 }
623
624
625 void FindAndReplaceWidget::showEvent(QShowEvent * /* ev */)
626 {
627         LYXERR(Debug::DEBUG, "showEvent()" << endl);
628         BufferView * bv = view_.documentBufferView();
629         if (bv) {
630                 copy_params(*bv, find_work_area_->bufferView());
631                 copy_params(*bv, replace_work_area_->bufferView());
632         }
633
634         find_work_area_->installEventFilter(this);
635         replace_work_area_->installEventFilter(this);
636
637         view_.setCurrentWorkArea(find_work_area_);
638         LYXERR(Debug::FINDVERBOSE, "Selecting entire find buffer");
639         dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
640         dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
641 }
642
643
644 void FindAndReplaceWidget::hideEvent(QHideEvent *ev)
645 {
646         replace_work_area_->removeEventFilter(this);
647         find_work_area_->removeEventFilter(this);
648         this->QWidget::hideEvent(ev);
649 }
650
651
652 bool FindAndReplaceWidget::initialiseParams(std::string const & /*params*/)
653 {
654         return true;
655 }
656
657
658 FindAndReplace::FindAndReplace(GuiView & parent,
659                                Qt::DockWidgetArea area,
660                                Qt::WindowFlags flags)
661     : DockView(parent, "findreplaceadv", qt_("Advanced Find and Replace"),
662                    area, flags)
663 {
664         widget_ = new FindAndReplaceWidget(parent);
665         setWidget(widget_);
666         setFocusProxy(widget_);
667 #ifdef Q_OS_MAC
668         // On Mac show and floating
669         setFloating(true);
670 #endif
671
672         connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
673                 widget_, SLOT(dockLocationChanged(Qt::DockWidgetArea)));
674 }
675
676
677 FindAndReplace::~FindAndReplace()
678 {
679         setFocusProxy(nullptr);
680         delete widget_;
681 }
682
683
684 bool FindAndReplace::initialiseParams(std::string const & params)
685 {
686         return widget_->initialiseParams(params);
687 }
688
689
690 void FindAndReplaceWidget::updateWorkAreas()
691 {
692         BufferView * bv = view_.documentBufferView();
693         if (bv) {
694                 if (old_buffer_ != &bv->buffer()) {
695                                 old_buffer_ = &bv->buffer();
696                                 copy_params(*bv, find_work_area_->bufferView());
697                                 copy_params(*bv, replace_work_area_->bufferView());
698                 }
699         } else
700                 old_buffer_ = nullptr;
701 }
702
703
704 void FindAndReplaceWidget::updateButtons()
705 {
706         if (searchbackCB->isChecked()) {
707                 findNextPB->setText(qt_("&< Find"));
708                 findNextPB->setToolTip(qt_("Find previous occurrence (Shift+Enter, forwards: Enter)"));
709                 replacePB->setText(qt_("< Rep&lace"));
710                 replacePB->setToolTip(qt_("Replace and find previous occurrence (Shift+Enter, forwards: Enter)"));
711         } else {
712                 findNextPB->setText(qt_("Find &>"));
713                 findNextPB->setToolTip(qt_("Find next occurrence (Enter, backwards: Shift+Enter)"));
714                 replacePB->setText(qt_("Rep&lace >"));
715                 replacePB->setToolTip(qt_("Replace and find next occurrence (Enter, backwards: Shift+Enter)"));
716         }
717
718         BufferView * bv = view_.documentBufferView();
719         bool const find_enabled = !find_work_area_->bufferView().buffer().empty();
720         findNextPB->setEnabled(find_enabled);
721         bool const replace_enabled = find_enabled && bv && !bv->buffer().isReadonly();
722         replaceLabel->setEnabled(replace_enabled);
723         replace_work_area_->setEnabled(replace_enabled);
724         replacePB->setEnabled(replace_enabled);
725         replaceallPB->setEnabled(replace_enabled);
726 }
727
728
729 void FindAndReplace::updateView()
730 {
731         widget_->updateWorkAreas();
732         widget_->updateButtons();
733 }
734
735
736 bool FindAndReplaceWidget::hasWorkArea(GuiWorkArea * wa) const
737 {
738         return wa == find_work_area_ || wa == replace_work_area_;
739 }
740
741 } // namespace frontend
742 } // namespace lyx
743
744
745 #include "moc_FindAndReplace.cpp"