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