]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
GuiBibtex: Use appropriate title when using Biblatex
[lyx.git] / src / frontends / qt4 / GuiView.cpp
1 /**
2  * \file GuiView.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author Abdelrazak Younes
9  * \author Peter Kümmel
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiView.h"
17
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToc.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
30 #include "Menus.h"
31 #include "TocModel.h"
32
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
35
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
38
39 #include "buffer_funcs.h"
40 #include "Buffer.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
44 #include "Compare.h"
45 #include "Converter.h"
46 #include "Cursor.h"
47 #include "CutAndPaste.h"
48 #include "Encoding.h"
49 #include "ErrorList.h"
50 #include "Format.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
53 #include "Intl.h"
54 #include "Layout.h"
55 #include "Lexer.h"
56 #include "LyXAction.h"
57 #include "LyX.h"
58 #include "LyXRC.h"
59 #include "LyXVC.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
62 #include "Session.h"
63 #include "TexRow.h"
64 #include "TextClass.h"
65 #include "Text.h"
66 #include "Toolbars.h"
67 #include "version.h"
68
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
85
86 #include <QAction>
87 #include <QApplication>
88 #include <QCloseEvent>
89 #include <QDebug>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
92 #include <QDropEvent>
93 #include <QFuture>
94 #include <QFutureWatcher>
95 #include <QLabel>
96 #include <QList>
97 #include <QMenu>
98 #include <QMenuBar>
99 #include <QMimeData>
100 #include <QMovie>
101 #include <QPainter>
102 #include <QPixmap>
103 #include <QPixmapCache>
104 #include <QPoint>
105 #include <QPushButton>
106 #include <QScrollBar>
107 #include <QSettings>
108 #include <QShowEvent>
109 #include <QSplitter>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
114 #include <QTime>
115 #include <QTimer>
116 #include <QToolBar>
117 #include <QUrl>
118
119
120
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
123
124
125 #include "support/bind.h"
126
127 #include <sstream>
128
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
131 #endif
132 #ifdef HAVE_UNISTD_H
133 # include <unistd.h>
134 #endif
135
136
137 using namespace std;
138 using namespace lyx::support;
139
140 namespace lyx {
141
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
145
146 namespace frontend {
147
148 namespace {
149
150 class BackgroundWidget : public QWidget
151 {
152 public:
153         BackgroundWidget(int width, int height)
154                 : width_(width), height_(height)
155         {
156                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157                 if (!lyxrc.show_banner)
158                         return;
159                 /// The text to be written on top of the pixmap
160                 QString const text = lyx_version ?
161                         qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163                 QString imagedir = "images/";
164                 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165                 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166                 if (svgRenderer.isValid()) {
167                         splash_ = QPixmap(splashSize());
168                         QPainter painter(&splash_);
169                         svgRenderer.render(&painter);
170                         splash_.setDevicePixelRatio(pixelRatio());
171                 } else {
172                         splash_ = getPixmap("images/", "banner", "png");
173                 }
174 #else
175                 splash_ = getPixmap("images/", "banner", "svgz,png");
176 #endif
177
178                 QPainter pain(&splash_);
179                 pain.setPen(QColor(0, 0, 0));
180                 qreal const fsize = fontSize();
181                 QPointF const position = textPosition();
182                 LYXERR(Debug::GUI,
183                         "widget pixel ratio: " << pixelRatio() <<
184                         " splash pixel ratio: " << splashPixelRatio() <<
185                         " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
186                 QFont font;
187                 // The font used to display the version info
188                 font.setStyleHint(QFont::SansSerif);
189                 font.setWeight(QFont::Bold);
190                 font.setPointSizeF(fsize);
191                 pain.setFont(font);
192                 pain.drawText(position, text);
193                 setFocusPolicy(Qt::StrongFocus);
194         }
195
196         void paintEvent(QPaintEvent *)
197         {
198                 int const w = width_;
199                 int const h = height_;
200                 int const x = (width() - w) / 2;
201                 int const y = (height() - h) / 2;
202                 LYXERR(Debug::GUI,
203                         "widget pixel ratio: " << pixelRatio() <<
204                         " splash pixel ratio: " << splashPixelRatio() <<
205                         " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
206                 QPainter pain(this);
207                 pain.drawPixmap(x, y, w, h, splash_);
208         }
209
210         void keyPressEvent(QKeyEvent * ev)
211         {
212                 KeySymbol sym;
213                 setKeySymbol(&sym, ev);
214                 if (sym.isOK()) {
215                         guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
216                         ev->accept();
217                 } else {
218                         ev->ignore();
219                 }
220         }
221
222 private:
223         QPixmap splash_;
224         int const width_;
225         int const height_;
226
227         /// Current ratio between physical pixels and device-independent pixels
228         double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230                 return qt_scale_factor * devicePixelRatio();
231 #else
232                 return 1.0;
233 #endif
234         }
235
236         qreal fontSize() const {
237                 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
238         }
239
240         QPointF textPosition() const {
241                 return QPointF(width_/2 - 18, height_/2 + 45);
242         }
243
244         QSize splashSize() const {
245                 return QSize(
246                         static_cast<unsigned int>(width_ * pixelRatio()),
247                         static_cast<unsigned int>(height_ * pixelRatio()));
248         }
249
250         /// Ratio between physical pixels and device-independent pixels of splash image
251         double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253                 return splash_.devicePixelRatio();
254 #else
255                 return 1.0;
256 #endif
257         }
258 };
259
260
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
263
264 typedef shared_ptr<Dialog> DialogPtr;
265
266 } // namespace
267
268
269 class GuiView::GuiViewPrivate
270 {
271         /// noncopyable
272         GuiViewPrivate(GuiViewPrivate const &);
273         void operator=(GuiViewPrivate const &);
274 public:
275         GuiViewPrivate(GuiView * gv)
276                 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277                 layout_(0), autosave_timeout_(5000),
278                 in_show_(false)
279         {
280                 // hardcode here the platform specific icon size
281                 smallIconSize = 16;  // scaling problems
282                 normalIconSize = 20; // ok, default if iconsize.png is missing
283                 bigIconSize = 26;       // better for some math icons
284                 hugeIconSize = 32;      // better for hires displays
285                 giantIconSize = 48;
286
287                 // if it exists, use width of iconsize.png as normal size
288                 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289                 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290                 if (!fn.empty()) {
291                         QImage image(toqstr(fn.absFileName()));
292                         if (image.width() < int(smallIconSize))
293                                 normalIconSize = smallIconSize;
294                         else if (image.width() > int(giantIconSize))
295                                 normalIconSize = giantIconSize;
296                         else
297                                 normalIconSize = image.width();
298                 }
299
300                 splitter_ = new QSplitter;
301                 bg_widget_ = new BackgroundWidget(400, 250);
302                 stack_widget_ = new QStackedWidget;
303                 stack_widget_->addWidget(bg_widget_);
304                 stack_widget_->addWidget(splitter_);
305                 setBackground();
306
307                 // TODO cleanup, remove the singleton, handle multiple Windows?
308                 progress_ = ProgressInterface::instance();
309                 if (!dynamic_cast<GuiProgress*>(progress_)) {
310                         progress_ = new GuiProgress;  // TODO who deletes it
311                         ProgressInterface::setInstance(progress_);
312                 }
313                 QObject::connect(
314                                 dynamic_cast<GuiProgress*>(progress_),
315                                 SIGNAL(updateStatusBarMessage(QString const&)),
316                                 gv, SLOT(updateStatusBarMessage(QString const&)));
317                 QObject::connect(
318                                 dynamic_cast<GuiProgress*>(progress_),
319                                 SIGNAL(clearMessageText()),
320                                 gv, SLOT(clearMessageText()));
321         }
322
323         ~GuiViewPrivate()
324         {
325                 delete splitter_;
326                 delete bg_widget_;
327                 delete stack_widget_;
328         }
329
330         void setBackground()
331         {
332                 stack_widget_->setCurrentWidget(bg_widget_);
333                 bg_widget_->setUpdatesEnabled(true);
334                 bg_widget_->setFocus();
335         }
336
337         int tabWorkAreaCount()
338         {
339                 return splitter_->count();
340         }
341
342         TabWorkArea * tabWorkArea(int i)
343         {
344                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
345         }
346
347         TabWorkArea * currentTabWorkArea()
348         {
349                 int areas = tabWorkAreaCount();
350                 if (areas == 1)
351                         // The first TabWorkArea is always the first one, if any.
352                         return tabWorkArea(0);
353
354                 for (int i = 0; i != areas;  ++i) {
355                         TabWorkArea * twa = tabWorkArea(i);
356                         if (current_main_work_area_ == twa->currentWorkArea())
357                                 return twa;
358                 }
359
360                 // None has the focus so we just take the first one.
361                 return tabWorkArea(0);
362         }
363
364         int countWorkAreasOf(Buffer & buf)
365         {
366                 int areas = tabWorkAreaCount();
367                 int count = 0;
368                 for (int i = 0; i != areas;  ++i) {
369                         TabWorkArea * twa = tabWorkArea(i);
370                         if (twa->workArea(buf))
371                                 ++count;
372                 }
373                 return count;
374         }
375
376         void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
377         {
378                 if (processing_thread_watcher_.isRunning()) {
379                         // we prefer to cancel this preview in order to keep a snappy
380                         // interface.
381                         return;
382                 }
383                 processing_thread_watcher_.setFuture(f);
384         }
385
386         QSize iconSize(docstring const & icon_size)
387         {
388                 unsigned int size;
389                 if (icon_size == "small")
390                         size = smallIconSize;
391                 else if (icon_size == "normal")
392                         size = normalIconSize;
393                 else if (icon_size == "big")
394                         size = bigIconSize;
395                 else if (icon_size == "huge")
396                         size = hugeIconSize;
397                 else if (icon_size == "giant")
398                         size = giantIconSize;
399                 else
400                         size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
401
402                 if (size < smallIconSize)
403                         size = smallIconSize;
404
405                 return QSize(size, size);
406         }
407
408         QSize iconSize(QString const & icon_size)
409         {
410                 return iconSize(qstring_to_ucs4(icon_size));
411         }
412
413         string & iconSize(QSize const & qsize)
414         {
415                 LATTEST(qsize.width() == qsize.height());
416
417                 static string icon_size;
418
419                 unsigned int size = qsize.width();
420
421                 if (size < smallIconSize)
422                         size = smallIconSize;
423
424                 if (size == smallIconSize)
425                         icon_size = "small";
426                 else if (size == normalIconSize)
427                         icon_size = "normal";
428                 else if (size == bigIconSize)
429                         icon_size = "big";
430                 else if (size == hugeIconSize)
431                         icon_size = "huge";
432                 else if (size == giantIconSize)
433                         icon_size = "giant";
434                 else
435                         icon_size = convert<string>(size);
436
437                 return icon_size;
438         }
439
440 public:
441         GuiView * gv_;
442         GuiWorkArea * current_work_area_;
443         GuiWorkArea * current_main_work_area_;
444         QSplitter * splitter_;
445         QStackedWidget * stack_widget_;
446         BackgroundWidget * bg_widget_;
447         /// view's toolbars
448         ToolbarMap toolbars_;
449         ProgressInterface* progress_;
450         /// The main layout box.
451         /**
452          * \warning Don't Delete! The layout box is actually owned by
453          * whichever toolbar contains it. All the GuiView class needs is a
454          * means of accessing it.
455          *
456          * FIXME: replace that with a proper model so that we are not limited
457          * to only one dialog.
458          */
459         LayoutBox * layout_;
460
461         ///
462         map<string, DialogPtr> dialogs_;
463
464         unsigned int smallIconSize;
465         unsigned int normalIconSize;
466         unsigned int bigIconSize;
467         unsigned int hugeIconSize;
468         unsigned int giantIconSize;
469         ///
470         QTimer statusbar_timer_;
471         /// auto-saving of buffers
472         Timeout autosave_timeout_;
473         /// flag against a race condition due to multiclicks, see bug #1119
474         bool in_show_;
475
476         ///
477         TocModels toc_models_;
478
479         ///
480         QFutureWatcher<docstring> autosave_watcher_;
481         QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
482         ///
483         string last_export_format;
484         string processing_format;
485
486         static QSet<Buffer const *> busyBuffers;
487         static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488         static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489         static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490         static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
491
492         template<class T>
493         static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
494
495         // TODO syncFunc/previewFunc: use bind
496         bool asyncBufferProcessing(string const & argument,
497                                    Buffer const * used_buffer,
498                                    docstring const & msg,
499                                    Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500                                    Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501                                    Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
502
503         QVector<GuiWorkArea*> guiWorkAreas();
504 };
505
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
507
508
509 GuiView::GuiView(int id)
510         : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511           command_execute_(false), minibuffer_focus_(false), devel_mode_(false)
512 {
513         connect(this, SIGNAL(bufferViewChanged()),
514                 this, SLOT(onBufferViewChanged()));
515
516         // GuiToolbars *must* be initialised before the menu bar.
517         setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
518         constructToolbars();
519
520         // set ourself as the current view. This is needed for the menu bar
521         // filling, at least for the static special menu item on Mac. Otherwise
522         // they are greyed out.
523         guiApp->setCurrentView(this);
524
525         // Fill up the menu bar.
526         guiApp->menus().fillMenuBar(menuBar(), this, true);
527
528         setCentralWidget(d.stack_widget_);
529
530         // Start autosave timer
531         if (lyxrc.autosave) {
532                 // The connection is closed when this is destroyed.
533                 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535                 d.autosave_timeout_.start();
536         }
537         connect(&d.statusbar_timer_, SIGNAL(timeout()),
538                 this, SLOT(clearMessage()));
539
540         // We don't want to keep the window in memory if it is closed.
541         setAttribute(Qt::WA_DeleteOnClose, true);
542
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544         // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546         // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547         // since the icon is provided in the application bundle. We use a themed
548         // version when available and use the bundled one as fallback.
549         setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 #else
551         setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
552 #endif
553
554 #endif
555         resetWindowTitle();
556
557         // use tabbed dock area for multiple docks
558         // (such as "source" and "messages")
559         setDockOptions(QMainWindow::ForceTabbedDocks);
560
561         // For Drag&Drop.
562         setAcceptDrops(true);
563
564         // add busy indicator to statusbar
565         QLabel * busylabel = new QLabel(statusBar());
566         statusBar()->addPermanentWidget(busylabel);
567         search_mode mode = theGuiApp()->imageSearchMode();
568         QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569         QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570         busylabel->setMovie(busyanim);
571         busyanim->start();
572         busylabel->hide();
573
574         connect(&d.processing_thread_watcher_, SIGNAL(started()),
575                 busylabel, SLOT(show()));
576         connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577                 busylabel, SLOT(hide()));
578
579         QFontMetrics const fm(statusBar()->fontMetrics());
580         int const iconheight = max(int(d.normalIconSize), fm.height());
581         QSize const iconsize(iconheight, iconheight);
582
583         QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
584         shell_escape_ = new QLabel(statusBar());
585         shell_escape_->setPixmap(shellescape);
586         shell_escape_->setScaledContents(true);
587         shell_escape_->setAlignment(Qt::AlignCenter);
588         shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
589         shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
590                                       "external commands for this document. "
591                                       "Right click to change."));
592         SEMenu * menu = new SEMenu(this);
593         connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
594                 menu, SLOT(showMenu(QPoint)));
595         shell_escape_->hide();
596         statusBar()->addPermanentWidget(shell_escape_);
597
598         QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
599         read_only_ = new QLabel(statusBar());
600         read_only_->setPixmap(readonly);
601         read_only_->setScaledContents(true);
602         read_only_->setAlignment(Qt::AlignCenter);
603         read_only_->hide();
604         statusBar()->addPermanentWidget(read_only_);
605
606         version_control_ = new QLabel(statusBar());
607         version_control_->setAlignment(Qt::AlignCenter);
608         version_control_->setFrameStyle(QFrame::StyledPanel);
609         version_control_->hide();
610         statusBar()->addPermanentWidget(version_control_);
611
612         statusBar()->setSizeGripEnabled(true);
613         updateStatusBar();
614
615         connect(&d.autosave_watcher_, SIGNAL(finished()), this,
616                 SLOT(autoSaveThreadFinished()));
617
618         connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
619                 SLOT(processingThreadStarted()));
620         connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
621                 SLOT(processingThreadFinished()));
622
623         connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
624                 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
625
626         // set custom application bars context menu, e.g. tool bar and menu bar
627         setContextMenuPolicy(Qt::CustomContextMenu);
628         connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
629                 SLOT(toolBarPopup(const QPoint &)));
630
631         // Forbid too small unresizable window because it can happen
632         // with some window manager under X11.
633         setMinimumSize(300, 200);
634
635         if (lyxrc.allow_geometry_session) {
636                 // Now take care of session management.
637                 if (restoreLayout())
638                         return;
639         }
640
641         // no session handling, default to a sane size.
642         setGeometry(50, 50, 690, 510);
643         initToolbars();
644
645         // clear session data if any.
646         QSettings settings;
647         settings.remove("views");
648 }
649
650
651 GuiView::~GuiView()
652 {
653         delete &d;
654 }
655
656
657 void GuiView::disableShellEscape()
658 {
659         BufferView * bv = documentBufferView();
660         if (!bv)
661                 return;
662         theSession().shellescapeFiles().remove(bv->buffer().absFileName());
663         bv->buffer().params().shell_escape = false;
664         bv->processUpdateFlags(Update::Force);
665 }
666
667
668 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
669 {
670         QVector<GuiWorkArea*> areas;
671         for (int i = 0; i < tabWorkAreaCount(); i++) {
672                 TabWorkArea* ta = tabWorkArea(i);
673                 for (int u = 0; u < ta->count(); u++) {
674                         areas << ta->workArea(u);
675                 }
676         }
677         return areas;
678 }
679
680 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
681         string const & format)
682 {
683         docstring const fmt = theFormats().prettyName(format);
684         docstring msg;
685         switch (status) {
686         case Buffer::ExportSuccess:
687                 msg = bformat(_("Successful export to format: %1$s"), fmt);
688                 break;
689         case Buffer::ExportCancel:
690                 msg = _("Document export cancelled.");
691                 break;
692         case Buffer::ExportError:
693         case Buffer::ExportNoPathToFormat:
694         case Buffer::ExportTexPathHasSpaces:
695         case Buffer::ExportConverterError:
696                 msg = bformat(_("Error while exporting format: %1$s"), fmt);
697                 break;
698         case Buffer::PreviewSuccess:
699                 msg = bformat(_("Successful preview of format: %1$s"), fmt);
700                 break;
701         case Buffer::PreviewError:
702                 msg = bformat(_("Error while previewing format: %1$s"), fmt);
703                 break;
704         }
705         view->message(msg);
706 }
707
708
709 void GuiView::processingThreadStarted()
710 {
711 }
712
713
714 void GuiView::processingThreadFinished()
715 {
716         QFutureWatcher<Buffer::ExportStatus> const * watcher =
717                 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
718
719         Buffer::ExportStatus const status = watcher->result();
720         handleExportStatus(this, status, d.processing_format);
721
722         updateToolbars();
723         BufferView const * const bv = currentBufferView();
724         if (bv && !bv->buffer().errorList("Export").empty()) {
725                 errors("Export");
726                 return;
727         }
728         errors(d.last_export_format);
729 }
730
731
732 void GuiView::autoSaveThreadFinished()
733 {
734         QFutureWatcher<docstring> const * watcher =
735                 static_cast<QFutureWatcher<docstring> const *>(sender());
736         message(watcher->result());
737         updateToolbars();
738 }
739
740
741 void GuiView::saveLayout() const
742 {
743         QSettings settings;
744         settings.setValue("zoom_ratio", zoom_ratio_);
745         settings.setValue("devel_mode", devel_mode_);
746         settings.beginGroup("views");
747         settings.beginGroup(QString::number(id_));
748 #if defined(Q_WS_X11) || defined(QPA_XCB)
749         settings.setValue("pos", pos());
750         settings.setValue("size", size());
751 #else
752         settings.setValue("geometry", saveGeometry());
753 #endif
754         settings.setValue("layout", saveState(0));
755         settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
756 }
757
758
759 void GuiView::saveUISettings() const
760 {
761         // Save the toolbar private states
762         ToolbarMap::iterator end = d.toolbars_.end();
763         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
764                 it->second->saveSession();
765         // Now take care of all other dialogs
766         map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
767         for (; it!= d.dialogs_.end(); ++it)
768                 it->second->saveSession();
769 }
770
771
772 bool GuiView::restoreLayout()
773 {
774         QSettings settings;
775         zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
776         // Actual zoom value: default zoom + fractional offset
777         int zoom = lyxrc.defaultZoom * zoom_ratio_;
778         if (zoom < static_cast<int>(zoom_min_))
779                 zoom = zoom_min_;
780         lyxrc.currentZoom = zoom;
781         devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
782         settings.beginGroup("views");
783         settings.beginGroup(QString::number(id_));
784         QString const icon_key = "icon_size";
785         if (!settings.contains(icon_key))
786                 return false;
787
788         //code below is skipped when when ~/.config/LyX is (re)created
789         setIconSize(d.iconSize(settings.value(icon_key).toString()));
790
791 #if defined(Q_WS_X11) || defined(QPA_XCB)
792         QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
793         QSize size = settings.value("size", QSize(690, 510)).toSize();
794         resize(size);
795         move(pos);
796 #else
797         // Work-around for bug #6034: the window ends up in an undetermined
798         // state when trying to restore a maximized window when it is
799         // already maximized.
800         if (!(windowState() & Qt::WindowMaximized))
801                 if (!restoreGeometry(settings.value("geometry").toByteArray()))
802                         setGeometry(50, 50, 690, 510);
803 #endif
804         // Make sure layout is correctly oriented.
805         setLayoutDirection(qApp->layoutDirection());
806
807         // Allow the toc and view-source dock widget to be restored if needed.
808         Dialog * dialog;
809         if ((dialog = findOrBuild("toc", true)))
810                 // see bug 5082. At least setup title and enabled state.
811                 // Visibility will be adjusted by restoreState below.
812                 dialog->prepareView();
813         if ((dialog = findOrBuild("view-source", true)))
814                 dialog->prepareView();
815         if ((dialog = findOrBuild("progress", true)))
816                 dialog->prepareView();
817
818         if (!restoreState(settings.value("layout").toByteArray(), 0))
819                 initToolbars();
820
821         // init the toolbars that have not been restored
822         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
823         Toolbars::Infos::iterator end = guiApp->toolbars().end();
824         for (; cit != end; ++cit) {
825                 GuiToolbar * tb = toolbar(cit->name);
826                 if (tb && !tb->isRestored())
827                         initToolbar(cit->name);
828         }
829
830         // update lock (all) toolbars positions
831         updateLockToolbars();
832
833         updateDialogs();
834         return true;
835 }
836
837
838 GuiToolbar * GuiView::toolbar(string const & name)
839 {
840         ToolbarMap::iterator it = d.toolbars_.find(name);
841         if (it != d.toolbars_.end())
842                 return it->second;
843
844         LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
845         return 0;
846 }
847
848
849 void GuiView::updateLockToolbars()
850 {
851         toolbarsMovable_ = false;
852         for (ToolbarInfo const & info : guiApp->toolbars()) {
853                 GuiToolbar * tb = toolbar(info.name);
854                 if (tb && tb->isMovable())
855                         toolbarsMovable_ = true;
856         }
857 }
858
859
860 void GuiView::constructToolbars()
861 {
862         ToolbarMap::iterator it = d.toolbars_.begin();
863         for (; it != d.toolbars_.end(); ++it)
864                 delete it->second;
865         d.toolbars_.clear();
866
867         // I don't like doing this here, but the standard toolbar
868         // destroys this object when it's destroyed itself (vfr)
869         d.layout_ = new LayoutBox(*this);
870         d.stack_widget_->addWidget(d.layout_);
871         d.layout_->move(0,0);
872
873         // extracts the toolbars from the backend
874         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
875         Toolbars::Infos::iterator end = guiApp->toolbars().end();
876         for (; cit != end; ++cit)
877                 d.toolbars_[cit->name] =  new GuiToolbar(*cit, *this);
878 }
879
880
881 void GuiView::initToolbars()
882 {
883         // extracts the toolbars from the backend
884         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
885         Toolbars::Infos::iterator end = guiApp->toolbars().end();
886         for (; cit != end; ++cit)
887                 initToolbar(cit->name);
888 }
889
890
891 void GuiView::initToolbar(string const & name)
892 {
893         GuiToolbar * tb = toolbar(name);
894         if (!tb)
895                 return;
896         int const visibility = guiApp->toolbars().defaultVisibility(name);
897         bool newline = !(visibility & Toolbars::SAMEROW);
898         tb->setVisible(false);
899         tb->setVisibility(visibility);
900
901         if (visibility & Toolbars::TOP) {
902                 if (newline)
903                         addToolBarBreak(Qt::TopToolBarArea);
904                 addToolBar(Qt::TopToolBarArea, tb);
905         }
906
907         if (visibility & Toolbars::BOTTOM) {
908                 if (newline)
909                         addToolBarBreak(Qt::BottomToolBarArea);
910                 addToolBar(Qt::BottomToolBarArea, tb);
911         }
912
913         if (visibility & Toolbars::LEFT) {
914                 if (newline)
915                         addToolBarBreak(Qt::LeftToolBarArea);
916                 addToolBar(Qt::LeftToolBarArea, tb);
917         }
918
919         if (visibility & Toolbars::RIGHT) {
920                 if (newline)
921                         addToolBarBreak(Qt::RightToolBarArea);
922                 addToolBar(Qt::RightToolBarArea, tb);
923         }
924
925         if (visibility & Toolbars::ON)
926                 tb->setVisible(true);
927
928         tb->setMovable(true);
929 }
930
931
932 TocModels & GuiView::tocModels()
933 {
934         return d.toc_models_;
935 }
936
937
938 void GuiView::setFocus()
939 {
940         LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
941         QMainWindow::setFocus();
942 }
943
944
945 bool GuiView::hasFocus() const
946 {
947         if (currentWorkArea())
948                 return currentWorkArea()->hasFocus();
949         if (currentMainWorkArea())
950                 return currentMainWorkArea()->hasFocus();
951         return d.bg_widget_->hasFocus();
952 }
953
954
955 void GuiView::focusInEvent(QFocusEvent * e)
956 {
957         LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
958         QMainWindow::focusInEvent(e);
959         // Make sure guiApp points to the correct view.
960         guiApp->setCurrentView(this);
961         if (currentWorkArea())
962                 currentWorkArea()->setFocus();
963         else if (currentMainWorkArea())
964                 currentMainWorkArea()->setFocus();
965         else
966                 d.bg_widget_->setFocus();
967 }
968
969
970 void GuiView::showEvent(QShowEvent * e)
971 {
972         LYXERR(Debug::GUI, "Passed Geometry "
973                 << size().height() << "x" << size().width()
974                 << "+" << pos().x() << "+" << pos().y());
975
976         if (d.splitter_->count() == 0)
977                 // No work area, switch to the background widget.
978                 d.setBackground();
979
980         updateToolbars();
981         QMainWindow::showEvent(e);
982 }
983
984
985 bool GuiView::closeScheduled()
986 {
987         closing_ = true;
988         return close();
989 }
990
991
992 bool GuiView::prepareAllBuffersForLogout()
993 {
994         Buffer * first = theBufferList().first();
995         if (!first)
996                 return true;
997
998         // First, iterate over all buffers and ask the users if unsaved
999         // changes should be saved.
1000         // We cannot use a for loop as the buffer list cycles.
1001         Buffer * b = first;
1002         do {
1003                 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1004                         return false;
1005                 b = theBufferList().next(b);
1006         } while (b != first);
1007
1008         // Next, save session state
1009         // When a view/window was closed before without quitting LyX, there
1010         // are already entries in the lastOpened list.
1011         theSession().lastOpened().clear();
1012         writeSession();
1013
1014         return true;
1015 }
1016
1017
1018 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1019  ** is responsibility of the container (e.g., dialog)
1020  **/
1021 void GuiView::closeEvent(QCloseEvent * close_event)
1022 {
1023         LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1024
1025         if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1026                 Alert::warning(_("Exit LyX"),
1027                         _("LyX could not be closed because documents are being processed by LyX."));
1028                 close_event->setAccepted(false);
1029                 return;
1030         }
1031
1032         // If the user pressed the x (so we didn't call closeView
1033         // programmatically), we want to clear all existing entries.
1034         if (!closing_)
1035                 theSession().lastOpened().clear();
1036         closing_ = true;
1037
1038         writeSession();
1039
1040         // it can happen that this event arrives without selecting the view,
1041         // e.g. when clicking the close button on a background window.
1042         setFocus();
1043         if (!closeWorkAreaAll()) {
1044                 closing_ = false;
1045                 close_event->ignore();
1046                 return;
1047         }
1048
1049         // Make sure that nothing will use this to be closed View.
1050         guiApp->unregisterView(this);
1051
1052         if (isFullScreen()) {
1053                 // Switch off fullscreen before closing.
1054                 toggleFullScreen();
1055                 updateDialogs();
1056         }
1057
1058         // Make sure the timer time out will not trigger a statusbar update.
1059         d.statusbar_timer_.stop();
1060
1061         // Saving fullscreen requires additional tweaks in the toolbar code.
1062         // It wouldn't also work under linux natively.
1063         if (lyxrc.allow_geometry_session) {
1064                 saveLayout();
1065                 saveUISettings();
1066         }
1067
1068         close_event->accept();
1069 }
1070
1071
1072 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1073 {
1074         if (event->mimeData()->hasUrls())
1075                 event->accept();
1076         /// \todo Ask lyx-devel is this is enough:
1077         /// if (event->mimeData()->hasFormat("text/plain"))
1078         ///     event->acceptProposedAction();
1079 }
1080
1081
1082 void GuiView::dropEvent(QDropEvent * event)
1083 {
1084         QList<QUrl> files = event->mimeData()->urls();
1085         if (files.isEmpty())
1086                 return;
1087
1088         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1089         for (int i = 0; i != files.size(); ++i) {
1090                 string const file = os::internal_path(fromqstr(
1091                         files.at(i).toLocalFile()));
1092                 if (file.empty())
1093                         continue;
1094
1095                 string const ext = support::getExtension(file);
1096                 vector<const Format *> found_formats;
1097
1098                 // Find all formats that have the correct extension.
1099                 vector<const Format *> const & import_formats
1100                         = theConverters().importableFormats();
1101                 vector<const Format *>::const_iterator it = import_formats.begin();
1102                 for (; it != import_formats.end(); ++it)
1103                         if ((*it)->hasExtension(ext))
1104                                 found_formats.push_back(*it);
1105
1106                 FuncRequest cmd;
1107                 if (found_formats.size() >= 1) {
1108                         if (found_formats.size() > 1) {
1109                                 //FIXME: show a dialog to choose the correct importable format
1110                                 LYXERR(Debug::FILES,
1111                                         "Multiple importable formats found, selecting first");
1112                         }
1113                         string const arg = found_formats[0]->name() + " " + file;
1114                         cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1115                 }
1116                 else {
1117                         //FIXME: do we have to explicitly check whether it's a lyx file?
1118                         LYXERR(Debug::FILES,
1119                                 "No formats found, trying to open it as a lyx file");
1120                         cmd = FuncRequest(LFUN_FILE_OPEN, file);
1121                 }
1122                 // add the functions to the queue
1123                 guiApp->addToFuncRequestQueue(cmd);
1124                 event->accept();
1125         }
1126         // now process the collected functions. We perform the events
1127         // asynchronously. This prevents potential problems in case the
1128         // BufferView is closed within an event.
1129         guiApp->processFuncRequestQueueAsync();
1130 }
1131
1132
1133 void GuiView::message(docstring const & str)
1134 {
1135         if (ForkedProcess::iAmAChild())
1136                 return;
1137
1138         // call is moved to GUI-thread by GuiProgress
1139         d.progress_->appendMessage(toqstr(str));
1140 }
1141
1142
1143 void GuiView::clearMessageText()
1144 {
1145         message(docstring());
1146 }
1147
1148
1149 void GuiView::updateStatusBarMessage(QString const & str)
1150 {
1151         statusBar()->showMessage(str);
1152         d.statusbar_timer_.stop();
1153         d.statusbar_timer_.start(3000);
1154 }
1155
1156
1157 void GuiView::clearMessage()
1158 {
1159         // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1160         // the hasFocus function mostly returns false, even if the focus is on
1161         // a workarea in this view.
1162         //if (!hasFocus())
1163         //      return;
1164         showMessage();
1165         d.statusbar_timer_.stop();
1166 }
1167
1168
1169 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1170 {
1171         if (wa != d.current_work_area_
1172                 || wa->bufferView().buffer().isInternal())
1173                 return;
1174         Buffer const & buf = wa->bufferView().buffer();
1175         // Set the windows title
1176         docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1177         if (buf.notifiesExternalModification()) {
1178                 title = bformat(_("%1$s (modified externally)"), title);
1179                 // If the external modification status has changed, then maybe the status of
1180                 // buffer-save has changed too.
1181                 updateToolbars();
1182         }
1183 #ifndef Q_WS_MAC
1184         title += from_ascii(" - LyX");
1185 #endif
1186         setWindowTitle(toqstr(title));
1187         // Sets the path for the window: this is used by OSX to
1188         // allow a context click on the title bar showing a menu
1189         // with the path up to the file
1190         setWindowFilePath(toqstr(buf.absFileName()));
1191         // Tell Qt whether the current document is changed
1192         setWindowModified(!buf.isClean());
1193
1194         if (buf.params().shell_escape)
1195                 shell_escape_->show();
1196         else
1197                 shell_escape_->hide();
1198
1199         if (buf.hasReadonlyFlag())
1200                 read_only_->show();
1201         else
1202                 read_only_->hide();
1203
1204         if (buf.lyxvc().inUse()) {
1205                 version_control_->show();
1206                 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1207         } else
1208                 version_control_->hide();
1209 }
1210
1211
1212 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1213 {
1214         if (d.current_work_area_)
1215                 // disconnect the current work area from all slots
1216                 QObject::disconnect(d.current_work_area_, 0, this, 0);
1217         disconnectBuffer();
1218         disconnectBufferView();
1219         connectBufferView(wa->bufferView());
1220         connectBuffer(wa->bufferView().buffer());
1221         d.current_work_area_ = wa;
1222         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1223                          this, SLOT(updateWindowTitle(GuiWorkArea *)));
1224         QObject::connect(wa, SIGNAL(busy(bool)),
1225                          this, SLOT(setBusy(bool)));
1226         // connection of a signal to a signal
1227         QObject::connect(wa, SIGNAL(bufferViewChanged()),
1228                          this, SIGNAL(bufferViewChanged()));
1229         Q_EMIT updateWindowTitle(wa);
1230         Q_EMIT bufferViewChanged();
1231 }
1232
1233
1234 void GuiView::onBufferViewChanged()
1235 {
1236         structureChanged();
1237         // Buffer-dependent dialogs must be updated. This is done here because
1238         // some dialogs require buffer()->text.
1239         updateDialogs();
1240 }
1241
1242
1243 void GuiView::on_lastWorkAreaRemoved()
1244 {
1245         if (closing_)
1246                 // We already are in a close event. Nothing more to do.
1247                 return;
1248
1249         if (d.splitter_->count() > 1)
1250                 // We have a splitter so don't close anything.
1251                 return;
1252
1253         // Reset and updates the dialogs.
1254         Q_EMIT bufferViewChanged();
1255
1256         resetWindowTitle();
1257         updateStatusBar();
1258
1259         if (lyxrc.open_buffers_in_tabs)
1260                 // Nothing more to do, the window should stay open.
1261                 return;
1262
1263         if (guiApp->viewIds().size() > 1) {
1264                 close();
1265                 return;
1266         }
1267
1268 #ifdef Q_OS_MAC
1269         // On Mac we also close the last window because the application stay
1270         // resident in memory. On other platforms we don't close the last
1271         // window because this would quit the application.
1272         close();
1273 #endif
1274 }
1275
1276
1277 void GuiView::updateStatusBar()
1278 {
1279         // let the user see the explicit message
1280         if (d.statusbar_timer_.isActive())
1281                 return;
1282
1283         showMessage();
1284 }
1285
1286
1287 void GuiView::showMessage()
1288 {
1289         if (busy_)
1290                 return;
1291         QString msg = toqstr(theGuiApp()->viewStatusMessage());
1292         if (msg.isEmpty()) {
1293                 BufferView const * bv = currentBufferView();
1294                 if (bv)
1295                         msg = toqstr(bv->cursor().currentState(devel_mode_));
1296                 else
1297                         msg = qt_("Welcome to LyX!");
1298         }
1299         statusBar()->showMessage(msg);
1300 }
1301
1302
1303 bool GuiView::event(QEvent * e)
1304 {
1305         switch (e->type())
1306         {
1307         // Useful debug code:
1308         //case QEvent::ActivationChange:
1309         //case QEvent::WindowDeactivate:
1310         //case QEvent::Paint:
1311         //case QEvent::Enter:
1312         //case QEvent::Leave:
1313         //case QEvent::HoverEnter:
1314         //case QEvent::HoverLeave:
1315         //case QEvent::HoverMove:
1316         //case QEvent::StatusTip:
1317         //case QEvent::DragEnter:
1318         //case QEvent::DragLeave:
1319         //case QEvent::Drop:
1320         //      break;
1321
1322         case QEvent::WindowActivate: {
1323                 GuiView * old_view = guiApp->currentView();
1324                 if (this == old_view) {
1325                         setFocus();
1326                         return QMainWindow::event(e);
1327                 }
1328                 if (old_view && old_view->currentBufferView()) {
1329                         // save current selection to the selection buffer to allow
1330                         // middle-button paste in this window.
1331                         cap::saveSelection(old_view->currentBufferView()->cursor());
1332                 }
1333                 guiApp->setCurrentView(this);
1334                 if (d.current_work_area_)
1335                         on_currentWorkAreaChanged(d.current_work_area_);
1336                 else
1337                         resetWindowTitle();
1338                 setFocus();
1339                 return QMainWindow::event(e);
1340         }
1341
1342         case QEvent::ShortcutOverride: {
1343                 // See bug 4888
1344                 if (isFullScreen() && menuBar()->isHidden()) {
1345                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1346                         // FIXME: we should also try to detect special LyX shortcut such as
1347                         // Alt-P and Alt-M. Right now there is a hack in
1348                         // GuiWorkArea::processKeySym() that hides again the menubar for
1349                         // those cases.
1350                         if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1351                                 menuBar()->show();
1352                                 return QMainWindow::event(e);
1353                         }
1354                 }
1355                 return QMainWindow::event(e);
1356         }
1357
1358         default:
1359                 return QMainWindow::event(e);
1360         }
1361 }
1362
1363 void GuiView::resetWindowTitle()
1364 {
1365         setWindowTitle(qt_("LyX"));
1366 }
1367
1368 bool GuiView::focusNextPrevChild(bool /*next*/)
1369 {
1370         setFocus();
1371         return true;
1372 }
1373
1374
1375 bool GuiView::busy() const
1376 {
1377         return busy_ > 0;
1378 }
1379
1380
1381 void GuiView::setBusy(bool busy)
1382 {
1383         bool const busy_before = busy_ > 0;
1384         busy ? ++busy_ : --busy_;
1385         if ((busy_ > 0) == busy_before)
1386                 // busy state didn't change
1387                 return;
1388
1389         if (busy) {
1390                 QApplication::setOverrideCursor(Qt::WaitCursor);
1391                 return;
1392         }
1393         QApplication::restoreOverrideCursor();
1394         updateLayoutList();
1395 }
1396
1397
1398 void GuiView::resetCommandExecute()
1399 {
1400         command_execute_ = false;
1401         updateToolbars();
1402 }
1403
1404
1405 double GuiView::pixelRatio() const
1406 {
1407 #if QT_VERSION >= 0x050000
1408         return qt_scale_factor * devicePixelRatio();
1409 #else
1410         return 1.0;
1411 #endif
1412 }
1413
1414
1415 GuiWorkArea * GuiView::workArea(int index)
1416 {
1417         if (TabWorkArea * twa = d.currentTabWorkArea())
1418                 if (index < twa->count())
1419                         return twa->workArea(index);
1420         return 0;
1421 }
1422
1423
1424 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1425 {
1426         if (currentWorkArea()
1427                 && &currentWorkArea()->bufferView().buffer() == &buffer)
1428                 return currentWorkArea();
1429         if (TabWorkArea * twa = d.currentTabWorkArea())
1430                 return twa->workArea(buffer);
1431         return 0;
1432 }
1433
1434
1435 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1436 {
1437         // Automatically create a TabWorkArea if there are none yet.
1438         TabWorkArea * tab_widget = d.splitter_->count()
1439                 ? d.currentTabWorkArea() : addTabWorkArea();
1440         return tab_widget->addWorkArea(buffer, *this);
1441 }
1442
1443
1444 TabWorkArea * GuiView::addTabWorkArea()
1445 {
1446         TabWorkArea * twa = new TabWorkArea;
1447         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1448                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1449         QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1450                          this, SLOT(on_lastWorkAreaRemoved()));
1451
1452         d.splitter_->addWidget(twa);
1453         d.stack_widget_->setCurrentWidget(d.splitter_);
1454         return twa;
1455 }
1456
1457
1458 GuiWorkArea const * GuiView::currentWorkArea() const
1459 {
1460         return d.current_work_area_;
1461 }
1462
1463
1464 GuiWorkArea * GuiView::currentWorkArea()
1465 {
1466         return d.current_work_area_;
1467 }
1468
1469
1470 GuiWorkArea const * GuiView::currentMainWorkArea() const
1471 {
1472         if (!d.currentTabWorkArea())
1473                 return 0;
1474         return d.currentTabWorkArea()->currentWorkArea();
1475 }
1476
1477
1478 GuiWorkArea * GuiView::currentMainWorkArea()
1479 {
1480         if (!d.currentTabWorkArea())
1481                 return 0;
1482         return d.currentTabWorkArea()->currentWorkArea();
1483 }
1484
1485
1486 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1487 {
1488         LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1489         if (!wa) {
1490                 d.current_work_area_ = 0;
1491                 d.setBackground();
1492                 Q_EMIT bufferViewChanged();
1493                 return;
1494         }
1495
1496         // FIXME: I've no clue why this is here and why it accesses
1497         //  theGuiApp()->currentView, which might be 0 (bug 6464).
1498         //  See also 27525 (vfr).
1499         if (theGuiApp()->currentView() == this
1500                   && theGuiApp()->currentView()->currentWorkArea() == wa)
1501                 return;
1502
1503         if (currentBufferView())
1504                 cap::saveSelection(currentBufferView()->cursor());
1505
1506         theGuiApp()->setCurrentView(this);
1507         d.current_work_area_ = wa;
1508
1509         // We need to reset this now, because it will need to be
1510         // right if the tabWorkArea gets reset in the for loop. We
1511         // will change it back if we aren't in that case.
1512         GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1513         d.current_main_work_area_ = wa;
1514
1515         for (int i = 0; i != d.splitter_->count(); ++i) {
1516                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1517                         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1518                                 << ", Current main wa: " << currentMainWorkArea());
1519                         return;
1520                 }
1521         }
1522
1523         d.current_main_work_area_ = old_cmwa;
1524
1525         LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1526         on_currentWorkAreaChanged(wa);
1527         BufferView & bv = wa->bufferView();
1528         bv.cursor().fixIfBroken();
1529         bv.updateMetrics();
1530         wa->setUpdatesEnabled(true);
1531         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1532 }
1533
1534
1535 void GuiView::removeWorkArea(GuiWorkArea * wa)
1536 {
1537         LASSERT(wa, return);
1538         if (wa == d.current_work_area_) {
1539                 disconnectBuffer();
1540                 disconnectBufferView();
1541                 d.current_work_area_ = 0;
1542                 d.current_main_work_area_ = 0;
1543         }
1544
1545         bool found_twa = false;
1546         for (int i = 0; i != d.splitter_->count(); ++i) {
1547                 TabWorkArea * twa = d.tabWorkArea(i);
1548                 if (twa->removeWorkArea(wa)) {
1549                         // Found in this tab group, and deleted the GuiWorkArea.
1550                         found_twa = true;
1551                         if (twa->count() != 0) {
1552                                 if (d.current_work_area_ == 0)
1553                                         // This means that we are closing the current GuiWorkArea, so
1554                                         // switch to the next GuiWorkArea in the found TabWorkArea.
1555                                         setCurrentWorkArea(twa->currentWorkArea());
1556                         } else {
1557                                 // No more WorkAreas in this tab group, so delete it.
1558                                 delete twa;
1559                         }
1560                         break;
1561                 }
1562         }
1563
1564         // It is not a tabbed work area (i.e., the search work area), so it
1565         // should be deleted by other means.
1566         LASSERT(found_twa, return);
1567
1568         if (d.current_work_area_ == 0) {
1569                 if (d.splitter_->count() != 0) {
1570                         TabWorkArea * twa = d.currentTabWorkArea();
1571                         setCurrentWorkArea(twa->currentWorkArea());
1572                 } else {
1573                         // No more work areas, switch to the background widget.
1574                         setCurrentWorkArea(0);
1575                 }
1576         }
1577 }
1578
1579
1580 LayoutBox * GuiView::getLayoutDialog() const
1581 {
1582         return d.layout_;
1583 }
1584
1585
1586 void GuiView::updateLayoutList()
1587 {
1588         if (d.layout_)
1589                 d.layout_->updateContents(false);
1590 }
1591
1592
1593 void GuiView::updateToolbars()
1594 {
1595         ToolbarMap::iterator end = d.toolbars_.end();
1596         if (d.current_work_area_) {
1597                 int context = 0;
1598                 if (d.current_work_area_->bufferView().cursor().inMathed()
1599                         && !d.current_work_area_->bufferView().cursor().inRegexped())
1600                         context |= Toolbars::MATH;
1601                 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1602                         context |= Toolbars::TABLE;
1603                 if (currentBufferView()->buffer().areChangesPresent()
1604                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1605                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1606                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1607                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1608                         context |= Toolbars::REVIEW;
1609                 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1610                         context |= Toolbars::MATHMACROTEMPLATE;
1611                 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1612                         context |= Toolbars::IPA;
1613                 if (command_execute_)
1614                         context |= Toolbars::MINIBUFFER;
1615                 if (minibuffer_focus_) {
1616                         context |= Toolbars::MINIBUFFER_FOCUS;
1617                         minibuffer_focus_ = false;
1618                 }
1619
1620                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1621                         it->second->update(context);
1622         } else
1623                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1624                         it->second->update();
1625 }
1626
1627
1628 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1629 {
1630         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1631         LASSERT(newBuffer, return);
1632
1633         GuiWorkArea * wa = workArea(*newBuffer);
1634         if (wa == 0) {
1635                 setBusy(true);
1636                 newBuffer->masterBuffer()->updateBuffer();
1637                 setBusy(false);
1638                 wa = addWorkArea(*newBuffer);
1639                 // scroll to the position when the BufferView was last closed
1640                 if (lyxrc.use_lastfilepos) {
1641                         LastFilePosSection::FilePos filepos =
1642                                 theSession().lastFilePos().load(newBuffer->fileName());
1643                         wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1644                 }
1645         } else {
1646                 //Disconnect the old buffer...there's no new one.
1647                 disconnectBuffer();
1648         }
1649         connectBuffer(*newBuffer);
1650         connectBufferView(wa->bufferView());
1651         if (switch_to)
1652                 setCurrentWorkArea(wa);
1653 }
1654
1655
1656 void GuiView::connectBuffer(Buffer & buf)
1657 {
1658         buf.setGuiDelegate(this);
1659 }
1660
1661
1662 void GuiView::disconnectBuffer()
1663 {
1664         if (d.current_work_area_)
1665                 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1666 }
1667
1668
1669 void GuiView::connectBufferView(BufferView & bv)
1670 {
1671         bv.setGuiDelegate(this);
1672 }
1673
1674
1675 void GuiView::disconnectBufferView()
1676 {
1677         if (d.current_work_area_)
1678                 d.current_work_area_->bufferView().setGuiDelegate(0);
1679 }
1680
1681
1682 void GuiView::errors(string const & error_type, bool from_master)
1683 {
1684         BufferView const * const bv = currentBufferView();
1685         if (!bv)
1686                 return;
1687
1688 #if EXPORT_in_THREAD
1689         // We are called with from_master == false by default, so we
1690         // have to figure out whether that is the case or not.
1691         ErrorList & el = bv->buffer().errorList(error_type);
1692         if (el.empty()) {
1693             el = bv->buffer().masterBuffer()->errorList(error_type);
1694             from_master = true;
1695         }
1696 #else
1697         ErrorList const & el = from_master ?
1698                 bv->buffer().masterBuffer()->errorList(error_type) :
1699                 bv->buffer().errorList(error_type);
1700 #endif
1701
1702         if (el.empty())
1703                 return;
1704
1705         string data = error_type;
1706         if (from_master)
1707                 data = "from_master|" + error_type;
1708         showDialog("errorlist", data);
1709 }
1710
1711
1712 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1713 {
1714         d.toc_models_.updateItem(toqstr(type), dit);
1715 }
1716
1717
1718 void GuiView::structureChanged()
1719 {
1720         // This is called from the Buffer, which has no way to ensure that cursors
1721         // in BufferView remain valid.
1722         if (documentBufferView())
1723                 documentBufferView()->cursor().sanitize();
1724         // FIXME: This is slightly expensive, though less than the tocBackend update
1725         // (#9880). This also resets the view in the Toc Widget (#6675).
1726         d.toc_models_.reset(documentBufferView());
1727         // Navigator needs more than a simple update in this case. It needs to be
1728         // rebuilt.
1729         updateDialog("toc", "");
1730 }
1731
1732
1733 void GuiView::updateDialog(string const & name, string const & data)
1734 {
1735         if (!isDialogVisible(name))
1736                 return;
1737
1738         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1739         if (it == d.dialogs_.end())
1740                 return;
1741
1742         Dialog * const dialog = it->second.get();
1743         if (dialog->isVisibleView())
1744                 dialog->initialiseParams(data);
1745 }
1746
1747
1748 BufferView * GuiView::documentBufferView()
1749 {
1750         return currentMainWorkArea()
1751                 ? &currentMainWorkArea()->bufferView()
1752                 : 0;
1753 }
1754
1755
1756 BufferView const * GuiView::documentBufferView() const
1757 {
1758         return currentMainWorkArea()
1759                 ? &currentMainWorkArea()->bufferView()
1760                 : 0;
1761 }
1762
1763
1764 BufferView * GuiView::currentBufferView()
1765 {
1766         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1767 }
1768
1769
1770 BufferView const * GuiView::currentBufferView() const
1771 {
1772         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1773 }
1774
1775
1776 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1777         Buffer const * orig, Buffer * clone)
1778 {
1779         bool const success = clone->autoSave();
1780         delete clone;
1781         busyBuffers.remove(orig);
1782         return success
1783                 ? _("Automatic save done.")
1784                 : _("Automatic save failed!");
1785 }
1786
1787
1788 void GuiView::autoSave()
1789 {
1790         LYXERR(Debug::INFO, "Running autoSave()");
1791
1792         Buffer * buffer = documentBufferView()
1793                 ? &documentBufferView()->buffer() : 0;
1794         if (!buffer) {
1795                 resetAutosaveTimers();
1796                 return;
1797         }
1798
1799         GuiViewPrivate::busyBuffers.insert(buffer);
1800         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1801                 buffer, buffer->cloneBufferOnly());
1802         d.autosave_watcher_.setFuture(f);
1803         resetAutosaveTimers();
1804 }
1805
1806
1807 void GuiView::resetAutosaveTimers()
1808 {
1809         if (lyxrc.autosave)
1810                 d.autosave_timeout_.restart();
1811 }
1812
1813
1814 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1815 {
1816         bool enable = true;
1817         Buffer * buf = currentBufferView()
1818                 ? &currentBufferView()->buffer() : 0;
1819         Buffer * doc_buffer = documentBufferView()
1820                 ? &(documentBufferView()->buffer()) : 0;
1821
1822 #ifdef Q_OS_MAC
1823         /* In LyX/Mac, when a dialog is open, the menus of the
1824            application can still be accessed without giving focus to
1825            the main window. In this case, we want to disable the menu
1826            entries that are buffer-related.
1827            This code must not be used on Linux and Windows, since it
1828            would disable buffer-related entries when hovering over the
1829            menu (see bug #9574).
1830          */
1831         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1832                 buf = 0;
1833                 doc_buffer = 0;
1834         }
1835 #endif
1836
1837         // Check whether we need a buffer
1838         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1839                 // no, exit directly
1840                 flag.message(from_utf8(N_("Command not allowed with"
1841                                         "out any document open")));
1842                 flag.setEnabled(false);
1843                 return true;
1844         }
1845
1846         if (cmd.origin() == FuncRequest::TOC) {
1847                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1848                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1849                         flag.setEnabled(false);
1850                 return true;
1851         }
1852
1853         switch(cmd.action()) {
1854         case LFUN_BUFFER_IMPORT:
1855                 break;
1856
1857         case LFUN_MASTER_BUFFER_UPDATE:
1858         case LFUN_MASTER_BUFFER_VIEW:
1859                 enable = doc_buffer
1860                         && (doc_buffer->parent() != 0
1861                             || doc_buffer->hasChildren())
1862                         && !d.processing_thread_watcher_.isRunning();
1863                 break;
1864
1865         case LFUN_BUFFER_UPDATE:
1866         case LFUN_BUFFER_VIEW: {
1867                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1868                         enable = false;
1869                         break;
1870                 }
1871                 string format = to_utf8(cmd.argument());
1872                 if (cmd.argument().empty())
1873                         format = doc_buffer->params().getDefaultOutputFormat();
1874                 enable = doc_buffer->params().isExportable(format, true);
1875                 break;
1876         }
1877
1878         case LFUN_BUFFER_RELOAD:
1879                 enable = doc_buffer && !doc_buffer->isUnnamed()
1880                         && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1881                 break;
1882
1883         case LFUN_BUFFER_CHILD_OPEN:
1884                 enable = doc_buffer != 0;
1885                 break;
1886
1887         case LFUN_BUFFER_WRITE:
1888                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1889                 break;
1890
1891         //FIXME: This LFUN should be moved to GuiApplication.
1892         case LFUN_BUFFER_WRITE_ALL: {
1893                 // We enable the command only if there are some modified buffers
1894                 Buffer * first = theBufferList().first();
1895                 enable = false;
1896                 if (!first)
1897                         break;
1898                 Buffer * b = first;
1899                 // We cannot use a for loop as the buffer list is a cycle.
1900                 do {
1901                         if (!b->isClean()) {
1902                                 enable = true;
1903                                 break;
1904                         }
1905                         b = theBufferList().next(b);
1906                 } while (b != first);
1907                 break;
1908         }
1909
1910         case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1911                 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1912                 break;
1913
1914         case LFUN_BUFFER_WRITE_AS:
1915         case LFUN_BUFFER_EXPORT_AS:
1916                 enable = doc_buffer != 0;
1917                 break;
1918
1919         case LFUN_BUFFER_CLOSE:
1920         case LFUN_VIEW_CLOSE:
1921                 enable = doc_buffer != 0;
1922                 break;
1923
1924         case LFUN_BUFFER_CLOSE_ALL:
1925                 enable = theBufferList().last() != theBufferList().first();
1926                 break;
1927
1928         case LFUN_VIEW_SPLIT:
1929                 if (cmd.getArg(0) == "vertical")
1930                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1931                                          d.splitter_->orientation() == Qt::Vertical);
1932                 else
1933                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1934                                          d.splitter_->orientation() == Qt::Horizontal);
1935                 break;
1936
1937         case LFUN_TAB_GROUP_CLOSE:
1938                 enable = d.tabWorkAreaCount() > 1;
1939                 break;
1940
1941         case LFUN_DEVEL_MODE_TOGGLE:
1942                 flag.setOnOff(devel_mode_);
1943                 break;
1944
1945         case LFUN_TOOLBAR_TOGGLE: {
1946                 string const name = cmd.getArg(0);
1947                 if (GuiToolbar * t = toolbar(name))
1948                         flag.setOnOff(t->isVisible());
1949                 else {
1950                         enable = false;
1951                         docstring const msg =
1952                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1953                         flag.message(msg);
1954                 }
1955                 break;
1956         }
1957
1958         case LFUN_TOOLBAR_MOVABLE: {
1959                 string const name = cmd.getArg(0);
1960                 // use negation since locked == !movable
1961                 if (name == "*")
1962                         // toolbar name * locks all toolbars
1963                         flag.setOnOff(!toolbarsMovable_);
1964                 else if (GuiToolbar * t = toolbar(name))
1965                         flag.setOnOff(!(t->isMovable()));
1966                 else {
1967                         enable = false;
1968                         docstring const msg =
1969                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1970                         flag.message(msg);
1971                 }
1972                 break;
1973         }
1974
1975         case LFUN_ICON_SIZE:
1976                 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1977                 break;
1978
1979         case LFUN_DROP_LAYOUTS_CHOICE:
1980                 enable = buf != 0;
1981                 break;
1982
1983         case LFUN_UI_TOGGLE:
1984                 flag.setOnOff(isFullScreen());
1985                 break;
1986
1987         case LFUN_DIALOG_DISCONNECT_INSET:
1988                 break;
1989
1990         case LFUN_DIALOG_HIDE:
1991                 // FIXME: should we check if the dialog is shown?
1992                 break;
1993
1994         case LFUN_DIALOG_TOGGLE:
1995                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1996                 // to set "enable"
1997                 // fall through
1998         case LFUN_DIALOG_SHOW: {
1999                 string const name = cmd.getArg(0);
2000                 if (!doc_buffer)
2001                         enable = name == "aboutlyx"
2002                                 || name == "file" //FIXME: should be removed.
2003                                 || name == "prefs"
2004                                 || name == "texinfo"
2005                                 || name == "progress"
2006                                 || name == "compare";
2007                 else if (name == "character" || name == "symbols"
2008                         || name == "mathdelimiter" || name == "mathmatrix") {
2009                         if (!buf || buf->isReadonly())
2010                                 enable = false;
2011                         else {
2012                                 Cursor const & cur = currentBufferView()->cursor();
2013                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2014                         }
2015                 }
2016                 else if (name == "latexlog")
2017                         enable = FileName(doc_buffer->logName()).isReadableFile();
2018                 else if (name == "spellchecker")
2019                         enable = theSpellChecker()
2020                                 && !doc_buffer->isReadonly()
2021                                 && !doc_buffer->text().empty();
2022                 else if (name == "vclog")
2023                         enable = doc_buffer->lyxvc().inUse();
2024                 break;
2025         }
2026
2027         case LFUN_DIALOG_UPDATE: {
2028                 string const name = cmd.getArg(0);
2029                 if (!buf)
2030                         enable = name == "prefs";
2031                 break;
2032         }
2033
2034         case LFUN_COMMAND_EXECUTE:
2035         case LFUN_MESSAGE:
2036         case LFUN_MENU_OPEN:
2037                 // Nothing to check.
2038                 break;
2039
2040         case LFUN_COMPLETION_INLINE:
2041                 if (!d.current_work_area_
2042                         || !d.current_work_area_->completer().inlinePossible(
2043                         currentBufferView()->cursor()))
2044                         enable = false;
2045                 break;
2046
2047         case LFUN_COMPLETION_POPUP:
2048                 if (!d.current_work_area_
2049                         || !d.current_work_area_->completer().popupPossible(
2050                         currentBufferView()->cursor()))
2051                         enable = false;
2052                 break;
2053
2054         case LFUN_COMPLETE:
2055                 if (!d.current_work_area_
2056                         || !d.current_work_area_->completer().inlinePossible(
2057                         currentBufferView()->cursor()))
2058                         enable = false;
2059                 break;
2060
2061         case LFUN_COMPLETION_ACCEPT:
2062                 if (!d.current_work_area_
2063                         || (!d.current_work_area_->completer().popupVisible()
2064                         && !d.current_work_area_->completer().inlineVisible()
2065                         && !d.current_work_area_->completer().completionAvailable()))
2066                         enable = false;
2067                 break;
2068
2069         case LFUN_COMPLETION_CANCEL:
2070                 if (!d.current_work_area_
2071                         || (!d.current_work_area_->completer().popupVisible()
2072                         && !d.current_work_area_->completer().inlineVisible()))
2073                         enable = false;
2074                 break;
2075
2076         case LFUN_BUFFER_ZOOM_OUT:
2077         case LFUN_BUFFER_ZOOM_IN: {
2078                 // only diff between these two is that the default for ZOOM_OUT
2079                 // is a neg. number
2080                 bool const neg_zoom =
2081                         convert<int>(cmd.argument()) < 0 ||
2082                         (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2083                 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2084                         docstring const msg =
2085                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2086                         flag.message(msg);
2087                         enable = false;
2088                 } else
2089                         enable = doc_buffer;
2090                 break;
2091         }
2092
2093         case LFUN_BUFFER_ZOOM: {
2094                 bool const less_than_min_zoom =
2095                         !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2096                 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2097                         docstring const msg =
2098                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2099                         flag.message(msg);
2100                         enable = false;
2101                 }
2102                 else
2103                         enable = doc_buffer;
2104                 break;
2105         }
2106
2107         case LFUN_BUFFER_MOVE_NEXT:
2108         case LFUN_BUFFER_MOVE_PREVIOUS:
2109                 // we do not cycle when moving
2110         case LFUN_BUFFER_NEXT:
2111         case LFUN_BUFFER_PREVIOUS:
2112                 // because we cycle, it doesn't matter whether on first or last
2113                 enable = (d.currentTabWorkArea()->count() > 1);
2114                 break;
2115         case LFUN_BUFFER_SWITCH:
2116                 // toggle on the current buffer, but do not toggle off
2117                 // the other ones (is that a good idea?)
2118                 if (doc_buffer
2119                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2120                         flag.setOnOff(true);
2121                 break;
2122
2123         case LFUN_VC_REGISTER:
2124                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2125                 break;
2126         case LFUN_VC_RENAME:
2127                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2128                 break;
2129         case LFUN_VC_COPY:
2130                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2131                 break;
2132         case LFUN_VC_CHECK_IN:
2133                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2134                 break;
2135         case LFUN_VC_CHECK_OUT:
2136                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2137                 break;
2138         case LFUN_VC_LOCKING_TOGGLE:
2139                 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2140                         && doc_buffer->lyxvc().lockingToggleEnabled();
2141                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2142                 break;
2143         case LFUN_VC_REVERT:
2144                 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2145                         && !doc_buffer->hasReadonlyFlag();
2146                 break;
2147         case LFUN_VC_UNDO_LAST:
2148                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2149                 break;
2150         case LFUN_VC_REPO_UPDATE:
2151                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2152                 break;
2153         case LFUN_VC_COMMAND: {
2154                 if (cmd.argument().empty())
2155                         enable = false;
2156                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2157                         enable = false;
2158                 break;
2159         }
2160         case LFUN_VC_COMPARE:
2161                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2162                 break;
2163
2164         case LFUN_SERVER_GOTO_FILE_ROW:
2165         case LFUN_LYX_ACTIVATE:
2166                 break;
2167         case LFUN_FORWARD_SEARCH:
2168                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2169                 break;
2170
2171         case LFUN_FILE_INSERT_PLAINTEXT:
2172         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2173                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2174                 break;
2175
2176         case LFUN_SPELLING_CONTINUOUSLY:
2177                 flag.setOnOff(lyxrc.spellcheck_continuously);
2178                 break;
2179
2180         default:
2181                 return false;
2182         }
2183
2184         if (!enable)
2185                 flag.setEnabled(false);
2186
2187         return true;
2188 }
2189
2190
2191 static FileName selectTemplateFile()
2192 {
2193         FileDialog dlg(qt_("Select template file"));
2194         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2195         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2196
2197         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2198                                  QStringList(qt_("LyX Documents (*.lyx)")));
2199
2200         if (result.first == FileDialog::Later)
2201                 return FileName();
2202         if (result.second.isEmpty())
2203                 return FileName();
2204         return FileName(fromqstr(result.second));
2205 }
2206
2207
2208 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2209 {
2210         setBusy(true);
2211
2212         Buffer * newBuffer = 0;
2213         try {
2214                 newBuffer = checkAndLoadLyXFile(filename);
2215         } catch (ExceptionMessage const & e) {
2216                 setBusy(false);
2217                 throw(e);
2218         }
2219         setBusy(false);
2220
2221         if (!newBuffer) {
2222                 message(_("Document not loaded."));
2223                 return 0;
2224         }
2225
2226         setBuffer(newBuffer);
2227         newBuffer->errors("Parse");
2228
2229         if (tolastfiles)
2230                 theSession().lastFiles().add(filename);
2231
2232         return newBuffer;
2233 }
2234
2235
2236 void GuiView::openDocument(string const & fname)
2237 {
2238         string initpath = lyxrc.document_path;
2239
2240         if (documentBufferView()) {
2241                 string const trypath = documentBufferView()->buffer().filePath();
2242                 // If directory is writeable, use this as default.
2243                 if (FileName(trypath).isDirWritable())
2244                         initpath = trypath;
2245         }
2246
2247         string filename;
2248
2249         if (fname.empty()) {
2250                 FileDialog dlg(qt_("Select document to open"));
2251                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2252                 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2253
2254                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2255                 FileDialog::Result result =
2256                         dlg.open(toqstr(initpath), filter);
2257
2258                 if (result.first == FileDialog::Later)
2259                         return;
2260
2261                 filename = fromqstr(result.second);
2262
2263                 // check selected filename
2264                 if (filename.empty()) {
2265                         message(_("Canceled."));
2266                         return;
2267                 }
2268         } else
2269                 filename = fname;
2270
2271         // get absolute path of file and add ".lyx" to the filename if
2272         // necessary.
2273         FileName const fullname =
2274                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2275         if (!fullname.empty())
2276                 filename = fullname.absFileName();
2277
2278         if (!fullname.onlyPath().isDirectory()) {
2279                 Alert::warning(_("Invalid filename"),
2280                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2281                                 from_utf8(fullname.absFileName())));
2282                 return;
2283         }
2284
2285         // if the file doesn't exist and isn't already open (bug 6645),
2286         // let the user create one
2287         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2288             !LyXVC::file_not_found_hook(fullname)) {
2289                 // the user specifically chose this name. Believe him.
2290                 Buffer * const b = newFile(filename, string(), true);
2291                 if (b)
2292                         setBuffer(b);
2293                 return;
2294         }
2295
2296         docstring const disp_fn = makeDisplayPath(filename);
2297         message(bformat(_("Opening document %1$s..."), disp_fn));
2298
2299         docstring str2;
2300         Buffer * buf = loadDocument(fullname);
2301         if (buf) {
2302                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2303                 if (buf->lyxvc().inUse())
2304                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2305                                 " " + _("Version control detected.");
2306         } else {
2307                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2308         }
2309         message(str2);
2310 }
2311
2312 // FIXME: clean that
2313 static bool import(GuiView * lv, FileName const & filename,
2314         string const & format, ErrorList & errorList)
2315 {
2316         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2317
2318         string loader_format;
2319         vector<string> loaders = theConverters().loaders();
2320         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2321                 vector<string>::const_iterator it = loaders.begin();
2322                 vector<string>::const_iterator en = loaders.end();
2323                 for (; it != en; ++it) {
2324                         if (!theConverters().isReachable(format, *it))
2325                                 continue;
2326
2327                         string const tofile =
2328                                 support::changeExtension(filename.absFileName(),
2329                                 theFormats().extension(*it));
2330                         if (!theConverters().convert(0, filename, FileName(tofile),
2331                                 filename, format, *it, errorList))
2332                                 return false;
2333                         loader_format = *it;
2334                         break;
2335                 }
2336                 if (loader_format.empty()) {
2337                         frontend::Alert::error(_("Couldn't import file"),
2338                                          bformat(_("No information for importing the format %1$s."),
2339                                          theFormats().prettyName(format)));
2340                         return false;
2341                 }
2342         } else
2343                 loader_format = format;
2344
2345         if (loader_format == "lyx") {
2346                 Buffer * buf = lv->loadDocument(lyxfile);
2347                 if (!buf)
2348                         return false;
2349         } else {
2350                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2351                 if (!b)
2352                         return false;
2353                 lv->setBuffer(b);
2354                 bool as_paragraphs = loader_format == "textparagraph";
2355                 string filename2 = (loader_format == format) ? filename.absFileName()
2356                         : support::changeExtension(filename.absFileName(),
2357                                           theFormats().extension(loader_format));
2358                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2359                         as_paragraphs);
2360                 guiApp->setCurrentView(lv);
2361                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2362         }
2363
2364         return true;
2365 }
2366
2367
2368 void GuiView::importDocument(string const & argument)
2369 {
2370         string format;
2371         string filename = split(argument, format, ' ');
2372
2373         LYXERR(Debug::INFO, format << " file: " << filename);
2374
2375         // need user interaction
2376         if (filename.empty()) {
2377                 string initpath = lyxrc.document_path;
2378                 if (documentBufferView()) {
2379                         string const trypath = documentBufferView()->buffer().filePath();
2380                         // If directory is writeable, use this as default.
2381                         if (FileName(trypath).isDirWritable())
2382                                 initpath = trypath;
2383                 }
2384
2385                 docstring const text = bformat(_("Select %1$s file to import"),
2386                         theFormats().prettyName(format));
2387
2388                 FileDialog dlg(toqstr(text));
2389                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2390                 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2391
2392                 docstring filter = theFormats().prettyName(format);
2393                 filter += " (*.{";
2394                 // FIXME UNICODE
2395                 filter += from_utf8(theFormats().extensions(format));
2396                 filter += "})";
2397
2398                 FileDialog::Result result =
2399                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2400
2401                 if (result.first == FileDialog::Later)
2402                         return;
2403
2404                 filename = fromqstr(result.second);
2405
2406                 // check selected filename
2407                 if (filename.empty())
2408                         message(_("Canceled."));
2409         }
2410
2411         if (filename.empty())
2412                 return;
2413
2414         // get absolute path of file
2415         FileName const fullname(support::makeAbsPath(filename));
2416
2417         // Can happen if the user entered a path into the dialog
2418         // (see bug #7437)
2419         if (fullname.onlyFileName().empty()) {
2420                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2421                                           "Aborting import."),
2422                                         from_utf8(fullname.absFileName()));
2423                 frontend::Alert::error(_("File name error"), msg);
2424                 message(_("Canceled."));
2425                 return;
2426         }
2427
2428
2429         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2430
2431         // Check if the document already is open
2432         Buffer * buf = theBufferList().getBuffer(lyxfile);
2433         if (buf) {
2434                 setBuffer(buf);
2435                 if (!closeBuffer()) {
2436                         message(_("Canceled."));
2437                         return;
2438                 }
2439         }
2440
2441         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2442
2443         // if the file exists already, and we didn't do
2444         // -i lyx thefile.lyx, warn
2445         if (lyxfile.exists() && fullname != lyxfile) {
2446
2447                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2448                         "Do you want to overwrite that document?"), displaypath);
2449                 int const ret = Alert::prompt(_("Overwrite document?"),
2450                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2451
2452                 if (ret == 1) {
2453                         message(_("Canceled."));
2454                         return;
2455                 }
2456         }
2457
2458         message(bformat(_("Importing %1$s..."), displaypath));
2459         ErrorList errorList;
2460         if (import(this, fullname, format, errorList))
2461                 message(_("imported."));
2462         else
2463                 message(_("file not imported!"));
2464
2465         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2466 }
2467
2468
2469 void GuiView::newDocument(string const & filename, bool from_template)
2470 {
2471         FileName initpath(lyxrc.document_path);
2472         if (documentBufferView()) {
2473                 FileName const trypath(documentBufferView()->buffer().filePath());
2474                 // If directory is writeable, use this as default.
2475                 if (trypath.isDirWritable())
2476                         initpath = trypath;
2477         }
2478
2479         string templatefile;
2480         if (from_template) {
2481                 templatefile = selectTemplateFile().absFileName();
2482                 if (templatefile.empty())
2483                         return;
2484         }
2485
2486         Buffer * b;
2487         if (filename.empty())
2488                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2489         else
2490                 b = newFile(filename, templatefile, true);
2491
2492         if (b)
2493                 setBuffer(b);
2494
2495         // If no new document could be created, it is unsure
2496         // whether there is a valid BufferView.
2497         if (currentBufferView())
2498                 // Ensure the cursor is correctly positioned on screen.
2499                 currentBufferView()->showCursor();
2500 }
2501
2502
2503 void GuiView::insertLyXFile(docstring const & fname)
2504 {
2505         BufferView * bv = documentBufferView();
2506         if (!bv)
2507                 return;
2508
2509         // FIXME UNICODE
2510         FileName filename(to_utf8(fname));
2511         if (filename.empty()) {
2512                 // Launch a file browser
2513                 // FIXME UNICODE
2514                 string initpath = lyxrc.document_path;
2515                 string const trypath = bv->buffer().filePath();
2516                 // If directory is writeable, use this as default.
2517                 if (FileName(trypath).isDirWritable())
2518                         initpath = trypath;
2519
2520                 // FIXME UNICODE
2521                 FileDialog dlg(qt_("Select LyX document to insert"));
2522                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2523                 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2524
2525                 FileDialog::Result result = dlg.open(toqstr(initpath),
2526                                          QStringList(qt_("LyX Documents (*.lyx)")));
2527
2528                 if (result.first == FileDialog::Later)
2529                         return;
2530
2531                 // FIXME UNICODE
2532                 filename.set(fromqstr(result.second));
2533
2534                 // check selected filename
2535                 if (filename.empty()) {
2536                         // emit message signal.
2537                         message(_("Canceled."));
2538                         return;
2539                 }
2540         }
2541
2542         bv->insertLyXFile(filename);
2543         bv->buffer().errors("Parse");
2544 }
2545
2546
2547 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2548 {
2549         FileName fname = b.fileName();
2550         FileName const oldname = fname;
2551
2552         if (!newname.empty()) {
2553                 // FIXME UNICODE
2554                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2555         } else {
2556                 // Switch to this Buffer.
2557                 setBuffer(&b);
2558
2559                 // No argument? Ask user through dialog.
2560                 // FIXME UNICODE
2561                 FileDialog dlg(qt_("Choose a filename to save document as"));
2562                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2563                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2564
2565                 if (!isLyXFileName(fname.absFileName()))
2566                         fname.changeExtension(".lyx");
2567
2568                 FileDialog::Result result =
2569                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2570                                    QStringList(qt_("LyX Documents (*.lyx)")),
2571                                          toqstr(fname.onlyFileName()));
2572
2573                 if (result.first == FileDialog::Later)
2574                         return false;
2575
2576                 fname.set(fromqstr(result.second));
2577
2578                 if (fname.empty())
2579                         return false;
2580
2581                 if (!isLyXFileName(fname.absFileName()))
2582                         fname.changeExtension(".lyx");
2583         }
2584
2585         // fname is now the new Buffer location.
2586
2587         // if there is already a Buffer open with this name, we do not want
2588         // to have another one. (the second test makes sure we're not just
2589         // trying to overwrite ourselves, which is fine.)
2590         if (theBufferList().exists(fname) && fname != oldname
2591                   && theBufferList().getBuffer(fname) != &b) {
2592                 docstring const text =
2593                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2594                             "Please close it before attempting to overwrite it.\n"
2595                             "Do you want to choose a new filename?"),
2596                                 from_utf8(fname.absFileName()));
2597                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2598                         text, 0, 1, _("&Rename"), _("&Cancel"));
2599                 switch (ret) {
2600                 case 0: return renameBuffer(b, docstring(), kind);
2601                 case 1: return false;
2602                 }
2603                 //return false;
2604         }
2605
2606         bool const existsLocal = fname.exists();
2607         bool const existsInVC = LyXVC::fileInVC(fname);
2608         if (existsLocal || existsInVC) {
2609                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2610                 if (kind != LV_WRITE_AS && existsInVC) {
2611                         // renaming to a name that is already in VC
2612                         // would not work
2613                         docstring text = bformat(_("The document %1$s "
2614                                         "is already registered.\n\n"
2615                                         "Do you want to choose a new name?"),
2616                                 file);
2617                         docstring const title = (kind == LV_VC_RENAME) ?
2618                                 _("Rename document?") : _("Copy document?");
2619                         docstring const button = (kind == LV_VC_RENAME) ?
2620                                 _("&Rename") : _("&Copy");
2621                         int const ret = Alert::prompt(title, text, 0, 1,
2622                                 button, _("&Cancel"));
2623                         switch (ret) {
2624                         case 0: return renameBuffer(b, docstring(), kind);
2625                         case 1: return false;
2626                         }
2627                 }
2628
2629                 if (existsLocal) {
2630                         docstring text = bformat(_("The document %1$s "
2631                                         "already exists.\n\n"
2632                                         "Do you want to overwrite that document?"),
2633                                 file);
2634                         int const ret = Alert::prompt(_("Overwrite document?"),
2635                                         text, 0, 2, _("&Overwrite"),
2636                                         _("&Rename"), _("&Cancel"));
2637                         switch (ret) {
2638                         case 0: break;
2639                         case 1: return renameBuffer(b, docstring(), kind);
2640                         case 2: return false;
2641                         }
2642                 }
2643         }
2644
2645         switch (kind) {
2646         case LV_VC_RENAME: {
2647                 string msg = b.lyxvc().rename(fname);
2648                 if (msg.empty())
2649                         return false;
2650                 message(from_utf8(msg));
2651                 break;
2652         }
2653         case LV_VC_COPY: {
2654                 string msg = b.lyxvc().copy(fname);
2655                 if (msg.empty())
2656                         return false;
2657                 message(from_utf8(msg));
2658                 break;
2659         }
2660         case LV_WRITE_AS:
2661                 break;
2662         }
2663         // LyXVC created the file already in case of LV_VC_RENAME or
2664         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2665         // relative paths of included stuff right if we moved e.g. from
2666         // /a/b.lyx to /a/c/b.lyx.
2667
2668         bool const saved = saveBuffer(b, fname);
2669         if (saved)
2670                 b.reload();
2671         return saved;
2672 }
2673
2674
2675 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2676 {
2677         FileName fname = b.fileName();
2678
2679         FileDialog dlg(qt_("Choose a filename to export the document as"));
2680         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2681
2682         QStringList types;
2683         QString const anyformat = qt_("Guess from extension (*.*)");
2684         types << anyformat;
2685
2686         vector<Format const *> export_formats;
2687         for (Format const & f : theFormats())
2688                 if (f.documentFormat())
2689                         export_formats.push_back(&f);
2690         sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2691         map<QString, string> fmap;
2692         QString filter;
2693         string ext;
2694         for (Format const * f : export_formats) {
2695                 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2696                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2697                                                      loc_prettyname,
2698                                                      from_ascii(f->extension())));
2699                 types << loc_filter;
2700                 fmap[loc_filter] = f->name();
2701                 if (from_ascii(f->name()) == iformat) {
2702                         filter = loc_filter;
2703                         ext = f->extension();
2704                 }
2705         }
2706         string ofname = fname.onlyFileName();
2707         if (!ext.empty())
2708                 ofname = support::changeExtension(ofname, ext);
2709         FileDialog::Result result =
2710                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2711                          types,
2712                          toqstr(ofname),
2713                          &filter);
2714         if (result.first != FileDialog::Chosen)
2715                 return false;
2716
2717         string fmt_name;
2718         fname.set(fromqstr(result.second));
2719         if (filter == anyformat)
2720                 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2721         else
2722                 fmt_name = fmap[filter];
2723         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2724                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2725
2726         if (fmt_name.empty() || fname.empty())
2727                 return false;
2728
2729         // fname is now the new Buffer location.
2730         if (FileName(fname).exists()) {
2731                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2732                 docstring text = bformat(_("The document %1$s already "
2733                                            "exists.\n\nDo you want to "
2734                                            "overwrite that document?"),
2735                                          file);
2736                 int const ret = Alert::prompt(_("Overwrite document?"),
2737                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2738                 switch (ret) {
2739                 case 0: break;
2740                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2741                 case 2: return false;
2742                 }
2743         }
2744
2745         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2746         DispatchResult dr;
2747         dispatch(cmd, dr);
2748         return dr.dispatched();
2749 }
2750
2751
2752 bool GuiView::saveBuffer(Buffer & b)
2753 {
2754         return saveBuffer(b, FileName());
2755 }
2756
2757
2758 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2759 {
2760         if (workArea(b) && workArea(b)->inDialogMode())
2761                 return true;
2762
2763         if (fn.empty() && b.isUnnamed())
2764                 return renameBuffer(b, docstring());
2765
2766         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2767         if (success) {
2768                 theSession().lastFiles().add(b.fileName());
2769                 return true;
2770         }
2771
2772         // Switch to this Buffer.
2773         setBuffer(&b);
2774
2775         // FIXME: we don't tell the user *WHY* the save failed !!
2776         docstring const file = makeDisplayPath(b.absFileName(), 30);
2777         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2778                                    "Do you want to rename the document and "
2779                                    "try again?"), file);
2780         int const ret = Alert::prompt(_("Rename and save?"),
2781                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2782         switch (ret) {
2783         case 0:
2784                 if (!renameBuffer(b, docstring()))
2785                         return false;
2786                 break;
2787         case 1:
2788                 break;
2789         case 2:
2790                 return false;
2791         }
2792
2793         return saveBuffer(b, fn);
2794 }
2795
2796
2797 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2798 {
2799         return closeWorkArea(wa, false);
2800 }
2801
2802
2803 // We only want to close the buffer if it is not visible in other workareas
2804 // of the same view, nor in other views, and if this is not a child
2805 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2806 {
2807         Buffer & buf = wa->bufferView().buffer();
2808
2809         bool last_wa = d.countWorkAreasOf(buf) == 1
2810                 && !inOtherView(buf) && !buf.parent();
2811
2812         bool close_buffer = last_wa;
2813
2814         if (last_wa) {
2815                 if (lyxrc.close_buffer_with_last_view == "yes")
2816                         ; // Nothing to do
2817                 else if (lyxrc.close_buffer_with_last_view == "no")
2818                         close_buffer = false;
2819                 else {
2820                         docstring file;
2821                         if (buf.isUnnamed())
2822                                 file = from_utf8(buf.fileName().onlyFileName());
2823                         else
2824                                 file = buf.fileName().displayName(30);
2825                         docstring const text = bformat(
2826                                 _("Last view on document %1$s is being closed.\n"
2827                                   "Would you like to close or hide the document?\n"
2828                                   "\n"
2829                                   "Hidden documents can be displayed back through\n"
2830                                   "the menu: View->Hidden->...\n"
2831                                   "\n"
2832                                   "To remove this question, set your preference in:\n"
2833                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2834                                 ), file);
2835                         int ret = Alert::prompt(_("Close or hide document?"),
2836                                 text, 0, 1, _("&Close"), _("&Hide"));
2837                         close_buffer = (ret == 0);
2838                 }
2839         }
2840
2841         return closeWorkArea(wa, close_buffer);
2842 }
2843
2844
2845 bool GuiView::closeBuffer()
2846 {
2847         GuiWorkArea * wa = currentMainWorkArea();
2848         // coverity complained about this
2849         // it seems unnecessary, but perhaps is worth the check
2850         LASSERT(wa, return false);
2851
2852         setCurrentWorkArea(wa);
2853         Buffer & buf = wa->bufferView().buffer();
2854         return closeWorkArea(wa, !buf.parent());
2855 }
2856
2857
2858 void GuiView::writeSession() const {
2859         GuiWorkArea const * active_wa = currentMainWorkArea();
2860         for (int i = 0; i < d.splitter_->count(); ++i) {
2861                 TabWorkArea * twa = d.tabWorkArea(i);
2862                 for (int j = 0; j < twa->count(); ++j) {
2863                         GuiWorkArea * wa = twa->workArea(j);
2864                         Buffer & buf = wa->bufferView().buffer();
2865                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2866                 }
2867         }
2868 }
2869
2870
2871 bool GuiView::closeBufferAll()
2872 {
2873         // Close the workareas in all other views
2874         QList<int> const ids = guiApp->viewIds();
2875         for (int i = 0; i != ids.size(); ++i) {
2876                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2877                         return false;
2878         }
2879
2880         // Close our own workareas
2881         if (!closeWorkAreaAll())
2882                 return false;
2883
2884         // Now close the hidden buffers. We prevent hidden buffers from being
2885         // dirty, so we can just close them.
2886         theBufferList().closeAll();
2887         return true;
2888 }
2889
2890
2891 bool GuiView::closeWorkAreaAll()
2892 {
2893         setCurrentWorkArea(currentMainWorkArea());
2894
2895         // We might be in a situation that there is still a tabWorkArea, but
2896         // there are no tabs anymore. This can happen when we get here after a
2897         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2898         // many TabWorkArea's have no documents anymore.
2899         int empty_twa = 0;
2900
2901         // We have to call count() each time, because it can happen that
2902         // more than one splitter will disappear in one iteration (bug 5998).
2903         while (d.splitter_->count() > empty_twa) {
2904                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2905
2906                 if (twa->count() == 0)
2907                         ++empty_twa;
2908                 else {
2909                         setCurrentWorkArea(twa->currentWorkArea());
2910                         if (!closeTabWorkArea(twa))
2911                                 return false;
2912                 }
2913         }
2914         return true;
2915 }
2916
2917
2918 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2919 {
2920         if (!wa)
2921                 return false;
2922
2923         Buffer & buf = wa->bufferView().buffer();
2924
2925         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2926                 Alert::warning(_("Close document"),
2927                         _("Document could not be closed because it is being processed by LyX."));
2928                 return false;
2929         }
2930
2931         if (close_buffer)
2932                 return closeBuffer(buf);
2933         else {
2934                 if (!inMultiTabs(wa))
2935                         if (!saveBufferIfNeeded(buf, true))
2936                                 return false;
2937                 removeWorkArea(wa);
2938                 return true;
2939         }
2940 }
2941
2942
2943 bool GuiView::closeBuffer(Buffer & buf)
2944 {
2945         // If we are in a close_event all children will be closed in some time,
2946         // so no need to do it here. This will ensure that the children end up
2947         // in the session file in the correct order. If we close the master
2948         // buffer, we can close or release the child buffers here too.
2949         bool success = true;
2950         if (!closing_) {
2951                 ListOfBuffers clist = buf.getChildren();
2952                 ListOfBuffers::const_iterator it = clist.begin();
2953                 ListOfBuffers::const_iterator const bend = clist.end();
2954                 for (; it != bend; ++it) {
2955                         Buffer * child_buf = *it;
2956                         if (theBufferList().isOthersChild(&buf, child_buf)) {
2957                                 child_buf->setParent(0);
2958                                 continue;
2959                         }
2960
2961                         // FIXME: should we look in other tabworkareas?
2962                         // ANSWER: I don't think so. I've tested, and if the child is
2963                         // open in some other window, it closes without a problem.
2964                         GuiWorkArea * child_wa = workArea(*child_buf);
2965                         if (child_wa) {
2966                                 success = closeWorkArea(child_wa, true);
2967                                 if (!success)
2968                                         break;
2969                         } else {
2970                                 // In this case the child buffer is open but hidden.
2971                                 // It therefore should not (MUST NOT) be dirty!
2972                                 LATTEST(child_buf->isClean());
2973                                 theBufferList().release(child_buf);
2974                         }
2975                 }
2976         }
2977         if (success) {
2978                 // goto bookmark to update bookmark pit.
2979                 // FIXME: we should update only the bookmarks related to this buffer!
2980                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2981                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2982                         guiApp->gotoBookmark(i+1, false, false);
2983
2984                 if (saveBufferIfNeeded(buf, false)) {
2985                         buf.removeAutosaveFile();
2986                         theBufferList().release(&buf);
2987                         return true;
2988                 }
2989         }
2990         // open all children again to avoid a crash because of dangling
2991         // pointers (bug 6603)
2992         buf.updateBuffer();
2993         return false;
2994 }
2995
2996
2997 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2998 {
2999         while (twa == d.currentTabWorkArea()) {
3000                 twa->setCurrentIndex(twa->count() - 1);
3001
3002                 GuiWorkArea * wa = twa->currentWorkArea();
3003                 Buffer & b = wa->bufferView().buffer();
3004
3005                 // We only want to close the buffer if the same buffer is not visible
3006                 // in another view, and if this is not a child and if we are closing
3007                 // a view (not a tabgroup).
3008                 bool const close_buffer =
3009                         !inOtherView(b) && !b.parent() && closing_;
3010
3011                 if (!closeWorkArea(wa, close_buffer))
3012                         return false;
3013         }
3014         return true;
3015 }
3016
3017
3018 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3019 {
3020         if (buf.isClean() || buf.paragraphs().empty())
3021                 return true;
3022
3023         // Switch to this Buffer.
3024         setBuffer(&buf);
3025
3026         docstring file;
3027         bool exists;
3028         // FIXME: Unicode?
3029         if (buf.isUnnamed()) {
3030                 file = from_utf8(buf.fileName().onlyFileName());
3031                 exists = false;
3032         } else {
3033                 FileName filename = buf.fileName();
3034                 filename.refresh();
3035                 file = filename.displayName(30);
3036                 exists = filename.exists();
3037         }
3038
3039         // Bring this window to top before asking questions.
3040         raise();
3041         activateWindow();
3042
3043         int ret;
3044         if (hiding && buf.isUnnamed()) {
3045                 docstring const text = bformat(_("The document %1$s has not been "
3046                                                  "saved yet.\n\nDo you want to save "
3047                                                  "the document?"), file);
3048                 ret = Alert::prompt(_("Save new document?"),
3049                         text, 0, 1, _("&Save"), _("&Cancel"));
3050                 if (ret == 1)
3051                         ++ret;
3052         } else {
3053                 docstring const text = exists ?
3054                         bformat(_("The document %1$s has unsaved changes."
3055                                   "\n\nDo you want to save the document or "
3056                                   "discard the changes?"), file) :
3057                         bformat(_("The document %1$s has not been saved yet."
3058                                   "\n\nDo you want to save the document or "
3059                                   "discard it entirely?"), file);
3060                 docstring const title = exists ?
3061                         _("Save changed document?") : _("Save document?");
3062                 ret = Alert::prompt(title, text, 0, 2,
3063                                     _("&Save"), _("&Discard"), _("&Cancel"));
3064         }
3065
3066         switch (ret) {
3067         case 0:
3068                 if (!saveBuffer(buf))
3069                         return false;
3070                 break;
3071         case 1:
3072                 // If we crash after this we could have no autosave file
3073                 // but I guess this is really improbable (Jug).
3074                 // Sometimes improbable things happen:
3075                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3076                 // buf.removeAutosaveFile();
3077                 if (hiding)
3078                         // revert all changes
3079                         reloadBuffer(buf);
3080                 buf.markClean();
3081                 break;
3082         case 2:
3083                 return false;
3084         }
3085         return true;
3086 }
3087
3088
3089 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3090 {
3091         Buffer & buf = wa->bufferView().buffer();
3092
3093         for (int i = 0; i != d.splitter_->count(); ++i) {
3094                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3095                 if (wa_ && wa_ != wa)
3096                         return true;
3097         }
3098         return inOtherView(buf);
3099 }
3100
3101
3102 bool GuiView::inOtherView(Buffer & buf)
3103 {
3104         QList<int> const ids = guiApp->viewIds();
3105
3106         for (int i = 0; i != ids.size(); ++i) {
3107                 if (id_ == ids[i])
3108                         continue;
3109
3110                 if (guiApp->view(ids[i]).workArea(buf))
3111                         return true;
3112         }
3113         return false;
3114 }
3115
3116
3117 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3118 {
3119         if (!documentBufferView())
3120                 return;
3121
3122         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3123                 Buffer * const curbuf = &documentBufferView()->buffer();
3124                 int nwa = twa->count();
3125                 for (int i = 0; i < nwa; ++i) {
3126                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3127                                 int next_index;
3128                                 if (np == NEXTBUFFER)
3129                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3130                                 else
3131                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3132                                 if (move)
3133                                         twa->moveTab(i, next_index);
3134                                 else
3135                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3136                                 break;
3137                         }
3138                 }
3139         }
3140 }
3141
3142
3143 /// make sure the document is saved
3144 static bool ensureBufferClean(Buffer * buffer)
3145 {
3146         LASSERT(buffer, return false);
3147         if (buffer->isClean() && !buffer->isUnnamed())
3148                 return true;
3149
3150         docstring const file = buffer->fileName().displayName(30);
3151         docstring title;
3152         docstring text;
3153         if (!buffer->isUnnamed()) {
3154                 text = bformat(_("The document %1$s has unsaved "
3155                                                  "changes.\n\nDo you want to save "
3156                                                  "the document?"), file);
3157                 title = _("Save changed document?");
3158
3159         } else {
3160                 text = bformat(_("The document %1$s has not been "
3161                                                  "saved yet.\n\nDo you want to save "
3162                                                  "the document?"), file);
3163                 title = _("Save new document?");
3164         }
3165         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3166
3167         if (ret == 0)
3168                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3169
3170         return buffer->isClean() && !buffer->isUnnamed();
3171 }
3172
3173
3174 bool GuiView::reloadBuffer(Buffer & buf)
3175 {
3176         Buffer::ReadStatus status = buf.reload();
3177         return status == Buffer::ReadSuccess;
3178 }
3179
3180
3181 void GuiView::checkExternallyModifiedBuffers()
3182 {
3183         BufferList::iterator bit = theBufferList().begin();
3184         BufferList::iterator const bend = theBufferList().end();
3185         for (; bit != bend; ++bit) {
3186                 Buffer * buf = *bit;
3187                 if (buf->fileName().exists() && buf->isChecksumModified()) {
3188                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3189                                         " Reload now? Any local changes will be lost."),
3190                                         from_utf8(buf->absFileName()));
3191                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3192                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3193                         if (!ret)
3194                                 reloadBuffer(*buf);
3195                 }
3196         }
3197 }
3198
3199
3200 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3201 {
3202         Buffer * buffer = documentBufferView()
3203                 ? &(documentBufferView()->buffer()) : 0;
3204
3205         switch (cmd.action()) {
3206         case LFUN_VC_REGISTER:
3207                 if (!buffer || !ensureBufferClean(buffer))
3208                         break;
3209                 if (!buffer->lyxvc().inUse()) {
3210                         if (buffer->lyxvc().registrer()) {
3211                                 reloadBuffer(*buffer);
3212                                 dr.clearMessageUpdate();
3213                         }
3214                 }
3215                 break;
3216
3217         case LFUN_VC_RENAME:
3218         case LFUN_VC_COPY: {
3219                 if (!buffer || !ensureBufferClean(buffer))
3220                         break;
3221                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3222                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3223                                 // Some changes are not yet committed.
3224                                 // We test here and not in getStatus(), since
3225                                 // this test is expensive.
3226                                 string log;
3227                                 LyXVC::CommandResult ret =
3228                                         buffer->lyxvc().checkIn(log);
3229                                 dr.setMessage(log);
3230                                 if (ret == LyXVC::ErrorCommand ||
3231                                     ret == LyXVC::VCSuccess)
3232                                         reloadBuffer(*buffer);
3233                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3234                                         frontend::Alert::error(
3235                                                 _("Revision control error."),
3236                                                 _("Document could not be checked in."));
3237                                         break;
3238                                 }
3239                         }
3240                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3241                                 LV_VC_RENAME : LV_VC_COPY;
3242                         renameBuffer(*buffer, cmd.argument(), kind);
3243                 }
3244                 break;
3245         }
3246
3247         case LFUN_VC_CHECK_IN:
3248                 if (!buffer || !ensureBufferClean(buffer))
3249                         break;
3250                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3251                         string log;
3252                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3253                         dr.setMessage(log);
3254                         // Only skip reloading if the checkin was cancelled or
3255                         // an error occurred before the real checkin VCS command
3256                         // was executed, since the VCS might have changed the
3257                         // file even if it could not checkin successfully.
3258                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3259                                 reloadBuffer(*buffer);
3260                 }
3261                 break;
3262
3263         case LFUN_VC_CHECK_OUT:
3264                 if (!buffer || !ensureBufferClean(buffer))
3265                         break;
3266                 if (buffer->lyxvc().inUse()) {
3267                         dr.setMessage(buffer->lyxvc().checkOut());
3268                         reloadBuffer(*buffer);
3269                 }
3270                 break;
3271
3272         case LFUN_VC_LOCKING_TOGGLE:
3273                 LASSERT(buffer, return);
3274                 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3275                         break;
3276                 if (buffer->lyxvc().inUse()) {
3277                         string res = buffer->lyxvc().lockingToggle();
3278                         if (res.empty()) {
3279                                 frontend::Alert::error(_("Revision control error."),
3280                                 _("Error when setting the locking property."));
3281                         } else {
3282                                 dr.setMessage(res);
3283                                 reloadBuffer(*buffer);
3284                         }
3285                 }
3286                 break;
3287
3288         case LFUN_VC_REVERT:
3289                 LASSERT(buffer, return);
3290                 if (buffer->lyxvc().revert()) {
3291                         reloadBuffer(*buffer);
3292                         dr.clearMessageUpdate();
3293                 }
3294                 break;
3295
3296         case LFUN_VC_UNDO_LAST:
3297                 LASSERT(buffer, return);
3298                 buffer->lyxvc().undoLast();
3299                 reloadBuffer(*buffer);
3300                 dr.clearMessageUpdate();
3301                 break;
3302
3303         case LFUN_VC_REPO_UPDATE:
3304                 LASSERT(buffer, return);
3305                 if (ensureBufferClean(buffer)) {
3306                         dr.setMessage(buffer->lyxvc().repoUpdate());
3307                         checkExternallyModifiedBuffers();
3308                 }
3309                 break;
3310
3311         case LFUN_VC_COMMAND: {
3312                 string flag = cmd.getArg(0);
3313                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3314                         break;
3315                 docstring message;
3316                 if (contains(flag, 'M')) {
3317                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3318                                 break;
3319                 }
3320                 string path = cmd.getArg(1);
3321                 if (contains(path, "$$p") && buffer)
3322                         path = subst(path, "$$p", buffer->filePath());
3323                 LYXERR(Debug::LYXVC, "Directory: " << path);
3324                 FileName pp(path);
3325                 if (!pp.isReadableDirectory()) {
3326                         lyxerr << _("Directory is not accessible.") << endl;
3327                         break;
3328                 }
3329                 support::PathChanger p(pp);
3330
3331                 string command = cmd.getArg(2);
3332                 if (command.empty())
3333                         break;
3334                 if (buffer) {
3335                         command = subst(command, "$$i", buffer->absFileName());
3336                         command = subst(command, "$$p", buffer->filePath());
3337                 }
3338                 command = subst(command, "$$m", to_utf8(message));
3339                 LYXERR(Debug::LYXVC, "Command: " << command);
3340                 Systemcall one;
3341                 one.startscript(Systemcall::Wait, command);
3342
3343                 if (!buffer)
3344                         break;
3345                 if (contains(flag, 'I'))
3346                         buffer->markDirty();
3347                 if (contains(flag, 'R'))
3348                         reloadBuffer(*buffer);
3349
3350                 break;
3351                 }
3352
3353         case LFUN_VC_COMPARE: {
3354                 if (cmd.argument().empty()) {
3355                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3356                         break;
3357                 }
3358
3359                 string rev1 = cmd.getArg(0);
3360                 string f1, f2;
3361                 LATTEST(buffer)
3362
3363                 // f1
3364                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3365                         break;
3366
3367                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3368                         f2 = buffer->absFileName();
3369                 } else {
3370                         string rev2 = cmd.getArg(1);
3371                         if (rev2.empty())
3372                                 break;
3373                         // f2
3374                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3375                                 break;
3376                 }
3377
3378                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3379                                         f1 << "\n"  << f2 << "\n" );
3380                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3381                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3382                 break;
3383         }
3384
3385         default:
3386                 break;
3387         }
3388 }
3389
3390
3391 void GuiView::openChildDocument(string const & fname)
3392 {
3393         LASSERT(documentBufferView(), return);
3394         Buffer & buffer = documentBufferView()->buffer();
3395         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3396         documentBufferView()->saveBookmark(false);
3397         Buffer * child = 0;
3398         if (theBufferList().exists(filename)) {
3399                 child = theBufferList().getBuffer(filename);
3400                 setBuffer(child);
3401         } else {
3402                 message(bformat(_("Opening child document %1$s..."),
3403                         makeDisplayPath(filename.absFileName())));
3404                 child = loadDocument(filename, false);
3405         }
3406         // Set the parent name of the child document.
3407         // This makes insertion of citations and references in the child work,
3408         // when the target is in the parent or another child document.
3409         if (child)
3410                 child->setParent(&buffer);
3411 }
3412
3413
3414 bool GuiView::goToFileRow(string const & argument)
3415 {
3416         string file_name;
3417         int row;
3418         size_t i = argument.find_last_of(' ');
3419         if (i != string::npos) {
3420                 file_name = os::internal_path(trim(argument.substr(0, i)));
3421                 istringstream is(argument.substr(i + 1));
3422                 is >> row;
3423                 if (is.fail())
3424                         i = string::npos;
3425         }
3426         if (i == string::npos) {
3427                 LYXERR0("Wrong argument: " << argument);
3428                 return false;
3429         }
3430         Buffer * buf = 0;
3431         string const abstmp = package().temp_dir().absFileName();
3432         string const realtmp = package().temp_dir().realPath();
3433         // We have to use os::path_prefix_is() here, instead of
3434         // simply prefixIs(), because the file name comes from
3435         // an external application and may need case adjustment.
3436         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3437                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3438                 // Needed by inverse dvi search. If it is a file
3439                 // in tmpdir, call the apropriated function.
3440                 // If tmpdir is a symlink, we may have the real
3441                 // path passed back, so we correct for that.
3442                 if (!prefixIs(file_name, abstmp))
3443                         file_name = subst(file_name, realtmp, abstmp);
3444                 buf = theBufferList().getBufferFromTmp(file_name);
3445         } else {
3446                 // Must replace extension of the file to be .lyx
3447                 // and get full path
3448                 FileName const s = fileSearch(string(),
3449                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3450                 // Either change buffer or load the file
3451                 if (theBufferList().exists(s))
3452                         buf = theBufferList().getBuffer(s);
3453                 else if (s.exists()) {
3454                         buf = loadDocument(s);
3455                         if (!buf)
3456                                 return false;
3457                 } else {
3458                         message(bformat(
3459                                         _("File does not exist: %1$s"),
3460                                         makeDisplayPath(file_name)));
3461                         return false;
3462                 }
3463         }
3464         if (!buf) {
3465                 message(bformat(
3466                         _("No buffer for file: %1$s."),
3467                         makeDisplayPath(file_name))
3468                 );
3469                 return false;
3470         }
3471         setBuffer(buf);
3472         bool success = documentBufferView()->setCursorFromRow(row);
3473         if (!success) {
3474                 LYXERR(Debug::LATEX,
3475                        "setCursorFromRow: invalid position for row " << row);
3476                 frontend::Alert::error(_("Inverse Search Failed"),
3477                                        _("Invalid position requested by inverse search.\n"
3478                                          "You may need to update the viewed document."));
3479         }
3480         return success;
3481 }
3482
3483
3484 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3485 {
3486         QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3487         menu->exec(QCursor::pos());
3488 }
3489
3490
3491 template<class T>
3492 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3493 {
3494         Buffer::ExportStatus const status = func(format);
3495
3496         // the cloning operation will have produced a clone of the entire set of
3497         // documents, starting from the master. so we must delete those.
3498         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3499         delete mbuf;
3500         busyBuffers.remove(orig);
3501         return status;
3502 }
3503
3504
3505 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3506 {
3507         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3508         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3509 }
3510
3511
3512 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3513 {
3514         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3515         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3516 }
3517
3518
3519 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3520 {
3521         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3522         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3523 }
3524
3525
3526 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3527                            string const & argument,
3528                            Buffer const * used_buffer,
3529                            docstring const & msg,
3530                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3531                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3532                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3533 {
3534         if (!used_buffer)
3535                 return false;
3536
3537         string format = argument;
3538         if (format.empty())
3539                 format = used_buffer->params().getDefaultOutputFormat();
3540         processing_format = format;
3541         if (!msg.empty()) {
3542                 progress_->clearMessages();
3543                 gv_->message(msg);
3544         }
3545 #if EXPORT_in_THREAD
3546         GuiViewPrivate::busyBuffers.insert(used_buffer);
3547         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3548         if (!cloned_buffer) {
3549                 Alert::error(_("Export Error"),
3550                              _("Error cloning the Buffer."));
3551                 return false;
3552         }
3553         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3554                                 asyncFunc,
3555                                 used_buffer,
3556                                 cloned_buffer,
3557                                 format);
3558         setPreviewFuture(f);
3559         last_export_format = used_buffer->params().bufferFormat();
3560         (void) syncFunc;
3561         (void) previewFunc;
3562         // We are asynchronous, so we don't know here anything about the success
3563         return true;
3564 #else
3565         Buffer::ExportStatus status;
3566         if (syncFunc) {
3567                 status = (used_buffer->*syncFunc)(format, true);
3568         } else if (previewFunc) {
3569                 status = (used_buffer->*previewFunc)(format);
3570         } else
3571                 return false;
3572         handleExportStatus(gv_, status, format);
3573         (void) asyncFunc;
3574         return (status == Buffer::ExportSuccess
3575                         || status == Buffer::PreviewSuccess);
3576 #endif
3577 }
3578
3579 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3580 {
3581         BufferView * bv = currentBufferView();
3582         LASSERT(bv, return);
3583
3584         // Let the current BufferView dispatch its own actions.
3585         bv->dispatch(cmd, dr);
3586         if (dr.dispatched())
3587                 return;
3588
3589         // Try with the document BufferView dispatch if any.
3590         BufferView * doc_bv = documentBufferView();
3591         if (doc_bv && doc_bv != bv) {
3592                 doc_bv->dispatch(cmd, dr);
3593                 if (dr.dispatched())
3594                         return;
3595         }
3596
3597         // Then let the current Cursor dispatch its own actions.
3598         bv->cursor().dispatch(cmd);
3599
3600         // update completion. We do it here and not in
3601         // processKeySym to avoid another redraw just for a
3602         // changed inline completion
3603         if (cmd.origin() == FuncRequest::KEYBOARD) {
3604                 if (cmd.action() == LFUN_SELF_INSERT
3605                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3606                         updateCompletion(bv->cursor(), true, true);
3607                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3608                         updateCompletion(bv->cursor(), false, true);
3609                 else
3610                         updateCompletion(bv->cursor(), false, false);
3611         }
3612
3613         dr = bv->cursor().result();
3614 }
3615
3616
3617 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3618 {
3619         BufferView * bv = currentBufferView();
3620         // By default we won't need any update.
3621         dr.screenUpdate(Update::None);
3622         // assume cmd will be dispatched
3623         dr.dispatched(true);
3624
3625         Buffer * doc_buffer = documentBufferView()
3626                 ? &(documentBufferView()->buffer()) : 0;
3627
3628         if (cmd.origin() == FuncRequest::TOC) {
3629                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3630                 // FIXME: do we need to pass a DispatchResult object here?
3631                 toc->doDispatch(bv->cursor(), cmd);
3632                 return;
3633         }
3634
3635         string const argument = to_utf8(cmd.argument());
3636
3637         switch(cmd.action()) {
3638                 case LFUN_BUFFER_CHILD_OPEN:
3639                         openChildDocument(to_utf8(cmd.argument()));
3640                         break;
3641
3642                 case LFUN_BUFFER_IMPORT:
3643                         importDocument(to_utf8(cmd.argument()));
3644                         break;
3645
3646                 case LFUN_BUFFER_EXPORT: {
3647                         if (!doc_buffer)
3648                                 break;
3649                         // GCC only sees strfwd.h when building merged
3650                         if (::lyx::operator==(cmd.argument(), "custom")) {
3651                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3652                                 break;
3653                         }
3654
3655                         string const dest = cmd.getArg(1);
3656                         FileName target_dir;
3657                         if (!dest.empty() && FileName::isAbsolute(dest))
3658                                 target_dir = FileName(support::onlyPath(dest));
3659                         else
3660                                 target_dir = doc_buffer->fileName().onlyPath();
3661
3662                         string const format = (argument.empty() || argument == "default") ?
3663                                 doc_buffer->params().getDefaultOutputFormat() : argument;
3664
3665                         if ((dest.empty() && doc_buffer->isUnnamed())
3666                             || !target_dir.isDirWritable()) {
3667                                 exportBufferAs(*doc_buffer, from_utf8(format));
3668                                 break;
3669                         }
3670                         /* TODO/Review: Is it a problem to also export the children?
3671                                         See the update_unincluded flag */
3672                         d.asyncBufferProcessing(format,
3673                                                 doc_buffer,
3674                                                 _("Exporting ..."),
3675                                                 &GuiViewPrivate::exportAndDestroy,
3676                                                 &Buffer::doExport,
3677                                                 0);
3678                         // TODO Inform user about success
3679                         break;
3680                 }
3681
3682                 case LFUN_BUFFER_EXPORT_AS: {
3683                         LASSERT(doc_buffer, break);
3684                         docstring f = cmd.argument();
3685                         if (f.empty())
3686                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3687                         exportBufferAs(*doc_buffer, f);
3688                         break;
3689                 }
3690
3691                 case LFUN_BUFFER_UPDATE: {
3692                         d.asyncBufferProcessing(argument,
3693                                                 doc_buffer,
3694                                                 _("Exporting ..."),
3695                                                 &GuiViewPrivate::compileAndDestroy,
3696                                                 &Buffer::doExport,
3697                                                 0);
3698                         break;
3699                 }
3700                 case LFUN_BUFFER_VIEW: {
3701                         d.asyncBufferProcessing(argument,
3702                                                 doc_buffer,
3703                                                 _("Previewing ..."),
3704                                                 &GuiViewPrivate::previewAndDestroy,
3705                                                 0,
3706                                                 &Buffer::preview);
3707                         break;
3708                 }
3709                 case LFUN_MASTER_BUFFER_UPDATE: {
3710                         d.asyncBufferProcessing(argument,
3711                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3712                                                 docstring(),
3713                                                 &GuiViewPrivate::compileAndDestroy,
3714                                                 &Buffer::doExport,
3715                                                 0);
3716                         break;
3717                 }
3718                 case LFUN_MASTER_BUFFER_VIEW: {
3719                         d.asyncBufferProcessing(argument,
3720                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3721                                                 docstring(),
3722                                                 &GuiViewPrivate::previewAndDestroy,
3723                                                 0, &Buffer::preview);
3724                         break;
3725                 }
3726                 case LFUN_BUFFER_SWITCH: {
3727                         string const file_name = to_utf8(cmd.argument());
3728                         if (!FileName::isAbsolute(file_name)) {
3729                                 dr.setError(true);
3730                                 dr.setMessage(_("Absolute filename expected."));
3731                                 break;
3732                         }
3733
3734                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3735                         if (!buffer) {
3736                                 dr.setError(true);
3737                                 dr.setMessage(_("Document not loaded"));
3738                                 break;
3739                         }
3740
3741                         // Do we open or switch to the buffer in this view ?
3742                         if (workArea(*buffer)
3743                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3744                                 setBuffer(buffer);
3745                                 break;
3746                         }
3747
3748                         // Look for the buffer in other views
3749                         QList<int> const ids = guiApp->viewIds();
3750                         int i = 0;
3751                         for (; i != ids.size(); ++i) {
3752                                 GuiView & gv = guiApp->view(ids[i]);
3753                                 if (gv.workArea(*buffer)) {
3754                                         gv.raise();
3755                                         gv.activateWindow();
3756                                         gv.setFocus();
3757                                         gv.setBuffer(buffer);
3758                                         break;
3759                                 }
3760                         }
3761
3762                         // If necessary, open a new window as a last resort
3763                         if (i == ids.size()) {
3764                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3765                                 lyx::dispatch(cmd);
3766                         }
3767                         break;
3768                 }
3769
3770                 case LFUN_BUFFER_NEXT:
3771                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3772                         break;
3773
3774                 case LFUN_BUFFER_MOVE_NEXT:
3775                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3776                         break;
3777
3778                 case LFUN_BUFFER_PREVIOUS:
3779                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3780                         break;
3781
3782                 case LFUN_BUFFER_MOVE_PREVIOUS:
3783                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3784                         break;
3785
3786                 case LFUN_COMMAND_EXECUTE: {
3787                         command_execute_ = true;
3788                         minibuffer_focus_ = true;
3789                         break;
3790                 }
3791                 case LFUN_DROP_LAYOUTS_CHOICE:
3792                         d.layout_->showPopup();
3793                         break;
3794
3795                 case LFUN_MENU_OPEN:
3796                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3797                                 menu->exec(QCursor::pos());
3798                         break;
3799
3800                 case LFUN_FILE_INSERT:
3801                         insertLyXFile(cmd.argument());
3802                         break;
3803
3804                 case LFUN_FILE_INSERT_PLAINTEXT:
3805                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3806                         string const fname = to_utf8(cmd.argument());
3807                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3808                                 dr.setMessage(_("Absolute filename expected."));
3809                                 break;
3810                         }
3811
3812                         FileName filename(fname);
3813                         if (fname.empty()) {
3814                                 FileDialog dlg(qt_("Select file to insert"));
3815
3816                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3817                                         QStringList(qt_("All Files (*)")));
3818
3819                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3820                                         dr.setMessage(_("Canceled."));
3821                                         break;
3822                                 }
3823
3824                                 filename.set(fromqstr(result.second));
3825                         }
3826
3827                         if (bv) {
3828                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3829                                 bv->dispatch(new_cmd, dr);
3830                         }
3831                         break;
3832                 }
3833
3834                 case LFUN_BUFFER_RELOAD: {
3835                         LASSERT(doc_buffer, break);
3836
3837                         int ret = 0;
3838                         if (!doc_buffer->isClean()) {
3839                                 docstring const file =
3840                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3841                                 if (doc_buffer->notifiesExternalModification()) {
3842                                         docstring text = _("The current version will be lost. "
3843                                             "Are you sure you want to load the version on disk "
3844                                             "of the document %1$s?");
3845                                         ret = Alert::prompt(_("Reload saved document?"),
3846                                                             bformat(text, file), 1, 1,
3847                                                             _("&Reload"), _("&Cancel"));
3848                                 } else {
3849                                         docstring text = _("Any changes will be lost. "
3850                                             "Are you sure you want to revert to the saved version "
3851                                             "of the document %1$s?");
3852                                         ret = Alert::prompt(_("Revert to saved document?"),
3853                                                             bformat(text, file), 1, 1,
3854                                                             _("&Revert"), _("&Cancel"));
3855                                 }
3856                         }
3857
3858                         if (ret == 0) {
3859                                 doc_buffer->markClean();
3860                                 reloadBuffer(*doc_buffer);
3861                                 dr.forceBufferUpdate();
3862                         }
3863                         break;
3864                 }
3865
3866                 case LFUN_BUFFER_WRITE:
3867                         LASSERT(doc_buffer, break);
3868                         saveBuffer(*doc_buffer);
3869                         break;
3870
3871                 case LFUN_BUFFER_WRITE_AS:
3872                         LASSERT(doc_buffer, break);
3873                         renameBuffer(*doc_buffer, cmd.argument());
3874                         break;
3875
3876                 case LFUN_BUFFER_WRITE_ALL: {
3877                         Buffer * first = theBufferList().first();
3878                         if (!first)
3879                                 break;
3880                         message(_("Saving all documents..."));
3881                         // We cannot use a for loop as the buffer list cycles.
3882                         Buffer * b = first;
3883                         do {
3884                                 if (!b->isClean()) {
3885                                         saveBuffer(*b);
3886                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3887                                 }
3888                                 b = theBufferList().next(b);
3889                         } while (b != first);
3890                         dr.setMessage(_("All documents saved."));
3891                         break;
3892                 }
3893
3894                 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3895                         LASSERT(doc_buffer, break);
3896                         doc_buffer->clearExternalModification();
3897                         break;
3898
3899                 case LFUN_BUFFER_CLOSE:
3900                         closeBuffer();
3901                         break;
3902
3903                 case LFUN_BUFFER_CLOSE_ALL:
3904                         closeBufferAll();
3905                         break;
3906
3907                 case LFUN_DEVEL_MODE_TOGGLE:
3908                         devel_mode_ = !devel_mode_;
3909                         if (devel_mode_)
3910                                 dr.setMessage(_("Developer mode is now enabled."));
3911                         else
3912                                 dr.setMessage(_("Developer mode is now disabled."));
3913                         break;
3914
3915                 case LFUN_TOOLBAR_TOGGLE: {
3916                         string const name = cmd.getArg(0);
3917                         if (GuiToolbar * t = toolbar(name))
3918                                 t->toggle();
3919                         break;
3920                 }
3921
3922                 case LFUN_TOOLBAR_MOVABLE: {
3923                         string const name = cmd.getArg(0);
3924                         if (name == "*") {
3925                                 // toggle (all) toolbars movablility
3926                                 toolbarsMovable_ = !toolbarsMovable_;
3927                                 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3928                                         GuiToolbar * tb = toolbar(ti.name);
3929                                         if (tb && tb->isMovable() != toolbarsMovable_)
3930                                                 // toggle toolbar movablity if it does not fit lock
3931                                                 // (all) toolbars positions state silent = true, since
3932                                                 // status bar notifications are slow
3933                                                 tb->movable(true);
3934                                 }
3935                                 if (toolbarsMovable_)
3936                                         dr.setMessage(_("Toolbars unlocked."));
3937                                 else
3938                                         dr.setMessage(_("Toolbars locked."));
3939                         } else if (GuiToolbar * t = toolbar(name)) {
3940                                 // toggle current toolbar movablity
3941                                 t->movable();
3942                                 // update lock (all) toolbars positions
3943                                 updateLockToolbars();
3944                         }
3945                         break;
3946                 }
3947
3948                 case LFUN_ICON_SIZE: {
3949                         QSize size = d.iconSize(cmd.argument());
3950                         setIconSize(size);
3951                         dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3952                                                 size.width(), size.height()));
3953                         break;
3954                 }
3955
3956                 case LFUN_DIALOG_UPDATE: {
3957                         string const name = to_utf8(cmd.argument());
3958                         if (name == "prefs" || name == "document")
3959                                 updateDialog(name, string());
3960                         else if (name == "paragraph")
3961                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3962                         else if (currentBufferView()) {
3963                                 Inset * inset = currentBufferView()->editedInset(name);
3964                                 // Can only update a dialog connected to an existing inset
3965                                 if (inset) {
3966                                         // FIXME: get rid of this indirection; GuiView ask the inset
3967                                         // if he is kind enough to update itself...
3968                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3969                                         //FIXME: pass DispatchResult here?
3970                                         inset->dispatch(currentBufferView()->cursor(), fr);
3971                                 }
3972                         }
3973                         break;
3974                 }
3975
3976                 case LFUN_DIALOG_TOGGLE: {
3977                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3978                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3979                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3980                         break;
3981                 }
3982
3983                 case LFUN_DIALOG_DISCONNECT_INSET:
3984                         disconnectDialog(to_utf8(cmd.argument()));
3985                         break;
3986
3987                 case LFUN_DIALOG_HIDE: {
3988                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3989                         break;
3990                 }
3991
3992                 case LFUN_DIALOG_SHOW: {
3993                         string const name = cmd.getArg(0);
3994                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3995
3996                         if (name == "character") {
3997                                 data = freefont2string();
3998                                 if (!data.empty())
3999                                         showDialog("character", data);
4000                         } else if (name == "latexlog") {
4001                                 // getStatus checks that
4002                                 LATTEST(doc_buffer);
4003                                 Buffer::LogType type;
4004                                 string const logfile = doc_buffer->logName(&type);
4005                                 switch (type) {
4006                                 case Buffer::latexlog:
4007                                         data = "latex ";
4008                                         break;
4009                                 case Buffer::buildlog:
4010                                         data = "literate ";
4011                                         break;
4012                                 }
4013                                 data += Lexer::quoteString(logfile);
4014                                 showDialog("log", data);
4015                         } else if (name == "vclog") {
4016                                 // getStatus checks that
4017                                 LATTEST(doc_buffer);
4018                                 string const data = "vc " +
4019                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4020                                 showDialog("log", data);
4021                         } else if (name == "symbols") {
4022                                 data = bv->cursor().getEncoding()->name();
4023                                 if (!data.empty())
4024                                         showDialog("symbols", data);
4025                         // bug 5274
4026                         } else if (name == "prefs" && isFullScreen()) {
4027                                 lfunUiToggle("fullscreen");
4028                                 showDialog("prefs", data);
4029                         } else
4030                                 showDialog(name, data);
4031                         break;
4032                 }
4033
4034                 case LFUN_MESSAGE:
4035                         dr.setMessage(cmd.argument());
4036                         break;
4037
4038                 case LFUN_UI_TOGGLE: {
4039                         string arg = cmd.getArg(0);
4040                         if (!lfunUiToggle(arg)) {
4041                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4042                                 dr.setMessage(bformat(msg, from_utf8(arg)));
4043                         }
4044                         // Make sure the keyboard focus stays in the work area.
4045                         setFocus();
4046                         break;
4047                 }
4048
4049                 case LFUN_VIEW_SPLIT: {
4050                         LASSERT(doc_buffer, break);
4051                         string const orientation = cmd.getArg(0);
4052                         d.splitter_->setOrientation(orientation == "vertical"
4053                                 ? Qt::Vertical : Qt::Horizontal);
4054                         TabWorkArea * twa = addTabWorkArea();
4055                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4056                         setCurrentWorkArea(wa);
4057                         break;
4058                 }
4059                 case LFUN_TAB_GROUP_CLOSE:
4060                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4061                                 closeTabWorkArea(twa);
4062                                 d.current_work_area_ = 0;
4063                                 twa = d.currentTabWorkArea();
4064                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4065                                 if (twa) {
4066                                         // Make sure the work area is up to date.
4067                                         setCurrentWorkArea(twa->currentWorkArea());
4068                                 } else {
4069                                         setCurrentWorkArea(0);
4070                                 }
4071                         }
4072                         break;
4073
4074                 case LFUN_VIEW_CLOSE:
4075                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4076                                 closeWorkArea(twa->currentWorkArea());
4077                                 d.current_work_area_ = 0;
4078                                 twa = d.currentTabWorkArea();
4079                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4080                                 if (twa) {
4081                                         // Make sure the work area is up to date.
4082                                         setCurrentWorkArea(twa->currentWorkArea());
4083                                 } else {
4084                                         setCurrentWorkArea(0);
4085                                 }
4086                         }
4087                         break;
4088
4089                 case LFUN_COMPLETION_INLINE:
4090                         if (d.current_work_area_)
4091                                 d.current_work_area_->completer().showInline();
4092                         break;
4093
4094                 case LFUN_COMPLETION_POPUP:
4095                         if (d.current_work_area_)
4096                                 d.current_work_area_->completer().showPopup();
4097                         break;
4098
4099
4100                 case LFUN_COMPLETE:
4101                         if (d.current_work_area_)
4102                                 d.current_work_area_->completer().tab();
4103                         break;
4104
4105                 case LFUN_COMPLETION_CANCEL:
4106                         if (d.current_work_area_) {
4107                                 if (d.current_work_area_->completer().popupVisible())
4108                                         d.current_work_area_->completer().hidePopup();
4109                                 else
4110                                         d.current_work_area_->completer().hideInline();
4111                         }
4112                         break;
4113
4114                 case LFUN_COMPLETION_ACCEPT:
4115                         if (d.current_work_area_)
4116                                 d.current_work_area_->completer().activate();
4117                         break;
4118
4119                 case LFUN_BUFFER_ZOOM_IN:
4120                 case LFUN_BUFFER_ZOOM_OUT:
4121                 case LFUN_BUFFER_ZOOM: {
4122                         if (cmd.argument().empty()) {
4123                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4124                                         zoom_ratio_ = 1.0;
4125                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4126                                         zoom_ratio_ += 0.1;
4127                                 else
4128                                         zoom_ratio_ -= 0.1;
4129                         } else {
4130                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4131                                         zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4132                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4133                                         zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4134                                 else
4135                                         zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4136                         }
4137
4138                         // Actual zoom value: default zoom + fractional extra value
4139                         int zoom = lyxrc.defaultZoom * zoom_ratio_;
4140                         if (zoom < static_cast<int>(zoom_min_))
4141                                 zoom = zoom_min_;
4142
4143                         lyxrc.currentZoom = zoom;
4144
4145                         dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4146                                               lyxrc.currentZoom, lyxrc.defaultZoom));
4147
4148                         // The global QPixmapCache is used in GuiPainter to cache text
4149                         // painting so we must reset it.
4150                         QPixmapCache::clear();
4151                         guiApp->fontLoader().update();
4152                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4153                         break;
4154                 }
4155
4156                 case LFUN_VC_REGISTER:
4157                 case LFUN_VC_RENAME:
4158                 case LFUN_VC_COPY:
4159                 case LFUN_VC_CHECK_IN:
4160                 case LFUN_VC_CHECK_OUT:
4161                 case LFUN_VC_REPO_UPDATE:
4162                 case LFUN_VC_LOCKING_TOGGLE:
4163                 case LFUN_VC_REVERT:
4164                 case LFUN_VC_UNDO_LAST:
4165                 case LFUN_VC_COMMAND:
4166                 case LFUN_VC_COMPARE:
4167                         dispatchVC(cmd, dr);
4168                         break;
4169
4170                 case LFUN_SERVER_GOTO_FILE_ROW:
4171                         if(goToFileRow(to_utf8(cmd.argument())))
4172                                 dr.screenUpdate(Update::Force | Update::FitCursor);
4173                         break;
4174
4175                 case LFUN_LYX_ACTIVATE:
4176                         activateWindow();
4177                         break;
4178
4179                 case LFUN_FORWARD_SEARCH: {
4180                         // it seems safe to assume we have a document buffer, since
4181                         // getStatus wants one.
4182                         LATTEST(doc_buffer);
4183                         Buffer const * doc_master = doc_buffer->masterBuffer();
4184                         FileName const path(doc_master->temppath());
4185                         string const texname = doc_master->isChild(doc_buffer)
4186                                 ? DocFileName(changeExtension(
4187                                         doc_buffer->absFileName(),
4188                                                 "tex")).mangledFileName()
4189                                 : doc_buffer->latexName();
4190                         string const fulltexname =
4191                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4192                         string const mastername =
4193                                 removeExtension(doc_master->latexName());
4194                         FileName const dviname(addName(path.absFileName(),
4195                                         addExtension(mastername, "dvi")));
4196                         FileName const pdfname(addName(path.absFileName(),
4197                                         addExtension(mastername, "pdf")));
4198                         bool const have_dvi = dviname.exists();
4199                         bool const have_pdf = pdfname.exists();
4200                         if (!have_dvi && !have_pdf) {
4201                                 dr.setMessage(_("Please, preview the document first."));
4202                                 break;
4203                         }
4204                         string outname = dviname.onlyFileName();
4205                         string command = lyxrc.forward_search_dvi;
4206                         if (!have_dvi || (have_pdf &&
4207                             pdfname.lastModified() > dviname.lastModified())) {
4208                                 outname = pdfname.onlyFileName();
4209                                 command = lyxrc.forward_search_pdf;
4210                         }
4211
4212                         DocIterator cur = bv->cursor();
4213                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4214                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4215                                    << " cur:" << cur);
4216                         if (row == -1 || command.empty()) {
4217                                 dr.setMessage(_("Couldn't proceed."));
4218                                 break;
4219                         }
4220                         string texrow = convert<string>(row);
4221
4222                         command = subst(command, "$$n", texrow);
4223                         command = subst(command, "$$f", fulltexname);
4224                         command = subst(command, "$$t", texname);
4225                         command = subst(command, "$$o", outname);
4226
4227                         PathChanger p(path);
4228                         Systemcall one;
4229                         one.startscript(Systemcall::DontWait, command);
4230                         break;
4231                 }
4232
4233                 case LFUN_SPELLING_CONTINUOUSLY:
4234                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4235                         dr.screenUpdate(Update::Force);
4236                         break;
4237
4238                 default:
4239                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4240                         // let's try that:
4241                         dispatchToBufferView(cmd, dr);
4242                         break;
4243         }
4244
4245         // Part of automatic menu appearance feature.
4246         if (isFullScreen()) {
4247                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4248                         menuBar()->hide();
4249         }
4250
4251         // Need to update bv because many LFUNs here might have destroyed it
4252         bv = currentBufferView();
4253
4254         // Clear non-empty selections
4255         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4256         if (bv) {
4257                 Cursor & cur = bv->cursor();
4258                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4259                         cur.clearSelection();
4260                 }
4261         }
4262 }
4263
4264
4265 bool GuiView::lfunUiToggle(string const & ui_component)
4266 {
4267         if (ui_component == "scrollbar") {
4268                 // hide() is of no help
4269                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4270                         Qt::ScrollBarAlwaysOff)
4271
4272                         d.current_work_area_->setVerticalScrollBarPolicy(
4273                                 Qt::ScrollBarAsNeeded);
4274                 else
4275                         d.current_work_area_->setVerticalScrollBarPolicy(
4276                                 Qt::ScrollBarAlwaysOff);
4277         } else if (ui_component == "statusbar") {
4278                 statusBar()->setVisible(!statusBar()->isVisible());
4279         } else if (ui_component == "menubar") {
4280                 menuBar()->setVisible(!menuBar()->isVisible());
4281         } else
4282         if (ui_component == "frame") {
4283                 int l, t, r, b;
4284                 getContentsMargins(&l, &t, &r, &b);
4285                 //are the frames in default state?
4286                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4287                 if (l == 0) {
4288                         setContentsMargins(-2, -2, -2, -2);
4289                 } else {
4290                         setContentsMargins(0, 0, 0, 0);
4291                 }
4292         } else
4293         if (ui_component == "fullscreen") {
4294                 toggleFullScreen();
4295         } else
4296                 return false;
4297         return true;
4298 }
4299
4300
4301 void GuiView::toggleFullScreen()
4302 {
4303         if (isFullScreen()) {
4304                 for (int i = 0; i != d.splitter_->count(); ++i)
4305                         d.tabWorkArea(i)->setFullScreen(false);
4306                 setContentsMargins(0, 0, 0, 0);
4307                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4308                 restoreLayout();
4309                 menuBar()->show();
4310                 statusBar()->show();
4311         } else {
4312                 // bug 5274
4313                 hideDialogs("prefs", 0);
4314                 for (int i = 0; i != d.splitter_->count(); ++i)
4315                         d.tabWorkArea(i)->setFullScreen(true);
4316                 setContentsMargins(-2, -2, -2, -2);
4317                 saveLayout();
4318                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4319                 if (lyxrc.full_screen_statusbar)
4320                         statusBar()->hide();
4321                 if (lyxrc.full_screen_menubar)
4322                         menuBar()->hide();
4323                 if (lyxrc.full_screen_toolbars) {
4324                         ToolbarMap::iterator end = d.toolbars_.end();
4325                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4326                                 it->second->hide();
4327                 }
4328         }
4329
4330         // give dialogs like the TOC a chance to adapt
4331         updateDialogs();
4332 }
4333
4334
4335 Buffer const * GuiView::updateInset(Inset const * inset)
4336 {
4337         if (!inset)
4338                 return 0;
4339
4340         Buffer const * inset_buffer = &(inset->buffer());
4341
4342         for (int i = 0; i != d.splitter_->count(); ++i) {
4343                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4344                 if (!wa)
4345                         continue;
4346                 Buffer const * buffer = &(wa->bufferView().buffer());
4347                 if (inset_buffer == buffer)
4348                         wa->scheduleRedraw();
4349         }
4350         return inset_buffer;
4351 }
4352
4353
4354 void GuiView::restartCursor()
4355 {
4356         /* When we move around, or type, it's nice to be able to see
4357          * the cursor immediately after the keypress.
4358          */
4359         if (d.current_work_area_)
4360                 d.current_work_area_->startBlinkingCursor();
4361
4362         // Take this occasion to update the other GUI elements.
4363         updateDialogs();
4364         updateStatusBar();
4365 }
4366
4367
4368 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4369 {
4370         if (d.current_work_area_)
4371                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4372 }
4373
4374 namespace {
4375
4376 // This list should be kept in sync with the list of insets in
4377 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4378 // dialog should have the same name as the inset.
4379 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4380 // docs in LyXAction.cpp.
4381
4382 char const * const dialognames[] = {
4383
4384 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4385 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4386 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4387 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4388 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4389 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4390 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4391 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4392
4393 char const * const * const end_dialognames =
4394         dialognames + (sizeof(dialognames) / sizeof(char *));
4395
4396 class cmpCStr {
4397 public:
4398         cmpCStr(char const * name) : name_(name) {}
4399         bool operator()(char const * other) {
4400                 return strcmp(other, name_) == 0;
4401         }
4402 private:
4403         char const * name_;
4404 };
4405
4406
4407 bool isValidName(string const & name)
4408 {
4409         return find_if(dialognames, end_dialognames,
4410                                 cmpCStr(name.c_str())) != end_dialognames;
4411 }
4412
4413 } // namespace
4414
4415
4416 void GuiView::resetDialogs()
4417 {
4418         // Make sure that no LFUN uses any GuiView.
4419         guiApp->setCurrentView(0);
4420         saveLayout();
4421         saveUISettings();
4422         menuBar()->clear();
4423         constructToolbars();
4424         guiApp->menus().fillMenuBar(menuBar(), this, false);
4425         d.layout_->updateContents(true);
4426         // Now update controls with current buffer.
4427         guiApp->setCurrentView(this);
4428         restoreLayout();
4429         restartCursor();
4430 }
4431
4432
4433 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4434 {
4435         if (!isValidName(name))
4436                 return 0;
4437
4438         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4439
4440         if (it != d.dialogs_.end()) {
4441                 if (hide_it)
4442                         it->second->hideView();
4443                 return it->second.get();
4444         }
4445
4446         Dialog * dialog = build(name);
4447         d.dialogs_[name].reset(dialog);
4448         if (lyxrc.allow_geometry_session)
4449                 dialog->restoreSession();
4450         if (hide_it)
4451                 dialog->hideView();
4452         return dialog;
4453 }
4454
4455
4456 void GuiView::showDialog(string const & name, string const & data,
4457         Inset * inset)
4458 {
4459         triggerShowDialog(toqstr(name), toqstr(data), inset);
4460 }
4461
4462
4463 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4464         Inset * inset)
4465 {
4466         if (d.in_show_)
4467                 return;
4468
4469         const string name = fromqstr(qname);
4470         const string data = fromqstr(qdata);
4471
4472         d.in_show_ = true;
4473         try {
4474                 Dialog * dialog = findOrBuild(name, false);
4475                 if (dialog) {
4476                         bool const visible = dialog->isVisibleView();
4477                         dialog->showData(data);
4478                         if (inset && currentBufferView())
4479                                 currentBufferView()->editInset(name, inset);
4480                         // We only set the focus to the new dialog if it was not yet
4481                         // visible in order not to change the existing previous behaviour
4482                         if (visible) {
4483                                 // activateWindow is needed for floating dockviews
4484                                 dialog->asQWidget()->raise();
4485                                 dialog->asQWidget()->activateWindow();
4486                                 dialog->asQWidget()->setFocus();
4487                         }
4488                 }
4489         }
4490         catch (ExceptionMessage const & ex) {
4491                 d.in_show_ = false;
4492                 throw ex;
4493         }
4494         d.in_show_ = false;
4495 }
4496
4497
4498 bool GuiView::isDialogVisible(string const & name) const
4499 {
4500         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4501         if (it == d.dialogs_.end())
4502                 return false;
4503         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4504 }
4505
4506
4507 void GuiView::hideDialog(string const & name, Inset * inset)
4508 {
4509         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4510         if (it == d.dialogs_.end())
4511                 return;
4512
4513         if (inset) {
4514                 if (!currentBufferView())
4515                         return;
4516                 if (inset != currentBufferView()->editedInset(name))
4517                         return;
4518         }
4519
4520         Dialog * const dialog = it->second.get();
4521         if (dialog->isVisibleView())
4522                 dialog->hideView();
4523         if (currentBufferView())
4524                 currentBufferView()->editInset(name, 0);
4525 }
4526
4527
4528 void GuiView::disconnectDialog(string const & name)
4529 {
4530         if (!isValidName(name))
4531                 return;
4532         if (currentBufferView())
4533                 currentBufferView()->editInset(name, 0);
4534 }
4535
4536
4537 void GuiView::hideAll() const
4538 {
4539         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4540         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4541
4542         for(; it != end; ++it)
4543                 it->second->hideView();
4544 }
4545
4546
4547 void GuiView::updateDialogs()
4548 {
4549         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4550         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4551
4552         for(; it != end; ++it) {
4553                 Dialog * dialog = it->second.get();
4554                 if (dialog) {
4555                         if (dialog->needBufferOpen() && !documentBufferView())
4556                                 hideDialog(fromqstr(dialog->name()), 0);
4557                         else if (dialog->isVisibleView())
4558                                 dialog->checkStatus();
4559                 }
4560         }
4561         updateToolbars();
4562         updateLayoutList();
4563 }
4564
4565 Dialog * createDialog(GuiView & lv, string const & name);
4566
4567 // will be replaced by a proper factory...
4568 Dialog * createGuiAbout(GuiView & lv);
4569 Dialog * createGuiBibtex(GuiView & lv);
4570 Dialog * createGuiChanges(GuiView & lv);
4571 Dialog * createGuiCharacter(GuiView & lv);
4572 Dialog * createGuiCitation(GuiView & lv);
4573 Dialog * createGuiCompare(GuiView & lv);
4574 Dialog * createGuiCompareHistory(GuiView & lv);
4575 Dialog * createGuiDelimiter(GuiView & lv);
4576 Dialog * createGuiDocument(GuiView & lv);
4577 Dialog * createGuiErrorList(GuiView & lv);
4578 Dialog * createGuiExternal(GuiView & lv);
4579 Dialog * createGuiGraphics(GuiView & lv);
4580 Dialog * createGuiInclude(GuiView & lv);
4581 Dialog * createGuiIndex(GuiView & lv);
4582 Dialog * createGuiListings(GuiView & lv);
4583 Dialog * createGuiLog(GuiView & lv);
4584 Dialog * createGuiMathMatrix(GuiView & lv);
4585 Dialog * createGuiNote(GuiView & lv);
4586 Dialog * createGuiParagraph(GuiView & lv);
4587 Dialog * createGuiPhantom(GuiView & lv);
4588 Dialog * createGuiPreferences(GuiView & lv);
4589 Dialog * createGuiPrint(GuiView & lv);
4590 Dialog * createGuiPrintindex(GuiView & lv);
4591 Dialog * createGuiRef(GuiView & lv);
4592 Dialog * createGuiSearch(GuiView & lv);
4593 Dialog * createGuiSearchAdv(GuiView & lv);
4594 Dialog * createGuiSendTo(GuiView & lv);
4595 Dialog * createGuiShowFile(GuiView & lv);
4596 Dialog * createGuiSpellchecker(GuiView & lv);
4597 Dialog * createGuiSymbols(GuiView & lv);
4598 Dialog * createGuiTabularCreate(GuiView & lv);
4599 Dialog * createGuiTexInfo(GuiView & lv);
4600 Dialog * createGuiToc(GuiView & lv);
4601 Dialog * createGuiThesaurus(GuiView & lv);
4602 Dialog * createGuiViewSource(GuiView & lv);
4603 Dialog * createGuiWrap(GuiView & lv);
4604 Dialog * createGuiProgressView(GuiView & lv);
4605
4606
4607
4608 Dialog * GuiView::build(string const & name)
4609 {
4610         LASSERT(isValidName(name), return 0);
4611
4612         Dialog * dialog = createDialog(*this, name);
4613         if (dialog)
4614                 return dialog;
4615
4616         if (name == "aboutlyx")
4617                 return createGuiAbout(*this);
4618         if (name == "bibtex")
4619                 return createGuiBibtex(*this);
4620         if (name == "changes")
4621                 return createGuiChanges(*this);
4622         if (name == "character")
4623                 return createGuiCharacter(*this);
4624         if (name == "citation")
4625                 return createGuiCitation(*this);
4626         if (name == "compare")
4627                 return createGuiCompare(*this);
4628         if (name == "comparehistory")
4629                 return createGuiCompareHistory(*this);
4630         if (name == "document")
4631                 return createGuiDocument(*this);
4632         if (name == "errorlist")
4633                 return createGuiErrorList(*this);
4634         if (name == "external")
4635                 return createGuiExternal(*this);
4636         if (name == "file")
4637                 return createGuiShowFile(*this);
4638         if (name == "findreplace")
4639                 return createGuiSearch(*this);
4640         if (name == "findreplaceadv")
4641                 return createGuiSearchAdv(*this);
4642         if (name == "graphics")
4643                 return createGuiGraphics(*this);
4644         if (name == "include")
4645                 return createGuiInclude(*this);
4646         if (name == "index")
4647                 return createGuiIndex(*this);
4648         if (name == "index_print")
4649                 return createGuiPrintindex(*this);
4650         if (name == "listings")
4651                 return createGuiListings(*this);
4652         if (name == "log")
4653                 return createGuiLog(*this);
4654         if (name == "mathdelimiter")
4655                 return createGuiDelimiter(*this);
4656         if (name == "mathmatrix")
4657                 return createGuiMathMatrix(*this);
4658         if (name == "note")
4659                 return createGuiNote(*this);
4660         if (name == "paragraph")
4661                 return createGuiParagraph(*this);
4662         if (name == "phantom")
4663                 return createGuiPhantom(*this);
4664         if (name == "prefs")
4665                 return createGuiPreferences(*this);
4666         if (name == "ref")
4667                 return createGuiRef(*this);
4668         if (name == "sendto")
4669                 return createGuiSendTo(*this);
4670         if (name == "spellchecker")
4671                 return createGuiSpellchecker(*this);
4672         if (name == "symbols")
4673                 return createGuiSymbols(*this);
4674         if (name == "tabularcreate")
4675                 return createGuiTabularCreate(*this);
4676         if (name == "texinfo")
4677                 return createGuiTexInfo(*this);
4678         if (name == "thesaurus")
4679                 return createGuiThesaurus(*this);
4680         if (name == "toc")
4681                 return createGuiToc(*this);
4682         if (name == "view-source")
4683                 return createGuiViewSource(*this);
4684         if (name == "wrap")
4685                 return createGuiWrap(*this);
4686         if (name == "progress")
4687                 return createGuiProgressView(*this);
4688
4689         return 0;
4690 }
4691
4692
4693 SEMenu::SEMenu(QWidget * parent)
4694 {
4695         QAction * action = addAction(qt_("Disable Shell Escape"));
4696         connect(action, SIGNAL(triggered()),
4697                 parent, SLOT(disableShellEscape()));
4698 }
4699
4700
4701 } // namespace frontend
4702 } // namespace lyx
4703
4704 #include "moc_GuiView.cpp"