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