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