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