]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Add new dialog for templates and examples
[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 == "lyxfiles"
2102                                 || name == "prefs"
2103                                 || name == "texinfo"
2104                                 || name == "progress"
2105                                 || name == "compare";
2106                 else if (name == "character" || name == "symbols"
2107                         || name == "mathdelimiter" || name == "mathmatrix") {
2108                         if (!buf || buf->isReadonly())
2109                                 enable = false;
2110                         else {
2111                                 Cursor const & cur = currentBufferView()->cursor();
2112                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2113                         }
2114                 }
2115                 else if (name == "latexlog")
2116                         enable = FileName(doc_buffer->logName()).isReadableFile();
2117                 else if (name == "spellchecker")
2118                         enable = theSpellChecker()
2119                                 && !doc_buffer->isReadonly()
2120                                 && !doc_buffer->text().empty();
2121                 else if (name == "vclog")
2122                         enable = doc_buffer->lyxvc().inUse();
2123                 break;
2124         }
2125
2126         case LFUN_DIALOG_UPDATE: {
2127                 string const name = cmd.getArg(0);
2128                 if (!buf)
2129                         enable = name == "prefs";
2130                 break;
2131         }
2132
2133         case LFUN_COMMAND_EXECUTE:
2134         case LFUN_MESSAGE:
2135         case LFUN_MENU_OPEN:
2136                 // Nothing to check.
2137                 break;
2138
2139         case LFUN_COMPLETION_INLINE:
2140                 if (!d.current_work_area_
2141                         || !d.current_work_area_->completer().inlinePossible(
2142                         currentBufferView()->cursor()))
2143                         enable = false;
2144                 break;
2145
2146         case LFUN_COMPLETION_POPUP:
2147                 if (!d.current_work_area_
2148                         || !d.current_work_area_->completer().popupPossible(
2149                         currentBufferView()->cursor()))
2150                         enable = false;
2151                 break;
2152
2153         case LFUN_COMPLETE:
2154                 if (!d.current_work_area_
2155                         || !d.current_work_area_->completer().inlinePossible(
2156                         currentBufferView()->cursor()))
2157                         enable = false;
2158                 break;
2159
2160         case LFUN_COMPLETION_ACCEPT:
2161                 if (!d.current_work_area_
2162                         || (!d.current_work_area_->completer().popupVisible()
2163                         && !d.current_work_area_->completer().inlineVisible()
2164                         && !d.current_work_area_->completer().completionAvailable()))
2165                         enable = false;
2166                 break;
2167
2168         case LFUN_COMPLETION_CANCEL:
2169                 if (!d.current_work_area_
2170                         || (!d.current_work_area_->completer().popupVisible()
2171                         && !d.current_work_area_->completer().inlineVisible()))
2172                         enable = false;
2173                 break;
2174
2175         case LFUN_BUFFER_ZOOM_OUT:
2176         case LFUN_BUFFER_ZOOM_IN: {
2177                 // only diff between these two is that the default for ZOOM_OUT
2178                 // is a neg. number
2179                 bool const neg_zoom =
2180                         convert<int>(cmd.argument()) < 0 ||
2181                         (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2182                 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2183                         docstring const msg =
2184                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2185                         flag.message(msg);
2186                         enable = false;
2187                 } else
2188                         enable = doc_buffer;
2189                 break;
2190         }
2191
2192         case LFUN_BUFFER_ZOOM: {
2193                 bool const less_than_min_zoom =
2194                         !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2195                 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2196                         docstring const msg =
2197                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2198                         flag.message(msg);
2199                         enable = false;
2200                 }
2201                 else
2202                         enable = doc_buffer;
2203                 break;
2204         }
2205
2206         case LFUN_BUFFER_MOVE_NEXT:
2207         case LFUN_BUFFER_MOVE_PREVIOUS:
2208                 // we do not cycle when moving
2209         case LFUN_BUFFER_NEXT:
2210         case LFUN_BUFFER_PREVIOUS:
2211                 // because we cycle, it doesn't matter whether on first or last
2212                 enable = (d.currentTabWorkArea()->count() > 1);
2213                 break;
2214         case LFUN_BUFFER_SWITCH:
2215                 // toggle on the current buffer, but do not toggle off
2216                 // the other ones (is that a good idea?)
2217                 if (doc_buffer
2218                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2219                         flag.setOnOff(true);
2220                 break;
2221
2222         case LFUN_VC_REGISTER:
2223                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2224                 break;
2225         case LFUN_VC_RENAME:
2226                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2227                 break;
2228         case LFUN_VC_COPY:
2229                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2230                 break;
2231         case LFUN_VC_CHECK_IN:
2232                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2233                 break;
2234         case LFUN_VC_CHECK_OUT:
2235                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2236                 break;
2237         case LFUN_VC_LOCKING_TOGGLE:
2238                 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2239                         && doc_buffer->lyxvc().lockingToggleEnabled();
2240                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2241                 break;
2242         case LFUN_VC_REVERT:
2243                 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2244                         && !doc_buffer->hasReadonlyFlag();
2245                 break;
2246         case LFUN_VC_UNDO_LAST:
2247                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2248                 break;
2249         case LFUN_VC_REPO_UPDATE:
2250                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2251                 break;
2252         case LFUN_VC_COMMAND: {
2253                 if (cmd.argument().empty())
2254                         enable = false;
2255                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2256                         enable = false;
2257                 break;
2258         }
2259         case LFUN_VC_COMPARE:
2260                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2261                 break;
2262
2263         case LFUN_SERVER_GOTO_FILE_ROW:
2264         case LFUN_LYX_ACTIVATE:
2265                 break;
2266         case LFUN_FORWARD_SEARCH:
2267                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2268                 break;
2269
2270         case LFUN_FILE_INSERT_PLAINTEXT:
2271         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2272                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2273                 break;
2274
2275         case LFUN_SPELLING_CONTINUOUSLY:
2276                 flag.setOnOff(lyxrc.spellcheck_continuously);
2277                 break;
2278
2279         default:
2280                 return false;
2281         }
2282
2283         if (!enable)
2284                 flag.setEnabled(false);
2285
2286         return true;
2287 }
2288
2289
2290 static FileName selectTemplateFile()
2291 {
2292         FileDialog dlg(qt_("Select template file"));
2293         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2294         dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2295
2296         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2297                                  QStringList(qt_("LyX Documents (*.lyx)")));
2298
2299         if (result.first == FileDialog::Later)
2300                 return FileName();
2301         if (result.second.isEmpty())
2302                 return FileName();
2303         return FileName(fromqstr(result.second));
2304 }
2305
2306
2307 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2308 {
2309         setBusy(true);
2310
2311         Buffer * newBuffer = 0;
2312         try {
2313                 newBuffer = checkAndLoadLyXFile(filename);
2314         } catch (ExceptionMessage const & e) {
2315                 setBusy(false);
2316                 throw(e);
2317         }
2318         setBusy(false);
2319
2320         if (!newBuffer) {
2321                 message(_("Document not loaded."));
2322                 return 0;
2323         }
2324
2325         setBuffer(newBuffer);
2326         newBuffer->errors("Parse");
2327
2328         if (tolastfiles) {
2329                 theSession().lastFiles().add(filename);
2330                 theSession().writeFile();
2331   }
2332
2333         return newBuffer;
2334 }
2335
2336
2337 void GuiView::openDocument(string const & fname)
2338 {
2339         string initpath = lyxrc.document_path;
2340
2341         if (documentBufferView()) {
2342                 string const trypath = documentBufferView()->buffer().filePath();
2343                 // If directory is writeable, use this as default.
2344                 if (FileName(trypath).isDirWritable())
2345                         initpath = trypath;
2346         }
2347
2348         string filename;
2349
2350         if (fname.empty()) {
2351                 FileDialog dlg(qt_("Select document to open"));
2352                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2353                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2354
2355                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2356                 FileDialog::Result result =
2357                         dlg.open(toqstr(initpath), filter);
2358
2359                 if (result.first == FileDialog::Later)
2360                         return;
2361
2362                 filename = fromqstr(result.second);
2363
2364                 // check selected filename
2365                 if (filename.empty()) {
2366                         message(_("Canceled."));
2367                         return;
2368                 }
2369         } else
2370                 filename = fname;
2371
2372         // get absolute path of file and add ".lyx" to the filename if
2373         // necessary.
2374         FileName const fullname =
2375                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2376         if (!fullname.empty())
2377                 filename = fullname.absFileName();
2378
2379         if (!fullname.onlyPath().isDirectory()) {
2380                 Alert::warning(_("Invalid filename"),
2381                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2382                                 from_utf8(fullname.absFileName())));
2383                 return;
2384         }
2385
2386         // if the file doesn't exist and isn't already open (bug 6645),
2387         // let the user create one
2388         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2389             !LyXVC::file_not_found_hook(fullname)) {
2390                 // the user specifically chose this name. Believe him.
2391                 Buffer * const b = newFile(filename, string(), true);
2392                 if (b)
2393                         setBuffer(b);
2394                 return;
2395         }
2396
2397         docstring const disp_fn = makeDisplayPath(filename);
2398         message(bformat(_("Opening document %1$s..."), disp_fn));
2399
2400         docstring str2;
2401         Buffer * buf = loadDocument(fullname);
2402         if (buf) {
2403                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2404                 if (buf->lyxvc().inUse())
2405                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2406                                 " " + _("Version control detected.");
2407         } else {
2408                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2409         }
2410         message(str2);
2411 }
2412
2413 // FIXME: clean that
2414 static bool import(GuiView * lv, FileName const & filename,
2415         string const & format, ErrorList & errorList)
2416 {
2417         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2418
2419         string loader_format;
2420         vector<string> loaders = theConverters().loaders();
2421         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2422                 vector<string>::const_iterator it = loaders.begin();
2423                 vector<string>::const_iterator en = loaders.end();
2424                 for (; it != en; ++it) {
2425                         if (!theConverters().isReachable(format, *it))
2426                                 continue;
2427
2428                         string const tofile =
2429                                 support::changeExtension(filename.absFileName(),
2430                                 theFormats().extension(*it));
2431                         if (theConverters().convert(0, filename, FileName(tofile),
2432                                 filename, format, *it, errorList) != Converters::SUCCESS)
2433                                 return false;
2434                         loader_format = *it;
2435                         break;
2436                 }
2437                 if (loader_format.empty()) {
2438                         frontend::Alert::error(_("Couldn't import file"),
2439                                          bformat(_("No information for importing the format %1$s."),
2440                                          theFormats().prettyName(format)));
2441                         return false;
2442                 }
2443         } else
2444                 loader_format = format;
2445
2446         if (loader_format == "lyx") {
2447                 Buffer * buf = lv->loadDocument(lyxfile);
2448                 if (!buf)
2449                         return false;
2450         } else {
2451                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2452                 if (!b)
2453                         return false;
2454                 lv->setBuffer(b);
2455                 bool as_paragraphs = loader_format == "textparagraph";
2456                 string filename2 = (loader_format == format) ? filename.absFileName()
2457                         : support::changeExtension(filename.absFileName(),
2458                                           theFormats().extension(loader_format));
2459                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2460                         as_paragraphs);
2461                 guiApp->setCurrentView(lv);
2462                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2463         }
2464
2465         return true;
2466 }
2467
2468
2469 void GuiView::importDocument(string const & argument)
2470 {
2471         string format;
2472         string filename = split(argument, format, ' ');
2473
2474         LYXERR(Debug::INFO, format << " file: " << filename);
2475
2476         // need user interaction
2477         if (filename.empty()) {
2478                 string initpath = lyxrc.document_path;
2479                 if (documentBufferView()) {
2480                         string const trypath = documentBufferView()->buffer().filePath();
2481                         // If directory is writeable, use this as default.
2482                         if (FileName(trypath).isDirWritable())
2483                                 initpath = trypath;
2484                 }
2485
2486                 docstring const text = bformat(_("Select %1$s file to import"),
2487                         theFormats().prettyName(format));
2488
2489                 FileDialog dlg(toqstr(text));
2490                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2491                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2492
2493                 docstring filter = theFormats().prettyName(format);
2494                 filter += " (*.{";
2495                 // FIXME UNICODE
2496                 filter += from_utf8(theFormats().extensions(format));
2497                 filter += "})";
2498
2499                 FileDialog::Result result =
2500                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2501
2502                 if (result.first == FileDialog::Later)
2503                         return;
2504
2505                 filename = fromqstr(result.second);
2506
2507                 // check selected filename
2508                 if (filename.empty())
2509                         message(_("Canceled."));
2510         }
2511
2512         if (filename.empty())
2513                 return;
2514
2515         // get absolute path of file
2516         FileName const fullname(support::makeAbsPath(filename));
2517
2518         // Can happen if the user entered a path into the dialog
2519         // (see bug #7437)
2520         if (fullname.onlyFileName().empty()) {
2521                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2522                                           "Aborting import."),
2523                                         from_utf8(fullname.absFileName()));
2524                 frontend::Alert::error(_("File name error"), msg);
2525                 message(_("Canceled."));
2526                 return;
2527         }
2528
2529
2530         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2531
2532         // Check if the document already is open
2533         Buffer * buf = theBufferList().getBuffer(lyxfile);
2534         if (buf) {
2535                 setBuffer(buf);
2536                 if (!closeBuffer()) {
2537                         message(_("Canceled."));
2538                         return;
2539                 }
2540         }
2541
2542         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2543
2544         // if the file exists already, and we didn't do
2545         // -i lyx thefile.lyx, warn
2546         if (lyxfile.exists() && fullname != lyxfile) {
2547
2548                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2549                         "Do you want to overwrite that document?"), displaypath);
2550                 int const ret = Alert::prompt(_("Overwrite document?"),
2551                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2552
2553                 if (ret == 1) {
2554                         message(_("Canceled."));
2555                         return;
2556                 }
2557         }
2558
2559         message(bformat(_("Importing %1$s..."), displaypath));
2560         ErrorList errorList;
2561         if (import(this, fullname, format, errorList))
2562                 message(_("imported."));
2563         else
2564                 message(_("file not imported!"));
2565
2566         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2567 }
2568
2569
2570 void GuiView::newDocument(string const & filename, string templatefile,
2571                           bool from_template)
2572 {
2573         FileName initpath(lyxrc.document_path);
2574         if (documentBufferView()) {
2575                 FileName const trypath(documentBufferView()->buffer().filePath());
2576                 // If directory is writeable, use this as default.
2577                 if (trypath.isDirWritable())
2578                         initpath = trypath;
2579         }
2580
2581         if (from_template) {
2582                 if (templatefile.empty())
2583                         templatefile =  selectTemplateFile().absFileName();
2584                 if (templatefile.empty())
2585                         return;
2586         }
2587
2588         Buffer * b;
2589         if (filename.empty())
2590                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2591         else
2592                 b = newFile(filename, templatefile, true);
2593
2594         if (b)
2595                 setBuffer(b);
2596
2597         // If no new document could be created, it is unsure
2598         // whether there is a valid BufferView.
2599         if (currentBufferView())
2600                 // Ensure the cursor is correctly positioned on screen.
2601                 currentBufferView()->showCursor();
2602 }
2603
2604
2605 void GuiView::insertLyXFile(docstring const & fname)
2606 {
2607         BufferView * bv = documentBufferView();
2608         if (!bv)
2609                 return;
2610
2611         // FIXME UNICODE
2612         FileName filename(to_utf8(fname));
2613         if (filename.empty()) {
2614                 // Launch a file browser
2615                 // FIXME UNICODE
2616                 string initpath = lyxrc.document_path;
2617                 string const trypath = bv->buffer().filePath();
2618                 // If directory is writeable, use this as default.
2619                 if (FileName(trypath).isDirWritable())
2620                         initpath = trypath;
2621
2622                 // FIXME UNICODE
2623                 FileDialog dlg(qt_("Select LyX document to insert"));
2624                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2625                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2626
2627                 FileDialog::Result result = dlg.open(toqstr(initpath),
2628                                          QStringList(qt_("LyX Documents (*.lyx)")));
2629
2630                 if (result.first == FileDialog::Later)
2631                         return;
2632
2633                 // FIXME UNICODE
2634                 filename.set(fromqstr(result.second));
2635
2636                 // check selected filename
2637                 if (filename.empty()) {
2638                         // emit message signal.
2639                         message(_("Canceled."));
2640                         return;
2641                 }
2642         }
2643
2644         bv->insertLyXFile(filename);
2645         bv->buffer().errors("Parse");
2646 }
2647
2648
2649 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2650 {
2651         FileName fname = b.fileName();
2652         FileName const oldname = fname;
2653
2654         if (!newname.empty()) {
2655                 // FIXME UNICODE
2656                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2657         } else {
2658                 // Switch to this Buffer.
2659                 setBuffer(&b);
2660
2661                 // No argument? Ask user through dialog.
2662                 // FIXME UNICODE
2663                 FileDialog dlg(qt_("Choose a filename to save document as"));
2664                 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2665                 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2666
2667                 if (!isLyXFileName(fname.absFileName()))
2668                         fname.changeExtension(".lyx");
2669
2670                 FileDialog::Result result =
2671                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2672                                    QStringList(qt_("LyX Documents (*.lyx)")),
2673                                          toqstr(fname.onlyFileName()));
2674
2675                 if (result.first == FileDialog::Later)
2676                         return false;
2677
2678                 fname.set(fromqstr(result.second));
2679
2680                 if (fname.empty())
2681                         return false;
2682
2683                 if (!isLyXFileName(fname.absFileName()))
2684                         fname.changeExtension(".lyx");
2685         }
2686
2687         // fname is now the new Buffer location.
2688
2689         // if there is already a Buffer open with this name, we do not want
2690         // to have another one. (the second test makes sure we're not just
2691         // trying to overwrite ourselves, which is fine.)
2692         if (theBufferList().exists(fname) && fname != oldname
2693                   && theBufferList().getBuffer(fname) != &b) {
2694                 docstring const text =
2695                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2696                             "Please close it before attempting to overwrite it.\n"
2697                             "Do you want to choose a new filename?"),
2698                                 from_utf8(fname.absFileName()));
2699                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2700                         text, 0, 1, _("&Rename"), _("&Cancel"));
2701                 switch (ret) {
2702                 case 0: return renameBuffer(b, docstring(), kind);
2703                 case 1: return false;
2704                 }
2705                 //return false;
2706         }
2707
2708         bool const existsLocal = fname.exists();
2709         bool const existsInVC = LyXVC::fileInVC(fname);
2710         if (existsLocal || existsInVC) {
2711                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2712                 if (kind != LV_WRITE_AS && existsInVC) {
2713                         // renaming to a name that is already in VC
2714                         // would not work
2715                         docstring text = bformat(_("The document %1$s "
2716                                         "is already registered.\n\n"
2717                                         "Do you want to choose a new name?"),
2718                                 file);
2719                         docstring const title = (kind == LV_VC_RENAME) ?
2720                                 _("Rename document?") : _("Copy document?");
2721                         docstring const button = (kind == LV_VC_RENAME) ?
2722                                 _("&Rename") : _("&Copy");
2723                         int const ret = Alert::prompt(title, text, 0, 1,
2724                                 button, _("&Cancel"));
2725                         switch (ret) {
2726                         case 0: return renameBuffer(b, docstring(), kind);
2727                         case 1: return false;
2728                         }
2729                 }
2730
2731                 if (existsLocal) {
2732                         docstring text = bformat(_("The document %1$s "
2733                                         "already exists.\n\n"
2734                                         "Do you want to overwrite that document?"),
2735                                 file);
2736                         int const ret = Alert::prompt(_("Overwrite document?"),
2737                                         text, 0, 2, _("&Overwrite"),
2738                                         _("&Rename"), _("&Cancel"));
2739                         switch (ret) {
2740                         case 0: break;
2741                         case 1: return renameBuffer(b, docstring(), kind);
2742                         case 2: return false;
2743                         }
2744                 }
2745         }
2746
2747         switch (kind) {
2748         case LV_VC_RENAME: {
2749                 string msg = b.lyxvc().rename(fname);
2750                 if (msg.empty())
2751                         return false;
2752                 message(from_utf8(msg));
2753                 break;
2754         }
2755         case LV_VC_COPY: {
2756                 string msg = b.lyxvc().copy(fname);
2757                 if (msg.empty())
2758                         return false;
2759                 message(from_utf8(msg));
2760                 break;
2761         }
2762         case LV_WRITE_AS:
2763                 break;
2764         }
2765         // LyXVC created the file already in case of LV_VC_RENAME or
2766         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2767         // relative paths of included stuff right if we moved e.g. from
2768         // /a/b.lyx to /a/c/b.lyx.
2769
2770         bool const saved = saveBuffer(b, fname);
2771         if (saved)
2772                 b.reload();
2773         return saved;
2774 }
2775
2776
2777 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2778 {
2779         FileName fname = b.fileName();
2780
2781         FileDialog dlg(qt_("Choose a filename to export the document as"));
2782         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2783
2784         QStringList types;
2785         QString const anyformat = qt_("Guess from extension (*.*)");
2786         types << anyformat;
2787
2788         vector<Format const *> export_formats;
2789         for (Format const & f : theFormats())
2790                 if (f.documentFormat())
2791                         export_formats.push_back(&f);
2792         sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2793         map<QString, string> fmap;
2794         QString filter;
2795         string ext;
2796         for (Format const * f : export_formats) {
2797                 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2798                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2799                                                      loc_prettyname,
2800                                                      from_ascii(f->extension())));
2801                 types << loc_filter;
2802                 fmap[loc_filter] = f->name();
2803                 if (from_ascii(f->name()) == iformat) {
2804                         filter = loc_filter;
2805                         ext = f->extension();
2806                 }
2807         }
2808         string ofname = fname.onlyFileName();
2809         if (!ext.empty())
2810                 ofname = support::changeExtension(ofname, ext);
2811         FileDialog::Result result =
2812                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2813                          types,
2814                          toqstr(ofname),
2815                          &filter);
2816         if (result.first != FileDialog::Chosen)
2817                 return false;
2818
2819         string fmt_name;
2820         fname.set(fromqstr(result.second));
2821         if (filter == anyformat)
2822                 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2823         else
2824                 fmt_name = fmap[filter];
2825         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2826                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2827
2828         if (fmt_name.empty() || fname.empty())
2829                 return false;
2830
2831         // fname is now the new Buffer location.
2832         if (FileName(fname).exists()) {
2833                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2834                 docstring text = bformat(_("The document %1$s already "
2835                                            "exists.\n\nDo you want to "
2836                                            "overwrite that document?"),
2837                                          file);
2838                 int const ret = Alert::prompt(_("Overwrite document?"),
2839                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2840                 switch (ret) {
2841                 case 0: break;
2842                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2843                 case 2: return false;
2844                 }
2845         }
2846
2847         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2848         DispatchResult dr;
2849         dispatch(cmd, dr);
2850         return dr.dispatched();
2851 }
2852
2853
2854 bool GuiView::saveBuffer(Buffer & b)
2855 {
2856         return saveBuffer(b, FileName());
2857 }
2858
2859
2860 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2861 {
2862         if (workArea(b) && workArea(b)->inDialogMode())
2863                 return true;
2864
2865         if (fn.empty() && b.isUnnamed())
2866                 return renameBuffer(b, docstring());
2867
2868         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2869         if (success) {
2870                 theSession().lastFiles().add(b.fileName());
2871                 theSession().writeFile();
2872                 return true;
2873         }
2874
2875         // Switch to this Buffer.
2876         setBuffer(&b);
2877
2878         // FIXME: we don't tell the user *WHY* the save failed !!
2879         docstring const file = makeDisplayPath(b.absFileName(), 30);
2880         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2881                                    "Do you want to rename the document and "
2882                                    "try again?"), file);
2883         int const ret = Alert::prompt(_("Rename and save?"),
2884                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2885         switch (ret) {
2886         case 0:
2887                 if (!renameBuffer(b, docstring()))
2888                         return false;
2889                 break;
2890         case 1:
2891                 break;
2892         case 2:
2893                 return false;
2894         }
2895
2896         return saveBuffer(b, fn);
2897 }
2898
2899
2900 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2901 {
2902         return closeWorkArea(wa, false);
2903 }
2904
2905
2906 // We only want to close the buffer if it is not visible in other workareas
2907 // of the same view, nor in other views, and if this is not a child
2908 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2909 {
2910         Buffer & buf = wa->bufferView().buffer();
2911
2912         bool last_wa = d.countWorkAreasOf(buf) == 1
2913                 && !inOtherView(buf) && !buf.parent();
2914
2915         bool close_buffer = last_wa;
2916
2917         if (last_wa) {
2918                 if (lyxrc.close_buffer_with_last_view == "yes")
2919                         ; // Nothing to do
2920                 else if (lyxrc.close_buffer_with_last_view == "no")
2921                         close_buffer = false;
2922                 else {
2923                         docstring file;
2924                         if (buf.isUnnamed())
2925                                 file = from_utf8(buf.fileName().onlyFileName());
2926                         else
2927                                 file = buf.fileName().displayName(30);
2928                         docstring const text = bformat(
2929                                 _("Last view on document %1$s is being closed.\n"
2930                                   "Would you like to close or hide the document?\n"
2931                                   "\n"
2932                                   "Hidden documents can be displayed back through\n"
2933                                   "the menu: View->Hidden->...\n"
2934                                   "\n"
2935                                   "To remove this question, set your preference in:\n"
2936                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2937                                 ), file);
2938                         int ret = Alert::prompt(_("Close or hide document?"),
2939                                 text, 0, 1, _("&Close"), _("&Hide"));
2940                         close_buffer = (ret == 0);
2941                 }
2942         }
2943
2944         return closeWorkArea(wa, close_buffer);
2945 }
2946
2947
2948 bool GuiView::closeBuffer()
2949 {
2950         GuiWorkArea * wa = currentMainWorkArea();
2951         // coverity complained about this
2952         // it seems unnecessary, but perhaps is worth the check
2953         LASSERT(wa, return false);
2954
2955         setCurrentWorkArea(wa);
2956         Buffer & buf = wa->bufferView().buffer();
2957         return closeWorkArea(wa, !buf.parent());
2958 }
2959
2960
2961 void GuiView::writeSession() const {
2962         GuiWorkArea const * active_wa = currentMainWorkArea();
2963         for (int i = 0; i < d.splitter_->count(); ++i) {
2964                 TabWorkArea * twa = d.tabWorkArea(i);
2965                 for (int j = 0; j < twa->count(); ++j) {
2966                         GuiWorkArea * wa = twa->workArea(j);
2967                         Buffer & buf = wa->bufferView().buffer();
2968                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2969                 }
2970         }
2971 }
2972
2973
2974 bool GuiView::closeBufferAll()
2975 {
2976         // Close the workareas in all other views
2977         QList<int> const ids = guiApp->viewIds();
2978         for (int i = 0; i != ids.size(); ++i) {
2979                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2980                         return false;
2981         }
2982
2983         // Close our own workareas
2984         if (!closeWorkAreaAll())
2985                 return false;
2986
2987         // Now close the hidden buffers. We prevent hidden buffers from being
2988         // dirty, so we can just close them.
2989         theBufferList().closeAll();
2990         return true;
2991 }
2992
2993
2994 bool GuiView::closeWorkAreaAll()
2995 {
2996         setCurrentWorkArea(currentMainWorkArea());
2997
2998         // We might be in a situation that there is still a tabWorkArea, but
2999         // there are no tabs anymore. This can happen when we get here after a
3000         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
3001         // many TabWorkArea's have no documents anymore.
3002         int empty_twa = 0;
3003
3004         // We have to call count() each time, because it can happen that
3005         // more than one splitter will disappear in one iteration (bug 5998).
3006         while (d.splitter_->count() > empty_twa) {
3007                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
3008
3009                 if (twa->count() == 0)
3010                         ++empty_twa;
3011                 else {
3012                         setCurrentWorkArea(twa->currentWorkArea());
3013                         if (!closeTabWorkArea(twa))
3014                                 return false;
3015                 }
3016         }
3017         return true;
3018 }
3019
3020
3021 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
3022 {
3023         if (!wa)
3024                 return false;
3025
3026         Buffer & buf = wa->bufferView().buffer();
3027
3028         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
3029                 Alert::warning(_("Close document"),
3030                         _("Document could not be closed because it is being processed by LyX."));
3031                 return false;
3032         }
3033
3034         if (close_buffer)
3035                 return closeBuffer(buf);
3036         else {
3037                 if (!inMultiTabs(wa))
3038                         if (!saveBufferIfNeeded(buf, true))
3039                                 return false;
3040                 removeWorkArea(wa);
3041                 return true;
3042         }
3043 }
3044
3045
3046 bool GuiView::closeBuffer(Buffer & buf)
3047 {
3048         bool success = true;
3049         ListOfBuffers clist = buf.getChildren();
3050         ListOfBuffers::const_iterator it = clist.begin();
3051         ListOfBuffers::const_iterator const bend = clist.end();
3052         for (; it != bend; ++it) {
3053                 Buffer * child_buf = *it;
3054                 if (theBufferList().isOthersChild(&buf, child_buf)) {
3055                         child_buf->setParent(0);
3056                         continue;
3057                 }
3058
3059                 // FIXME: should we look in other tabworkareas?
3060                 // ANSWER: I don't think so. I've tested, and if the child is
3061                 // open in some other window, it closes without a problem.
3062                 GuiWorkArea * child_wa = workArea(*child_buf);
3063                 if (child_wa) {
3064                         if (closing_)
3065                                 // If we are in a close_event all children will be closed in some time,
3066                                 // so no need to do it here. This will ensure that the children end up
3067                                 // in the session file in the correct order. If we close the master
3068                                 // buffer, we can close or release the child buffers here too.
3069                                 continue;
3070                         success = closeWorkArea(child_wa, true);
3071                         if (!success)
3072                                 break;
3073                 } else {
3074                         // In this case the child buffer is open but hidden.
3075                         // Even in this case, children can be dirty (e.g.,
3076                         // after a label change in the master, see #11405).
3077                         // Therefore, check this
3078                         if (closing_ && (child_buf->isClean() || child_buf->paragraphs().empty()))
3079                                 // If we are in a close_event all children will be closed in some time,
3080                                 // so no need to do it here. This will ensure that the children end up
3081                                 // in the session file in the correct order. If we close the master
3082                                 // buffer, we can close or release the child buffers here too.
3083                                 continue;
3084                         // Save dirty buffers also if closing_!
3085                         if (saveBufferIfNeeded(*child_buf, false)) {
3086                                 child_buf->removeAutosaveFile();
3087                                 theBufferList().release(child_buf);
3088                         } else {
3089                                 // Saving of dirty children has been cancelled.
3090                                 // Cancel the whole process.
3091                                 success = false;
3092                                 break;
3093                         }
3094                 }
3095         }
3096         if (success) {
3097                 // goto bookmark to update bookmark pit.
3098                 // FIXME: we should update only the bookmarks related to this buffer!
3099                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3100                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3101                         guiApp->gotoBookmark(i+1, false, false);
3102
3103                 if (saveBufferIfNeeded(buf, false)) {
3104                         buf.removeAutosaveFile();
3105                         theBufferList().release(&buf);
3106                         return true;
3107                 }
3108         }
3109         // open all children again to avoid a crash because of dangling
3110         // pointers (bug 6603)
3111         buf.updateBuffer();
3112         return false;
3113 }
3114
3115
3116 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3117 {
3118         while (twa == d.currentTabWorkArea()) {
3119                 twa->setCurrentIndex(twa->count() - 1);
3120
3121                 GuiWorkArea * wa = twa->currentWorkArea();
3122                 Buffer & b = wa->bufferView().buffer();
3123
3124                 // We only want to close the buffer if the same buffer is not visible
3125                 // in another view, and if this is not a child and if we are closing
3126                 // a view (not a tabgroup).
3127                 bool const close_buffer =
3128                         !inOtherView(b) && !b.parent() && closing_;
3129
3130                 if (!closeWorkArea(wa, close_buffer))
3131                         return false;
3132         }
3133         return true;
3134 }
3135
3136
3137 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3138 {
3139         if (buf.isClean() || buf.paragraphs().empty())
3140                 return true;
3141
3142         // Switch to this Buffer.
3143         setBuffer(&buf);
3144
3145         docstring file;
3146         bool exists;
3147         // FIXME: Unicode?
3148         if (buf.isUnnamed()) {
3149                 file = from_utf8(buf.fileName().onlyFileName());
3150                 exists = false;
3151         } else {
3152                 FileName filename = buf.fileName();
3153                 filename.refresh();
3154                 file = filename.displayName(30);
3155                 exists = filename.exists();
3156         }
3157
3158         // Bring this window to top before asking questions.
3159         raise();
3160         activateWindow();
3161
3162         int ret;
3163         if (hiding && buf.isUnnamed()) {
3164                 docstring const text = bformat(_("The document %1$s has not been "
3165                                                  "saved yet.\n\nDo you want to save "
3166                                                  "the document?"), file);
3167                 ret = Alert::prompt(_("Save new document?"),
3168                         text, 0, 1, _("&Save"), _("&Cancel"));
3169                 if (ret == 1)
3170                         ++ret;
3171         } else {
3172                 docstring const text = exists ?
3173                         bformat(_("The document %1$s has unsaved changes."
3174                                   "\n\nDo you want to save the document or "
3175                                   "discard the changes?"), file) :
3176                         bformat(_("The document %1$s has not been saved yet."
3177                                   "\n\nDo you want to save the document or "
3178                                   "discard it entirely?"), file);
3179                 docstring const title = exists ?
3180                         _("Save changed document?") : _("Save document?");
3181                 ret = Alert::prompt(title, text, 0, 2,
3182                                     _("&Save"), _("&Discard"), _("&Cancel"));
3183         }
3184
3185         switch (ret) {
3186         case 0:
3187                 if (!saveBuffer(buf))
3188                         return false;
3189                 break;
3190         case 1:
3191                 // If we crash after this we could have no autosave file
3192                 // but I guess this is really improbable (Jug).
3193                 // Sometimes improbable things happen:
3194                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3195                 // buf.removeAutosaveFile();
3196                 if (hiding)
3197                         // revert all changes
3198                         reloadBuffer(buf);
3199                 buf.markClean();
3200                 break;
3201         case 2:
3202                 return false;
3203         }
3204         return true;
3205 }
3206
3207
3208 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3209 {
3210         Buffer & buf = wa->bufferView().buffer();
3211
3212         for (int i = 0; i != d.splitter_->count(); ++i) {
3213                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3214                 if (wa_ && wa_ != wa)
3215                         return true;
3216         }
3217         return inOtherView(buf);
3218 }
3219
3220
3221 bool GuiView::inOtherView(Buffer & buf)
3222 {
3223         QList<int> const ids = guiApp->viewIds();
3224
3225         for (int i = 0; i != ids.size(); ++i) {
3226                 if (id_ == ids[i])
3227                         continue;
3228
3229                 if (guiApp->view(ids[i]).workArea(buf))
3230                         return true;
3231         }
3232         return false;
3233 }
3234
3235
3236 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3237 {
3238         if (!documentBufferView())
3239                 return;
3240
3241         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3242                 Buffer * const curbuf = &documentBufferView()->buffer();
3243                 int nwa = twa->count();
3244                 for (int i = 0; i < nwa; ++i) {
3245                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3246                                 int next_index;
3247                                 if (np == NEXTBUFFER)
3248                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3249                                 else
3250                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3251                                 if (move)
3252                                         twa->moveTab(i, next_index);
3253                                 else
3254                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3255                                 break;
3256                         }
3257                 }
3258         }
3259 }
3260
3261
3262 /// make sure the document is saved
3263 static bool ensureBufferClean(Buffer * buffer)
3264 {
3265         LASSERT(buffer, return false);
3266         if (buffer->isClean() && !buffer->isUnnamed())
3267                 return true;
3268
3269         docstring const file = buffer->fileName().displayName(30);
3270         docstring title;
3271         docstring text;
3272         if (!buffer->isUnnamed()) {
3273                 text = bformat(_("The document %1$s has unsaved "
3274                                                  "changes.\n\nDo you want to save "
3275                                                  "the document?"), file);
3276                 title = _("Save changed document?");
3277
3278         } else {
3279                 text = bformat(_("The document %1$s has not been "
3280                                                  "saved yet.\n\nDo you want to save "
3281                                                  "the document?"), file);
3282                 title = _("Save new document?");
3283         }
3284         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3285
3286         if (ret == 0)
3287                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3288
3289         return buffer->isClean() && !buffer->isUnnamed();
3290 }
3291
3292
3293 bool GuiView::reloadBuffer(Buffer & buf)
3294 {
3295         currentBufferView()->cursor().reset();
3296         Buffer::ReadStatus status = buf.reload();
3297         return status == Buffer::ReadSuccess;
3298 }
3299
3300
3301 void GuiView::checkExternallyModifiedBuffers()
3302 {
3303         BufferList::iterator bit = theBufferList().begin();
3304         BufferList::iterator const bend = theBufferList().end();
3305         for (; bit != bend; ++bit) {
3306                 Buffer * buf = *bit;
3307                 if (buf->fileName().exists() && buf->isChecksumModified()) {
3308                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3309                                         " Reload now? Any local changes will be lost."),
3310                                         from_utf8(buf->absFileName()));
3311                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3312                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3313                         if (!ret)
3314                                 reloadBuffer(*buf);
3315                 }
3316         }
3317 }
3318
3319
3320 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3321 {
3322         Buffer * buffer = documentBufferView()
3323                 ? &(documentBufferView()->buffer()) : 0;
3324
3325         switch (cmd.action()) {
3326         case LFUN_VC_REGISTER:
3327                 if (!buffer || !ensureBufferClean(buffer))
3328                         break;
3329                 if (!buffer->lyxvc().inUse()) {
3330                         if (buffer->lyxvc().registrer()) {
3331                                 reloadBuffer(*buffer);
3332                                 dr.clearMessageUpdate();
3333                         }
3334                 }
3335                 break;
3336
3337         case LFUN_VC_RENAME:
3338         case LFUN_VC_COPY: {
3339                 if (!buffer || !ensureBufferClean(buffer))
3340                         break;
3341                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3342                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3343                                 // Some changes are not yet committed.
3344                                 // We test here and not in getStatus(), since
3345                                 // this test is expensive.
3346                                 string log;
3347                                 LyXVC::CommandResult ret =
3348                                         buffer->lyxvc().checkIn(log);
3349                                 dr.setMessage(log);
3350                                 if (ret == LyXVC::ErrorCommand ||
3351                                     ret == LyXVC::VCSuccess)
3352                                         reloadBuffer(*buffer);
3353                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3354                                         frontend::Alert::error(
3355                                                 _("Revision control error."),
3356                                                 _("Document could not be checked in."));
3357                                         break;
3358                                 }
3359                         }
3360                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3361                                 LV_VC_RENAME : LV_VC_COPY;
3362                         renameBuffer(*buffer, cmd.argument(), kind);
3363                 }
3364                 break;
3365         }
3366
3367         case LFUN_VC_CHECK_IN:
3368                 if (!buffer || !ensureBufferClean(buffer))
3369                         break;
3370                 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3371                         string log;
3372                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3373                         dr.setMessage(log);
3374                         // Only skip reloading if the checkin was cancelled or
3375                         // an error occurred before the real checkin VCS command
3376                         // was executed, since the VCS might have changed the
3377                         // file even if it could not checkin successfully.
3378                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3379                                 reloadBuffer(*buffer);
3380                 }
3381                 break;
3382
3383         case LFUN_VC_CHECK_OUT:
3384                 if (!buffer || !ensureBufferClean(buffer))
3385                         break;
3386                 if (buffer->lyxvc().inUse()) {
3387                         dr.setMessage(buffer->lyxvc().checkOut());
3388                         reloadBuffer(*buffer);
3389                 }
3390                 break;
3391
3392         case LFUN_VC_LOCKING_TOGGLE:
3393                 LASSERT(buffer, return);
3394                 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3395                         break;
3396                 if (buffer->lyxvc().inUse()) {
3397                         string res = buffer->lyxvc().lockingToggle();
3398                         if (res.empty()) {
3399                                 frontend::Alert::error(_("Revision control error."),
3400                                 _("Error when setting the locking property."));
3401                         } else {
3402                                 dr.setMessage(res);
3403                                 reloadBuffer(*buffer);
3404                         }
3405                 }
3406                 break;
3407
3408         case LFUN_VC_REVERT:
3409                 LASSERT(buffer, return);
3410                 if (buffer->lyxvc().revert()) {
3411                         reloadBuffer(*buffer);
3412                         dr.clearMessageUpdate();
3413                 }
3414                 break;
3415
3416         case LFUN_VC_UNDO_LAST:
3417                 LASSERT(buffer, return);
3418                 buffer->lyxvc().undoLast();
3419                 reloadBuffer(*buffer);
3420                 dr.clearMessageUpdate();
3421                 break;
3422
3423         case LFUN_VC_REPO_UPDATE:
3424                 LASSERT(buffer, return);
3425                 if (ensureBufferClean(buffer)) {
3426                         dr.setMessage(buffer->lyxvc().repoUpdate());
3427                         checkExternallyModifiedBuffers();
3428                 }
3429                 break;
3430
3431         case LFUN_VC_COMMAND: {
3432                 string flag = cmd.getArg(0);
3433                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3434                         break;
3435                 docstring message;
3436                 if (contains(flag, 'M')) {
3437                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3438                                 break;
3439                 }
3440                 string path = cmd.getArg(1);
3441                 if (contains(path, "$$p") && buffer)
3442                         path = subst(path, "$$p", buffer->filePath());
3443                 LYXERR(Debug::LYXVC, "Directory: " << path);
3444                 FileName pp(path);
3445                 if (!pp.isReadableDirectory()) {
3446                         lyxerr << _("Directory is not accessible.") << endl;
3447                         break;
3448                 }
3449                 support::PathChanger p(pp);
3450
3451                 string command = cmd.getArg(2);
3452                 if (command.empty())
3453                         break;
3454                 if (buffer) {
3455                         command = subst(command, "$$i", buffer->absFileName());
3456                         command = subst(command, "$$p", buffer->filePath());
3457                 }
3458                 command = subst(command, "$$m", to_utf8(message));
3459                 LYXERR(Debug::LYXVC, "Command: " << command);
3460                 Systemcall one;
3461                 one.startscript(Systemcall::Wait, command);
3462
3463                 if (!buffer)
3464                         break;
3465                 if (contains(flag, 'I'))
3466                         buffer->markDirty();
3467                 if (contains(flag, 'R'))
3468                         reloadBuffer(*buffer);
3469
3470                 break;
3471                 }
3472
3473         case LFUN_VC_COMPARE: {
3474                 if (cmd.argument().empty()) {
3475                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3476                         break;
3477                 }
3478
3479                 string rev1 = cmd.getArg(0);
3480                 string f1, f2;
3481                 LATTEST(buffer)
3482
3483                 // f1
3484                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3485                         break;
3486
3487                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3488                         f2 = buffer->absFileName();
3489                 } else {
3490                         string rev2 = cmd.getArg(1);
3491                         if (rev2.empty())
3492                                 break;
3493                         // f2
3494                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3495                                 break;
3496                 }
3497
3498                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3499                                         f1 << "\n"  << f2 << "\n" );
3500                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3501                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3502                 break;
3503         }
3504
3505         default:
3506                 break;
3507         }
3508 }
3509
3510
3511 void GuiView::openChildDocument(string const & fname)
3512 {
3513         LASSERT(documentBufferView(), return);
3514         Buffer & buffer = documentBufferView()->buffer();
3515         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3516         documentBufferView()->saveBookmark(false);
3517         Buffer * child = 0;
3518         if (theBufferList().exists(filename)) {
3519                 child = theBufferList().getBuffer(filename);
3520                 setBuffer(child);
3521         } else {
3522                 message(bformat(_("Opening child document %1$s..."),
3523                         makeDisplayPath(filename.absFileName())));
3524                 child = loadDocument(filename, false);
3525         }
3526         // Set the parent name of the child document.
3527         // This makes insertion of citations and references in the child work,
3528         // when the target is in the parent or another child document.
3529         if (child)
3530                 child->setParent(&buffer);
3531 }
3532
3533
3534 bool GuiView::goToFileRow(string const & argument)
3535 {
3536         string file_name;
3537         int row = -1;
3538         size_t i = argument.find_last_of(' ');
3539         if (i != string::npos) {
3540                 file_name = os::internal_path(trim(argument.substr(0, i)));
3541                 istringstream is(argument.substr(i + 1));
3542                 is >> row;
3543                 if (is.fail())
3544                         i = string::npos;
3545         }
3546         if (i == string::npos) {
3547                 LYXERR0("Wrong argument: " << argument);
3548                 return false;
3549         }
3550         Buffer * buf = 0;
3551         string const abstmp = package().temp_dir().absFileName();
3552         string const realtmp = package().temp_dir().realPath();
3553         // We have to use os::path_prefix_is() here, instead of
3554         // simply prefixIs(), because the file name comes from
3555         // an external application and may need case adjustment.
3556         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3557                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3558                 // Needed by inverse dvi search. If it is a file
3559                 // in tmpdir, call the apropriated function.
3560                 // If tmpdir is a symlink, we may have the real
3561                 // path passed back, so we correct for that.
3562                 if (!prefixIs(file_name, abstmp))
3563                         file_name = subst(file_name, realtmp, abstmp);
3564                 buf = theBufferList().getBufferFromTmp(file_name);
3565         } else {
3566                 // Must replace extension of the file to be .lyx
3567                 // and get full path
3568                 FileName const s = fileSearch(string(),
3569                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3570                 // Either change buffer or load the file
3571                 if (theBufferList().exists(s))
3572                         buf = theBufferList().getBuffer(s);
3573                 else if (s.exists()) {
3574                         buf = loadDocument(s);
3575                         if (!buf)
3576                                 return false;
3577                 } else {
3578                         message(bformat(
3579                                         _("File does not exist: %1$s"),
3580                                         makeDisplayPath(file_name)));
3581                         return false;
3582                 }
3583         }
3584         if (!buf) {
3585                 message(bformat(
3586                         _("No buffer for file: %1$s."),
3587                         makeDisplayPath(file_name))
3588                 );
3589                 return false;
3590         }
3591         setBuffer(buf);
3592         bool success = documentBufferView()->setCursorFromRow(row);
3593         if (!success) {
3594                 LYXERR(Debug::LATEX,
3595                        "setCursorFromRow: invalid position for row " << row);
3596                 frontend::Alert::error(_("Inverse Search Failed"),
3597                                        _("Invalid position requested by inverse search.\n"
3598                                          "You may need to update the viewed document."));
3599         }
3600         return success;
3601 }
3602
3603
3604 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3605 {
3606         QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3607         menu->exec(QCursor::pos());
3608 }
3609
3610
3611 template<class T>
3612 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func,
3613                 Buffer const * orig, Buffer * clone, string const & format)
3614 {
3615         Buffer::ExportStatus const status = func(format);
3616
3617         // the cloning operation will have produced a clone of the entire set of
3618         // documents, starting from the master. so we must delete those.
3619         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3620         delete mbuf;
3621         busyBuffers.remove(orig);
3622         return status;
3623 }
3624
3625
3626 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(
3627                 Buffer const * orig, Buffer * clone, string const & format)
3628 {
3629         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
3630                         &Buffer::doExport;
3631         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3632 }
3633
3634
3635 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(
3636                 Buffer const * orig, Buffer * clone, string const & format)
3637 {
3638         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
3639                         &Buffer::doExport;
3640         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3641 }
3642
3643
3644 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(
3645                 Buffer const * orig, Buffer * clone, string const & format)
3646 {
3647         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const =
3648                         &Buffer::preview;
3649         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3650 }
3651
3652
3653 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3654                            string const & argument,
3655                            Buffer const * used_buffer,
3656                            docstring const & msg,
3657                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3658                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3659                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const,
3660                            bool allow_async)
3661 {
3662         if (!used_buffer)
3663                 return false;
3664
3665         string format = argument;
3666         if (format.empty())
3667                 format = used_buffer->params().getDefaultOutputFormat();
3668         processing_format = format;
3669         if (!msg.empty()) {
3670                 progress_->clearMessages();
3671                 gv_->message(msg);
3672         }
3673 #if EXPORT_in_THREAD
3674         if (allow_async) {
3675                 GuiViewPrivate::busyBuffers.insert(used_buffer);
3676                 Buffer * cloned_buffer = used_buffer->cloneWithChildren();
3677                 if (!cloned_buffer) {
3678                         Alert::error(_("Export Error"),
3679                                      _("Error cloning the Buffer."));
3680                         return false;
3681                 }
3682                 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3683                                         asyncFunc,
3684                                         used_buffer,
3685                                         cloned_buffer,
3686                                         format);
3687                 setPreviewFuture(f);
3688                 last_export_format = used_buffer->params().bufferFormat();
3689                 (void) syncFunc;
3690                 (void) previewFunc;
3691                 // We are asynchronous, so we don't know here anything about the success
3692                 return true;
3693         } else {
3694                 Buffer::ExportStatus status;
3695                 if (syncFunc) {
3696                         status = (used_buffer->*syncFunc)(format, false);
3697                 } else if (previewFunc) {
3698                         status = (used_buffer->*previewFunc)(format);
3699                 } else
3700                         return false;
3701                 handleExportStatus(gv_, status, format);
3702                 (void) asyncFunc;
3703                 return (status == Buffer::ExportSuccess
3704                                 || status == Buffer::PreviewSuccess);
3705         }
3706 #else
3707         (void) allow_async;
3708         Buffer::ExportStatus status;
3709         if (syncFunc) {
3710                 status = (used_buffer->*syncFunc)(format, true);
3711         } else if (previewFunc) {
3712                 status = (used_buffer->*previewFunc)(format);
3713         } else
3714                 return false;
3715         handleExportStatus(gv_, status, format);
3716         (void) asyncFunc;
3717         return (status == Buffer::ExportSuccess
3718                         || status == Buffer::PreviewSuccess);
3719 #endif
3720 }
3721
3722 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3723 {
3724         BufferView * bv = currentBufferView();
3725         LASSERT(bv, return);
3726
3727         // Let the current BufferView dispatch its own actions.
3728         bv->dispatch(cmd, dr);
3729         if (dr.dispatched()) {
3730                 if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3731                         updateDialog("document", "");
3732                 return;
3733         }
3734
3735         // Try with the document BufferView dispatch if any.
3736         BufferView * doc_bv = documentBufferView();
3737         if (doc_bv && doc_bv != bv) {
3738                 doc_bv->dispatch(cmd, dr);
3739                 if (dr.dispatched()) {
3740                         if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3741                                 updateDialog("document", "");
3742                         return;
3743                 }
3744         }
3745
3746         // Then let the current Cursor dispatch its own actions.
3747         bv->cursor().dispatch(cmd);
3748
3749         // update completion. We do it here and not in
3750         // processKeySym to avoid another redraw just for a
3751         // changed inline completion
3752         if (cmd.origin() == FuncRequest::KEYBOARD) {
3753                 if (cmd.action() == LFUN_SELF_INSERT
3754                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3755                         updateCompletion(bv->cursor(), true, true);
3756                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3757                         updateCompletion(bv->cursor(), false, true);
3758                 else
3759                         updateCompletion(bv->cursor(), false, false);
3760         }
3761
3762         dr = bv->cursor().result();
3763 }
3764
3765
3766 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3767 {
3768         BufferView * bv = currentBufferView();
3769         // By default we won't need any update.
3770         dr.screenUpdate(Update::None);
3771         // assume cmd will be dispatched
3772         dr.dispatched(true);
3773
3774         Buffer * doc_buffer = documentBufferView()
3775                 ? &(documentBufferView()->buffer()) : 0;
3776
3777         if (cmd.origin() == FuncRequest::TOC) {
3778                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3779                 // FIXME: do we need to pass a DispatchResult object here?
3780                 toc->doDispatch(bv->cursor(), cmd);
3781                 return;
3782         }
3783
3784         string const argument = to_utf8(cmd.argument());
3785
3786         switch(cmd.action()) {
3787                 case LFUN_BUFFER_CHILD_OPEN:
3788                         openChildDocument(to_utf8(cmd.argument()));
3789                         break;
3790
3791                 case LFUN_BUFFER_IMPORT:
3792                         importDocument(to_utf8(cmd.argument()));
3793                         break;
3794
3795                 case LFUN_MASTER_BUFFER_EXPORT:
3796                         if (doc_buffer)
3797                                 doc_buffer = const_cast<Buffer *>(doc_buffer->masterBuffer());
3798                         // fall through
3799                 case LFUN_BUFFER_EXPORT: {
3800                         if (!doc_buffer)
3801                                 break;
3802                         // GCC only sees strfwd.h when building merged
3803                         if (::lyx::operator==(cmd.argument(), "custom")) {
3804                                 // LFUN_MASTER_BUFFER_EXPORT is not enabled for this case,
3805                                 // so the following test should not be needed.
3806                                 // In principle, we could try to switch to such a view...
3807                                 // if (cmd.action() == LFUN_BUFFER_EXPORT)
3808                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3809                                 break;
3810                         }
3811
3812                         string const dest = cmd.getArg(1);
3813                         FileName target_dir;
3814                         if (!dest.empty() && FileName::isAbsolute(dest))
3815                                 target_dir = FileName(support::onlyPath(dest));
3816                         else
3817                                 target_dir = doc_buffer->fileName().onlyPath();
3818
3819                         string const format = (argument.empty() || argument == "default") ?
3820                                 doc_buffer->params().getDefaultOutputFormat() : argument;
3821
3822                         if ((dest.empty() && doc_buffer->isUnnamed())
3823                             || !target_dir.isDirWritable()) {
3824                                 exportBufferAs(*doc_buffer, from_utf8(format));
3825                                 break;
3826                         }
3827                         /* TODO/Review: Is it a problem to also export the children?
3828                                         See the update_unincluded flag */
3829                         d.asyncBufferProcessing(format,
3830                                                 doc_buffer,
3831                                                 _("Exporting ..."),
3832                                                 &GuiViewPrivate::exportAndDestroy,
3833                                                 &Buffer::doExport,
3834                                                 0, cmd.allowAsync());
3835                         // TODO Inform user about success
3836                         break;
3837                 }
3838
3839                 case LFUN_BUFFER_EXPORT_AS: {
3840                         LASSERT(doc_buffer, break);
3841                         docstring f = cmd.argument();
3842                         if (f.empty())
3843                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3844                         exportBufferAs(*doc_buffer, f);
3845                         break;
3846                 }
3847
3848                 case LFUN_BUFFER_UPDATE: {
3849                         d.asyncBufferProcessing(argument,
3850                                                 doc_buffer,
3851                                                 _("Exporting ..."),
3852                                                 &GuiViewPrivate::compileAndDestroy,
3853                                                 &Buffer::doExport,
3854                                                 0, cmd.allowAsync());
3855                         break;
3856                 }
3857                 case LFUN_BUFFER_VIEW: {
3858                         d.asyncBufferProcessing(argument,
3859                                                 doc_buffer,
3860                                                 _("Previewing ..."),
3861                                                 &GuiViewPrivate::previewAndDestroy,
3862                                                 0,
3863                                                 &Buffer::preview, cmd.allowAsync());
3864                         break;
3865                 }
3866                 case LFUN_MASTER_BUFFER_UPDATE: {
3867                         d.asyncBufferProcessing(argument,
3868                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3869                                                 docstring(),
3870                                                 &GuiViewPrivate::compileAndDestroy,
3871                                                 &Buffer::doExport,
3872                                                 0, cmd.allowAsync());
3873                         break;
3874                 }
3875                 case LFUN_MASTER_BUFFER_VIEW: {
3876                         d.asyncBufferProcessing(argument,
3877                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3878                                                 docstring(),
3879                                                 &GuiViewPrivate::previewAndDestroy,
3880                                                 0, &Buffer::preview, cmd.allowAsync());
3881                         break;
3882                 }
3883                 case LFUN_EXPORT_CANCEL: {
3884                         Systemcall::killscript();
3885                         break;
3886                 }
3887                 case LFUN_BUFFER_SWITCH: {
3888                         string const file_name = to_utf8(cmd.argument());
3889                         if (!FileName::isAbsolute(file_name)) {
3890                                 dr.setError(true);
3891                                 dr.setMessage(_("Absolute filename expected."));
3892                                 break;
3893                         }
3894
3895                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3896                         if (!buffer) {
3897                                 dr.setError(true);
3898                                 dr.setMessage(_("Document not loaded"));
3899                                 break;
3900                         }
3901
3902                         // Do we open or switch to the buffer in this view ?
3903                         if (workArea(*buffer)
3904                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3905                                 setBuffer(buffer);
3906                                 break;
3907                         }
3908
3909                         // Look for the buffer in other views
3910                         QList<int> const ids = guiApp->viewIds();
3911                         int i = 0;
3912                         for (; i != ids.size(); ++i) {
3913                                 GuiView & gv = guiApp->view(ids[i]);
3914                                 if (gv.workArea(*buffer)) {
3915                                         gv.raise();
3916                                         gv.activateWindow();
3917                                         gv.setFocus();
3918                                         gv.setBuffer(buffer);
3919                                         break;
3920                                 }
3921                         }
3922
3923                         // If necessary, open a new window as a last resort
3924                         if (i == ids.size()) {
3925                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3926                                 lyx::dispatch(cmd);
3927                         }
3928                         break;
3929                 }
3930
3931                 case LFUN_BUFFER_NEXT:
3932                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3933                         break;
3934
3935                 case LFUN_BUFFER_MOVE_NEXT:
3936                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3937                         break;
3938
3939                 case LFUN_BUFFER_PREVIOUS:
3940                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3941                         break;
3942
3943                 case LFUN_BUFFER_MOVE_PREVIOUS:
3944                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3945                         break;
3946
3947                 case LFUN_BUFFER_CHKTEX:
3948                         LASSERT(doc_buffer, break);
3949                         doc_buffer->runChktex();
3950                         break;
3951
3952                 case LFUN_COMMAND_EXECUTE: {
3953                         command_execute_ = true;
3954                         minibuffer_focus_ = true;
3955                         break;
3956                 }
3957                 case LFUN_DROP_LAYOUTS_CHOICE:
3958                         d.layout_->showPopup();
3959                         break;
3960
3961                 case LFUN_MENU_OPEN:
3962                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3963                                 menu->exec(QCursor::pos());
3964                         break;
3965
3966                 case LFUN_FILE_INSERT:
3967                         insertLyXFile(cmd.argument());
3968                         break;
3969
3970                 case LFUN_FILE_INSERT_PLAINTEXT:
3971                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3972                         string const fname = to_utf8(cmd.argument());
3973                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3974                                 dr.setMessage(_("Absolute filename expected."));
3975                                 break;
3976                         }
3977
3978                         FileName filename(fname);
3979                         if (fname.empty()) {
3980                                 FileDialog dlg(qt_("Select file to insert"));
3981
3982                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3983                                         QStringList(qt_("All Files (*)")));
3984
3985                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3986                                         dr.setMessage(_("Canceled."));
3987                                         break;
3988                                 }
3989
3990                                 filename.set(fromqstr(result.second));
3991                         }
3992
3993                         if (bv) {
3994                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3995                                 bv->dispatch(new_cmd, dr);
3996                         }
3997                         break;
3998                 }
3999
4000                 case LFUN_BUFFER_RELOAD: {
4001                         LASSERT(doc_buffer, break);
4002
4003                         // drop changes?
4004                         bool drop = (cmd.argument() == "dump");
4005
4006                         int ret = 0;
4007                         if (!drop && !doc_buffer->isClean()) {
4008                                 docstring const file =
4009                                         makeDisplayPath(doc_buffer->absFileName(), 20);
4010                                 if (doc_buffer->notifiesExternalModification()) {
4011                                         docstring text = _("The current version will be lost. "
4012                                             "Are you sure you want to load the version on disk "
4013                                             "of the document %1$s?");
4014                                         ret = Alert::prompt(_("Reload saved document?"),
4015                                                             bformat(text, file), 1, 1,
4016                                                             _("&Reload"), _("&Cancel"));
4017                                 } else {
4018                                         docstring text = _("Any changes will be lost. "
4019                                             "Are you sure you want to revert to the saved version "
4020                                             "of the document %1$s?");
4021                                         ret = Alert::prompt(_("Revert to saved document?"),
4022                                                             bformat(text, file), 1, 1,
4023                                                             _("&Revert"), _("&Cancel"));
4024                                 }
4025                         }
4026
4027                         if (ret == 0) {
4028                                 doc_buffer->markClean();
4029                                 reloadBuffer(*doc_buffer);
4030                                 dr.forceBufferUpdate();
4031                         }
4032                         break;
4033                 }
4034
4035                 case LFUN_BUFFER_WRITE:
4036                         LASSERT(doc_buffer, break);
4037                         saveBuffer(*doc_buffer);
4038                         break;
4039
4040                 case LFUN_BUFFER_WRITE_AS:
4041                         LASSERT(doc_buffer, break);
4042                         renameBuffer(*doc_buffer, cmd.argument());
4043                         break;
4044
4045                 case LFUN_BUFFER_WRITE_ALL: {
4046                         Buffer * first = theBufferList().first();
4047                         if (!first)
4048                                 break;
4049                         message(_("Saving all documents..."));
4050                         // We cannot use a for loop as the buffer list cycles.
4051                         Buffer * b = first;
4052                         do {
4053                                 if (!b->isClean()) {
4054                                         saveBuffer(*b);
4055                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
4056                                 }
4057                                 b = theBufferList().next(b);
4058                         } while (b != first);
4059                         dr.setMessage(_("All documents saved."));
4060                         break;
4061                 }
4062
4063                 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
4064                         LASSERT(doc_buffer, break);
4065                         doc_buffer->clearExternalModification();
4066                         break;
4067
4068                 case LFUN_BUFFER_CLOSE:
4069                         closeBuffer();
4070                         break;
4071
4072                 case LFUN_BUFFER_CLOSE_ALL:
4073                         closeBufferAll();
4074                         break;
4075
4076                 case LFUN_DEVEL_MODE_TOGGLE:
4077                         devel_mode_ = !devel_mode_;
4078                         if (devel_mode_)
4079                                 dr.setMessage(_("Developer mode is now enabled."));
4080                         else
4081                                 dr.setMessage(_("Developer mode is now disabled."));
4082                         break;
4083
4084                 case LFUN_TOOLBAR_TOGGLE: {
4085                         string const name = cmd.getArg(0);
4086                         if (GuiToolbar * t = toolbar(name))
4087                                 t->toggle();
4088                         break;
4089                 }
4090
4091                 case LFUN_TOOLBAR_MOVABLE: {
4092                         string const name = cmd.getArg(0);
4093                         if (name == "*") {
4094                                 // toggle (all) toolbars movablility
4095                                 toolbarsMovable_ = !toolbarsMovable_;
4096                                 for (ToolbarInfo const & ti : guiApp->toolbars()) {
4097                                         GuiToolbar * tb = toolbar(ti.name);
4098                                         if (tb && tb->isMovable() != toolbarsMovable_)
4099                                                 // toggle toolbar movablity if it does not fit lock
4100                                                 // (all) toolbars positions state silent = true, since
4101                                                 // status bar notifications are slow
4102                                                 tb->movable(true);
4103                                 }
4104                                 if (toolbarsMovable_)
4105                                         dr.setMessage(_("Toolbars unlocked."));
4106                                 else
4107                                         dr.setMessage(_("Toolbars locked."));
4108                         } else if (GuiToolbar * t = toolbar(name)) {
4109                                 // toggle current toolbar movablity
4110                                 t->movable();
4111                                 // update lock (all) toolbars positions
4112                                 updateLockToolbars();
4113                         }
4114                         break;
4115                 }
4116
4117                 case LFUN_ICON_SIZE: {
4118                         QSize size = d.iconSize(cmd.argument());
4119                         setIconSize(size);
4120                         dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
4121                                                 size.width(), size.height()));
4122                         break;
4123                 }
4124
4125                 case LFUN_DIALOG_UPDATE: {
4126                         string const name = to_utf8(cmd.argument());
4127                         if (name == "prefs" || name == "document")
4128                                 updateDialog(name, string());
4129                         else if (name == "paragraph")
4130                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4131                         else if (currentBufferView()) {
4132                                 Inset * inset = currentBufferView()->editedInset(name);
4133                                 // Can only update a dialog connected to an existing inset
4134                                 if (inset) {
4135                                         // FIXME: get rid of this indirection; GuiView ask the inset
4136                                         // if he is kind enough to update itself...
4137                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4138                                         //FIXME: pass DispatchResult here?
4139                                         inset->dispatch(currentBufferView()->cursor(), fr);
4140                                 }
4141                         }
4142                         break;
4143                 }
4144
4145                 case LFUN_DIALOG_TOGGLE: {
4146                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4147                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4148                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
4149                         break;
4150                 }
4151
4152                 case LFUN_DIALOG_DISCONNECT_INSET:
4153                         disconnectDialog(to_utf8(cmd.argument()));
4154                         break;
4155
4156                 case LFUN_DIALOG_HIDE: {
4157                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4158                         break;
4159                 }
4160
4161                 case LFUN_DIALOG_SHOW: {
4162                         string const name = cmd.getArg(0);
4163                         string sdata = trim(to_utf8(cmd.argument()).substr(name.size()));
4164
4165                         if (name == "latexlog") {
4166                                 // gettatus checks that
4167                                 LATTEST(doc_buffer);
4168                                 Buffer::LogType type;
4169                                 string const logfile = doc_buffer->logName(&type);
4170                                 switch (type) {
4171                                 case Buffer::latexlog:
4172                                         sdata = "latex ";
4173                                         break;
4174                                 case Buffer::buildlog:
4175                                         sdata = "literate ";
4176                                         break;
4177                                 }
4178                                 sdata += Lexer::quoteString(logfile);
4179                                 showDialog("log", sdata);
4180                         } else if (name == "vclog") {
4181                                 // getStatus checks that
4182                                 LATTEST(doc_buffer);
4183                                 string const sdata2 = "vc " +
4184                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4185                                 showDialog("log", sdata2);
4186                         } else if (name == "symbols") {
4187                                 sdata = bv->cursor().getEncoding()->name();
4188                                 if (!sdata.empty())
4189                                         showDialog("symbols", sdata);
4190                         // bug 5274
4191                         } else if (name == "prefs" && isFullScreen()) {
4192                                 lfunUiToggle("fullscreen");
4193                                 showDialog("prefs", sdata);
4194                         } else
4195                                 showDialog(name, sdata);
4196                         break;
4197                 }
4198
4199                 case LFUN_MESSAGE:
4200                         dr.setMessage(cmd.argument());
4201                         break;
4202
4203                 case LFUN_UI_TOGGLE: {
4204                         string arg = cmd.getArg(0);
4205                         if (!lfunUiToggle(arg)) {
4206                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4207                                 dr.setMessage(bformat(msg, from_utf8(arg)));
4208                         }
4209                         // Make sure the keyboard focus stays in the work area.
4210                         setFocus();
4211                         break;
4212                 }
4213
4214                 case LFUN_VIEW_SPLIT: {
4215                         LASSERT(doc_buffer, break);
4216                         string const orientation = cmd.getArg(0);
4217                         d.splitter_->setOrientation(orientation == "vertical"
4218                                 ? Qt::Vertical : Qt::Horizontal);
4219                         TabWorkArea * twa = addTabWorkArea();
4220                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4221                         setCurrentWorkArea(wa);
4222                         break;
4223                 }
4224                 case LFUN_TAB_GROUP_CLOSE:
4225                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4226                                 closeTabWorkArea(twa);
4227                                 d.current_work_area_ = 0;
4228                                 twa = d.currentTabWorkArea();
4229                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4230                                 if (twa) {
4231                                         // Make sure the work area is up to date.
4232                                         setCurrentWorkArea(twa->currentWorkArea());
4233                                 } else {
4234                                         setCurrentWorkArea(0);
4235                                 }
4236                         }
4237                         break;
4238
4239                 case LFUN_VIEW_CLOSE:
4240                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4241                                 closeWorkArea(twa->currentWorkArea());
4242                                 d.current_work_area_ = 0;
4243                                 twa = d.currentTabWorkArea();
4244                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4245                                 if (twa) {
4246                                         // Make sure the work area is up to date.
4247                                         setCurrentWorkArea(twa->currentWorkArea());
4248                                 } else {
4249                                         setCurrentWorkArea(0);
4250                                 }
4251                         }
4252                         break;
4253
4254                 case LFUN_COMPLETION_INLINE:
4255                         if (d.current_work_area_)
4256                                 d.current_work_area_->completer().showInline();
4257                         break;
4258
4259                 case LFUN_COMPLETION_POPUP:
4260                         if (d.current_work_area_)
4261                                 d.current_work_area_->completer().showPopup();
4262                         break;
4263
4264
4265                 case LFUN_COMPLETE:
4266                         if (d.current_work_area_)
4267                                 d.current_work_area_->completer().tab();
4268                         break;
4269
4270                 case LFUN_COMPLETION_CANCEL:
4271                         if (d.current_work_area_) {
4272                                 if (d.current_work_area_->completer().popupVisible())
4273                                         d.current_work_area_->completer().hidePopup();
4274                                 else
4275                                         d.current_work_area_->completer().hideInline();
4276                         }
4277                         break;
4278
4279                 case LFUN_COMPLETION_ACCEPT:
4280                         if (d.current_work_area_)
4281                                 d.current_work_area_->completer().activate();
4282                         break;
4283
4284                 case LFUN_BUFFER_ZOOM_IN:
4285                 case LFUN_BUFFER_ZOOM_OUT:
4286                 case LFUN_BUFFER_ZOOM: {
4287                         if (cmd.argument().empty()) {
4288                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4289                                         zoom_ratio_ = 1.0;
4290                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4291                                         zoom_ratio_ += 0.1;
4292                                 else
4293                                         zoom_ratio_ -= 0.1;
4294                         } else {
4295                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4296                                         zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4297                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4298                                         zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4299                                 else
4300                                         zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4301                         }
4302
4303                         // Actual zoom value: default zoom + fractional extra value
4304                         int zoom = lyxrc.defaultZoom * zoom_ratio_;
4305                         if (zoom < static_cast<int>(zoom_min_))
4306                                 zoom = zoom_min_;
4307
4308                         lyxrc.currentZoom = zoom;
4309
4310                         dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4311                                               lyxrc.currentZoom, lyxrc.defaultZoom));
4312
4313                         guiApp->fontLoader().update();
4314                         dr.screenUpdate(Update::Force | Update::FitCursor);
4315                         break;
4316                 }
4317
4318                 case LFUN_VC_REGISTER:
4319                 case LFUN_VC_RENAME:
4320                 case LFUN_VC_COPY:
4321                 case LFUN_VC_CHECK_IN:
4322                 case LFUN_VC_CHECK_OUT:
4323                 case LFUN_VC_REPO_UPDATE:
4324                 case LFUN_VC_LOCKING_TOGGLE:
4325                 case LFUN_VC_REVERT:
4326                 case LFUN_VC_UNDO_LAST:
4327                 case LFUN_VC_COMMAND:
4328                 case LFUN_VC_COMPARE:
4329                         dispatchVC(cmd, dr);
4330                         break;
4331
4332                 case LFUN_SERVER_GOTO_FILE_ROW:
4333                         if(goToFileRow(to_utf8(cmd.argument())))
4334                                 dr.screenUpdate(Update::Force | Update::FitCursor);
4335                         break;
4336
4337                 case LFUN_LYX_ACTIVATE:
4338                         activateWindow();
4339                         break;
4340
4341                 case LFUN_FORWARD_SEARCH: {
4342                         // it seems safe to assume we have a document buffer, since
4343                         // getStatus wants one.
4344                         LATTEST(doc_buffer);
4345                         Buffer const * doc_master = doc_buffer->masterBuffer();
4346                         FileName const path(doc_master->temppath());
4347                         string const texname = doc_master->isChild(doc_buffer)
4348                                 ? DocFileName(changeExtension(
4349                                         doc_buffer->absFileName(),
4350                                                 "tex")).mangledFileName()
4351                                 : doc_buffer->latexName();
4352                         string const fulltexname =
4353                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4354                         string const mastername =
4355                                 removeExtension(doc_master->latexName());
4356                         FileName const dviname(addName(path.absFileName(),
4357                                         addExtension(mastername, "dvi")));
4358                         FileName const pdfname(addName(path.absFileName(),
4359                                         addExtension(mastername, "pdf")));
4360                         bool const have_dvi = dviname.exists();
4361                         bool const have_pdf = pdfname.exists();
4362                         if (!have_dvi && !have_pdf) {
4363                                 dr.setMessage(_("Please, preview the document first."));
4364                                 break;
4365                         }
4366                         string outname = dviname.onlyFileName();
4367                         string command = lyxrc.forward_search_dvi;
4368                         if (!have_dvi || (have_pdf &&
4369                             pdfname.lastModified() > dviname.lastModified())) {
4370                                 outname = pdfname.onlyFileName();
4371                                 command = lyxrc.forward_search_pdf;
4372                         }
4373
4374                         DocIterator cur = bv->cursor();
4375                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4376                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4377                                    << " cur:" << cur);
4378                         if (row == -1 || command.empty()) {
4379                                 dr.setMessage(_("Couldn't proceed."));
4380                                 break;
4381                         }
4382                         string texrow = convert<string>(row);
4383
4384                         command = subst(command, "$$n", texrow);
4385                         command = subst(command, "$$f", fulltexname);
4386                         command = subst(command, "$$t", texname);
4387                         command = subst(command, "$$o", outname);
4388
4389                         PathChanger p(path);
4390                         Systemcall one;
4391                         one.startscript(Systemcall::DontWait, command);
4392                         break;
4393                 }
4394
4395                 case LFUN_SPELLING_CONTINUOUSLY:
4396                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4397                         dr.screenUpdate(Update::Force);
4398                         break;
4399
4400                 default:
4401                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4402                         // let's try that:
4403                         dispatchToBufferView(cmd, dr);
4404                         break;
4405         }
4406
4407         // Part of automatic menu appearance feature.
4408         if (isFullScreen()) {
4409                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4410                         menuBar()->hide();
4411         }
4412
4413         // Need to update bv because many LFUNs here might have destroyed it
4414         bv = currentBufferView();
4415
4416         // Clear non-empty selections
4417         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4418         if (bv) {
4419                 Cursor & cur = bv->cursor();
4420                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4421                         cur.clearSelection();
4422                 }
4423         }
4424 }
4425
4426
4427 bool GuiView::lfunUiToggle(string const & ui_component)
4428 {
4429         if (ui_component == "scrollbar") {
4430                 // hide() is of no help
4431                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4432                         Qt::ScrollBarAlwaysOff)
4433
4434                         d.current_work_area_->setVerticalScrollBarPolicy(
4435                                 Qt::ScrollBarAsNeeded);
4436                 else
4437                         d.current_work_area_->setVerticalScrollBarPolicy(
4438                                 Qt::ScrollBarAlwaysOff);
4439         } else if (ui_component == "statusbar") {
4440                 statusBar()->setVisible(!statusBar()->isVisible());
4441         } else if (ui_component == "menubar") {
4442                 menuBar()->setVisible(!menuBar()->isVisible());
4443         } else
4444         if (ui_component == "frame") {
4445                 int l, t, r, b;
4446                 getContentsMargins(&l, &t, &r, &b);
4447                 //are the frames in default state?
4448                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4449                 if (l == 0) {
4450                         setContentsMargins(-2, -2, -2, -2);
4451                 } else {
4452                         setContentsMargins(0, 0, 0, 0);
4453                 }
4454         } else
4455         if (ui_component == "fullscreen") {
4456                 toggleFullScreen();
4457         } else
4458                 return false;
4459         return true;
4460 }
4461
4462
4463 void GuiView::toggleFullScreen()
4464 {
4465         if (isFullScreen()) {
4466                 for (int i = 0; i != d.splitter_->count(); ++i)
4467                         d.tabWorkArea(i)->setFullScreen(false);
4468                 setContentsMargins(0, 0, 0, 0);
4469                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4470                 restoreLayout();
4471                 menuBar()->show();
4472                 statusBar()->show();
4473         } else {
4474                 // bug 5274
4475                 hideDialogs("prefs", 0);
4476                 for (int i = 0; i != d.splitter_->count(); ++i)
4477                         d.tabWorkArea(i)->setFullScreen(true);
4478                 setContentsMargins(-2, -2, -2, -2);
4479                 saveLayout();
4480                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4481                 if (lyxrc.full_screen_statusbar)
4482                         statusBar()->hide();
4483                 if (lyxrc.full_screen_menubar)
4484                         menuBar()->hide();
4485                 if (lyxrc.full_screen_toolbars) {
4486                         ToolbarMap::iterator end = d.toolbars_.end();
4487                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4488                                 it->second->hide();
4489                 }
4490         }
4491
4492         // give dialogs like the TOC a chance to adapt
4493         updateDialogs();
4494 }
4495
4496
4497 Buffer const * GuiView::updateInset(Inset const * inset)
4498 {
4499         if (!inset)
4500                 return 0;
4501
4502         Buffer const * inset_buffer = &(inset->buffer());
4503
4504         for (int i = 0; i != d.splitter_->count(); ++i) {
4505                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4506                 if (!wa)
4507                         continue;
4508                 Buffer const * buffer = &(wa->bufferView().buffer());
4509                 if (inset_buffer == buffer)
4510                         wa->scheduleRedraw(true);
4511         }
4512         return inset_buffer;
4513 }
4514
4515
4516 void GuiView::restartCaret()
4517 {
4518         /* When we move around, or type, it's nice to be able to see
4519          * the caret immediately after the keypress.
4520          */
4521         if (d.current_work_area_)
4522                 d.current_work_area_->startBlinkingCaret();
4523
4524         // Take this occasion to update the other GUI elements.
4525         updateDialogs();
4526         updateStatusBar();
4527 }
4528
4529
4530 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4531 {
4532         if (d.current_work_area_)
4533                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4534 }
4535
4536 namespace {
4537
4538 // This list should be kept in sync with the list of insets in
4539 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4540 // dialog should have the same name as the inset.
4541 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4542 // docs in LyXAction.cpp.
4543
4544 char const * const dialognames[] = {
4545
4546 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4547 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4548 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4549 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4550 "log", "lyxfiles", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4551 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4552 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4553 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4554
4555 char const * const * const end_dialognames =
4556         dialognames + (sizeof(dialognames) / sizeof(char *));
4557
4558 class cmpCStr {
4559 public:
4560         cmpCStr(char const * name) : name_(name) {}
4561         bool operator()(char const * other) {
4562                 return strcmp(other, name_) == 0;
4563         }
4564 private:
4565         char const * name_;
4566 };
4567
4568
4569 bool isValidName(string const & name)
4570 {
4571         return find_if(dialognames, end_dialognames,
4572                                 cmpCStr(name.c_str())) != end_dialognames;
4573 }
4574
4575 } // namespace
4576
4577
4578 void GuiView::resetDialogs()
4579 {
4580         // Make sure that no LFUN uses any GuiView.
4581         guiApp->setCurrentView(0);
4582         saveLayout();
4583         saveUISettings();
4584         menuBar()->clear();
4585         constructToolbars();
4586         guiApp->menus().fillMenuBar(menuBar(), this, false);
4587         d.layout_->updateContents(true);
4588         // Now update controls with current buffer.
4589         guiApp->setCurrentView(this);
4590         restoreLayout();
4591         restartCaret();
4592 }
4593
4594
4595 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4596 {
4597         if (!isValidName(name))
4598                 return 0;
4599
4600         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4601
4602         if (it != d.dialogs_.end()) {
4603                 if (hide_it)
4604                         it->second->hideView();
4605                 return it->second.get();
4606         }
4607
4608         Dialog * dialog = build(name);
4609         d.dialogs_[name].reset(dialog);
4610         if (lyxrc.allow_geometry_session)
4611                 dialog->restoreSession();
4612         if (hide_it)
4613                 dialog->hideView();
4614         return dialog;
4615 }
4616
4617
4618 void GuiView::showDialog(string const & name, string const & sdata,
4619         Inset * inset)
4620 {
4621         triggerShowDialog(toqstr(name), toqstr(sdata), inset);
4622 }
4623
4624
4625 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4626         Inset * inset)
4627 {
4628         if (d.in_show_)
4629                 return;
4630
4631         const string name = fromqstr(qname);
4632         const string sdata = fromqstr(qdata);
4633
4634         d.in_show_ = true;
4635         try {
4636                 Dialog * dialog = findOrBuild(name, false);
4637                 if (dialog) {
4638                         bool const visible = dialog->isVisibleView();
4639                         dialog->showData(sdata);
4640                         if (currentBufferView())
4641                                 currentBufferView()->editInset(name, inset);
4642                         // We only set the focus to the new dialog if it was not yet
4643                         // visible in order not to change the existing previous behaviour
4644                         if (visible) {
4645                                 // activateWindow is needed for floating dockviews
4646                                 dialog->asQWidget()->raise();
4647                                 dialog->asQWidget()->activateWindow();
4648                                 dialog->asQWidget()->setFocus();
4649                         }
4650                 }
4651         }
4652         catch (ExceptionMessage const & ex) {
4653                 d.in_show_ = false;
4654                 throw ex;
4655         }
4656         d.in_show_ = false;
4657 }
4658
4659
4660 bool GuiView::isDialogVisible(string const & name) const
4661 {
4662         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4663         if (it == d.dialogs_.end())
4664                 return false;
4665         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4666 }
4667
4668
4669 void GuiView::hideDialog(string const & name, Inset * inset)
4670 {
4671         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4672         if (it == d.dialogs_.end())
4673                 return;
4674
4675         if (inset) {
4676                 if (!currentBufferView())
4677                         return;
4678                 if (inset != currentBufferView()->editedInset(name))
4679                         return;
4680         }
4681
4682         Dialog * const dialog = it->second.get();
4683         if (dialog->isVisibleView())
4684                 dialog->hideView();
4685         if (currentBufferView())
4686                 currentBufferView()->editInset(name, 0);
4687 }
4688
4689
4690 void GuiView::disconnectDialog(string const & name)
4691 {
4692         if (!isValidName(name))
4693                 return;
4694         if (currentBufferView())
4695                 currentBufferView()->editInset(name, 0);
4696 }
4697
4698
4699 void GuiView::hideAll() const
4700 {
4701         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4702         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4703
4704         for(; it != end; ++it)
4705                 it->second->hideView();
4706 }
4707
4708
4709 void GuiView::updateDialogs()
4710 {
4711         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4712         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4713
4714         for(; it != end; ++it) {
4715                 Dialog * dialog = it->second.get();
4716                 if (dialog) {
4717                         if (dialog->needBufferOpen() && !documentBufferView())
4718                                 hideDialog(fromqstr(dialog->name()), 0);
4719                         else if (dialog->isVisibleView())
4720                                 dialog->checkStatus();
4721                 }
4722         }
4723         updateToolbars();
4724         updateLayoutList();
4725 }
4726
4727 Dialog * createDialog(GuiView & lv, string const & name);
4728
4729 // will be replaced by a proper factory...
4730 Dialog * createGuiAbout(GuiView & lv);
4731 Dialog * createGuiBibtex(GuiView & lv);
4732 Dialog * createGuiChanges(GuiView & lv);
4733 Dialog * createGuiCharacter(GuiView & lv);
4734 Dialog * createGuiCitation(GuiView & lv);
4735 Dialog * createGuiCompare(GuiView & lv);
4736 Dialog * createGuiCompareHistory(GuiView & lv);
4737 Dialog * createGuiDelimiter(GuiView & lv);
4738 Dialog * createGuiDocument(GuiView & lv);
4739 Dialog * createGuiErrorList(GuiView & lv);
4740 Dialog * createGuiExternal(GuiView & lv);
4741 Dialog * createGuiGraphics(GuiView & lv);
4742 Dialog * createGuiInclude(GuiView & lv);
4743 Dialog * createGuiIndex(GuiView & lv);
4744 Dialog * createGuiListings(GuiView & lv);
4745 Dialog * createGuiLog(GuiView & lv);
4746 Dialog * createGuiLyXFiles(GuiView & lv);
4747 Dialog * createGuiMathMatrix(GuiView & lv);
4748 Dialog * createGuiNote(GuiView & lv);
4749 Dialog * createGuiParagraph(GuiView & lv);
4750 Dialog * createGuiPhantom(GuiView & lv);
4751 Dialog * createGuiPreferences(GuiView & lv);
4752 Dialog * createGuiPrint(GuiView & lv);
4753 Dialog * createGuiPrintindex(GuiView & lv);
4754 Dialog * createGuiRef(GuiView & lv);
4755 Dialog * createGuiSearch(GuiView & lv);
4756 Dialog * createGuiSearchAdv(GuiView & lv);
4757 Dialog * createGuiSendTo(GuiView & lv);
4758 Dialog * createGuiShowFile(GuiView & lv);
4759 Dialog * createGuiSpellchecker(GuiView & lv);
4760 Dialog * createGuiSymbols(GuiView & lv);
4761 Dialog * createGuiTabularCreate(GuiView & lv);
4762 Dialog * createGuiTexInfo(GuiView & lv);
4763 Dialog * createGuiToc(GuiView & lv);
4764 Dialog * createGuiThesaurus(GuiView & lv);
4765 Dialog * createGuiViewSource(GuiView & lv);
4766 Dialog * createGuiWrap(GuiView & lv);
4767 Dialog * createGuiProgressView(GuiView & lv);
4768
4769
4770
4771 Dialog * GuiView::build(string const & name)
4772 {
4773         LASSERT(isValidName(name), return 0);
4774
4775         Dialog * dialog = createDialog(*this, name);
4776         if (dialog)
4777                 return dialog;
4778
4779         if (name == "aboutlyx")
4780                 return createGuiAbout(*this);
4781         if (name == "bibtex")
4782                 return createGuiBibtex(*this);
4783         if (name == "changes")
4784                 return createGuiChanges(*this);
4785         if (name == "character")
4786                 return createGuiCharacter(*this);
4787         if (name == "citation")
4788                 return createGuiCitation(*this);
4789         if (name == "compare")
4790                 return createGuiCompare(*this);
4791         if (name == "comparehistory")
4792                 return createGuiCompareHistory(*this);
4793         if (name == "document")
4794                 return createGuiDocument(*this);
4795         if (name == "errorlist")
4796                 return createGuiErrorList(*this);
4797         if (name == "external")
4798                 return createGuiExternal(*this);
4799         if (name == "file")
4800                 return createGuiShowFile(*this);
4801         if (name == "findreplace")
4802                 return createGuiSearch(*this);
4803         if (name == "findreplaceadv")
4804                 return createGuiSearchAdv(*this);
4805         if (name == "graphics")
4806                 return createGuiGraphics(*this);
4807         if (name == "include")
4808                 return createGuiInclude(*this);
4809         if (name == "index")
4810                 return createGuiIndex(*this);
4811         if (name == "index_print")
4812                 return createGuiPrintindex(*this);
4813         if (name == "listings")
4814                 return createGuiListings(*this);
4815         if (name == "log")
4816                 return createGuiLog(*this);
4817         if (name == "lyxfiles")
4818                 return createGuiLyXFiles(*this);
4819         if (name == "mathdelimiter")
4820                 return createGuiDelimiter(*this);
4821         if (name == "mathmatrix")
4822                 return createGuiMathMatrix(*this);
4823         if (name == "note")
4824                 return createGuiNote(*this);
4825         if (name == "paragraph")
4826                 return createGuiParagraph(*this);
4827         if (name == "phantom")
4828                 return createGuiPhantom(*this);
4829         if (name == "prefs")
4830                 return createGuiPreferences(*this);
4831         if (name == "ref")
4832                 return createGuiRef(*this);
4833         if (name == "sendto")
4834                 return createGuiSendTo(*this);
4835         if (name == "spellchecker")
4836                 return createGuiSpellchecker(*this);
4837         if (name == "symbols")
4838                 return createGuiSymbols(*this);
4839         if (name == "tabularcreate")
4840                 return createGuiTabularCreate(*this);
4841         if (name == "texinfo")
4842                 return createGuiTexInfo(*this);
4843         if (name == "thesaurus")
4844                 return createGuiThesaurus(*this);
4845         if (name == "toc")
4846                 return createGuiToc(*this);
4847         if (name == "view-source")
4848                 return createGuiViewSource(*this);
4849         if (name == "wrap")
4850                 return createGuiWrap(*this);
4851         if (name == "progress")
4852                 return createGuiProgressView(*this);
4853
4854         return 0;
4855 }
4856
4857
4858 SEMenu::SEMenu(QWidget * parent)
4859 {
4860         QAction * action = addAction(qt_("Disable Shell Escape"));
4861         connect(action, SIGNAL(triggered()),
4862                 parent, SLOT(disableShellEscape()));
4863 }
4864
4865 } // namespace frontend
4866 } // namespace lyx
4867
4868 #include "moc_GuiView.cpp"