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