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