]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Check for hidden dirty child on close
[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                                 // Even in this case, children can be dirty (e.g.,
3073                                 // after a label change in the master, see #11405).
3074                                 // Therefore, check this.
3075                                 if (saveBufferIfNeeded(*child_buf, false)) {
3076                                         child_buf->removeAutosaveFile();
3077                                         theBufferList().release(child_buf);
3078                                 }
3079                         }
3080                 }
3081         }
3082         if (success) {
3083                 // goto bookmark to update bookmark pit.
3084                 // FIXME: we should update only the bookmarks related to this buffer!
3085                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3086                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3087                         guiApp->gotoBookmark(i+1, false, false);
3088
3089                 if (saveBufferIfNeeded(buf, false)) {
3090                         buf.removeAutosaveFile();
3091                         theBufferList().release(&buf);
3092                         return true;
3093                 }
3094         }
3095         // open all children again to avoid a crash because of dangling
3096         // pointers (bug 6603)
3097         buf.updateBuffer();
3098         return false;
3099 }
3100
3101
3102 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3103 {
3104         while (twa == d.currentTabWorkArea()) {
3105                 twa->setCurrentIndex(twa->count() - 1);
3106
3107                 GuiWorkArea * wa = twa->currentWorkArea();
3108                 Buffer & b = wa->bufferView().buffer();
3109
3110                 // We only want to close the buffer if the same buffer is not visible
3111                 // in another view, and if this is not a child and if we are closing
3112                 // a view (not a tabgroup).
3113                 bool const close_buffer =
3114                         !inOtherView(b) && !b.parent() && closing_;
3115
3116                 if (!closeWorkArea(wa, close_buffer))
3117                         return false;
3118         }
3119         return true;
3120 }
3121
3122
3123 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3124 {
3125         if (buf.isClean() || buf.paragraphs().empty())
3126                 return true;
3127
3128         // Switch to this Buffer.
3129         setBuffer(&buf);
3130
3131         docstring file;
3132         bool exists;
3133         // FIXME: Unicode?
3134         if (buf.isUnnamed()) {
3135                 file = from_utf8(buf.fileName().onlyFileName());
3136                 exists = false;
3137         } else {
3138                 FileName filename = buf.fileName();
3139                 filename.refresh();
3140                 file = filename.displayName(30);
3141                 exists = filename.exists();
3142         }
3143
3144         // Bring this window to top before asking questions.
3145         raise();
3146         activateWindow();
3147
3148         int ret;
3149         if (hiding && buf.isUnnamed()) {
3150                 docstring const text = bformat(_("The document %1$s has not been "
3151                                                  "saved yet.\n\nDo you want to save "
3152                                                  "the document?"), file);
3153                 ret = Alert::prompt(_("Save new document?"),
3154                         text, 0, 1, _("&Save"), _("&Cancel"));
3155                 if (ret == 1)
3156                         ++ret;
3157         } else {
3158                 docstring const text = exists ?
3159                         bformat(_("The document %1$s has unsaved changes."
3160                                   "\n\nDo you want to save the document or "
3161                                   "discard the changes?"), file) :
3162                         bformat(_("The document %1$s has not been saved yet."
3163                                   "\n\nDo you want to save the document or "
3164                                   "discard it entirely?"), file);
3165                 docstring const title = exists ?
3166                         _("Save changed document?") : _("Save document?");
3167                 ret = Alert::prompt(title, text, 0, 2,
3168                                     _("&Save"), _("&Discard"), _("&Cancel"));
3169         }
3170
3171         switch (ret) {
3172         case 0:
3173                 if (!saveBuffer(buf))
3174                         return false;
3175                 break;
3176         case 1:
3177                 // If we crash after this we could have no autosave file
3178                 // but I guess this is really improbable (Jug).
3179                 // Sometimes improbable things happen:
3180                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3181                 // buf.removeAutosaveFile();
3182                 if (hiding)
3183                         // revert all changes
3184                         reloadBuffer(buf);
3185                 buf.markClean();
3186                 break;
3187         case 2:
3188                 return false;
3189         }
3190         return true;
3191 }
3192
3193
3194 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3195 {
3196         Buffer & buf = wa->bufferView().buffer();
3197
3198         for (int i = 0; i != d.splitter_->count(); ++i) {
3199                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3200                 if (wa_ && wa_ != wa)
3201                         return true;
3202         }
3203         return inOtherView(buf);
3204 }
3205
3206
3207 bool GuiView::inOtherView(Buffer & buf)
3208 {
3209         QList<int> const ids = guiApp->viewIds();
3210
3211         for (int i = 0; i != ids.size(); ++i) {
3212                 if (id_ == ids[i])
3213                         continue;
3214
3215                 if (guiApp->view(ids[i]).workArea(buf))
3216                         return true;
3217         }
3218         return false;
3219 }
3220
3221
3222 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3223 {
3224         if (!documentBufferView())
3225                 return;
3226
3227         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3228                 Buffer * const curbuf = &documentBufferView()->buffer();
3229                 int nwa = twa->count();
3230                 for (int i = 0; i < nwa; ++i) {
3231                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3232                                 int next_index;
3233                                 if (np == NEXTBUFFER)
3234                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3235                                 else
3236                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3237                                 if (move)
3238                                         twa->moveTab(i, next_index);
3239                                 else
3240                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3241                                 break;
3242                         }
3243                 }
3244         }
3245 }
3246
3247
3248 /// make sure the document is saved
3249 static bool ensureBufferClean(Buffer * buffer)
3250 {
3251         LASSERT(buffer, return false);
3252         if (buffer->isClean() && !buffer->isUnnamed())
3253                 return true;
3254
3255         docstring const file = buffer->fileName().displayName(30);
3256         docstring title;
3257         docstring text;
3258         if (!buffer->isUnnamed()) {
3259                 text = bformat(_("The document %1$s has unsaved "
3260                                                  "changes.\n\nDo you want to save "
3261                                                  "the document?"), file);
3262                 title = _("Save changed document?");
3263
3264         } else {
3265                 text = bformat(_("The document %1$s has not been "
3266                                                  "saved yet.\n\nDo you want to save "
3267                                                  "the document?"), file);
3268                 title = _("Save new document?");
3269         }
3270         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3271
3272         if (ret == 0)
3273                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3274
3275         return buffer->isClean() && !buffer->isUnnamed();
3276 }
3277
3278
3279 bool GuiView::reloadBuffer(Buffer & buf)
3280 {
3281         currentBufferView()->cursor().reset();
3282         Buffer::ReadStatus status = buf.reload();
3283         return status == Buffer::ReadSuccess;
3284 }
3285
3286
3287 void GuiView::checkExternallyModifiedBuffers()
3288 {
3289         BufferList::iterator bit = theBufferList().begin();
3290         BufferList::iterator const bend = theBufferList().end();
3291         for (; bit != bend; ++bit) {
3292                 Buffer * buf = *bit;
3293                 if (buf->fileName().exists() && buf->isChecksumModified()) {
3294                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3295                                         " Reload now? Any local changes will be lost."),
3296                                         from_utf8(buf->absFileName()));
3297                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3298                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3299                         if (!ret)
3300                                 reloadBuffer(*buf);
3301                 }
3302         }
3303 }
3304
3305
3306 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3307 {
3308         Buffer * buffer = documentBufferView()
3309                 ? &(documentBufferView()->buffer()) : 0;
3310
3311         switch (cmd.action()) {
3312         case LFUN_VC_REGISTER:
3313                 if (!buffer || !ensureBufferClean(buffer))
3314                         break;
3315                 if (!buffer->lyxvc().inUse()) {
3316                         if (buffer->lyxvc().registrer()) {
3317                                 reloadBuffer(*buffer);
3318                                 dr.clearMessageUpdate();
3319                         }
3320                 }
3321                 break;
3322
3323         case LFUN_VC_RENAME:
3324         case LFUN_VC_COPY: {
3325                 if (!buffer || !ensureBufferClean(buffer))
3326                         break;
3327                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3328                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3329                                 // Some changes are not yet committed.
3330                                 // We test here and not in getStatus(), since
3331                                 // this test is expensive.
3332                                 string log;
3333                                 LyXVC::CommandResult ret =
3334                                         buffer->lyxvc().checkIn(log);
3335                                 dr.setMessage(log);
3336                                 if (ret == LyXVC::ErrorCommand ||
3337                                     ret == LyXVC::VCSuccess)
3338                                         reloadBuffer(*buffer);
3339                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3340                                         frontend::Alert::error(
3341                                                 _("Revision control error."),
3342                                                 _("Document could not be checked in."));
3343                                         break;
3344                                 }
3345                         }
3346                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3347                                 LV_VC_RENAME : LV_VC_COPY;
3348                         renameBuffer(*buffer, cmd.argument(), kind);
3349                 }
3350                 break;
3351         }
3352
3353         case LFUN_VC_CHECK_IN:
3354                 if (!buffer || !ensureBufferClean(buffer))
3355                         break;
3356                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3357                         string log;
3358                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3359                         dr.setMessage(log);
3360                         // Only skip reloading if the checkin was cancelled or
3361                         // an error occurred before the real checkin VCS command
3362                         // was executed, since the VCS might have changed the
3363                         // file even if it could not checkin successfully.
3364                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3365                                 reloadBuffer(*buffer);
3366                 }
3367                 break;
3368
3369         case LFUN_VC_CHECK_OUT:
3370                 if (!buffer || !ensureBufferClean(buffer))
3371                         break;
3372                 if (buffer->lyxvc().inUse()) {
3373                         dr.setMessage(buffer->lyxvc().checkOut());
3374                         reloadBuffer(*buffer);
3375                 }
3376                 break;
3377
3378         case LFUN_VC_LOCKING_TOGGLE:
3379                 LASSERT(buffer, return);
3380                 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3381                         break;
3382                 if (buffer->lyxvc().inUse()) {
3383                         string res = buffer->lyxvc().lockingToggle();
3384                         if (res.empty()) {
3385                                 frontend::Alert::error(_("Revision control error."),
3386                                 _("Error when setting the locking property."));
3387                         } else {
3388                                 dr.setMessage(res);
3389                                 reloadBuffer(*buffer);
3390                         }
3391                 }
3392                 break;
3393
3394         case LFUN_VC_REVERT:
3395                 LASSERT(buffer, return);
3396                 if (buffer->lyxvc().revert()) {
3397                         reloadBuffer(*buffer);
3398                         dr.clearMessageUpdate();
3399                 }
3400                 break;
3401
3402         case LFUN_VC_UNDO_LAST:
3403                 LASSERT(buffer, return);
3404                 buffer->lyxvc().undoLast();
3405                 reloadBuffer(*buffer);
3406                 dr.clearMessageUpdate();
3407                 break;
3408
3409         case LFUN_VC_REPO_UPDATE:
3410                 LASSERT(buffer, return);
3411                 if (ensureBufferClean(buffer)) {
3412                         dr.setMessage(buffer->lyxvc().repoUpdate());
3413                         checkExternallyModifiedBuffers();
3414                 }
3415                 break;
3416
3417         case LFUN_VC_COMMAND: {
3418                 string flag = cmd.getArg(0);
3419                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3420                         break;
3421                 docstring message;
3422                 if (contains(flag, 'M')) {
3423                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3424                                 break;
3425                 }
3426                 string path = cmd.getArg(1);
3427                 if (contains(path, "$$p") && buffer)
3428                         path = subst(path, "$$p", buffer->filePath());
3429                 LYXERR(Debug::LYXVC, "Directory: " << path);
3430                 FileName pp(path);
3431                 if (!pp.isReadableDirectory()) {
3432                         lyxerr << _("Directory is not accessible.") << endl;
3433                         break;
3434                 }
3435                 support::PathChanger p(pp);
3436
3437                 string command = cmd.getArg(2);
3438                 if (command.empty())
3439                         break;
3440                 if (buffer) {
3441                         command = subst(command, "$$i", buffer->absFileName());
3442                         command = subst(command, "$$p", buffer->filePath());
3443                 }
3444                 command = subst(command, "$$m", to_utf8(message));
3445                 LYXERR(Debug::LYXVC, "Command: " << command);
3446                 Systemcall one;
3447                 one.startscript(Systemcall::Wait, command);
3448
3449                 if (!buffer)
3450                         break;
3451                 if (contains(flag, 'I'))
3452                         buffer->markDirty();
3453                 if (contains(flag, 'R'))
3454                         reloadBuffer(*buffer);
3455
3456                 break;
3457                 }
3458
3459         case LFUN_VC_COMPARE: {
3460                 if (cmd.argument().empty()) {
3461                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3462                         break;
3463                 }
3464
3465                 string rev1 = cmd.getArg(0);
3466                 string f1, f2;
3467                 LATTEST(buffer)
3468
3469                 // f1
3470                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3471                         break;
3472
3473                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3474                         f2 = buffer->absFileName();
3475                 } else {
3476                         string rev2 = cmd.getArg(1);
3477                         if (rev2.empty())
3478                                 break;
3479                         // f2
3480                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3481                                 break;
3482                 }
3483
3484                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3485                                         f1 << "\n"  << f2 << "\n" );
3486                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3487                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3488                 break;
3489         }
3490
3491         default:
3492                 break;
3493         }
3494 }
3495
3496
3497 void GuiView::openChildDocument(string const & fname)
3498 {
3499         LASSERT(documentBufferView(), return);
3500         Buffer & buffer = documentBufferView()->buffer();
3501         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3502         documentBufferView()->saveBookmark(false);
3503         Buffer * child = 0;
3504         if (theBufferList().exists(filename)) {
3505                 child = theBufferList().getBuffer(filename);
3506                 setBuffer(child);
3507         } else {
3508                 message(bformat(_("Opening child document %1$s..."),
3509                         makeDisplayPath(filename.absFileName())));
3510                 child = loadDocument(filename, false);
3511         }
3512         // Set the parent name of the child document.
3513         // This makes insertion of citations and references in the child work,
3514         // when the target is in the parent or another child document.
3515         if (child)
3516                 child->setParent(&buffer);
3517 }
3518
3519
3520 bool GuiView::goToFileRow(string const & argument)
3521 {
3522         string file_name;
3523         int row = -1;
3524         size_t i = argument.find_last_of(' ');
3525         if (i != string::npos) {
3526                 file_name = os::internal_path(trim(argument.substr(0, i)));
3527                 istringstream is(argument.substr(i + 1));
3528                 is >> row;
3529                 if (is.fail())
3530                         i = string::npos;
3531         }
3532         if (i == string::npos) {
3533                 LYXERR0("Wrong argument: " << argument);
3534                 return false;
3535         }
3536         Buffer * buf = 0;
3537         string const abstmp = package().temp_dir().absFileName();
3538         string const realtmp = package().temp_dir().realPath();
3539         // We have to use os::path_prefix_is() here, instead of
3540         // simply prefixIs(), because the file name comes from
3541         // an external application and may need case adjustment.
3542         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3543                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3544                 // Needed by inverse dvi search. If it is a file
3545                 // in tmpdir, call the apropriated function.
3546                 // If tmpdir is a symlink, we may have the real
3547                 // path passed back, so we correct for that.
3548                 if (!prefixIs(file_name, abstmp))
3549                         file_name = subst(file_name, realtmp, abstmp);
3550                 buf = theBufferList().getBufferFromTmp(file_name);
3551         } else {
3552                 // Must replace extension of the file to be .lyx
3553                 // and get full path
3554                 FileName const s = fileSearch(string(),
3555                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3556                 // Either change buffer or load the file
3557                 if (theBufferList().exists(s))
3558                         buf = theBufferList().getBuffer(s);
3559                 else if (s.exists()) {
3560                         buf = loadDocument(s);
3561                         if (!buf)
3562                                 return false;
3563                 } else {
3564                         message(bformat(
3565                                         _("File does not exist: %1$s"),
3566                                         makeDisplayPath(file_name)));
3567                         return false;
3568                 }
3569         }
3570         if (!buf) {
3571                 message(bformat(
3572                         _("No buffer for file: %1$s."),
3573                         makeDisplayPath(file_name))
3574                 );
3575                 return false;
3576         }
3577         setBuffer(buf);
3578         bool success = documentBufferView()->setCursorFromRow(row);
3579         if (!success) {
3580                 LYXERR(Debug::LATEX,
3581                        "setCursorFromRow: invalid position for row " << row);
3582                 frontend::Alert::error(_("Inverse Search Failed"),
3583                                        _("Invalid position requested by inverse search.\n"
3584                                          "You may need to update the viewed document."));
3585         }
3586         return success;
3587 }
3588
3589
3590 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3591 {
3592         QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3593         menu->exec(QCursor::pos());
3594 }
3595
3596
3597 template<class T>
3598 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func,
3599                 Buffer const * orig, Buffer * clone, string const & format)
3600 {
3601         Buffer::ExportStatus const status = func(format);
3602
3603         // the cloning operation will have produced a clone of the entire set of
3604         // documents, starting from the master. so we must delete those.
3605         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3606         delete mbuf;
3607         busyBuffers.remove(orig);
3608         return status;
3609 }
3610
3611
3612 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(
3613                 Buffer const * orig, Buffer * clone, string const & format)
3614 {
3615         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
3616                         &Buffer::doExport;
3617         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3618 }
3619
3620
3621 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(
3622                 Buffer const * orig, Buffer * clone, string const & format)
3623 {
3624         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
3625                         &Buffer::doExport;
3626         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3627 }
3628
3629
3630 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(
3631                 Buffer const * orig, Buffer * clone, string const & format)
3632 {
3633         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const =
3634                         &Buffer::preview;
3635         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3636 }
3637
3638
3639 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3640                            string const & argument,
3641                            Buffer const * used_buffer,
3642                            docstring const & msg,
3643                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3644                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3645                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const,
3646                            bool allow_async)
3647 {
3648         if (!used_buffer)
3649                 return false;
3650
3651         string format = argument;
3652         if (format.empty())
3653                 format = used_buffer->params().getDefaultOutputFormat();
3654         processing_format = format;
3655         if (!msg.empty()) {
3656                 progress_->clearMessages();
3657                 gv_->message(msg);
3658         }
3659 #if EXPORT_in_THREAD
3660         if (allow_async) {
3661                 GuiViewPrivate::busyBuffers.insert(used_buffer);
3662                 Buffer * cloned_buffer = used_buffer->cloneWithChildren();
3663                 if (!cloned_buffer) {
3664                         Alert::error(_("Export Error"),
3665                                      _("Error cloning the Buffer."));
3666                         return false;
3667                 }
3668                 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3669                                         asyncFunc,
3670                                         used_buffer,
3671                                         cloned_buffer,
3672                                         format);
3673                 setPreviewFuture(f);
3674                 last_export_format = used_buffer->params().bufferFormat();
3675                 (void) syncFunc;
3676                 (void) previewFunc;
3677                 // We are asynchronous, so we don't know here anything about the success
3678                 return true;
3679         } else {
3680                 Buffer::ExportStatus status;
3681                 if (syncFunc) {
3682                         status = (used_buffer->*syncFunc)(format, false);
3683                 } else if (previewFunc) {
3684                         status = (used_buffer->*previewFunc)(format);
3685                 } else
3686                         return false;
3687                 handleExportStatus(gv_, status, format);
3688                 (void) asyncFunc;
3689                 return (status == Buffer::ExportSuccess
3690                                 || status == Buffer::PreviewSuccess);
3691         }
3692 #else
3693         (void) allow_async;
3694         Buffer::ExportStatus status;
3695         if (syncFunc) {
3696                 status = (used_buffer->*syncFunc)(format, true);
3697         } else if (previewFunc) {
3698                 status = (used_buffer->*previewFunc)(format);
3699         } else
3700                 return false;
3701         handleExportStatus(gv_, status, format);
3702         (void) asyncFunc;
3703         return (status == Buffer::ExportSuccess
3704                         || status == Buffer::PreviewSuccess);
3705 #endif
3706 }
3707
3708 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3709 {
3710         BufferView * bv = currentBufferView();
3711         LASSERT(bv, return);
3712
3713         // Let the current BufferView dispatch its own actions.
3714         bv->dispatch(cmd, dr);
3715         if (dr.dispatched()) {
3716                 if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3717                         updateDialog("document", "");
3718                 return;
3719         }
3720
3721         // Try with the document BufferView dispatch if any.
3722         BufferView * doc_bv = documentBufferView();
3723         if (doc_bv && doc_bv != bv) {
3724                 doc_bv->dispatch(cmd, dr);
3725                 if (dr.dispatched()) {
3726                         if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3727                                 updateDialog("document", "");
3728                         return;
3729                 }
3730         }
3731
3732         // Then let the current Cursor dispatch its own actions.
3733         bv->cursor().dispatch(cmd);
3734
3735         // update completion. We do it here and not in
3736         // processKeySym to avoid another redraw just for a
3737         // changed inline completion
3738         if (cmd.origin() == FuncRequest::KEYBOARD) {
3739                 if (cmd.action() == LFUN_SELF_INSERT
3740                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3741                         updateCompletion(bv->cursor(), true, true);
3742                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3743                         updateCompletion(bv->cursor(), false, true);
3744                 else
3745                         updateCompletion(bv->cursor(), false, false);
3746         }
3747
3748         dr = bv->cursor().result();
3749 }
3750
3751
3752 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3753 {
3754         BufferView * bv = currentBufferView();
3755         // By default we won't need any update.
3756         dr.screenUpdate(Update::None);
3757         // assume cmd will be dispatched
3758         dr.dispatched(true);
3759
3760         Buffer * doc_buffer = documentBufferView()
3761                 ? &(documentBufferView()->buffer()) : 0;
3762
3763         if (cmd.origin() == FuncRequest::TOC) {
3764                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3765                 // FIXME: do we need to pass a DispatchResult object here?
3766                 toc->doDispatch(bv->cursor(), cmd);
3767                 return;
3768         }
3769
3770         string const argument = to_utf8(cmd.argument());
3771
3772         switch(cmd.action()) {
3773                 case LFUN_BUFFER_CHILD_OPEN:
3774                         openChildDocument(to_utf8(cmd.argument()));
3775                         break;
3776
3777                 case LFUN_BUFFER_IMPORT:
3778                         importDocument(to_utf8(cmd.argument()));
3779                         break;
3780
3781                 case LFUN_MASTER_BUFFER_EXPORT:
3782                         if (doc_buffer)
3783                                 doc_buffer = const_cast<Buffer *>(doc_buffer->masterBuffer());
3784                         // fall through
3785                 case LFUN_BUFFER_EXPORT: {
3786                         if (!doc_buffer)
3787                                 break;
3788                         // GCC only sees strfwd.h when building merged
3789                         if (::lyx::operator==(cmd.argument(), "custom")) {
3790                                 // LFUN_MASTER_BUFFER_EXPORT is not enabled for this case,
3791                                 // so the following test should not be needed.
3792                                 // In principle, we could try to switch to such a view...
3793                                 // if (cmd.action() == LFUN_BUFFER_EXPORT)
3794                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3795                                 break;
3796                         }
3797
3798                         string const dest = cmd.getArg(1);
3799                         FileName target_dir;
3800                         if (!dest.empty() && FileName::isAbsolute(dest))
3801                                 target_dir = FileName(support::onlyPath(dest));
3802                         else
3803                                 target_dir = doc_buffer->fileName().onlyPath();
3804
3805                         string const format = (argument.empty() || argument == "default") ?
3806                                 doc_buffer->params().getDefaultOutputFormat() : argument;
3807
3808                         if ((dest.empty() && doc_buffer->isUnnamed())
3809                             || !target_dir.isDirWritable()) {
3810                                 exportBufferAs(*doc_buffer, from_utf8(format));
3811                                 break;
3812                         }
3813                         /* TODO/Review: Is it a problem to also export the children?
3814                                         See the update_unincluded flag */
3815                         d.asyncBufferProcessing(format,
3816                                                 doc_buffer,
3817                                                 _("Exporting ..."),
3818                                                 &GuiViewPrivate::exportAndDestroy,
3819                                                 &Buffer::doExport,
3820                                                 0, cmd.allowAsync());
3821                         // TODO Inform user about success
3822                         break;
3823                 }
3824
3825                 case LFUN_BUFFER_EXPORT_AS: {
3826                         LASSERT(doc_buffer, break);
3827                         docstring f = cmd.argument();
3828                         if (f.empty())
3829                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3830                         exportBufferAs(*doc_buffer, f);
3831                         break;
3832                 }
3833
3834                 case LFUN_BUFFER_UPDATE: {
3835                         d.asyncBufferProcessing(argument,
3836                                                 doc_buffer,
3837                                                 _("Exporting ..."),
3838                                                 &GuiViewPrivate::compileAndDestroy,
3839                                                 &Buffer::doExport,
3840                                                 0, cmd.allowAsync());
3841                         break;
3842                 }
3843                 case LFUN_BUFFER_VIEW: {
3844                         d.asyncBufferProcessing(argument,
3845                                                 doc_buffer,
3846                                                 _("Previewing ..."),
3847                                                 &GuiViewPrivate::previewAndDestroy,
3848                                                 0,
3849                                                 &Buffer::preview, cmd.allowAsync());
3850                         break;
3851                 }
3852                 case LFUN_MASTER_BUFFER_UPDATE: {
3853                         d.asyncBufferProcessing(argument,
3854                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3855                                                 docstring(),
3856                                                 &GuiViewPrivate::compileAndDestroy,
3857                                                 &Buffer::doExport,
3858                                                 0, cmd.allowAsync());
3859                         break;
3860                 }
3861                 case LFUN_MASTER_BUFFER_VIEW: {
3862                         d.asyncBufferProcessing(argument,
3863                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3864                                                 docstring(),
3865                                                 &GuiViewPrivate::previewAndDestroy,
3866                                                 0, &Buffer::preview, cmd.allowAsync());
3867                         break;
3868                 }
3869                 case LFUN_EXPORT_CANCEL: {
3870                         Systemcall::killscript();
3871                         break;
3872                 }
3873                 case LFUN_BUFFER_SWITCH: {
3874                         string const file_name = to_utf8(cmd.argument());
3875                         if (!FileName::isAbsolute(file_name)) {
3876                                 dr.setError(true);
3877                                 dr.setMessage(_("Absolute filename expected."));
3878                                 break;
3879                         }
3880
3881                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3882                         if (!buffer) {
3883                                 dr.setError(true);
3884                                 dr.setMessage(_("Document not loaded"));
3885                                 break;
3886                         }
3887
3888                         // Do we open or switch to the buffer in this view ?
3889                         if (workArea(*buffer)
3890                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3891                                 setBuffer(buffer);
3892                                 break;
3893                         }
3894
3895                         // Look for the buffer in other views
3896                         QList<int> const ids = guiApp->viewIds();
3897                         int i = 0;
3898                         for (; i != ids.size(); ++i) {
3899                                 GuiView & gv = guiApp->view(ids[i]);
3900                                 if (gv.workArea(*buffer)) {
3901                                         gv.raise();
3902                                         gv.activateWindow();
3903                                         gv.setFocus();
3904                                         gv.setBuffer(buffer);
3905                                         break;
3906                                 }
3907                         }
3908
3909                         // If necessary, open a new window as a last resort
3910                         if (i == ids.size()) {
3911                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3912                                 lyx::dispatch(cmd);
3913                         }
3914                         break;
3915                 }
3916
3917                 case LFUN_BUFFER_NEXT:
3918                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3919                         break;
3920
3921                 case LFUN_BUFFER_MOVE_NEXT:
3922                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3923                         break;
3924
3925                 case LFUN_BUFFER_PREVIOUS:
3926                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3927                         break;
3928
3929                 case LFUN_BUFFER_MOVE_PREVIOUS:
3930                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3931                         break;
3932
3933                 case LFUN_BUFFER_CHKTEX:
3934                         LASSERT(doc_buffer, break);
3935                         doc_buffer->runChktex();
3936                         break;
3937
3938                 case LFUN_COMMAND_EXECUTE: {
3939                         command_execute_ = true;
3940                         minibuffer_focus_ = true;
3941                         break;
3942                 }
3943                 case LFUN_DROP_LAYOUTS_CHOICE:
3944                         d.layout_->showPopup();
3945                         break;
3946
3947                 case LFUN_MENU_OPEN:
3948                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3949                                 menu->exec(QCursor::pos());
3950                         break;
3951
3952                 case LFUN_FILE_INSERT:
3953                         insertLyXFile(cmd.argument());
3954                         break;
3955
3956                 case LFUN_FILE_INSERT_PLAINTEXT:
3957                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3958                         string const fname = to_utf8(cmd.argument());
3959                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3960                                 dr.setMessage(_("Absolute filename expected."));
3961                                 break;
3962                         }
3963
3964                         FileName filename(fname);
3965                         if (fname.empty()) {
3966                                 FileDialog dlg(qt_("Select file to insert"));
3967
3968                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3969                                         QStringList(qt_("All Files (*)")));
3970
3971                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3972                                         dr.setMessage(_("Canceled."));
3973                                         break;
3974                                 }
3975
3976                                 filename.set(fromqstr(result.second));
3977                         }
3978
3979                         if (bv) {
3980                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3981                                 bv->dispatch(new_cmd, dr);
3982                         }
3983                         break;
3984                 }
3985
3986                 case LFUN_BUFFER_RELOAD: {
3987                         LASSERT(doc_buffer, break);
3988
3989                         // drop changes?
3990                         bool drop = (cmd.argument() == "dump");
3991
3992                         int ret = 0;
3993                         if (!drop && !doc_buffer->isClean()) {
3994                                 docstring const file =
3995                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3996                                 if (doc_buffer->notifiesExternalModification()) {
3997                                         docstring text = _("The current version will be lost. "
3998                                             "Are you sure you want to load the version on disk "
3999                                             "of the document %1$s?");
4000                                         ret = Alert::prompt(_("Reload saved document?"),
4001                                                             bformat(text, file), 1, 1,
4002                                                             _("&Reload"), _("&Cancel"));
4003                                 } else {
4004                                         docstring text = _("Any changes will be lost. "
4005                                             "Are you sure you want to revert to the saved version "
4006                                             "of the document %1$s?");
4007                                         ret = Alert::prompt(_("Revert to saved document?"),
4008                                                             bformat(text, file), 1, 1,
4009                                                             _("&Revert"), _("&Cancel"));
4010                                 }
4011                         }
4012
4013                         if (ret == 0) {
4014                                 doc_buffer->markClean();
4015                                 reloadBuffer(*doc_buffer);
4016                                 dr.forceBufferUpdate();
4017                         }
4018                         break;
4019                 }
4020
4021                 case LFUN_BUFFER_WRITE:
4022                         LASSERT(doc_buffer, break);
4023                         saveBuffer(*doc_buffer);
4024                         break;
4025
4026                 case LFUN_BUFFER_WRITE_AS:
4027                         LASSERT(doc_buffer, break);
4028                         renameBuffer(*doc_buffer, cmd.argument());
4029                         break;
4030
4031                 case LFUN_BUFFER_WRITE_ALL: {
4032                         Buffer * first = theBufferList().first();
4033                         if (!first)
4034                                 break;
4035                         message(_("Saving all documents..."));
4036                         // We cannot use a for loop as the buffer list cycles.
4037                         Buffer * b = first;
4038                         do {
4039                                 if (!b->isClean()) {
4040                                         saveBuffer(*b);
4041                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
4042                                 }
4043                                 b = theBufferList().next(b);
4044                         } while (b != first);
4045                         dr.setMessage(_("All documents saved."));
4046                         break;
4047                 }
4048
4049                 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
4050                         LASSERT(doc_buffer, break);
4051                         doc_buffer->clearExternalModification();
4052                         break;
4053
4054                 case LFUN_BUFFER_CLOSE:
4055                         closeBuffer();
4056                         break;
4057
4058                 case LFUN_BUFFER_CLOSE_ALL:
4059                         closeBufferAll();
4060                         break;
4061
4062                 case LFUN_DEVEL_MODE_TOGGLE:
4063                         devel_mode_ = !devel_mode_;
4064                         if (devel_mode_)
4065                                 dr.setMessage(_("Developer mode is now enabled."));
4066                         else
4067                                 dr.setMessage(_("Developer mode is now disabled."));
4068                         break;
4069
4070                 case LFUN_TOOLBAR_TOGGLE: {
4071                         string const name = cmd.getArg(0);
4072                         if (GuiToolbar * t = toolbar(name))
4073                                 t->toggle();
4074                         break;
4075                 }
4076
4077                 case LFUN_TOOLBAR_MOVABLE: {
4078                         string const name = cmd.getArg(0);
4079                         if (name == "*") {
4080                                 // toggle (all) toolbars movablility
4081                                 toolbarsMovable_ = !toolbarsMovable_;
4082                                 for (ToolbarInfo const & ti : guiApp->toolbars()) {
4083                                         GuiToolbar * tb = toolbar(ti.name);
4084                                         if (tb && tb->isMovable() != toolbarsMovable_)
4085                                                 // toggle toolbar movablity if it does not fit lock
4086                                                 // (all) toolbars positions state silent = true, since
4087                                                 // status bar notifications are slow
4088                                                 tb->movable(true);
4089                                 }
4090                                 if (toolbarsMovable_)
4091                                         dr.setMessage(_("Toolbars unlocked."));
4092                                 else
4093                                         dr.setMessage(_("Toolbars locked."));
4094                         } else if (GuiToolbar * t = toolbar(name)) {
4095                                 // toggle current toolbar movablity
4096                                 t->movable();
4097                                 // update lock (all) toolbars positions
4098                                 updateLockToolbars();
4099                         }
4100                         break;
4101                 }
4102
4103                 case LFUN_ICON_SIZE: {
4104                         QSize size = d.iconSize(cmd.argument());
4105                         setIconSize(size);
4106                         dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
4107                                                 size.width(), size.height()));
4108                         break;
4109                 }
4110
4111                 case LFUN_DIALOG_UPDATE: {
4112                         string const name = to_utf8(cmd.argument());
4113                         if (name == "prefs" || name == "document")
4114                                 updateDialog(name, string());
4115                         else if (name == "paragraph")
4116                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4117                         else if (currentBufferView()) {
4118                                 Inset * inset = currentBufferView()->editedInset(name);
4119                                 // Can only update a dialog connected to an existing inset
4120                                 if (inset) {
4121                                         // FIXME: get rid of this indirection; GuiView ask the inset
4122                                         // if he is kind enough to update itself...
4123                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4124                                         //FIXME: pass DispatchResult here?
4125                                         inset->dispatch(currentBufferView()->cursor(), fr);
4126                                 }
4127                         }
4128                         break;
4129                 }
4130
4131                 case LFUN_DIALOG_TOGGLE: {
4132                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4133                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4134                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
4135                         break;
4136                 }
4137
4138                 case LFUN_DIALOG_DISCONNECT_INSET:
4139                         disconnectDialog(to_utf8(cmd.argument()));
4140                         break;
4141
4142                 case LFUN_DIALOG_HIDE: {
4143                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4144                         break;
4145                 }
4146
4147                 case LFUN_DIALOG_SHOW: {
4148                         string const name = cmd.getArg(0);
4149                         string sdata = trim(to_utf8(cmd.argument()).substr(name.size()));
4150
4151                         if (name == "latexlog") {
4152                                 // gettatus checks that
4153                                 LATTEST(doc_buffer);
4154                                 Buffer::LogType type;
4155                                 string const logfile = doc_buffer->logName(&type);
4156                                 switch (type) {
4157                                 case Buffer::latexlog:
4158                                         sdata = "latex ";
4159                                         break;
4160                                 case Buffer::buildlog:
4161                                         sdata = "literate ";
4162                                         break;
4163                                 }
4164                                 sdata += Lexer::quoteString(logfile);
4165                                 showDialog("log", sdata);
4166                         } else if (name == "vclog") {
4167                                 // getStatus checks that
4168                                 LATTEST(doc_buffer);
4169                                 string const sdata2 = "vc " +
4170                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4171                                 showDialog("log", sdata2);
4172                         } else if (name == "symbols") {
4173                                 sdata = bv->cursor().getEncoding()->name();
4174                                 if (!sdata.empty())
4175                                         showDialog("symbols", sdata);
4176                         // bug 5274
4177                         } else if (name == "prefs" && isFullScreen()) {
4178                                 lfunUiToggle("fullscreen");
4179                                 showDialog("prefs", sdata);
4180                         } else
4181                                 showDialog(name, sdata);
4182                         break;
4183                 }
4184
4185                 case LFUN_MESSAGE:
4186                         dr.setMessage(cmd.argument());
4187                         break;
4188
4189                 case LFUN_UI_TOGGLE: {
4190                         string arg = cmd.getArg(0);
4191                         if (!lfunUiToggle(arg)) {
4192                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4193                                 dr.setMessage(bformat(msg, from_utf8(arg)));
4194                         }
4195                         // Make sure the keyboard focus stays in the work area.
4196                         setFocus();
4197                         break;
4198                 }
4199
4200                 case LFUN_VIEW_SPLIT: {
4201                         LASSERT(doc_buffer, break);
4202                         string const orientation = cmd.getArg(0);
4203                         d.splitter_->setOrientation(orientation == "vertical"
4204                                 ? Qt::Vertical : Qt::Horizontal);
4205                         TabWorkArea * twa = addTabWorkArea();
4206                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4207                         setCurrentWorkArea(wa);
4208                         break;
4209                 }
4210                 case LFUN_TAB_GROUP_CLOSE:
4211                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4212                                 closeTabWorkArea(twa);
4213                                 d.current_work_area_ = 0;
4214                                 twa = d.currentTabWorkArea();
4215                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4216                                 if (twa) {
4217                                         // Make sure the work area is up to date.
4218                                         setCurrentWorkArea(twa->currentWorkArea());
4219                                 } else {
4220                                         setCurrentWorkArea(0);
4221                                 }
4222                         }
4223                         break;
4224
4225                 case LFUN_VIEW_CLOSE:
4226                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4227                                 closeWorkArea(twa->currentWorkArea());
4228                                 d.current_work_area_ = 0;
4229                                 twa = d.currentTabWorkArea();
4230                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4231                                 if (twa) {
4232                                         // Make sure the work area is up to date.
4233                                         setCurrentWorkArea(twa->currentWorkArea());
4234                                 } else {
4235                                         setCurrentWorkArea(0);
4236                                 }
4237                         }
4238                         break;
4239
4240                 case LFUN_COMPLETION_INLINE:
4241                         if (d.current_work_area_)
4242                                 d.current_work_area_->completer().showInline();
4243                         break;
4244
4245                 case LFUN_COMPLETION_POPUP:
4246                         if (d.current_work_area_)
4247                                 d.current_work_area_->completer().showPopup();
4248                         break;
4249
4250
4251                 case LFUN_COMPLETE:
4252                         if (d.current_work_area_)
4253                                 d.current_work_area_->completer().tab();
4254                         break;
4255
4256                 case LFUN_COMPLETION_CANCEL:
4257                         if (d.current_work_area_) {
4258                                 if (d.current_work_area_->completer().popupVisible())
4259                                         d.current_work_area_->completer().hidePopup();
4260                                 else
4261                                         d.current_work_area_->completer().hideInline();
4262                         }
4263                         break;
4264
4265                 case LFUN_COMPLETION_ACCEPT:
4266                         if (d.current_work_area_)
4267                                 d.current_work_area_->completer().activate();
4268                         break;
4269
4270                 case LFUN_BUFFER_ZOOM_IN:
4271                 case LFUN_BUFFER_ZOOM_OUT:
4272                 case LFUN_BUFFER_ZOOM: {
4273                         if (cmd.argument().empty()) {
4274                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4275                                         zoom_ratio_ = 1.0;
4276                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4277                                         zoom_ratio_ += 0.1;
4278                                 else
4279                                         zoom_ratio_ -= 0.1;
4280                         } else {
4281                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4282                                         zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4283                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4284                                         zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4285                                 else
4286                                         zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4287                         }
4288
4289                         // Actual zoom value: default zoom + fractional extra value
4290                         int zoom = lyxrc.defaultZoom * zoom_ratio_;
4291                         if (zoom < static_cast<int>(zoom_min_))
4292                                 zoom = zoom_min_;
4293
4294                         lyxrc.currentZoom = zoom;
4295
4296                         dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4297                                               lyxrc.currentZoom, lyxrc.defaultZoom));
4298
4299                         guiApp->fontLoader().update();
4300                         dr.screenUpdate(Update::Force | Update::FitCursor);
4301                         break;
4302                 }
4303
4304                 case LFUN_VC_REGISTER:
4305                 case LFUN_VC_RENAME:
4306                 case LFUN_VC_COPY:
4307                 case LFUN_VC_CHECK_IN:
4308                 case LFUN_VC_CHECK_OUT:
4309                 case LFUN_VC_REPO_UPDATE:
4310                 case LFUN_VC_LOCKING_TOGGLE:
4311                 case LFUN_VC_REVERT:
4312                 case LFUN_VC_UNDO_LAST:
4313                 case LFUN_VC_COMMAND:
4314                 case LFUN_VC_COMPARE:
4315                         dispatchVC(cmd, dr);
4316                         break;
4317
4318                 case LFUN_SERVER_GOTO_FILE_ROW:
4319                         if(goToFileRow(to_utf8(cmd.argument())))
4320                                 dr.screenUpdate(Update::Force | Update::FitCursor);
4321                         break;
4322
4323                 case LFUN_LYX_ACTIVATE:
4324                         activateWindow();
4325                         break;
4326
4327                 case LFUN_FORWARD_SEARCH: {
4328                         // it seems safe to assume we have a document buffer, since
4329                         // getStatus wants one.
4330                         LATTEST(doc_buffer);
4331                         Buffer const * doc_master = doc_buffer->masterBuffer();
4332                         FileName const path(doc_master->temppath());
4333                         string const texname = doc_master->isChild(doc_buffer)
4334                                 ? DocFileName(changeExtension(
4335                                         doc_buffer->absFileName(),
4336                                                 "tex")).mangledFileName()
4337                                 : doc_buffer->latexName();
4338                         string const fulltexname =
4339                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4340                         string const mastername =
4341                                 removeExtension(doc_master->latexName());
4342                         FileName const dviname(addName(path.absFileName(),
4343                                         addExtension(mastername, "dvi")));
4344                         FileName const pdfname(addName(path.absFileName(),
4345                                         addExtension(mastername, "pdf")));
4346                         bool const have_dvi = dviname.exists();
4347                         bool const have_pdf = pdfname.exists();
4348                         if (!have_dvi && !have_pdf) {
4349                                 dr.setMessage(_("Please, preview the document first."));
4350                                 break;
4351                         }
4352                         string outname = dviname.onlyFileName();
4353                         string command = lyxrc.forward_search_dvi;
4354                         if (!have_dvi || (have_pdf &&
4355                             pdfname.lastModified() > dviname.lastModified())) {
4356                                 outname = pdfname.onlyFileName();
4357                                 command = lyxrc.forward_search_pdf;
4358                         }
4359
4360                         DocIterator cur = bv->cursor();
4361                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4362                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4363                                    << " cur:" << cur);
4364                         if (row == -1 || command.empty()) {
4365                                 dr.setMessage(_("Couldn't proceed."));
4366                                 break;
4367                         }
4368                         string texrow = convert<string>(row);
4369
4370                         command = subst(command, "$$n", texrow);
4371                         command = subst(command, "$$f", fulltexname);
4372                         command = subst(command, "$$t", texname);
4373                         command = subst(command, "$$o", outname);
4374
4375                         PathChanger p(path);
4376                         Systemcall one;
4377                         one.startscript(Systemcall::DontWait, command);
4378                         break;
4379                 }
4380
4381                 case LFUN_SPELLING_CONTINUOUSLY:
4382                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4383                         dr.screenUpdate(Update::Force);
4384                         break;
4385
4386                 default:
4387                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4388                         // let's try that:
4389                         dispatchToBufferView(cmd, dr);
4390                         break;
4391         }
4392
4393         // Part of automatic menu appearance feature.
4394         if (isFullScreen()) {
4395                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4396                         menuBar()->hide();
4397         }
4398
4399         // Need to update bv because many LFUNs here might have destroyed it
4400         bv = currentBufferView();
4401
4402         // Clear non-empty selections
4403         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4404         if (bv) {
4405                 Cursor & cur = bv->cursor();
4406                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4407                         cur.clearSelection();
4408                 }
4409         }
4410 }
4411
4412
4413 bool GuiView::lfunUiToggle(string const & ui_component)
4414 {
4415         if (ui_component == "scrollbar") {
4416                 // hide() is of no help
4417                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4418                         Qt::ScrollBarAlwaysOff)
4419
4420                         d.current_work_area_->setVerticalScrollBarPolicy(
4421                                 Qt::ScrollBarAsNeeded);
4422                 else
4423                         d.current_work_area_->setVerticalScrollBarPolicy(
4424                                 Qt::ScrollBarAlwaysOff);
4425         } else if (ui_component == "statusbar") {
4426                 statusBar()->setVisible(!statusBar()->isVisible());
4427         } else if (ui_component == "menubar") {
4428                 menuBar()->setVisible(!menuBar()->isVisible());
4429         } else
4430         if (ui_component == "frame") {
4431                 int l, t, r, b;
4432                 getContentsMargins(&l, &t, &r, &b);
4433                 //are the frames in default state?
4434                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4435                 if (l == 0) {
4436                         setContentsMargins(-2, -2, -2, -2);
4437                 } else {
4438                         setContentsMargins(0, 0, 0, 0);
4439                 }
4440         } else
4441         if (ui_component == "fullscreen") {
4442                 toggleFullScreen();
4443         } else
4444                 return false;
4445         return true;
4446 }
4447
4448
4449 void GuiView::toggleFullScreen()
4450 {
4451         if (isFullScreen()) {
4452                 for (int i = 0; i != d.splitter_->count(); ++i)
4453                         d.tabWorkArea(i)->setFullScreen(false);
4454                 setContentsMargins(0, 0, 0, 0);
4455                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4456                 restoreLayout();
4457                 menuBar()->show();
4458                 statusBar()->show();
4459         } else {
4460                 // bug 5274
4461                 hideDialogs("prefs", 0);
4462                 for (int i = 0; i != d.splitter_->count(); ++i)
4463                         d.tabWorkArea(i)->setFullScreen(true);
4464                 setContentsMargins(-2, -2, -2, -2);
4465                 saveLayout();
4466                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4467                 if (lyxrc.full_screen_statusbar)
4468                         statusBar()->hide();
4469                 if (lyxrc.full_screen_menubar)
4470                         menuBar()->hide();
4471                 if (lyxrc.full_screen_toolbars) {
4472                         ToolbarMap::iterator end = d.toolbars_.end();
4473                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4474                                 it->second->hide();
4475                 }
4476         }
4477
4478         // give dialogs like the TOC a chance to adapt
4479         updateDialogs();
4480 }
4481
4482
4483 Buffer const * GuiView::updateInset(Inset const * inset)
4484 {
4485         if (!inset)
4486                 return 0;
4487
4488         Buffer const * inset_buffer = &(inset->buffer());
4489
4490         for (int i = 0; i != d.splitter_->count(); ++i) {
4491                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4492                 if (!wa)
4493                         continue;
4494                 Buffer const * buffer = &(wa->bufferView().buffer());
4495                 if (inset_buffer == buffer)
4496                         wa->scheduleRedraw(true);
4497         }
4498         return inset_buffer;
4499 }
4500
4501
4502 void GuiView::restartCaret()
4503 {
4504         /* When we move around, or type, it's nice to be able to see
4505          * the caret immediately after the keypress.
4506          */
4507         if (d.current_work_area_)
4508                 d.current_work_area_->startBlinkingCaret();
4509
4510         // Take this occasion to update the other GUI elements.
4511         updateDialogs();
4512         updateStatusBar();
4513 }
4514
4515
4516 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4517 {
4518         if (d.current_work_area_)
4519                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4520 }
4521
4522 namespace {
4523
4524 // This list should be kept in sync with the list of insets in
4525 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4526 // dialog should have the same name as the inset.
4527 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4528 // docs in LyXAction.cpp.
4529
4530 char const * const dialognames[] = {
4531
4532 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4533 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4534 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4535 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4536 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4537 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4538 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4539 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4540
4541 char const * const * const end_dialognames =
4542         dialognames + (sizeof(dialognames) / sizeof(char *));
4543
4544 class cmpCStr {
4545 public:
4546         cmpCStr(char const * name) : name_(name) {}
4547         bool operator()(char const * other) {
4548                 return strcmp(other, name_) == 0;
4549         }
4550 private:
4551         char const * name_;
4552 };
4553
4554
4555 bool isValidName(string const & name)
4556 {
4557         return find_if(dialognames, end_dialognames,
4558                                 cmpCStr(name.c_str())) != end_dialognames;
4559 }
4560
4561 } // namespace
4562
4563
4564 void GuiView::resetDialogs()
4565 {
4566         // Make sure that no LFUN uses any GuiView.
4567         guiApp->setCurrentView(0);
4568         saveLayout();
4569         saveUISettings();
4570         menuBar()->clear();
4571         constructToolbars();
4572         guiApp->menus().fillMenuBar(menuBar(), this, false);
4573         d.layout_->updateContents(true);
4574         // Now update controls with current buffer.
4575         guiApp->setCurrentView(this);
4576         restoreLayout();
4577         restartCaret();
4578 }
4579
4580
4581 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4582 {
4583         if (!isValidName(name))
4584                 return 0;
4585
4586         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4587
4588         if (it != d.dialogs_.end()) {
4589                 if (hide_it)
4590                         it->second->hideView();
4591                 return it->second.get();
4592         }
4593
4594         Dialog * dialog = build(name);
4595         d.dialogs_[name].reset(dialog);
4596         if (lyxrc.allow_geometry_session)
4597                 dialog->restoreSession();
4598         if (hide_it)
4599                 dialog->hideView();
4600         return dialog;
4601 }
4602
4603
4604 void GuiView::showDialog(string const & name, string const & sdata,
4605         Inset * inset)
4606 {
4607         triggerShowDialog(toqstr(name), toqstr(sdata), inset);
4608 }
4609
4610
4611 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4612         Inset * inset)
4613 {
4614         if (d.in_show_)
4615                 return;
4616
4617         const string name = fromqstr(qname);
4618         const string sdata = fromqstr(qdata);
4619
4620         d.in_show_ = true;
4621         try {
4622                 Dialog * dialog = findOrBuild(name, false);
4623                 if (dialog) {
4624                         bool const visible = dialog->isVisibleView();
4625                         dialog->showData(sdata);
4626                         if (inset && currentBufferView())
4627                                 currentBufferView()->editInset(name, inset);
4628                         // We only set the focus to the new dialog if it was not yet
4629                         // visible in order not to change the existing previous behaviour
4630                         if (visible) {
4631                                 // activateWindow is needed for floating dockviews
4632                                 dialog->asQWidget()->raise();
4633                                 dialog->asQWidget()->activateWindow();
4634                                 dialog->asQWidget()->setFocus();
4635                         }
4636                 }
4637         }
4638         catch (ExceptionMessage const & ex) {
4639                 d.in_show_ = false;
4640                 throw ex;
4641         }
4642         d.in_show_ = false;
4643 }
4644
4645
4646 bool GuiView::isDialogVisible(string const & name) const
4647 {
4648         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4649         if (it == d.dialogs_.end())
4650                 return false;
4651         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4652 }
4653
4654
4655 void GuiView::hideDialog(string const & name, Inset * inset)
4656 {
4657         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4658         if (it == d.dialogs_.end())
4659                 return;
4660
4661         if (inset) {
4662                 if (!currentBufferView())
4663                         return;
4664                 if (inset != currentBufferView()->editedInset(name))
4665                         return;
4666         }
4667
4668         Dialog * const dialog = it->second.get();
4669         if (dialog->isVisibleView())
4670                 dialog->hideView();
4671         if (currentBufferView())
4672                 currentBufferView()->editInset(name, 0);
4673 }
4674
4675
4676 void GuiView::disconnectDialog(string const & name)
4677 {
4678         if (!isValidName(name))
4679                 return;
4680         if (currentBufferView())
4681                 currentBufferView()->editInset(name, 0);
4682 }
4683
4684
4685 void GuiView::hideAll() const
4686 {
4687         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4688         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4689
4690         for(; it != end; ++it)
4691                 it->second->hideView();
4692 }
4693
4694
4695 void GuiView::updateDialogs()
4696 {
4697         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4698         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4699
4700         for(; it != end; ++it) {
4701                 Dialog * dialog = it->second.get();
4702                 if (dialog) {
4703                         if (dialog->needBufferOpen() && !documentBufferView())
4704                                 hideDialog(fromqstr(dialog->name()), 0);
4705                         else if (dialog->isVisibleView())
4706                                 dialog->checkStatus();
4707                 }
4708         }
4709         updateToolbars();
4710         updateLayoutList();
4711 }
4712
4713 Dialog * createDialog(GuiView & lv, string const & name);
4714
4715 // will be replaced by a proper factory...
4716 Dialog * createGuiAbout(GuiView & lv);
4717 Dialog * createGuiBibtex(GuiView & lv);
4718 Dialog * createGuiChanges(GuiView & lv);
4719 Dialog * createGuiCharacter(GuiView & lv);
4720 Dialog * createGuiCitation(GuiView & lv);
4721 Dialog * createGuiCompare(GuiView & lv);
4722 Dialog * createGuiCompareHistory(GuiView & lv);
4723 Dialog * createGuiDelimiter(GuiView & lv);
4724 Dialog * createGuiDocument(GuiView & lv);
4725 Dialog * createGuiErrorList(GuiView & lv);
4726 Dialog * createGuiExternal(GuiView & lv);
4727 Dialog * createGuiGraphics(GuiView & lv);
4728 Dialog * createGuiInclude(GuiView & lv);
4729 Dialog * createGuiIndex(GuiView & lv);
4730 Dialog * createGuiListings(GuiView & lv);
4731 Dialog * createGuiLog(GuiView & lv);
4732 Dialog * createGuiMathMatrix(GuiView & lv);
4733 Dialog * createGuiNote(GuiView & lv);
4734 Dialog * createGuiParagraph(GuiView & lv);
4735 Dialog * createGuiPhantom(GuiView & lv);
4736 Dialog * createGuiPreferences(GuiView & lv);
4737 Dialog * createGuiPrint(GuiView & lv);
4738 Dialog * createGuiPrintindex(GuiView & lv);
4739 Dialog * createGuiRef(GuiView & lv);
4740 Dialog * createGuiSearch(GuiView & lv);
4741 Dialog * createGuiSearchAdv(GuiView & lv);
4742 Dialog * createGuiSendTo(GuiView & lv);
4743 Dialog * createGuiShowFile(GuiView & lv);
4744 Dialog * createGuiSpellchecker(GuiView & lv);
4745 Dialog * createGuiSymbols(GuiView & lv);
4746 Dialog * createGuiTabularCreate(GuiView & lv);
4747 Dialog * createGuiTexInfo(GuiView & lv);
4748 Dialog * createGuiToc(GuiView & lv);
4749 Dialog * createGuiThesaurus(GuiView & lv);
4750 Dialog * createGuiViewSource(GuiView & lv);
4751 Dialog * createGuiWrap(GuiView & lv);
4752 Dialog * createGuiProgressView(GuiView & lv);
4753
4754
4755
4756 Dialog * GuiView::build(string const & name)
4757 {
4758         LASSERT(isValidName(name), return 0);
4759
4760         Dialog * dialog = createDialog(*this, name);
4761         if (dialog)
4762                 return dialog;
4763
4764         if (name == "aboutlyx")
4765                 return createGuiAbout(*this);
4766         if (name == "bibtex")
4767                 return createGuiBibtex(*this);
4768         if (name == "changes")
4769                 return createGuiChanges(*this);
4770         if (name == "character")
4771                 return createGuiCharacter(*this);
4772         if (name == "citation")
4773                 return createGuiCitation(*this);
4774         if (name == "compare")
4775                 return createGuiCompare(*this);
4776         if (name == "comparehistory")
4777                 return createGuiCompareHistory(*this);
4778         if (name == "document")
4779                 return createGuiDocument(*this);
4780         if (name == "errorlist")
4781                 return createGuiErrorList(*this);
4782         if (name == "external")
4783                 return createGuiExternal(*this);
4784         if (name == "file")
4785                 return createGuiShowFile(*this);
4786         if (name == "findreplace")
4787                 return createGuiSearch(*this);
4788         if (name == "findreplaceadv")
4789                 return createGuiSearchAdv(*this);
4790         if (name == "graphics")
4791                 return createGuiGraphics(*this);
4792         if (name == "include")
4793                 return createGuiInclude(*this);
4794         if (name == "index")
4795                 return createGuiIndex(*this);
4796         if (name == "index_print")
4797                 return createGuiPrintindex(*this);
4798         if (name == "listings")
4799                 return createGuiListings(*this);
4800         if (name == "log")
4801                 return createGuiLog(*this);
4802         if (name == "mathdelimiter")
4803                 return createGuiDelimiter(*this);
4804         if (name == "mathmatrix")
4805                 return createGuiMathMatrix(*this);
4806         if (name == "note")
4807                 return createGuiNote(*this);
4808         if (name == "paragraph")
4809                 return createGuiParagraph(*this);
4810         if (name == "phantom")
4811                 return createGuiPhantom(*this);
4812         if (name == "prefs")
4813                 return createGuiPreferences(*this);
4814         if (name == "ref")
4815                 return createGuiRef(*this);
4816         if (name == "sendto")
4817                 return createGuiSendTo(*this);
4818         if (name == "spellchecker")
4819                 return createGuiSpellchecker(*this);
4820         if (name == "symbols")
4821                 return createGuiSymbols(*this);
4822         if (name == "tabularcreate")
4823                 return createGuiTabularCreate(*this);
4824         if (name == "texinfo")
4825                 return createGuiTexInfo(*this);
4826         if (name == "thesaurus")
4827                 return createGuiThesaurus(*this);
4828         if (name == "toc")
4829                 return createGuiToc(*this);
4830         if (name == "view-source")
4831                 return createGuiViewSource(*this);
4832         if (name == "wrap")
4833                 return createGuiWrap(*this);
4834         if (name == "progress")
4835                 return createGuiProgressView(*this);
4836
4837         return 0;
4838 }
4839
4840
4841 SEMenu::SEMenu(QWidget * parent)
4842 {
4843         QAction * action = addAction(qt_("Disable Shell Escape"));
4844         connect(action, SIGNAL(triggered()),
4845                 parent, SLOT(disableShellEscape()));
4846 }
4847
4848 } // namespace frontend
4849 } // namespace lyx
4850
4851 #include "moc_GuiView.cpp"