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