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