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