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