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