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