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