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