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