]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
6f5441ceee8306af236d948076e6f1c96489462f
[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                 // hide if we have no checktex command
1936                 if (lyxrc.chktex_command.empty()) {
1937                         flag.setUnknown(true);
1938                         enable = false;
1939                         break;
1940                 }
1941                 if (!doc_buffer || !doc_buffer->params().isLatex()
1942                     || d.processing_thread_watcher_.isRunning()) {
1943                         // grey out, don't hide
1944                         enable = false;
1945                         break;
1946                 }
1947                 enable = true;
1948                 break;
1949         }
1950
1951         case LFUN_VIEW_SPLIT:
1952                 if (cmd.getArg(0) == "vertical")
1953                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1954                                          d.splitter_->orientation() == Qt::Vertical);
1955                 else
1956                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1957                                          d.splitter_->orientation() == Qt::Horizontal);
1958                 break;
1959
1960         case LFUN_TAB_GROUP_CLOSE:
1961                 enable = d.tabWorkAreaCount() > 1;
1962                 break;
1963
1964         case LFUN_DEVEL_MODE_TOGGLE:
1965                 flag.setOnOff(devel_mode_);
1966                 break;
1967
1968         case LFUN_TOOLBAR_TOGGLE: {
1969                 string const name = cmd.getArg(0);
1970                 if (GuiToolbar * t = toolbar(name))
1971                         flag.setOnOff(t->isVisible());
1972                 else {
1973                         enable = false;
1974                         docstring const msg =
1975                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1976                         flag.message(msg);
1977                 }
1978                 break;
1979         }
1980
1981         case LFUN_TOOLBAR_MOVABLE: {
1982                 string const name = cmd.getArg(0);
1983                 // use negation since locked == !movable
1984                 if (name == "*")
1985                         // toolbar name * locks all toolbars
1986                         flag.setOnOff(!toolbarsMovable_);
1987                 else if (GuiToolbar * t = toolbar(name))
1988                         flag.setOnOff(!(t->isMovable()));
1989                 else {
1990                         enable = false;
1991                         docstring const msg =
1992                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1993                         flag.message(msg);
1994                 }
1995                 break;
1996         }
1997
1998         case LFUN_ICON_SIZE:
1999                 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
2000                 break;
2001
2002         case LFUN_DROP_LAYOUTS_CHOICE:
2003                 enable = buf != 0;
2004                 break;
2005
2006         case LFUN_UI_TOGGLE:
2007                 flag.setOnOff(isFullScreen());
2008                 break;
2009
2010         case LFUN_DIALOG_DISCONNECT_INSET:
2011                 break;
2012
2013         case LFUN_DIALOG_HIDE:
2014                 // FIXME: should we check if the dialog is shown?
2015                 break;
2016
2017         case LFUN_DIALOG_TOGGLE:
2018                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
2019                 // to set "enable"
2020                 // fall through
2021         case LFUN_DIALOG_SHOW: {
2022                 string const name = cmd.getArg(0);
2023                 if (!doc_buffer)
2024                         enable = name == "aboutlyx"
2025                                 || name == "file" //FIXME: should be removed.
2026                                 || name == "prefs"
2027                                 || name == "texinfo"
2028                                 || name == "progress"
2029                                 || name == "compare";
2030                 else if (name == "character" || name == "symbols"
2031                         || name == "mathdelimiter" || name == "mathmatrix") {
2032                         if (!buf || buf->isReadonly())
2033                                 enable = false;
2034                         else {
2035                                 Cursor const & cur = currentBufferView()->cursor();
2036                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2037                         }
2038                 }
2039                 else if (name == "latexlog")
2040                         enable = FileName(doc_buffer->logName()).isReadableFile();
2041                 else if (name == "spellchecker")
2042                         enable = theSpellChecker()
2043                                 && !doc_buffer->isReadonly()
2044                                 && !doc_buffer->text().empty();
2045                 else if (name == "vclog")
2046                         enable = doc_buffer->lyxvc().inUse();
2047                 break;
2048         }
2049
2050         case LFUN_DIALOG_UPDATE: {
2051                 string const name = cmd.getArg(0);
2052                 if (!buf)
2053                         enable = name == "prefs";
2054                 break;
2055         }
2056
2057         case LFUN_COMMAND_EXECUTE:
2058         case LFUN_MESSAGE:
2059         case LFUN_MENU_OPEN:
2060                 // Nothing to check.
2061                 break;
2062
2063         case LFUN_COMPLETION_INLINE:
2064                 if (!d.current_work_area_
2065                         || !d.current_work_area_->completer().inlinePossible(
2066                         currentBufferView()->cursor()))
2067                         enable = false;
2068                 break;
2069
2070         case LFUN_COMPLETION_POPUP:
2071                 if (!d.current_work_area_
2072                         || !d.current_work_area_->completer().popupPossible(
2073                         currentBufferView()->cursor()))
2074                         enable = false;
2075                 break;
2076
2077         case LFUN_COMPLETE:
2078                 if (!d.current_work_area_
2079                         || !d.current_work_area_->completer().inlinePossible(
2080                         currentBufferView()->cursor()))
2081                         enable = false;
2082                 break;
2083
2084         case LFUN_COMPLETION_ACCEPT:
2085                 if (!d.current_work_area_
2086                         || (!d.current_work_area_->completer().popupVisible()
2087                         && !d.current_work_area_->completer().inlineVisible()
2088                         && !d.current_work_area_->completer().completionAvailable()))
2089                         enable = false;
2090                 break;
2091
2092         case LFUN_COMPLETION_CANCEL:
2093                 if (!d.current_work_area_
2094                         || (!d.current_work_area_->completer().popupVisible()
2095                         && !d.current_work_area_->completer().inlineVisible()))
2096                         enable = false;
2097                 break;
2098
2099         case LFUN_BUFFER_ZOOM_OUT:
2100         case LFUN_BUFFER_ZOOM_IN: {
2101                 // only diff between these two is that the default for ZOOM_OUT
2102                 // is a neg. number
2103                 bool const neg_zoom =
2104                         convert<int>(cmd.argument()) < 0 ||
2105                         (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2106                 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2107                         docstring const msg =
2108                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2109                         flag.message(msg);
2110                         enable = false;
2111                 } else
2112                         enable = doc_buffer;
2113                 break;
2114         }
2115
2116         case LFUN_BUFFER_ZOOM: {
2117                 bool const less_than_min_zoom =
2118                         !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2119                 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2120                         docstring const msg =
2121                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2122                         flag.message(msg);
2123                         enable = false;
2124                 }
2125                 else
2126                         enable = doc_buffer;
2127                 break;
2128         }
2129
2130         case LFUN_BUFFER_MOVE_NEXT:
2131         case LFUN_BUFFER_MOVE_PREVIOUS:
2132                 // we do not cycle when moving
2133         case LFUN_BUFFER_NEXT:
2134         case LFUN_BUFFER_PREVIOUS:
2135                 // because we cycle, it doesn't matter whether on first or last
2136                 enable = (d.currentTabWorkArea()->count() > 1);
2137                 break;
2138         case LFUN_BUFFER_SWITCH:
2139                 // toggle on the current buffer, but do not toggle off
2140                 // the other ones (is that a good idea?)
2141                 if (doc_buffer
2142                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2143                         flag.setOnOff(true);
2144                 break;
2145
2146         case LFUN_VC_REGISTER:
2147                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2148                 break;
2149         case LFUN_VC_RENAME:
2150                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2151                 break;
2152         case LFUN_VC_COPY:
2153                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2154                 break;
2155         case LFUN_VC_CHECK_IN:
2156                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2157                 break;
2158         case LFUN_VC_CHECK_OUT:
2159                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2160                 break;
2161         case LFUN_VC_LOCKING_TOGGLE:
2162                 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2163                         && doc_buffer->lyxvc().lockingToggleEnabled();
2164                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2165                 break;
2166         case LFUN_VC_REVERT:
2167                 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2168                         && !doc_buffer->hasReadonlyFlag();
2169                 break;
2170         case LFUN_VC_UNDO_LAST:
2171                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2172                 break;
2173         case LFUN_VC_REPO_UPDATE:
2174                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2175                 break;
2176         case LFUN_VC_COMMAND: {
2177                 if (cmd.argument().empty())
2178                         enable = false;
2179                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2180                         enable = false;
2181                 break;
2182         }
2183         case LFUN_VC_COMPARE:
2184                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2185                 break;
2186
2187         case LFUN_SERVER_GOTO_FILE_ROW:
2188         case LFUN_LYX_ACTIVATE:
2189                 break;
2190         case LFUN_FORWARD_SEARCH:
2191                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2192                 break;
2193
2194         case LFUN_FILE_INSERT_PLAINTEXT:
2195         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2196                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2197                 break;
2198
2199         case LFUN_SPELLING_CONTINUOUSLY:
2200                 flag.setOnOff(lyxrc.spellcheck_continuously);
2201                 break;
2202
2203         default:
2204                 return false;
2205         }
2206
2207         if (!enable)
2208                 flag.setEnabled(false);
2209
2210         return true;
2211 }
2212
2213
2214 static FileName selectTemplateFile()
2215 {
2216         FileDialog dlg(qt_("Select template file"));
2217         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2218         dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2219
2220         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2221                                  QStringList(qt_("LyX Documents (*.lyx)")));
2222
2223         if (result.first == FileDialog::Later)
2224                 return FileName();
2225         if (result.second.isEmpty())
2226                 return FileName();
2227         return FileName(fromqstr(result.second));
2228 }
2229
2230
2231 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2232 {
2233         setBusy(true);
2234
2235         Buffer * newBuffer = 0;
2236         try {
2237                 newBuffer = checkAndLoadLyXFile(filename);
2238         } catch (ExceptionMessage const & e) {
2239                 setBusy(false);
2240                 throw(e);
2241         }
2242         setBusy(false);
2243
2244         if (!newBuffer) {
2245                 message(_("Document not loaded."));
2246                 return 0;
2247         }
2248
2249         setBuffer(newBuffer);
2250         newBuffer->errors("Parse");
2251
2252         if (tolastfiles)
2253                 theSession().lastFiles().add(filename);
2254
2255         return newBuffer;
2256 }
2257
2258
2259 void GuiView::openDocument(string const & fname)
2260 {
2261         string initpath = lyxrc.document_path;
2262
2263         if (documentBufferView()) {
2264                 string const trypath = documentBufferView()->buffer().filePath();
2265                 // If directory is writeable, use this as default.
2266                 if (FileName(trypath).isDirWritable())
2267                         initpath = trypath;
2268         }
2269
2270         string filename;
2271
2272         if (fname.empty()) {
2273                 FileDialog dlg(qt_("Select document to open"));
2274                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2275                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2276
2277                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2278                 FileDialog::Result result =
2279                         dlg.open(toqstr(initpath), filter);
2280
2281                 if (result.first == FileDialog::Later)
2282                         return;
2283
2284                 filename = fromqstr(result.second);
2285
2286                 // check selected filename
2287                 if (filename.empty()) {
2288                         message(_("Canceled."));
2289                         return;
2290                 }
2291         } else
2292                 filename = fname;
2293
2294         // get absolute path of file and add ".lyx" to the filename if
2295         // necessary.
2296         FileName const fullname =
2297                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2298         if (!fullname.empty())
2299                 filename = fullname.absFileName();
2300
2301         if (!fullname.onlyPath().isDirectory()) {
2302                 Alert::warning(_("Invalid filename"),
2303                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2304                                 from_utf8(fullname.absFileName())));
2305                 return;
2306         }
2307
2308         // if the file doesn't exist and isn't already open (bug 6645),
2309         // let the user create one
2310         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2311             !LyXVC::file_not_found_hook(fullname)) {
2312                 // the user specifically chose this name. Believe him.
2313                 Buffer * const b = newFile(filename, string(), true);
2314                 if (b)
2315                         setBuffer(b);
2316                 return;
2317         }
2318
2319         docstring const disp_fn = makeDisplayPath(filename);
2320         message(bformat(_("Opening document %1$s..."), disp_fn));
2321
2322         docstring str2;
2323         Buffer * buf = loadDocument(fullname);
2324         if (buf) {
2325                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2326                 if (buf->lyxvc().inUse())
2327                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2328                                 " " + _("Version control detected.");
2329         } else {
2330                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2331         }
2332         message(str2);
2333 }
2334
2335 // FIXME: clean that
2336 static bool import(GuiView * lv, FileName const & filename,
2337         string const & format, ErrorList & errorList)
2338 {
2339         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2340
2341         string loader_format;
2342         vector<string> loaders = theConverters().loaders();
2343         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2344                 vector<string>::const_iterator it = loaders.begin();
2345                 vector<string>::const_iterator en = loaders.end();
2346                 for (; it != en; ++it) {
2347                         if (!theConverters().isReachable(format, *it))
2348                                 continue;
2349
2350                         string const tofile =
2351                                 support::changeExtension(filename.absFileName(),
2352                                 theFormats().extension(*it));
2353                         if (!theConverters().convert(0, filename, FileName(tofile),
2354                                 filename, format, *it, errorList))
2355                                 return false;
2356                         loader_format = *it;
2357                         break;
2358                 }
2359                 if (loader_format.empty()) {
2360                         frontend::Alert::error(_("Couldn't import file"),
2361                                          bformat(_("No information for importing the format %1$s."),
2362                                          theFormats().prettyName(format)));
2363                         return false;
2364                 }
2365         } else
2366                 loader_format = format;
2367
2368         if (loader_format == "lyx") {
2369                 Buffer * buf = lv->loadDocument(lyxfile);
2370                 if (!buf)
2371                         return false;
2372         } else {
2373                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2374                 if (!b)
2375                         return false;
2376                 lv->setBuffer(b);
2377                 bool as_paragraphs = loader_format == "textparagraph";
2378                 string filename2 = (loader_format == format) ? filename.absFileName()
2379                         : support::changeExtension(filename.absFileName(),
2380                                           theFormats().extension(loader_format));
2381                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2382                         as_paragraphs);
2383                 guiApp->setCurrentView(lv);
2384                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2385         }
2386
2387         return true;
2388 }
2389
2390
2391 void GuiView::importDocument(string const & argument)
2392 {
2393         string format;
2394         string filename = split(argument, format, ' ');
2395
2396         LYXERR(Debug::INFO, format << " file: " << filename);
2397
2398         // need user interaction
2399         if (filename.empty()) {
2400                 string initpath = lyxrc.document_path;
2401                 if (documentBufferView()) {
2402                         string const trypath = documentBufferView()->buffer().filePath();
2403                         // If directory is writeable, use this as default.
2404                         if (FileName(trypath).isDirWritable())
2405                                 initpath = trypath;
2406                 }
2407
2408                 docstring const text = bformat(_("Select %1$s file to import"),
2409                         theFormats().prettyName(format));
2410
2411                 FileDialog dlg(toqstr(text));
2412                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2413                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2414
2415                 docstring filter = theFormats().prettyName(format);
2416                 filter += " (*.{";
2417                 // FIXME UNICODE
2418                 filter += from_utf8(theFormats().extensions(format));
2419                 filter += "})";
2420
2421                 FileDialog::Result result =
2422                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2423
2424                 if (result.first == FileDialog::Later)
2425                         return;
2426
2427                 filename = fromqstr(result.second);
2428
2429                 // check selected filename
2430                 if (filename.empty())
2431                         message(_("Canceled."));
2432         }
2433
2434         if (filename.empty())
2435                 return;
2436
2437         // get absolute path of file
2438         FileName const fullname(support::makeAbsPath(filename));
2439
2440         // Can happen if the user entered a path into the dialog
2441         // (see bug #7437)
2442         if (fullname.onlyFileName().empty()) {
2443                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2444                                           "Aborting import."),
2445                                         from_utf8(fullname.absFileName()));
2446                 frontend::Alert::error(_("File name error"), msg);
2447                 message(_("Canceled."));
2448                 return;
2449         }
2450
2451
2452         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2453
2454         // Check if the document already is open
2455         Buffer * buf = theBufferList().getBuffer(lyxfile);
2456         if (buf) {
2457                 setBuffer(buf);
2458                 if (!closeBuffer()) {
2459                         message(_("Canceled."));
2460                         return;
2461                 }
2462         }
2463
2464         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2465
2466         // if the file exists already, and we didn't do
2467         // -i lyx thefile.lyx, warn
2468         if (lyxfile.exists() && fullname != lyxfile) {
2469
2470                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2471                         "Do you want to overwrite that document?"), displaypath);
2472                 int const ret = Alert::prompt(_("Overwrite document?"),
2473                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2474
2475                 if (ret == 1) {
2476                         message(_("Canceled."));
2477                         return;
2478                 }
2479         }
2480
2481         message(bformat(_("Importing %1$s..."), displaypath));
2482         ErrorList errorList;
2483         if (import(this, fullname, format, errorList))
2484                 message(_("imported."));
2485         else
2486                 message(_("file not imported!"));
2487
2488         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2489 }
2490
2491
2492 void GuiView::newDocument(string const & filename, bool from_template)
2493 {
2494         FileName initpath(lyxrc.document_path);
2495         if (documentBufferView()) {
2496                 FileName const trypath(documentBufferView()->buffer().filePath());
2497                 // If directory is writeable, use this as default.
2498                 if (trypath.isDirWritable())
2499                         initpath = trypath;
2500         }
2501
2502         string templatefile;
2503         if (from_template) {
2504                 templatefile = selectTemplateFile().absFileName();
2505                 if (templatefile.empty())
2506                         return;
2507         }
2508
2509         Buffer * b;
2510         if (filename.empty())
2511                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2512         else
2513                 b = newFile(filename, templatefile, true);
2514
2515         if (b)
2516                 setBuffer(b);
2517
2518         // If no new document could be created, it is unsure
2519         // whether there is a valid BufferView.
2520         if (currentBufferView())
2521                 // Ensure the cursor is correctly positioned on screen.
2522                 currentBufferView()->showCursor();
2523 }
2524
2525
2526 void GuiView::insertLyXFile(docstring const & fname)
2527 {
2528         BufferView * bv = documentBufferView();
2529         if (!bv)
2530                 return;
2531
2532         // FIXME UNICODE
2533         FileName filename(to_utf8(fname));
2534         if (filename.empty()) {
2535                 // Launch a file browser
2536                 // FIXME UNICODE
2537                 string initpath = lyxrc.document_path;
2538                 string const trypath = bv->buffer().filePath();
2539                 // If directory is writeable, use this as default.
2540                 if (FileName(trypath).isDirWritable())
2541                         initpath = trypath;
2542
2543                 // FIXME UNICODE
2544                 FileDialog dlg(qt_("Select LyX document to insert"));
2545                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2546                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2547
2548                 FileDialog::Result result = dlg.open(toqstr(initpath),
2549                                          QStringList(qt_("LyX Documents (*.lyx)")));
2550
2551                 if (result.first == FileDialog::Later)
2552                         return;
2553
2554                 // FIXME UNICODE
2555                 filename.set(fromqstr(result.second));
2556
2557                 // check selected filename
2558                 if (filename.empty()) {
2559                         // emit message signal.
2560                         message(_("Canceled."));
2561                         return;
2562                 }
2563         }
2564
2565         bv->insertLyXFile(filename);
2566         bv->buffer().errors("Parse");
2567 }
2568
2569
2570 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2571 {
2572         FileName fname = b.fileName();
2573         FileName const oldname = fname;
2574
2575         if (!newname.empty()) {
2576                 // FIXME UNICODE
2577                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2578         } else {
2579                 // Switch to this Buffer.
2580                 setBuffer(&b);
2581
2582                 // No argument? Ask user through dialog.
2583                 // FIXME UNICODE
2584                 FileDialog dlg(qt_("Choose a filename to save document as"));
2585                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2586                 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2587
2588                 if (!isLyXFileName(fname.absFileName()))
2589                         fname.changeExtension(".lyx");
2590
2591                 FileDialog::Result result =
2592                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2593                                    QStringList(qt_("LyX Documents (*.lyx)")),
2594                                          toqstr(fname.onlyFileName()));
2595
2596                 if (result.first == FileDialog::Later)
2597                         return false;
2598
2599                 fname.set(fromqstr(result.second));
2600
2601                 if (fname.empty())
2602                         return false;
2603
2604                 if (!isLyXFileName(fname.absFileName()))
2605                         fname.changeExtension(".lyx");
2606         }
2607
2608         // fname is now the new Buffer location.
2609
2610         // if there is already a Buffer open with this name, we do not want
2611         // to have another one. (the second test makes sure we're not just
2612         // trying to overwrite ourselves, which is fine.)
2613         if (theBufferList().exists(fname) && fname != oldname
2614                   && theBufferList().getBuffer(fname) != &b) {
2615                 docstring const text =
2616                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2617                             "Please close it before attempting to overwrite it.\n"
2618                             "Do you want to choose a new filename?"),
2619                                 from_utf8(fname.absFileName()));
2620                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2621                         text, 0, 1, _("&Rename"), _("&Cancel"));
2622                 switch (ret) {
2623                 case 0: return renameBuffer(b, docstring(), kind);
2624                 case 1: return false;
2625                 }
2626                 //return false;
2627         }
2628
2629         bool const existsLocal = fname.exists();
2630         bool const existsInVC = LyXVC::fileInVC(fname);
2631         if (existsLocal || existsInVC) {
2632                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2633                 if (kind != LV_WRITE_AS && existsInVC) {
2634                         // renaming to a name that is already in VC
2635                         // would not work
2636                         docstring text = bformat(_("The document %1$s "
2637                                         "is already registered.\n\n"
2638                                         "Do you want to choose a new name?"),
2639                                 file);
2640                         docstring const title = (kind == LV_VC_RENAME) ?
2641                                 _("Rename document?") : _("Copy document?");
2642                         docstring const button = (kind == LV_VC_RENAME) ?
2643                                 _("&Rename") : _("&Copy");
2644                         int const ret = Alert::prompt(title, text, 0, 1,
2645                                 button, _("&Cancel"));
2646                         switch (ret) {
2647                         case 0: return renameBuffer(b, docstring(), kind);
2648                         case 1: return false;
2649                         }
2650                 }
2651
2652                 if (existsLocal) {
2653                         docstring text = bformat(_("The document %1$s "
2654                                         "already exists.\n\n"
2655                                         "Do you want to overwrite that document?"),
2656                                 file);
2657                         int const ret = Alert::prompt(_("Overwrite document?"),
2658                                         text, 0, 2, _("&Overwrite"),
2659                                         _("&Rename"), _("&Cancel"));
2660                         switch (ret) {
2661                         case 0: break;
2662                         case 1: return renameBuffer(b, docstring(), kind);
2663                         case 2: return false;
2664                         }
2665                 }
2666         }
2667
2668         switch (kind) {
2669         case LV_VC_RENAME: {
2670                 string msg = b.lyxvc().rename(fname);
2671                 if (msg.empty())
2672                         return false;
2673                 message(from_utf8(msg));
2674                 break;
2675         }
2676         case LV_VC_COPY: {
2677                 string msg = b.lyxvc().copy(fname);
2678                 if (msg.empty())
2679                         return false;
2680                 message(from_utf8(msg));
2681                 break;
2682         }
2683         case LV_WRITE_AS:
2684                 break;
2685         }
2686         // LyXVC created the file already in case of LV_VC_RENAME or
2687         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2688         // relative paths of included stuff right if we moved e.g. from
2689         // /a/b.lyx to /a/c/b.lyx.
2690
2691         bool const saved = saveBuffer(b, fname);
2692         if (saved)
2693                 b.reload();
2694         return saved;
2695 }
2696
2697
2698 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2699 {
2700         FileName fname = b.fileName();
2701
2702         FileDialog dlg(qt_("Choose a filename to export the document as"));
2703         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2704
2705         QStringList types;
2706         QString const anyformat = qt_("Guess from extension (*.*)");
2707         types << anyformat;
2708
2709         vector<Format const *> export_formats;
2710         for (Format const & f : theFormats())
2711                 if (f.documentFormat())
2712                         export_formats.push_back(&f);
2713         sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2714         map<QString, string> fmap;
2715         QString filter;
2716         string ext;
2717         for (Format const * f : export_formats) {
2718                 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2719                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2720                                                      loc_prettyname,
2721                                                      from_ascii(f->extension())));
2722                 types << loc_filter;
2723                 fmap[loc_filter] = f->name();
2724                 if (from_ascii(f->name()) == iformat) {
2725                         filter = loc_filter;
2726                         ext = f->extension();
2727                 }
2728         }
2729         string ofname = fname.onlyFileName();
2730         if (!ext.empty())
2731                 ofname = support::changeExtension(ofname, ext);
2732         FileDialog::Result result =
2733                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2734                          types,
2735                          toqstr(ofname),
2736                          &filter);
2737         if (result.first != FileDialog::Chosen)
2738                 return false;
2739
2740         string fmt_name;
2741         fname.set(fromqstr(result.second));
2742         if (filter == anyformat)
2743                 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2744         else
2745                 fmt_name = fmap[filter];
2746         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2747                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2748
2749         if (fmt_name.empty() || fname.empty())
2750                 return false;
2751
2752         // fname is now the new Buffer location.
2753         if (FileName(fname).exists()) {
2754                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2755                 docstring text = bformat(_("The document %1$s already "
2756                                            "exists.\n\nDo you want to "
2757                                            "overwrite that document?"),
2758                                          file);
2759                 int const ret = Alert::prompt(_("Overwrite document?"),
2760                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2761                 switch (ret) {
2762                 case 0: break;
2763                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2764                 case 2: return false;
2765                 }
2766         }
2767
2768         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2769         DispatchResult dr;
2770         dispatch(cmd, dr);
2771         return dr.dispatched();
2772 }
2773
2774
2775 bool GuiView::saveBuffer(Buffer & b)
2776 {
2777         return saveBuffer(b, FileName());
2778 }
2779
2780
2781 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2782 {
2783         if (workArea(b) && workArea(b)->inDialogMode())
2784                 return true;
2785
2786         if (fn.empty() && b.isUnnamed())
2787                 return renameBuffer(b, docstring());
2788
2789         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2790         if (success) {
2791                 theSession().lastFiles().add(b.fileName());
2792                 return true;
2793         }
2794
2795         // Switch to this Buffer.
2796         setBuffer(&b);
2797
2798         // FIXME: we don't tell the user *WHY* the save failed !!
2799         docstring const file = makeDisplayPath(b.absFileName(), 30);
2800         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2801                                    "Do you want to rename the document and "
2802                                    "try again?"), file);
2803         int const ret = Alert::prompt(_("Rename and save?"),
2804                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2805         switch (ret) {
2806         case 0:
2807                 if (!renameBuffer(b, docstring()))
2808                         return false;
2809                 break;
2810         case 1:
2811                 break;
2812         case 2:
2813                 return false;
2814         }
2815
2816         return saveBuffer(b, fn);
2817 }
2818
2819
2820 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2821 {
2822         return closeWorkArea(wa, false);
2823 }
2824
2825
2826 // We only want to close the buffer if it is not visible in other workareas
2827 // of the same view, nor in other views, and if this is not a child
2828 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2829 {
2830         Buffer & buf = wa->bufferView().buffer();
2831
2832         bool last_wa = d.countWorkAreasOf(buf) == 1
2833                 && !inOtherView(buf) && !buf.parent();
2834
2835         bool close_buffer = last_wa;
2836
2837         if (last_wa) {
2838                 if (lyxrc.close_buffer_with_last_view == "yes")
2839                         ; // Nothing to do
2840                 else if (lyxrc.close_buffer_with_last_view == "no")
2841                         close_buffer = false;
2842                 else {
2843                         docstring file;
2844                         if (buf.isUnnamed())
2845                                 file = from_utf8(buf.fileName().onlyFileName());
2846                         else
2847                                 file = buf.fileName().displayName(30);
2848                         docstring const text = bformat(
2849                                 _("Last view on document %1$s is being closed.\n"
2850                                   "Would you like to close or hide the document?\n"
2851                                   "\n"
2852                                   "Hidden documents can be displayed back through\n"
2853                                   "the menu: View->Hidden->...\n"
2854                                   "\n"
2855                                   "To remove this question, set your preference in:\n"
2856                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2857                                 ), file);
2858                         int ret = Alert::prompt(_("Close or hide document?"),
2859                                 text, 0, 1, _("&Close"), _("&Hide"));
2860                         close_buffer = (ret == 0);
2861                 }
2862         }
2863
2864         return closeWorkArea(wa, close_buffer);
2865 }
2866
2867
2868 bool GuiView::closeBuffer()
2869 {
2870         GuiWorkArea * wa = currentMainWorkArea();
2871         // coverity complained about this
2872         // it seems unnecessary, but perhaps is worth the check
2873         LASSERT(wa, return false);
2874
2875         setCurrentWorkArea(wa);
2876         Buffer & buf = wa->bufferView().buffer();
2877         return closeWorkArea(wa, !buf.parent());
2878 }
2879
2880
2881 void GuiView::writeSession() const {
2882         GuiWorkArea const * active_wa = currentMainWorkArea();
2883         for (int i = 0; i < d.splitter_->count(); ++i) {
2884                 TabWorkArea * twa = d.tabWorkArea(i);
2885                 for (int j = 0; j < twa->count(); ++j) {
2886                         GuiWorkArea * wa = twa->workArea(j);
2887                         Buffer & buf = wa->bufferView().buffer();
2888                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2889                 }
2890         }
2891 }
2892
2893
2894 bool GuiView::closeBufferAll()
2895 {
2896         // Close the workareas in all other views
2897         QList<int> const ids = guiApp->viewIds();
2898         for (int i = 0; i != ids.size(); ++i) {
2899                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2900                         return false;
2901         }
2902
2903         // Close our own workareas
2904         if (!closeWorkAreaAll())
2905                 return false;
2906
2907         // Now close the hidden buffers. We prevent hidden buffers from being
2908         // dirty, so we can just close them.
2909         theBufferList().closeAll();
2910         return true;
2911 }
2912
2913
2914 bool GuiView::closeWorkAreaAll()
2915 {
2916         setCurrentWorkArea(currentMainWorkArea());
2917
2918         // We might be in a situation that there is still a tabWorkArea, but
2919         // there are no tabs anymore. This can happen when we get here after a
2920         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2921         // many TabWorkArea's have no documents anymore.
2922         int empty_twa = 0;
2923
2924         // We have to call count() each time, because it can happen that
2925         // more than one splitter will disappear in one iteration (bug 5998).
2926         while (d.splitter_->count() > empty_twa) {
2927                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2928
2929                 if (twa->count() == 0)
2930                         ++empty_twa;
2931                 else {
2932                         setCurrentWorkArea(twa->currentWorkArea());
2933                         if (!closeTabWorkArea(twa))
2934                                 return false;
2935                 }
2936         }
2937         return true;
2938 }
2939
2940
2941 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2942 {
2943         if (!wa)
2944                 return false;
2945
2946         Buffer & buf = wa->bufferView().buffer();
2947
2948         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2949                 Alert::warning(_("Close document"),
2950                         _("Document could not be closed because it is being processed by LyX."));
2951                 return false;
2952         }
2953
2954         if (close_buffer)
2955                 return closeBuffer(buf);
2956         else {
2957                 if (!inMultiTabs(wa))
2958                         if (!saveBufferIfNeeded(buf, true))
2959                                 return false;
2960                 removeWorkArea(wa);
2961                 return true;
2962         }
2963 }
2964
2965
2966 bool GuiView::closeBuffer(Buffer & buf)
2967 {
2968         // If we are in a close_event all children will be closed in some time,
2969         // so no need to do it here. This will ensure that the children end up
2970         // in the session file in the correct order. If we close the master
2971         // buffer, we can close or release the child buffers here too.
2972         bool success = true;
2973         if (!closing_) {
2974                 ListOfBuffers clist = buf.getChildren();
2975                 ListOfBuffers::const_iterator it = clist.begin();
2976                 ListOfBuffers::const_iterator const bend = clist.end();
2977                 for (; it != bend; ++it) {
2978                         Buffer * child_buf = *it;
2979                         if (theBufferList().isOthersChild(&buf, child_buf)) {
2980                                 child_buf->setParent(0);
2981                                 continue;
2982                         }
2983
2984                         // FIXME: should we look in other tabworkareas?
2985                         // ANSWER: I don't think so. I've tested, and if the child is
2986                         // open in some other window, it closes without a problem.
2987                         GuiWorkArea * child_wa = workArea(*child_buf);
2988                         if (child_wa) {
2989                                 success = closeWorkArea(child_wa, true);
2990                                 if (!success)
2991                                         break;
2992                         } else {
2993                                 // In this case the child buffer is open but hidden.
2994                                 // It therefore should not (MUST NOT) be dirty!
2995                                 LATTEST(child_buf->isClean());
2996                                 theBufferList().release(child_buf);
2997                         }
2998                 }
2999         }
3000         if (success) {
3001                 // goto bookmark to update bookmark pit.
3002                 // FIXME: we should update only the bookmarks related to this buffer!
3003                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3004                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3005                         guiApp->gotoBookmark(i+1, false, false);
3006
3007                 if (saveBufferIfNeeded(buf, false)) {
3008                         buf.removeAutosaveFile();
3009                         theBufferList().release(&buf);
3010                         return true;
3011                 }
3012         }
3013         // open all children again to avoid a crash because of dangling
3014         // pointers (bug 6603)
3015         buf.updateBuffer();
3016         return false;
3017 }
3018
3019
3020 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3021 {
3022         while (twa == d.currentTabWorkArea()) {
3023                 twa->setCurrentIndex(twa->count() - 1);
3024
3025                 GuiWorkArea * wa = twa->currentWorkArea();
3026                 Buffer & b = wa->bufferView().buffer();
3027
3028                 // We only want to close the buffer if the same buffer is not visible
3029                 // in another view, and if this is not a child and if we are closing
3030                 // a view (not a tabgroup).
3031                 bool const close_buffer =
3032                         !inOtherView(b) && !b.parent() && closing_;
3033
3034                 if (!closeWorkArea(wa, close_buffer))
3035                         return false;
3036         }
3037         return true;
3038 }
3039
3040
3041 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3042 {
3043         if (buf.isClean() || buf.paragraphs().empty())
3044                 return true;
3045
3046         // Switch to this Buffer.
3047         setBuffer(&buf);
3048
3049         docstring file;
3050         bool exists;
3051         // FIXME: Unicode?
3052         if (buf.isUnnamed()) {
3053                 file = from_utf8(buf.fileName().onlyFileName());
3054                 exists = false;
3055         } else {
3056                 FileName filename = buf.fileName();
3057                 filename.refresh();
3058                 file = filename.displayName(30);
3059                 exists = filename.exists();
3060         }
3061
3062         // Bring this window to top before asking questions.
3063         raise();
3064         activateWindow();
3065
3066         int ret;
3067         if (hiding && buf.isUnnamed()) {
3068                 docstring const text = bformat(_("The document %1$s has not been "
3069                                                  "saved yet.\n\nDo you want to save "
3070                                                  "the document?"), file);
3071                 ret = Alert::prompt(_("Save new document?"),
3072                         text, 0, 1, _("&Save"), _("&Cancel"));
3073                 if (ret == 1)
3074                         ++ret;
3075         } else {
3076                 docstring const text = exists ?
3077                         bformat(_("The document %1$s has unsaved changes."
3078                                   "\n\nDo you want to save the document or "
3079                                   "discard the changes?"), file) :
3080                         bformat(_("The document %1$s has not been saved yet."
3081                                   "\n\nDo you want to save the document or "
3082                                   "discard it entirely?"), file);
3083                 docstring const title = exists ?
3084                         _("Save changed document?") : _("Save document?");
3085                 ret = Alert::prompt(title, text, 0, 2,
3086                                     _("&Save"), _("&Discard"), _("&Cancel"));
3087         }
3088
3089         switch (ret) {
3090         case 0:
3091                 if (!saveBuffer(buf))
3092                         return false;
3093                 break;
3094         case 1:
3095                 // If we crash after this we could have no autosave file
3096                 // but I guess this is really improbable (Jug).
3097                 // Sometimes improbable things happen:
3098                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3099                 // buf.removeAutosaveFile();
3100                 if (hiding)
3101                         // revert all changes
3102                         reloadBuffer(buf);
3103                 buf.markClean();
3104                 break;
3105         case 2:
3106                 return false;
3107         }
3108         return true;
3109 }
3110
3111
3112 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3113 {
3114         Buffer & buf = wa->bufferView().buffer();
3115
3116         for (int i = 0; i != d.splitter_->count(); ++i) {
3117                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3118                 if (wa_ && wa_ != wa)
3119                         return true;
3120         }
3121         return inOtherView(buf);
3122 }
3123
3124
3125 bool GuiView::inOtherView(Buffer & buf)
3126 {
3127         QList<int> const ids = guiApp->viewIds();
3128
3129         for (int i = 0; i != ids.size(); ++i) {
3130                 if (id_ == ids[i])
3131                         continue;
3132
3133                 if (guiApp->view(ids[i]).workArea(buf))
3134                         return true;
3135         }
3136         return false;
3137 }
3138
3139
3140 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3141 {
3142         if (!documentBufferView())
3143                 return;
3144
3145         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3146                 Buffer * const curbuf = &documentBufferView()->buffer();
3147                 int nwa = twa->count();
3148                 for (int i = 0; i < nwa; ++i) {
3149                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3150                                 int next_index;
3151                                 if (np == NEXTBUFFER)
3152                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3153                                 else
3154                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3155                                 if (move)
3156                                         twa->moveTab(i, next_index);
3157                                 else
3158                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3159                                 break;
3160                         }
3161                 }
3162         }
3163 }
3164
3165
3166 /// make sure the document is saved
3167 static bool ensureBufferClean(Buffer * buffer)
3168 {
3169         LASSERT(buffer, return false);
3170         if (buffer->isClean() && !buffer->isUnnamed())
3171                 return true;
3172
3173         docstring const file = buffer->fileName().displayName(30);
3174         docstring title;
3175         docstring text;
3176         if (!buffer->isUnnamed()) {
3177                 text = bformat(_("The document %1$s has unsaved "
3178                                                  "changes.\n\nDo you want to save "
3179                                                  "the document?"), file);
3180                 title = _("Save changed document?");
3181
3182         } else {
3183                 text = bformat(_("The document %1$s has not been "
3184                                                  "saved yet.\n\nDo you want to save "
3185                                                  "the document?"), file);
3186                 title = _("Save new document?");
3187         }
3188         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3189
3190         if (ret == 0)
3191                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3192
3193         return buffer->isClean() && !buffer->isUnnamed();
3194 }
3195
3196
3197 bool GuiView::reloadBuffer(Buffer & buf)
3198 {
3199         currentBufferView()->cursor().reset();
3200         Buffer::ReadStatus status = buf.reload();
3201         return status == Buffer::ReadSuccess;
3202 }
3203
3204
3205 void GuiView::checkExternallyModifiedBuffers()
3206 {
3207         BufferList::iterator bit = theBufferList().begin();
3208         BufferList::iterator const bend = theBufferList().end();
3209         for (; bit != bend; ++bit) {
3210                 Buffer * buf = *bit;
3211                 if (buf->fileName().exists() && buf->isChecksumModified()) {
3212                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3213                                         " Reload now? Any local changes will be lost."),
3214                                         from_utf8(buf->absFileName()));
3215                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3216                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3217                         if (!ret)
3218                                 reloadBuffer(*buf);
3219                 }
3220         }
3221 }
3222
3223
3224 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3225 {
3226         Buffer * buffer = documentBufferView()
3227                 ? &(documentBufferView()->buffer()) : 0;
3228
3229         switch (cmd.action()) {
3230         case LFUN_VC_REGISTER:
3231                 if (!buffer || !ensureBufferClean(buffer))
3232                         break;
3233                 if (!buffer->lyxvc().inUse()) {
3234                         if (buffer->lyxvc().registrer()) {
3235                                 reloadBuffer(*buffer);
3236                                 dr.clearMessageUpdate();
3237                         }
3238                 }
3239                 break;
3240
3241         case LFUN_VC_RENAME:
3242         case LFUN_VC_COPY: {
3243                 if (!buffer || !ensureBufferClean(buffer))
3244                         break;
3245                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3246                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3247                                 // Some changes are not yet committed.
3248                                 // We test here and not in getStatus(), since
3249                                 // this test is expensive.
3250                                 string log;
3251                                 LyXVC::CommandResult ret =
3252                                         buffer->lyxvc().checkIn(log);
3253                                 dr.setMessage(log);
3254                                 if (ret == LyXVC::ErrorCommand ||
3255                                     ret == LyXVC::VCSuccess)
3256                                         reloadBuffer(*buffer);
3257                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3258                                         frontend::Alert::error(
3259                                                 _("Revision control error."),
3260                                                 _("Document could not be checked in."));
3261                                         break;
3262                                 }
3263                         }
3264                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3265                                 LV_VC_RENAME : LV_VC_COPY;
3266                         renameBuffer(*buffer, cmd.argument(), kind);
3267                 }
3268                 break;
3269         }
3270
3271         case LFUN_VC_CHECK_IN:
3272                 if (!buffer || !ensureBufferClean(buffer))
3273                         break;
3274                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3275                         string log;
3276                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3277                         dr.setMessage(log);
3278                         // Only skip reloading if the checkin was cancelled or
3279                         // an error occurred before the real checkin VCS command
3280                         // was executed, since the VCS might have changed the
3281                         // file even if it could not checkin successfully.
3282                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3283                                 reloadBuffer(*buffer);
3284                 }
3285                 break;
3286
3287         case LFUN_VC_CHECK_OUT:
3288                 if (!buffer || !ensureBufferClean(buffer))
3289                         break;
3290                 if (buffer->lyxvc().inUse()) {
3291                         dr.setMessage(buffer->lyxvc().checkOut());
3292                         reloadBuffer(*buffer);
3293                 }
3294                 break;
3295
3296         case LFUN_VC_LOCKING_TOGGLE:
3297                 LASSERT(buffer, return);
3298                 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3299                         break;
3300                 if (buffer->lyxvc().inUse()) {
3301                         string res = buffer->lyxvc().lockingToggle();
3302                         if (res.empty()) {
3303                                 frontend::Alert::error(_("Revision control error."),
3304                                 _("Error when setting the locking property."));
3305                         } else {
3306                                 dr.setMessage(res);
3307                                 reloadBuffer(*buffer);
3308                         }
3309                 }
3310                 break;
3311
3312         case LFUN_VC_REVERT:
3313                 LASSERT(buffer, return);
3314                 if (buffer->lyxvc().revert()) {
3315                         reloadBuffer(*buffer);
3316                         dr.clearMessageUpdate();
3317                 }
3318                 break;
3319
3320         case LFUN_VC_UNDO_LAST:
3321                 LASSERT(buffer, return);
3322                 buffer->lyxvc().undoLast();
3323                 reloadBuffer(*buffer);
3324                 dr.clearMessageUpdate();
3325                 break;
3326
3327         case LFUN_VC_REPO_UPDATE:
3328                 LASSERT(buffer, return);
3329                 if (ensureBufferClean(buffer)) {
3330                         dr.setMessage(buffer->lyxvc().repoUpdate());
3331                         checkExternallyModifiedBuffers();
3332                 }
3333                 break;
3334
3335         case LFUN_VC_COMMAND: {
3336                 string flag = cmd.getArg(0);
3337                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3338                         break;
3339                 docstring message;
3340                 if (contains(flag, 'M')) {
3341                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3342                                 break;
3343                 }
3344                 string path = cmd.getArg(1);
3345                 if (contains(path, "$$p") && buffer)
3346                         path = subst(path, "$$p", buffer->filePath());
3347                 LYXERR(Debug::LYXVC, "Directory: " << path);
3348                 FileName pp(path);
3349                 if (!pp.isReadableDirectory()) {
3350                         lyxerr << _("Directory is not accessible.") << endl;
3351                         break;
3352                 }
3353                 support::PathChanger p(pp);
3354
3355                 string command = cmd.getArg(2);
3356                 if (command.empty())
3357                         break;
3358                 if (buffer) {
3359                         command = subst(command, "$$i", buffer->absFileName());
3360                         command = subst(command, "$$p", buffer->filePath());
3361                 }
3362                 command = subst(command, "$$m", to_utf8(message));
3363                 LYXERR(Debug::LYXVC, "Command: " << command);
3364                 Systemcall one;
3365                 one.startscript(Systemcall::Wait, command);
3366
3367                 if (!buffer)
3368                         break;
3369                 if (contains(flag, 'I'))
3370                         buffer->markDirty();
3371                 if (contains(flag, 'R'))
3372                         reloadBuffer(*buffer);
3373
3374                 break;
3375                 }
3376
3377         case LFUN_VC_COMPARE: {
3378                 if (cmd.argument().empty()) {
3379                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3380                         break;
3381                 }
3382
3383                 string rev1 = cmd.getArg(0);
3384                 string f1, f2;
3385                 LATTEST(buffer)
3386
3387                 // f1
3388                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3389                         break;
3390
3391                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3392                         f2 = buffer->absFileName();
3393                 } else {
3394                         string rev2 = cmd.getArg(1);
3395                         if (rev2.empty())
3396                                 break;
3397                         // f2
3398                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3399                                 break;
3400                 }
3401
3402                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3403                                         f1 << "\n"  << f2 << "\n" );
3404                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3405                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3406                 break;
3407         }
3408
3409         default:
3410                 break;
3411         }
3412 }
3413
3414
3415 void GuiView::openChildDocument(string const & fname)
3416 {
3417         LASSERT(documentBufferView(), return);
3418         Buffer & buffer = documentBufferView()->buffer();
3419         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3420         documentBufferView()->saveBookmark(false);
3421         Buffer * child = 0;
3422         if (theBufferList().exists(filename)) {
3423                 child = theBufferList().getBuffer(filename);
3424                 setBuffer(child);
3425         } else {
3426                 message(bformat(_("Opening child document %1$s..."),
3427                         makeDisplayPath(filename.absFileName())));
3428                 child = loadDocument(filename, false);
3429         }
3430         // Set the parent name of the child document.
3431         // This makes insertion of citations and references in the child work,
3432         // when the target is in the parent or another child document.
3433         if (child)
3434                 child->setParent(&buffer);
3435 }
3436
3437
3438 bool GuiView::goToFileRow(string const & argument)
3439 {
3440         string file_name;
3441         int row;
3442         size_t i = argument.find_last_of(' ');
3443         if (i != string::npos) {
3444                 file_name = os::internal_path(trim(argument.substr(0, i)));
3445                 istringstream is(argument.substr(i + 1));
3446                 is >> row;
3447                 if (is.fail())
3448                         i = string::npos;
3449         }
3450         if (i == string::npos) {
3451                 LYXERR0("Wrong argument: " << argument);
3452                 return false;
3453         }
3454         Buffer * buf = 0;
3455         string const abstmp = package().temp_dir().absFileName();
3456         string const realtmp = package().temp_dir().realPath();
3457         // We have to use os::path_prefix_is() here, instead of
3458         // simply prefixIs(), because the file name comes from
3459         // an external application and may need case adjustment.
3460         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3461                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3462                 // Needed by inverse dvi search. If it is a file
3463                 // in tmpdir, call the apropriated function.
3464                 // If tmpdir is a symlink, we may have the real
3465                 // path passed back, so we correct for that.
3466                 if (!prefixIs(file_name, abstmp))
3467                         file_name = subst(file_name, realtmp, abstmp);
3468                 buf = theBufferList().getBufferFromTmp(file_name);
3469         } else {
3470                 // Must replace extension of the file to be .lyx
3471                 // and get full path
3472                 FileName const s = fileSearch(string(),
3473                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3474                 // Either change buffer or load the file
3475                 if (theBufferList().exists(s))
3476                         buf = theBufferList().getBuffer(s);
3477                 else if (s.exists()) {
3478                         buf = loadDocument(s);
3479                         if (!buf)
3480                                 return false;
3481                 } else {
3482                         message(bformat(
3483                                         _("File does not exist: %1$s"),
3484                                         makeDisplayPath(file_name)));
3485                         return false;
3486                 }
3487         }
3488         if (!buf) {
3489                 message(bformat(
3490                         _("No buffer for file: %1$s."),
3491                         makeDisplayPath(file_name))
3492                 );
3493                 return false;
3494         }
3495         setBuffer(buf);
3496         bool success = documentBufferView()->setCursorFromRow(row);
3497         if (!success) {
3498                 LYXERR(Debug::LATEX,
3499                        "setCursorFromRow: invalid position for row " << row);
3500                 frontend::Alert::error(_("Inverse Search Failed"),
3501                                        _("Invalid position requested by inverse search.\n"
3502                                          "You may need to update the viewed document."));
3503         }
3504         return success;
3505 }
3506
3507
3508 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3509 {
3510         QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3511         menu->exec(QCursor::pos());
3512 }
3513
3514
3515 template<class T>
3516 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3517 {
3518         Buffer::ExportStatus const status = func(format);
3519
3520         // the cloning operation will have produced a clone of the entire set of
3521         // documents, starting from the master. so we must delete those.
3522         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3523         delete mbuf;
3524         busyBuffers.remove(orig);
3525         return status;
3526 }
3527
3528
3529 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3530 {
3531         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3532         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3533 }
3534
3535
3536 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3537 {
3538         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3539         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3540 }
3541
3542
3543 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3544 {
3545         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3546         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3547 }
3548
3549
3550 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3551                            string const & argument,
3552                            Buffer const * used_buffer,
3553                            docstring const & msg,
3554                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3555                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3556                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3557 {
3558         if (!used_buffer)
3559                 return false;
3560
3561         string format = argument;
3562         if (format.empty())
3563                 format = used_buffer->params().getDefaultOutputFormat();
3564         processing_format = format;
3565         if (!msg.empty()) {
3566                 progress_->clearMessages();
3567                 gv_->message(msg);
3568         }
3569 #if EXPORT_in_THREAD
3570         GuiViewPrivate::busyBuffers.insert(used_buffer);
3571         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3572         if (!cloned_buffer) {
3573                 Alert::error(_("Export Error"),
3574                              _("Error cloning the Buffer."));
3575                 return false;
3576         }
3577         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3578                                 asyncFunc,
3579                                 used_buffer,
3580                                 cloned_buffer,
3581                                 format);
3582         setPreviewFuture(f);
3583         last_export_format = used_buffer->params().bufferFormat();
3584         (void) syncFunc;
3585         (void) previewFunc;
3586         // We are asynchronous, so we don't know here anything about the success
3587         return true;
3588 #else
3589         Buffer::ExportStatus status;
3590         if (syncFunc) {
3591                 status = (used_buffer->*syncFunc)(format, true);
3592         } else if (previewFunc) {
3593                 status = (used_buffer->*previewFunc)(format);
3594         } else
3595                 return false;
3596         handleExportStatus(gv_, status, format);
3597         (void) asyncFunc;
3598         return (status == Buffer::ExportSuccess
3599                         || status == Buffer::PreviewSuccess);
3600 #endif
3601 }
3602
3603 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3604 {
3605         BufferView * bv = currentBufferView();
3606         LASSERT(bv, return);
3607
3608         // Let the current BufferView dispatch its own actions.
3609         bv->dispatch(cmd, dr);
3610         if (dr.dispatched())
3611                 return;
3612
3613         // Try with the document BufferView dispatch if any.
3614         BufferView * doc_bv = documentBufferView();
3615         if (doc_bv && doc_bv != bv) {
3616                 doc_bv->dispatch(cmd, dr);
3617                 if (dr.dispatched())
3618                         return;
3619         }
3620
3621         // Then let the current Cursor dispatch its own actions.
3622         bv->cursor().dispatch(cmd);
3623
3624         // update completion. We do it here and not in
3625         // processKeySym to avoid another redraw just for a
3626         // changed inline completion
3627         if (cmd.origin() == FuncRequest::KEYBOARD) {
3628                 if (cmd.action() == LFUN_SELF_INSERT
3629                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3630                         updateCompletion(bv->cursor(), true, true);
3631                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3632                         updateCompletion(bv->cursor(), false, true);
3633                 else
3634                         updateCompletion(bv->cursor(), false, false);
3635         }
3636
3637         dr = bv->cursor().result();
3638 }
3639
3640
3641 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3642 {
3643         BufferView * bv = currentBufferView();
3644         // By default we won't need any update.
3645         dr.screenUpdate(Update::None);
3646         // assume cmd will be dispatched
3647         dr.dispatched(true);
3648
3649         Buffer * doc_buffer = documentBufferView()
3650                 ? &(documentBufferView()->buffer()) : 0;
3651
3652         if (cmd.origin() == FuncRequest::TOC) {
3653                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3654                 // FIXME: do we need to pass a DispatchResult object here?
3655                 toc->doDispatch(bv->cursor(), cmd);
3656                 return;
3657         }
3658
3659         string const argument = to_utf8(cmd.argument());
3660
3661         switch(cmd.action()) {
3662                 case LFUN_BUFFER_CHILD_OPEN:
3663                         openChildDocument(to_utf8(cmd.argument()));
3664                         break;
3665
3666                 case LFUN_BUFFER_IMPORT:
3667                         importDocument(to_utf8(cmd.argument()));
3668                         break;
3669
3670                 case LFUN_BUFFER_EXPORT: {
3671                         if (!doc_buffer)
3672                                 break;
3673                         // GCC only sees strfwd.h when building merged
3674                         if (::lyx::operator==(cmd.argument(), "custom")) {
3675                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3676                                 break;
3677                         }
3678
3679                         string const dest = cmd.getArg(1);
3680                         FileName target_dir;
3681                         if (!dest.empty() && FileName::isAbsolute(dest))
3682                                 target_dir = FileName(support::onlyPath(dest));
3683                         else
3684                                 target_dir = doc_buffer->fileName().onlyPath();
3685
3686                         string const format = (argument.empty() || argument == "default") ?
3687                                 doc_buffer->params().getDefaultOutputFormat() : argument;
3688
3689                         if ((dest.empty() && doc_buffer->isUnnamed())
3690                             || !target_dir.isDirWritable()) {
3691                                 exportBufferAs(*doc_buffer, from_utf8(format));
3692                                 break;
3693                         }
3694                         /* TODO/Review: Is it a problem to also export the children?
3695                                         See the update_unincluded flag */
3696                         d.asyncBufferProcessing(format,
3697                                                 doc_buffer,
3698                                                 _("Exporting ..."),
3699                                                 &GuiViewPrivate::exportAndDestroy,
3700                                                 &Buffer::doExport,
3701                                                 0);
3702                         // TODO Inform user about success
3703                         break;
3704                 }
3705
3706                 case LFUN_BUFFER_EXPORT_AS: {
3707                         LASSERT(doc_buffer, break);
3708                         docstring f = cmd.argument();
3709                         if (f.empty())
3710                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3711                         exportBufferAs(*doc_buffer, f);
3712                         break;
3713                 }
3714
3715                 case LFUN_BUFFER_UPDATE: {
3716                         d.asyncBufferProcessing(argument,
3717                                                 doc_buffer,
3718                                                 _("Exporting ..."),
3719                                                 &GuiViewPrivate::compileAndDestroy,
3720                                                 &Buffer::doExport,
3721                                                 0);
3722                         break;
3723                 }
3724                 case LFUN_BUFFER_VIEW: {
3725                         d.asyncBufferProcessing(argument,
3726                                                 doc_buffer,
3727                                                 _("Previewing ..."),
3728                                                 &GuiViewPrivate::previewAndDestroy,
3729                                                 0,
3730                                                 &Buffer::preview);
3731                         break;
3732                 }
3733                 case LFUN_MASTER_BUFFER_UPDATE: {
3734                         d.asyncBufferProcessing(argument,
3735                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3736                                                 docstring(),
3737                                                 &GuiViewPrivate::compileAndDestroy,
3738                                                 &Buffer::doExport,
3739                                                 0);
3740                         break;
3741                 }
3742                 case LFUN_MASTER_BUFFER_VIEW: {
3743                         d.asyncBufferProcessing(argument,
3744                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3745                                                 docstring(),
3746                                                 &GuiViewPrivate::previewAndDestroy,
3747                                                 0, &Buffer::preview);
3748                         break;
3749                 }
3750                 case LFUN_EXPORT_CANCEL: {
3751                         Systemcall::killscript();
3752                         break;
3753                 }
3754                 case LFUN_BUFFER_SWITCH: {
3755                         string const file_name = to_utf8(cmd.argument());
3756                         if (!FileName::isAbsolute(file_name)) {
3757                                 dr.setError(true);
3758                                 dr.setMessage(_("Absolute filename expected."));
3759                                 break;
3760                         }
3761
3762                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3763                         if (!buffer) {
3764                                 dr.setError(true);
3765                                 dr.setMessage(_("Document not loaded"));
3766                                 break;
3767                         }
3768
3769                         // Do we open or switch to the buffer in this view ?
3770                         if (workArea(*buffer)
3771                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3772                                 setBuffer(buffer);
3773                                 break;
3774                         }
3775
3776                         // Look for the buffer in other views
3777                         QList<int> const ids = guiApp->viewIds();
3778                         int i = 0;
3779                         for (; i != ids.size(); ++i) {
3780                                 GuiView & gv = guiApp->view(ids[i]);
3781                                 if (gv.workArea(*buffer)) {
3782                                         gv.raise();
3783                                         gv.activateWindow();
3784                                         gv.setFocus();
3785                                         gv.setBuffer(buffer);
3786                                         break;
3787                                 }
3788                         }
3789
3790                         // If necessary, open a new window as a last resort
3791                         if (i == ids.size()) {
3792                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3793                                 lyx::dispatch(cmd);
3794                         }
3795                         break;
3796                 }
3797
3798                 case LFUN_BUFFER_NEXT:
3799                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3800                         break;
3801
3802                 case LFUN_BUFFER_MOVE_NEXT:
3803                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3804                         break;
3805
3806                 case LFUN_BUFFER_PREVIOUS:
3807                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3808                         break;
3809
3810                 case LFUN_BUFFER_MOVE_PREVIOUS:
3811                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3812                         break;
3813
3814                 case LFUN_BUFFER_CHKTEX:
3815                         LASSERT(doc_buffer, break);
3816                         doc_buffer->runChktex();
3817                         break;
3818
3819                 case LFUN_COMMAND_EXECUTE: {
3820                         command_execute_ = true;
3821                         minibuffer_focus_ = true;
3822                         break;
3823                 }
3824                 case LFUN_DROP_LAYOUTS_CHOICE:
3825                         d.layout_->showPopup();
3826                         break;
3827
3828                 case LFUN_MENU_OPEN:
3829                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3830                                 menu->exec(QCursor::pos());
3831                         break;
3832
3833                 case LFUN_FILE_INSERT:
3834                         insertLyXFile(cmd.argument());
3835                         break;
3836
3837                 case LFUN_FILE_INSERT_PLAINTEXT:
3838                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3839                         string const fname = to_utf8(cmd.argument());
3840                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3841                                 dr.setMessage(_("Absolute filename expected."));
3842                                 break;
3843                         }
3844
3845                         FileName filename(fname);
3846                         if (fname.empty()) {
3847                                 FileDialog dlg(qt_("Select file to insert"));
3848
3849                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3850                                         QStringList(qt_("All Files (*)")));
3851
3852                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3853                                         dr.setMessage(_("Canceled."));
3854                                         break;
3855                                 }
3856
3857                                 filename.set(fromqstr(result.second));
3858                         }
3859
3860                         if (bv) {
3861                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3862                                 bv->dispatch(new_cmd, dr);
3863                         }
3864                         break;
3865                 }
3866
3867                 case LFUN_BUFFER_RELOAD: {
3868                         LASSERT(doc_buffer, break);
3869
3870                         //drop changes?
3871                         bool drop = (cmd.argument()=="dump");
3872
3873                         int ret = 0;
3874                         if (!drop && !doc_buffer->isClean()) {
3875                                 docstring const file =
3876                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3877                                 if (doc_buffer->notifiesExternalModification()) {
3878                                         docstring text = _("The current version will be lost. "
3879                                             "Are you sure you want to load the version on disk "
3880                                             "of the document %1$s?");
3881                                         ret = Alert::prompt(_("Reload saved document?"),
3882                                                             bformat(text, file), 1, 1,
3883                                                             _("&Reload"), _("&Cancel"));
3884                                 } else {
3885                                         docstring text = _("Any changes will be lost. "
3886                                             "Are you sure you want to revert to the saved version "
3887                                             "of the document %1$s?");
3888                                         ret = Alert::prompt(_("Revert to saved document?"),
3889                                                             bformat(text, file), 1, 1,
3890                                                             _("&Revert"), _("&Cancel"));
3891                                 }
3892                         }
3893
3894                         if (ret == 0) {
3895                                 doc_buffer->markClean();
3896                                 reloadBuffer(*doc_buffer);
3897                                 dr.forceBufferUpdate();
3898                         }
3899                         break;
3900                 }
3901
3902                 case LFUN_BUFFER_WRITE:
3903                         LASSERT(doc_buffer, break);
3904                         saveBuffer(*doc_buffer);
3905                         break;
3906
3907                 case LFUN_BUFFER_WRITE_AS:
3908                         LASSERT(doc_buffer, break);
3909                         renameBuffer(*doc_buffer, cmd.argument());
3910                         break;
3911
3912                 case LFUN_BUFFER_WRITE_ALL: {
3913                         Buffer * first = theBufferList().first();
3914                         if (!first)
3915                                 break;
3916                         message(_("Saving all documents..."));
3917                         // We cannot use a for loop as the buffer list cycles.
3918                         Buffer * b = first;
3919                         do {
3920                                 if (!b->isClean()) {
3921                                         saveBuffer(*b);
3922                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3923                                 }
3924                                 b = theBufferList().next(b);
3925                         } while (b != first);
3926                         dr.setMessage(_("All documents saved."));
3927                         break;
3928                 }
3929
3930                 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3931                         LASSERT(doc_buffer, break);
3932                         doc_buffer->clearExternalModification();
3933                         break;
3934
3935                 case LFUN_BUFFER_CLOSE:
3936                         closeBuffer();
3937                         break;
3938
3939                 case LFUN_BUFFER_CLOSE_ALL:
3940                         closeBufferAll();
3941                         break;
3942
3943                 case LFUN_DEVEL_MODE_TOGGLE:
3944                         devel_mode_ = !devel_mode_;
3945                         if (devel_mode_)
3946                                 dr.setMessage(_("Developer mode is now enabled."));
3947                         else
3948                                 dr.setMessage(_("Developer mode is now disabled."));
3949                         break;
3950
3951                 case LFUN_TOOLBAR_TOGGLE: {
3952                         string const name = cmd.getArg(0);
3953                         if (GuiToolbar * t = toolbar(name))
3954                                 t->toggle();
3955                         break;
3956                 }
3957
3958                 case LFUN_TOOLBAR_MOVABLE: {
3959                         string const name = cmd.getArg(0);
3960                         if (name == "*") {
3961                                 // toggle (all) toolbars movablility
3962                                 toolbarsMovable_ = !toolbarsMovable_;
3963                                 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3964                                         GuiToolbar * tb = toolbar(ti.name);
3965                                         if (tb && tb->isMovable() != toolbarsMovable_)
3966                                                 // toggle toolbar movablity if it does not fit lock
3967                                                 // (all) toolbars positions state silent = true, since
3968                                                 // status bar notifications are slow
3969                                                 tb->movable(true);
3970                                 }
3971                                 if (toolbarsMovable_)
3972                                         dr.setMessage(_("Toolbars unlocked."));
3973                                 else
3974                                         dr.setMessage(_("Toolbars locked."));
3975                         } else if (GuiToolbar * t = toolbar(name)) {
3976                                 // toggle current toolbar movablity
3977                                 t->movable();
3978                                 // update lock (all) toolbars positions
3979                                 updateLockToolbars();
3980                         }
3981                         break;
3982                 }
3983
3984                 case LFUN_ICON_SIZE: {
3985                         QSize size = d.iconSize(cmd.argument());
3986                         setIconSize(size);
3987                         dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3988                                                 size.width(), size.height()));
3989                         break;
3990                 }
3991
3992                 case LFUN_DIALOG_UPDATE: {
3993                         string const name = to_utf8(cmd.argument());
3994                         if (name == "prefs" || name == "document")
3995                                 updateDialog(name, string());
3996                         else if (name == "paragraph")
3997                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3998                         else if (currentBufferView()) {
3999                                 Inset * inset = currentBufferView()->editedInset(name);
4000                                 // Can only update a dialog connected to an existing inset
4001                                 if (inset) {
4002                                         // FIXME: get rid of this indirection; GuiView ask the inset
4003                                         // if he is kind enough to update itself...
4004                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4005                                         //FIXME: pass DispatchResult here?
4006                                         inset->dispatch(currentBufferView()->cursor(), fr);
4007                                 }
4008                         }
4009                         break;
4010                 }
4011
4012                 case LFUN_DIALOG_TOGGLE: {
4013                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4014                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4015                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
4016                         break;
4017                 }
4018
4019                 case LFUN_DIALOG_DISCONNECT_INSET:
4020                         disconnectDialog(to_utf8(cmd.argument()));
4021                         break;
4022
4023                 case LFUN_DIALOG_HIDE: {
4024                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4025                         break;
4026                 }
4027
4028                 case LFUN_DIALOG_SHOW: {
4029                         string const name = cmd.getArg(0);
4030                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4031
4032                         if (name == "character") {
4033                                 data = freefont2string();
4034                                 if (!data.empty())
4035                                         showDialog("character", data);
4036                         } else if (name == "latexlog") {
4037                                 // getStatus checks that
4038                                 LATTEST(doc_buffer);
4039                                 Buffer::LogType type;
4040                                 string const logfile = doc_buffer->logName(&type);
4041                                 switch (type) {
4042                                 case Buffer::latexlog:
4043                                         data = "latex ";
4044                                         break;
4045                                 case Buffer::buildlog:
4046                                         data = "literate ";
4047                                         break;
4048                                 }
4049                                 data += Lexer::quoteString(logfile);
4050                                 showDialog("log", data);
4051                         } else if (name == "vclog") {
4052                                 // getStatus checks that
4053                                 LATTEST(doc_buffer);
4054                                 string const data = "vc " +
4055                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4056                                 showDialog("log", data);
4057                         } else if (name == "symbols") {
4058                                 data = bv->cursor().getEncoding()->name();
4059                                 if (!data.empty())
4060                                         showDialog("symbols", data);
4061                         // bug 5274
4062                         } else if (name == "prefs" && isFullScreen()) {
4063                                 lfunUiToggle("fullscreen");
4064                                 showDialog("prefs", data);
4065                         } else
4066                                 showDialog(name, data);
4067                         break;
4068                 }
4069
4070                 case LFUN_MESSAGE:
4071                         dr.setMessage(cmd.argument());
4072                         break;
4073
4074                 case LFUN_UI_TOGGLE: {
4075                         string arg = cmd.getArg(0);
4076                         if (!lfunUiToggle(arg)) {
4077                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4078                                 dr.setMessage(bformat(msg, from_utf8(arg)));
4079                         }
4080                         // Make sure the keyboard focus stays in the work area.
4081                         setFocus();
4082                         break;
4083                 }
4084
4085                 case LFUN_VIEW_SPLIT: {
4086                         LASSERT(doc_buffer, break);
4087                         string const orientation = cmd.getArg(0);
4088                         d.splitter_->setOrientation(orientation == "vertical"
4089                                 ? Qt::Vertical : Qt::Horizontal);
4090                         TabWorkArea * twa = addTabWorkArea();
4091                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4092                         setCurrentWorkArea(wa);
4093                         break;
4094                 }
4095                 case LFUN_TAB_GROUP_CLOSE:
4096                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4097                                 closeTabWorkArea(twa);
4098                                 d.current_work_area_ = 0;
4099                                 twa = d.currentTabWorkArea();
4100                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4101                                 if (twa) {
4102                                         // Make sure the work area is up to date.
4103                                         setCurrentWorkArea(twa->currentWorkArea());
4104                                 } else {
4105                                         setCurrentWorkArea(0);
4106                                 }
4107                         }
4108                         break;
4109
4110                 case LFUN_VIEW_CLOSE:
4111                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4112                                 closeWorkArea(twa->currentWorkArea());
4113                                 d.current_work_area_ = 0;
4114                                 twa = d.currentTabWorkArea();
4115                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4116                                 if (twa) {
4117                                         // Make sure the work area is up to date.
4118                                         setCurrentWorkArea(twa->currentWorkArea());
4119                                 } else {
4120                                         setCurrentWorkArea(0);
4121                                 }
4122                         }
4123                         break;
4124
4125                 case LFUN_COMPLETION_INLINE:
4126                         if (d.current_work_area_)
4127                                 d.current_work_area_->completer().showInline();
4128                         break;
4129
4130                 case LFUN_COMPLETION_POPUP:
4131                         if (d.current_work_area_)
4132                                 d.current_work_area_->completer().showPopup();
4133                         break;
4134
4135
4136                 case LFUN_COMPLETE:
4137                         if (d.current_work_area_)
4138                                 d.current_work_area_->completer().tab();
4139                         break;
4140
4141                 case LFUN_COMPLETION_CANCEL:
4142                         if (d.current_work_area_) {
4143                                 if (d.current_work_area_->completer().popupVisible())
4144                                         d.current_work_area_->completer().hidePopup();
4145                                 else
4146                                         d.current_work_area_->completer().hideInline();
4147                         }
4148                         break;
4149
4150                 case LFUN_COMPLETION_ACCEPT:
4151                         if (d.current_work_area_)
4152                                 d.current_work_area_->completer().activate();
4153                         break;
4154
4155                 case LFUN_BUFFER_ZOOM_IN:
4156                 case LFUN_BUFFER_ZOOM_OUT:
4157                 case LFUN_BUFFER_ZOOM: {
4158                         if (cmd.argument().empty()) {
4159                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4160                                         zoom_ratio_ = 1.0;
4161                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4162                                         zoom_ratio_ += 0.1;
4163                                 else
4164                                         zoom_ratio_ -= 0.1;
4165                         } else {
4166                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4167                                         zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4168                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4169                                         zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4170                                 else
4171                                         zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4172                         }
4173
4174                         // Actual zoom value: default zoom + fractional extra value
4175                         int zoom = lyxrc.defaultZoom * zoom_ratio_;
4176                         if (zoom < static_cast<int>(zoom_min_))
4177                                 zoom = zoom_min_;
4178
4179                         lyxrc.currentZoom = zoom;
4180
4181                         dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4182                                               lyxrc.currentZoom, lyxrc.defaultZoom));
4183
4184                         // The global QPixmapCache is used in GuiPainter to cache text
4185                         // painting so we must reset it.
4186                         QPixmapCache::clear();
4187                         guiApp->fontLoader().update();
4188                         dr.screenUpdate(Update::Force | Update::FitCursor);
4189                         break;
4190                 }
4191
4192                 case LFUN_VC_REGISTER:
4193                 case LFUN_VC_RENAME:
4194                 case LFUN_VC_COPY:
4195                 case LFUN_VC_CHECK_IN:
4196                 case LFUN_VC_CHECK_OUT:
4197                 case LFUN_VC_REPO_UPDATE:
4198                 case LFUN_VC_LOCKING_TOGGLE:
4199                 case LFUN_VC_REVERT:
4200                 case LFUN_VC_UNDO_LAST:
4201                 case LFUN_VC_COMMAND:
4202                 case LFUN_VC_COMPARE:
4203                         dispatchVC(cmd, dr);
4204                         break;
4205
4206                 case LFUN_SERVER_GOTO_FILE_ROW:
4207                         if(goToFileRow(to_utf8(cmd.argument())))
4208                                 dr.screenUpdate(Update::Force | Update::FitCursor);
4209                         break;
4210
4211                 case LFUN_LYX_ACTIVATE:
4212                         activateWindow();
4213                         break;
4214
4215                 case LFUN_FORWARD_SEARCH: {
4216                         // it seems safe to assume we have a document buffer, since
4217                         // getStatus wants one.
4218                         LATTEST(doc_buffer);
4219                         Buffer const * doc_master = doc_buffer->masterBuffer();
4220                         FileName const path(doc_master->temppath());
4221                         string const texname = doc_master->isChild(doc_buffer)
4222                                 ? DocFileName(changeExtension(
4223                                         doc_buffer->absFileName(),
4224                                                 "tex")).mangledFileName()
4225                                 : doc_buffer->latexName();
4226                         string const fulltexname =
4227                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4228                         string const mastername =
4229                                 removeExtension(doc_master->latexName());
4230                         FileName const dviname(addName(path.absFileName(),
4231                                         addExtension(mastername, "dvi")));
4232                         FileName const pdfname(addName(path.absFileName(),
4233                                         addExtension(mastername, "pdf")));
4234                         bool const have_dvi = dviname.exists();
4235                         bool const have_pdf = pdfname.exists();
4236                         if (!have_dvi && !have_pdf) {
4237                                 dr.setMessage(_("Please, preview the document first."));
4238                                 break;
4239                         }
4240                         string outname = dviname.onlyFileName();
4241                         string command = lyxrc.forward_search_dvi;
4242                         if (!have_dvi || (have_pdf &&
4243                             pdfname.lastModified() > dviname.lastModified())) {
4244                                 outname = pdfname.onlyFileName();
4245                                 command = lyxrc.forward_search_pdf;
4246                         }
4247
4248                         DocIterator cur = bv->cursor();
4249                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4250                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4251                                    << " cur:" << cur);
4252                         if (row == -1 || command.empty()) {
4253                                 dr.setMessage(_("Couldn't proceed."));
4254                                 break;
4255                         }
4256                         string texrow = convert<string>(row);
4257
4258                         command = subst(command, "$$n", texrow);
4259                         command = subst(command, "$$f", fulltexname);
4260                         command = subst(command, "$$t", texname);
4261                         command = subst(command, "$$o", outname);
4262
4263                         PathChanger p(path);
4264                         Systemcall one;
4265                         one.startscript(Systemcall::DontWait, command);
4266                         break;
4267                 }
4268
4269                 case LFUN_SPELLING_CONTINUOUSLY:
4270                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4271                         dr.screenUpdate(Update::Force);
4272                         break;
4273
4274                 default:
4275                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4276                         // let's try that:
4277                         dispatchToBufferView(cmd, dr);
4278                         break;
4279         }
4280
4281         // Part of automatic menu appearance feature.
4282         if (isFullScreen()) {
4283                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4284                         menuBar()->hide();
4285         }
4286
4287         // Need to update bv because many LFUNs here might have destroyed it
4288         bv = currentBufferView();
4289
4290         // Clear non-empty selections
4291         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4292         if (bv) {
4293                 Cursor & cur = bv->cursor();
4294                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4295                         cur.clearSelection();
4296                 }
4297         }
4298 }
4299
4300
4301 bool GuiView::lfunUiToggle(string const & ui_component)
4302 {
4303         if (ui_component == "scrollbar") {
4304                 // hide() is of no help
4305                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4306                         Qt::ScrollBarAlwaysOff)
4307
4308                         d.current_work_area_->setVerticalScrollBarPolicy(
4309                                 Qt::ScrollBarAsNeeded);
4310                 else
4311                         d.current_work_area_->setVerticalScrollBarPolicy(
4312                                 Qt::ScrollBarAlwaysOff);
4313         } else if (ui_component == "statusbar") {
4314                 statusBar()->setVisible(!statusBar()->isVisible());
4315         } else if (ui_component == "menubar") {
4316                 menuBar()->setVisible(!menuBar()->isVisible());
4317         } else
4318         if (ui_component == "frame") {
4319                 int l, t, r, b;
4320                 getContentsMargins(&l, &t, &r, &b);
4321                 //are the frames in default state?
4322                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4323                 if (l == 0) {
4324                         setContentsMargins(-2, -2, -2, -2);
4325                 } else {
4326                         setContentsMargins(0, 0, 0, 0);
4327                 }
4328         } else
4329         if (ui_component == "fullscreen") {
4330                 toggleFullScreen();
4331         } else
4332                 return false;
4333         return true;
4334 }
4335
4336
4337 void GuiView::toggleFullScreen()
4338 {
4339         if (isFullScreen()) {
4340                 for (int i = 0; i != d.splitter_->count(); ++i)
4341                         d.tabWorkArea(i)->setFullScreen(false);
4342                 setContentsMargins(0, 0, 0, 0);
4343                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4344                 restoreLayout();
4345                 menuBar()->show();
4346                 statusBar()->show();
4347         } else {
4348                 // bug 5274
4349                 hideDialogs("prefs", 0);
4350                 for (int i = 0; i != d.splitter_->count(); ++i)
4351                         d.tabWorkArea(i)->setFullScreen(true);
4352                 setContentsMargins(-2, -2, -2, -2);
4353                 saveLayout();
4354                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4355                 if (lyxrc.full_screen_statusbar)
4356                         statusBar()->hide();
4357                 if (lyxrc.full_screen_menubar)
4358                         menuBar()->hide();
4359                 if (lyxrc.full_screen_toolbars) {
4360                         ToolbarMap::iterator end = d.toolbars_.end();
4361                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4362                                 it->second->hide();
4363                 }
4364         }
4365
4366         // give dialogs like the TOC a chance to adapt
4367         updateDialogs();
4368 }
4369
4370
4371 Buffer const * GuiView::updateInset(Inset const * inset)
4372 {
4373         if (!inset)
4374                 return 0;
4375
4376         Buffer const * inset_buffer = &(inset->buffer());
4377
4378         for (int i = 0; i != d.splitter_->count(); ++i) {
4379                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4380                 if (!wa)
4381                         continue;
4382                 Buffer const * buffer = &(wa->bufferView().buffer());
4383                 if (inset_buffer == buffer)
4384                         wa->scheduleRedraw(true);
4385         }
4386         return inset_buffer;
4387 }
4388
4389
4390 void GuiView::restartCaret()
4391 {
4392         /* When we move around, or type, it's nice to be able to see
4393          * the caret immediately after the keypress.
4394          */
4395         if (d.current_work_area_)
4396                 d.current_work_area_->startBlinkingCaret();
4397
4398         // Take this occasion to update the other GUI elements.
4399         updateDialogs();
4400         updateStatusBar();
4401 }
4402
4403
4404 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4405 {
4406         if (d.current_work_area_)
4407                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4408 }
4409
4410 namespace {
4411
4412 // This list should be kept in sync with the list of insets in
4413 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4414 // dialog should have the same name as the inset.
4415 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4416 // docs in LyXAction.cpp.
4417
4418 char const * const dialognames[] = {
4419
4420 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4421 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4422 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4423 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4424 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4425 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4426 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4427 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4428
4429 char const * const * const end_dialognames =
4430         dialognames + (sizeof(dialognames) / sizeof(char *));
4431
4432 class cmpCStr {
4433 public:
4434         cmpCStr(char const * name) : name_(name) {}
4435         bool operator()(char const * other) {
4436                 return strcmp(other, name_) == 0;
4437         }
4438 private:
4439         char const * name_;
4440 };
4441
4442
4443 bool isValidName(string const & name)
4444 {
4445         return find_if(dialognames, end_dialognames,
4446                                 cmpCStr(name.c_str())) != end_dialognames;
4447 }
4448
4449 } // namespace
4450
4451
4452 void GuiView::resetDialogs()
4453 {
4454         // Make sure that no LFUN uses any GuiView.
4455         guiApp->setCurrentView(0);
4456         saveLayout();
4457         saveUISettings();
4458         menuBar()->clear();
4459         constructToolbars();
4460         guiApp->menus().fillMenuBar(menuBar(), this, false);
4461         d.layout_->updateContents(true);
4462         // Now update controls with current buffer.
4463         guiApp->setCurrentView(this);
4464         restoreLayout();
4465         restartCaret();
4466 }
4467
4468
4469 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4470 {
4471         if (!isValidName(name))
4472                 return 0;
4473
4474         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4475
4476         if (it != d.dialogs_.end()) {
4477                 if (hide_it)
4478                         it->second->hideView();
4479                 return it->second.get();
4480         }
4481
4482         Dialog * dialog = build(name);
4483         d.dialogs_[name].reset(dialog);
4484         if (lyxrc.allow_geometry_session)
4485                 dialog->restoreSession();
4486         if (hide_it)
4487                 dialog->hideView();
4488         return dialog;
4489 }
4490
4491
4492 void GuiView::showDialog(string const & name, string const & data,
4493         Inset * inset)
4494 {
4495         triggerShowDialog(toqstr(name), toqstr(data), inset);
4496 }
4497
4498
4499 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4500         Inset * inset)
4501 {
4502         if (d.in_show_)
4503                 return;
4504
4505         const string name = fromqstr(qname);
4506         const string data = fromqstr(qdata);
4507
4508         d.in_show_ = true;
4509         try {
4510                 Dialog * dialog = findOrBuild(name, false);
4511                 if (dialog) {
4512                         bool const visible = dialog->isVisibleView();
4513                         dialog->showData(data);
4514                         if (inset && currentBufferView())
4515                                 currentBufferView()->editInset(name, inset);
4516                         // We only set the focus to the new dialog if it was not yet
4517                         // visible in order not to change the existing previous behaviour
4518                         if (visible) {
4519                                 // activateWindow is needed for floating dockviews
4520                                 dialog->asQWidget()->raise();
4521                                 dialog->asQWidget()->activateWindow();
4522                                 dialog->asQWidget()->setFocus();
4523                         }
4524                 }
4525         }
4526         catch (ExceptionMessage const & ex) {
4527                 d.in_show_ = false;
4528                 throw ex;
4529         }
4530         d.in_show_ = false;
4531 }
4532
4533
4534 bool GuiView::isDialogVisible(string const & name) const
4535 {
4536         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4537         if (it == d.dialogs_.end())
4538                 return false;
4539         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4540 }
4541
4542
4543 void GuiView::hideDialog(string const & name, Inset * inset)
4544 {
4545         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4546         if (it == d.dialogs_.end())
4547                 return;
4548
4549         if (inset) {
4550                 if (!currentBufferView())
4551                         return;
4552                 if (inset != currentBufferView()->editedInset(name))
4553                         return;
4554         }
4555
4556         Dialog * const dialog = it->second.get();
4557         if (dialog->isVisibleView())
4558                 dialog->hideView();
4559         if (currentBufferView())
4560                 currentBufferView()->editInset(name, 0);
4561 }
4562
4563
4564 void GuiView::disconnectDialog(string const & name)
4565 {
4566         if (!isValidName(name))
4567                 return;
4568         if (currentBufferView())
4569                 currentBufferView()->editInset(name, 0);
4570 }
4571
4572
4573 void GuiView::hideAll() const
4574 {
4575         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4576         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4577
4578         for(; it != end; ++it)
4579                 it->second->hideView();
4580 }
4581
4582
4583 void GuiView::updateDialogs()
4584 {
4585         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4586         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4587
4588         for(; it != end; ++it) {
4589                 Dialog * dialog = it->second.get();
4590                 if (dialog) {
4591                         if (dialog->needBufferOpen() && !documentBufferView())
4592                                 hideDialog(fromqstr(dialog->name()), 0);
4593                         else if (dialog->isVisibleView())
4594                                 dialog->checkStatus();
4595                 }
4596         }
4597         updateToolbars();
4598         updateLayoutList();
4599 }
4600
4601 Dialog * createDialog(GuiView & lv, string const & name);
4602
4603 // will be replaced by a proper factory...
4604 Dialog * createGuiAbout(GuiView & lv);
4605 Dialog * createGuiBibtex(GuiView & lv);
4606 Dialog * createGuiChanges(GuiView & lv);
4607 Dialog * createGuiCharacter(GuiView & lv);
4608 Dialog * createGuiCitation(GuiView & lv);
4609 Dialog * createGuiCompare(GuiView & lv);
4610 Dialog * createGuiCompareHistory(GuiView & lv);
4611 Dialog * createGuiDelimiter(GuiView & lv);
4612 Dialog * createGuiDocument(GuiView & lv);
4613 Dialog * createGuiErrorList(GuiView & lv);
4614 Dialog * createGuiExternal(GuiView & lv);
4615 Dialog * createGuiGraphics(GuiView & lv);
4616 Dialog * createGuiInclude(GuiView & lv);
4617 Dialog * createGuiIndex(GuiView & lv);
4618 Dialog * createGuiListings(GuiView & lv);
4619 Dialog * createGuiLog(GuiView & lv);
4620 Dialog * createGuiMathMatrix(GuiView & lv);
4621 Dialog * createGuiNote(GuiView & lv);
4622 Dialog * createGuiParagraph(GuiView & lv);
4623 Dialog * createGuiPhantom(GuiView & lv);
4624 Dialog * createGuiPreferences(GuiView & lv);
4625 Dialog * createGuiPrint(GuiView & lv);
4626 Dialog * createGuiPrintindex(GuiView & lv);
4627 Dialog * createGuiRef(GuiView & lv);
4628 Dialog * createGuiSearch(GuiView & lv);
4629 Dialog * createGuiSearchAdv(GuiView & lv);
4630 Dialog * createGuiSendTo(GuiView & lv);
4631 Dialog * createGuiShowFile(GuiView & lv);
4632 Dialog * createGuiSpellchecker(GuiView & lv);
4633 Dialog * createGuiSymbols(GuiView & lv);
4634 Dialog * createGuiTabularCreate(GuiView & lv);
4635 Dialog * createGuiTexInfo(GuiView & lv);
4636 Dialog * createGuiToc(GuiView & lv);
4637 Dialog * createGuiThesaurus(GuiView & lv);
4638 Dialog * createGuiViewSource(GuiView & lv);
4639 Dialog * createGuiWrap(GuiView & lv);
4640 Dialog * createGuiProgressView(GuiView & lv);
4641
4642
4643
4644 Dialog * GuiView::build(string const & name)
4645 {
4646         LASSERT(isValidName(name), return 0);
4647
4648         Dialog * dialog = createDialog(*this, name);
4649         if (dialog)
4650                 return dialog;
4651
4652         if (name == "aboutlyx")
4653                 return createGuiAbout(*this);
4654         if (name == "bibtex")
4655                 return createGuiBibtex(*this);
4656         if (name == "changes")
4657                 return createGuiChanges(*this);
4658         if (name == "character")
4659                 return createGuiCharacter(*this);
4660         if (name == "citation")
4661                 return createGuiCitation(*this);
4662         if (name == "compare")
4663                 return createGuiCompare(*this);
4664         if (name == "comparehistory")
4665                 return createGuiCompareHistory(*this);
4666         if (name == "document")
4667                 return createGuiDocument(*this);
4668         if (name == "errorlist")
4669                 return createGuiErrorList(*this);
4670         if (name == "external")
4671                 return createGuiExternal(*this);
4672         if (name == "file")
4673                 return createGuiShowFile(*this);
4674         if (name == "findreplace")
4675                 return createGuiSearch(*this);
4676         if (name == "findreplaceadv")
4677                 return createGuiSearchAdv(*this);
4678         if (name == "graphics")
4679                 return createGuiGraphics(*this);
4680         if (name == "include")
4681                 return createGuiInclude(*this);
4682         if (name == "index")
4683                 return createGuiIndex(*this);
4684         if (name == "index_print")
4685                 return createGuiPrintindex(*this);
4686         if (name == "listings")
4687                 return createGuiListings(*this);
4688         if (name == "log")
4689                 return createGuiLog(*this);
4690         if (name == "mathdelimiter")
4691                 return createGuiDelimiter(*this);
4692         if (name == "mathmatrix")
4693                 return createGuiMathMatrix(*this);
4694         if (name == "note")
4695                 return createGuiNote(*this);
4696         if (name == "paragraph")
4697                 return createGuiParagraph(*this);
4698         if (name == "phantom")
4699                 return createGuiPhantom(*this);
4700         if (name == "prefs")
4701                 return createGuiPreferences(*this);
4702         if (name == "ref")
4703                 return createGuiRef(*this);
4704         if (name == "sendto")
4705                 return createGuiSendTo(*this);
4706         if (name == "spellchecker")
4707                 return createGuiSpellchecker(*this);
4708         if (name == "symbols")
4709                 return createGuiSymbols(*this);
4710         if (name == "tabularcreate")
4711                 return createGuiTabularCreate(*this);
4712         if (name == "texinfo")
4713                 return createGuiTexInfo(*this);
4714         if (name == "thesaurus")
4715                 return createGuiThesaurus(*this);
4716         if (name == "toc")
4717                 return createGuiToc(*this);
4718         if (name == "view-source")
4719                 return createGuiViewSource(*this);
4720         if (name == "wrap")
4721                 return createGuiWrap(*this);
4722         if (name == "progress")
4723                 return createGuiProgressView(*this);
4724
4725         return 0;
4726 }
4727
4728
4729 SEMenu::SEMenu(QWidget * parent)
4730 {
4731         QAction * action = addAction(qt_("Disable Shell Escape"));
4732         connect(action, SIGNAL(triggered()),
4733                 parent, SLOT(disableShellEscape()));
4734 }
4735
4736 } // namespace frontend
4737 } // namespace lyx
4738
4739 #include "moc_GuiView.cpp"