]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Merge branch '2.3.2-staging' into 2.3.x
[lyx.git] / src / frontends / qt4 / GuiView.cpp
1 /**
2  * \file GuiView.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author Abdelrazak Younes
9  * \author Peter Kümmel
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiView.h"
17
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToc.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
30 #include "Menus.h"
31 #include "TocModel.h"
32
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
35
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
38
39 #include "buffer_funcs.h"
40 #include "Buffer.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
44 #include "Compare.h"
45 #include "Converter.h"
46 #include "Cursor.h"
47 #include "CutAndPaste.h"
48 #include "Encoding.h"
49 #include "ErrorList.h"
50 #include "Format.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
53 #include "Intl.h"
54 #include "Layout.h"
55 #include "Lexer.h"
56 #include "LyXAction.h"
57 #include "LyX.h"
58 #include "LyXRC.h"
59 #include "LyXVC.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
62 #include "Session.h"
63 #include "TexRow.h"
64 #include "TextClass.h"
65 #include "Text.h"
66 #include "Toolbars.h"
67 #include "version.h"
68
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
85
86 #include <QAction>
87 #include <QApplication>
88 #include <QCloseEvent>
89 #include <QDebug>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
92 #include <QDropEvent>
93 #include <QFuture>
94 #include <QFutureWatcher>
95 #include <QLabel>
96 #include <QList>
97 #include <QMenu>
98 #include <QMenuBar>
99 #include <QMimeData>
100 #include <QMovie>
101 #include <QPainter>
102 #include <QPixmap>
103 #include <QPixmapCache>
104 #include <QPoint>
105 #include <QPushButton>
106 #include <QScrollBar>
107 #include <QSettings>
108 #include <QShowEvent>
109 #include <QSplitter>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
114 #include <QTime>
115 #include <QTimer>
116 #include <QToolBar>
117 #include <QUrl>
118
119
120
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
123
124
125 #include "support/bind.h"
126
127 #include <sstream>
128
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
131 #endif
132 #ifdef HAVE_UNISTD_H
133 # include <unistd.h>
134 #endif
135
136
137 using namespace std;
138 using namespace lyx::support;
139
140 namespace lyx {
141
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
145
146 namespace frontend {
147
148 namespace {
149
150 class BackgroundWidget : public QWidget
151 {
152 public:
153         BackgroundWidget(int width, int height)
154                 : width_(width), height_(height)
155         {
156                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157                 if (!lyxrc.show_banner)
158                         return;
159                 /// The text to be written on top of the pixmap
160                 QString const text = lyx_version ?
161                         qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163                 QString imagedir = "images/";
164                 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165                 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166                 if (svgRenderer.isValid()) {
167                         splash_ = QPixmap(splashSize());
168                         QPainter painter(&splash_);
169                         svgRenderer.render(&painter);
170                         splash_.setDevicePixelRatio(pixelRatio());
171                 } else {
172                         splash_ = getPixmap("images/", "banner", "png");
173                 }
174 #else
175                 splash_ = getPixmap("images/", "banner", "svgz,png");
176 #endif
177
178                 QPainter pain(&splash_);
179                 pain.setPen(QColor(0, 0, 0));
180                 qreal const fsize = fontSize();
181                 QPointF const position = textPosition();
182                 LYXERR(Debug::GUI,
183                         "widget pixel ratio: " << pixelRatio() <<
184                         " splash pixel ratio: " << splashPixelRatio() <<
185                         " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
186                 QFont font;
187                 // The font used to display the version info
188                 font.setStyleHint(QFont::SansSerif);
189                 font.setWeight(QFont::Bold);
190                 font.setPointSizeF(fsize);
191                 pain.setFont(font);
192                 pain.drawText(position, text);
193                 setFocusPolicy(Qt::StrongFocus);
194         }
195
196         void paintEvent(QPaintEvent *)
197         {
198                 int const w = width_;
199                 int const h = height_;
200                 int const x = (width() - w) / 2;
201                 int const y = (height() - h) / 2;
202                 LYXERR(Debug::GUI,
203                         "widget pixel ratio: " << pixelRatio() <<
204                         " splash pixel ratio: " << splashPixelRatio() <<
205                         " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
206                 QPainter pain(this);
207                 pain.drawPixmap(x, y, w, h, splash_);
208         }
209
210         void keyPressEvent(QKeyEvent * ev)
211         {
212                 KeySymbol sym;
213                 setKeySymbol(&sym, ev);
214                 if (sym.isOK()) {
215                         guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
216                         ev->accept();
217                 } else {
218                         ev->ignore();
219                 }
220         }
221
222 private:
223         QPixmap splash_;
224         int const width_;
225         int const height_;
226
227         /// Current ratio between physical pixels and device-independent pixels
228         double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230                 return qt_scale_factor * devicePixelRatio();
231 #else
232                 return 1.0;
233 #endif
234         }
235
236         qreal fontSize() const {
237                 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
238         }
239
240         QPointF textPosition() const {
241                 return QPointF(width_/2 - 18, height_/2 + 45);
242         }
243
244         QSize splashSize() const {
245                 return QSize(
246                         static_cast<unsigned int>(width_ * pixelRatio()),
247                         static_cast<unsigned int>(height_ * pixelRatio()));
248         }
249
250         /// Ratio between physical pixels and device-independent pixels of splash image
251         double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253                 return splash_.devicePixelRatio();
254 #else
255                 return 1.0;
256 #endif
257         }
258 };
259
260
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
263
264 typedef shared_ptr<Dialog> DialogPtr;
265
266 } // namespace
267
268
269 class GuiView::GuiViewPrivate
270 {
271         /// noncopyable
272         GuiViewPrivate(GuiViewPrivate const &);
273         void operator=(GuiViewPrivate const &);
274 public:
275         GuiViewPrivate(GuiView * gv)
276                 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277                 layout_(0), autosave_timeout_(5000),
278                 in_show_(false)
279         {
280                 // hardcode here the platform specific icon size
281                 smallIconSize = 16;  // scaling problems
282                 normalIconSize = 20; // ok, default if iconsize.png is missing
283                 bigIconSize = 26;       // better for some math icons
284                 hugeIconSize = 32;      // better for hires displays
285                 giantIconSize = 48;
286
287                 // if it exists, use width of iconsize.png as normal size
288                 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289                 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290                 if (!fn.empty()) {
291                         QImage image(toqstr(fn.absFileName()));
292                         if (image.width() < int(smallIconSize))
293                                 normalIconSize = smallIconSize;
294                         else if (image.width() > int(giantIconSize))
295                                 normalIconSize = giantIconSize;
296                         else
297                                 normalIconSize = image.width();
298                 }
299
300                 splitter_ = new QSplitter;
301                 bg_widget_ = new BackgroundWidget(400, 250);
302                 stack_widget_ = new QStackedWidget;
303                 stack_widget_->addWidget(bg_widget_);
304                 stack_widget_->addWidget(splitter_);
305                 setBackground();
306
307                 // TODO cleanup, remove the singleton, handle multiple Windows?
308                 progress_ = ProgressInterface::instance();
309                 if (!dynamic_cast<GuiProgress*>(progress_)) {
310                         progress_ = new GuiProgress;  // TODO who deletes it
311                         ProgressInterface::setInstance(progress_);
312                 }
313                 QObject::connect(
314                                 dynamic_cast<GuiProgress*>(progress_),
315                                 SIGNAL(updateStatusBarMessage(QString const&)),
316                                 gv, SLOT(updateStatusBarMessage(QString const&)));
317                 QObject::connect(
318                                 dynamic_cast<GuiProgress*>(progress_),
319                                 SIGNAL(clearMessageText()),
320                                 gv, SLOT(clearMessageText()));
321         }
322
323         ~GuiViewPrivate()
324         {
325                 delete splitter_;
326                 delete bg_widget_;
327                 delete stack_widget_;
328         }
329
330         void setBackground()
331         {
332                 stack_widget_->setCurrentWidget(bg_widget_);
333                 bg_widget_->setUpdatesEnabled(true);
334                 bg_widget_->setFocus();
335         }
336
337         int tabWorkAreaCount()
338         {
339                 return splitter_->count();
340         }
341
342         TabWorkArea * tabWorkArea(int i)
343         {
344                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
345         }
346
347         TabWorkArea * currentTabWorkArea()
348         {
349                 int areas = tabWorkAreaCount();
350                 if (areas == 1)
351                         // The first TabWorkArea is always the first one, if any.
352                         return tabWorkArea(0);
353
354                 for (int i = 0; i != areas;  ++i) {
355                         TabWorkArea * twa = tabWorkArea(i);
356                         if (current_main_work_area_ == twa->currentWorkArea())
357                                 return twa;
358                 }
359
360                 // None has the focus so we just take the first one.
361                 return tabWorkArea(0);
362         }
363
364         int countWorkAreasOf(Buffer & buf)
365         {
366                 int areas = tabWorkAreaCount();
367                 int count = 0;
368                 for (int i = 0; i != areas;  ++i) {
369                         TabWorkArea * twa = tabWorkArea(i);
370                         if (twa->workArea(buf))
371                                 ++count;
372                 }
373                 return count;
374         }
375
376         void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
377         {
378                 if (processing_thread_watcher_.isRunning()) {
379                         // we prefer to cancel this preview in order to keep a snappy
380                         // interface.
381                         return;
382                 }
383                 processing_thread_watcher_.setFuture(f);
384         }
385
386         QSize iconSize(docstring const & icon_size)
387         {
388                 unsigned int size;
389                 if (icon_size == "small")
390                         size = smallIconSize;
391                 else if (icon_size == "normal")
392                         size = normalIconSize;
393                 else if (icon_size == "big")
394                         size = bigIconSize;
395                 else if (icon_size == "huge")
396                         size = hugeIconSize;
397                 else if (icon_size == "giant")
398                         size = giantIconSize;
399                 else
400                         size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
401
402                 if (size < smallIconSize)
403                         size = smallIconSize;
404
405                 return QSize(size, size);
406         }
407
408         QSize iconSize(QString const & icon_size)
409         {
410                 return iconSize(qstring_to_ucs4(icon_size));
411         }
412
413         string & iconSize(QSize const & qsize)
414         {
415                 LATTEST(qsize.width() == qsize.height());
416
417                 static string icon_size;
418
419                 unsigned int size = qsize.width();
420
421                 if (size < smallIconSize)
422                         size = smallIconSize;
423
424                 if (size == smallIconSize)
425                         icon_size = "small";
426                 else if (size == normalIconSize)
427                         icon_size = "normal";
428                 else if (size == bigIconSize)
429                         icon_size = "big";
430                 else if (size == hugeIconSize)
431                         icon_size = "huge";
432                 else if (size == giantIconSize)
433                         icon_size = "giant";
434                 else
435                         icon_size = convert<string>(size);
436
437                 return icon_size;
438         }
439
440 public:
441         GuiView * gv_;
442         GuiWorkArea * current_work_area_;
443         GuiWorkArea * current_main_work_area_;
444         QSplitter * splitter_;
445         QStackedWidget * stack_widget_;
446         BackgroundWidget * bg_widget_;
447         /// view's toolbars
448         ToolbarMap toolbars_;
449         ProgressInterface* progress_;
450         /// The main layout box.
451         /**
452          * \warning Don't Delete! The layout box is actually owned by
453          * whichever toolbar contains it. All the GuiView class needs is a
454          * means of accessing it.
455          *
456          * FIXME: replace that with a proper model so that we are not limited
457          * to only one dialog.
458          */
459         LayoutBox * layout_;
460
461         ///
462         map<string, DialogPtr> dialogs_;
463
464         unsigned int smallIconSize;
465         unsigned int normalIconSize;
466         unsigned int bigIconSize;
467         unsigned int hugeIconSize;
468         unsigned int giantIconSize;
469         ///
470         QTimer statusbar_timer_;
471         /// auto-saving of buffers
472         Timeout autosave_timeout_;
473         /// flag against a race condition due to multiclicks, see bug #1119
474         bool in_show_;
475
476         ///
477         TocModels toc_models_;
478
479         ///
480         QFutureWatcher<docstring> autosave_watcher_;
481         QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
482         ///
483         string last_export_format;
484         string processing_format;
485
486         static QSet<Buffer const *> busyBuffers;
487         static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488         static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489         static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490         static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
491
492         template<class T>
493         static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
494
495         // TODO syncFunc/previewFunc: use bind
496         bool asyncBufferProcessing(string const & argument,
497                                    Buffer const * used_buffer,
498                                    docstring const & msg,
499                                    Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500                                    Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501                                    Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
502
503         QVector<GuiWorkArea*> guiWorkAreas();
504 };
505
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
507
508
509 GuiView::GuiView(int id)
510         : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511           command_execute_(false), minibuffer_focus_(false), devel_mode_(false)
512 {
513         connect(this, SIGNAL(bufferViewChanged()),
514                 this, SLOT(onBufferViewChanged()));
515
516         // GuiToolbars *must* be initialised before the menu bar.
517         setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
518         constructToolbars();
519
520         // set ourself as the current view. This is needed for the menu bar
521         // filling, at least for the static special menu item on Mac. Otherwise
522         // they are greyed out.
523         guiApp->setCurrentView(this);
524
525         // Fill up the menu bar.
526         guiApp->menus().fillMenuBar(menuBar(), this, true);
527
528         setCentralWidget(d.stack_widget_);
529
530         // Start autosave timer
531         if (lyxrc.autosave) {
532                 // The connection is closed when this is destroyed.
533                 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535                 d.autosave_timeout_.start();
536         }
537         connect(&d.statusbar_timer_, SIGNAL(timeout()),
538                 this, SLOT(clearMessage()));
539
540         // We don't want to keep the window in memory if it is closed.
541         setAttribute(Qt::WA_DeleteOnClose, true);
542
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544         // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546         // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547         // since the icon is provided in the application bundle. We use a themed
548         // version when available and use the bundled one as fallback.
549         setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 #else
551         setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
552 #endif
553
554 #endif
555         resetWindowTitle();
556
557         // use tabbed dock area for multiple docks
558         // (such as "source" and "messages")
559         setDockOptions(QMainWindow::ForceTabbedDocks);
560
561         // For Drag&Drop.
562         setAcceptDrops(true);
563
564         // add busy indicator to statusbar
565         QLabel * busylabel = new QLabel(statusBar());
566         statusBar()->addPermanentWidget(busylabel);
567         search_mode mode = theGuiApp()->imageSearchMode();
568         QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569         QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570         busylabel->setMovie(busyanim);
571         busyanim->start();
572         busylabel->hide();
573
574         connect(&d.processing_thread_watcher_, SIGNAL(started()),
575                 busylabel, SLOT(show()));
576         connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577                 busylabel, SLOT(hide()));
578
579         QFontMetrics const fm(statusBar()->fontMetrics());
580         int const iconheight = max(int(d.normalIconSize), fm.height());
581         QSize const iconsize(iconheight, iconheight);
582
583         QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
584         shell_escape_ = new QLabel(statusBar());
585         shell_escape_->setPixmap(shellescape);
586         shell_escape_->setScaledContents(true);
587         shell_escape_->setAlignment(Qt::AlignCenter);
588         shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
589         shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
590                                       "external commands for this document. "
591                                       "Right click to change."));
592         SEMenu * menu = new SEMenu(this);
593         connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
594                 menu, SLOT(showMenu(QPoint)));
595         shell_escape_->hide();
596         statusBar()->addPermanentWidget(shell_escape_);
597
598         QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
599         read_only_ = new QLabel(statusBar());
600         read_only_->setPixmap(readonly);
601         read_only_->setScaledContents(true);
602         read_only_->setAlignment(Qt::AlignCenter);
603         read_only_->hide();
604         statusBar()->addPermanentWidget(read_only_);
605
606         version_control_ = new QLabel(statusBar());
607         version_control_->setAlignment(Qt::AlignCenter);
608         version_control_->setFrameStyle(QFrame::StyledPanel);
609         version_control_->hide();
610         statusBar()->addPermanentWidget(version_control_);
611
612         statusBar()->setSizeGripEnabled(true);
613         updateStatusBar();
614
615         connect(&d.autosave_watcher_, SIGNAL(finished()), this,
616                 SLOT(autoSaveThreadFinished()));
617
618         connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
619                 SLOT(processingThreadStarted()));
620         connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
621                 SLOT(processingThreadFinished()));
622
623         connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
624                 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
625
626         // set custom application bars context menu, e.g. tool bar and menu bar
627         setContextMenuPolicy(Qt::CustomContextMenu);
628         connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
629                 SLOT(toolBarPopup(const QPoint &)));
630
631         // Forbid too small unresizable window because it can happen
632         // with some window manager under X11.
633         setMinimumSize(300, 200);
634
635         if (lyxrc.allow_geometry_session) {
636                 // Now take care of session management.
637                 if (restoreLayout())
638                         return;
639         }
640
641         // no session handling, default to a sane size.
642         setGeometry(50, 50, 690, 510);
643         initToolbars();
644
645         // clear session data if any.
646         QSettings settings;
647         settings.remove("views");
648 }
649
650
651 GuiView::~GuiView()
652 {
653         delete &d;
654 }
655
656
657 void GuiView::disableShellEscape()
658 {
659         BufferView * bv = documentBufferView();
660         if (!bv)
661                 return;
662         theSession().shellescapeFiles().remove(bv->buffer().absFileName());
663         bv->buffer().params().shell_escape = false;
664         bv->processUpdateFlags(Update::Force);
665 }
666
667
668 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
669 {
670         QVector<GuiWorkArea*> areas;
671         for (int i = 0; i < tabWorkAreaCount(); i++) {
672                 TabWorkArea* ta = tabWorkArea(i);
673                 for (int u = 0; u < ta->count(); u++) {
674                         areas << ta->workArea(u);
675                 }
676         }
677         return areas;
678 }
679
680 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
681         string const & format)
682 {
683         docstring const fmt = theFormats().prettyName(format);
684         docstring msg;
685         switch (status) {
686         case Buffer::ExportSuccess:
687                 msg = bformat(_("Successful export to format: %1$s"), fmt);
688                 break;
689         case Buffer::ExportCancel:
690                 msg = _("Document export cancelled.");
691                 break;
692         case Buffer::ExportError:
693         case Buffer::ExportNoPathToFormat:
694         case Buffer::ExportTexPathHasSpaces:
695         case Buffer::ExportConverterError:
696                 msg = bformat(_("Error while exporting format: %1$s"), fmt);
697                 break;
698         case Buffer::PreviewSuccess:
699                 msg = bformat(_("Successful preview of format: %1$s"), fmt);
700                 break;
701         case Buffer::PreviewError:
702                 msg = bformat(_("Error while previewing format: %1$s"), fmt);
703                 break;
704         }
705         view->message(msg);
706 }
707
708
709 void GuiView::processingThreadStarted()
710 {
711 }
712
713
714 void GuiView::processingThreadFinished()
715 {
716         QFutureWatcher<Buffer::ExportStatus> const * watcher =
717                 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
718
719         Buffer::ExportStatus const status = watcher->result();
720         handleExportStatus(this, status, d.processing_format);
721
722         updateToolbars();
723         BufferView const * const bv = currentBufferView();
724         if (bv && !bv->buffer().errorList("Export").empty()) {
725                 errors("Export");
726                 return;
727         }
728         if (status != Buffer::ExportSuccess && status != Buffer::PreviewSuccess &&
729             status != Buffer::ExportCancel) {
730                 errors(d.last_export_format);
731         }
732 }
733
734
735 void GuiView::autoSaveThreadFinished()
736 {
737         QFutureWatcher<docstring> const * watcher =
738                 static_cast<QFutureWatcher<docstring> const *>(sender());
739         message(watcher->result());
740         updateToolbars();
741 }
742
743
744 void GuiView::saveLayout() const
745 {
746         QSettings settings;
747         settings.setValue("zoom_ratio", zoom_ratio_);
748         settings.setValue("devel_mode", devel_mode_);
749         settings.beginGroup("views");
750         settings.beginGroup(QString::number(id_));
751 #if defined(Q_WS_X11) || defined(QPA_XCB)
752         settings.setValue("pos", pos());
753         settings.setValue("size", size());
754 #else
755         settings.setValue("geometry", saveGeometry());
756 #endif
757         settings.setValue("layout", saveState(0));
758         settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
759 }
760
761
762 void GuiView::saveUISettings() const
763 {
764         QSettings settings;
765
766         // Save the toolbar private states
767         ToolbarMap::iterator end = d.toolbars_.end();
768         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
769                 it->second->saveSession(settings);
770         // Now take care of all other dialogs
771         map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
772         for (; it!= d.dialogs_.end(); ++it)
773                 it->second->saveSession(settings);
774 }
775
776
777 bool GuiView::restoreLayout()
778 {
779         QSettings settings;
780         zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
781         // Actual zoom value: default zoom + fractional offset
782         int zoom = lyxrc.defaultZoom * zoom_ratio_;
783         if (zoom < static_cast<int>(zoom_min_))
784                 zoom = zoom_min_;
785         lyxrc.currentZoom = zoom;
786         devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
787         settings.beginGroup("views");
788         settings.beginGroup(QString::number(id_));
789         QString const icon_key = "icon_size";
790         if (!settings.contains(icon_key))
791                 return false;
792
793         //code below is skipped when when ~/.config/LyX is (re)created
794         setIconSize(d.iconSize(settings.value(icon_key).toString()));
795
796 #if defined(Q_WS_X11) || defined(QPA_XCB)
797         QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
798         QSize size = settings.value("size", QSize(690, 510)).toSize();
799         resize(size);
800         move(pos);
801 #else
802         // Work-around for bug #6034: the window ends up in an undetermined
803         // state when trying to restore a maximized window when it is
804         // already maximized.
805         if (!(windowState() & Qt::WindowMaximized))
806                 if (!restoreGeometry(settings.value("geometry").toByteArray()))
807                         setGeometry(50, 50, 690, 510);
808 #endif
809         // Make sure layout is correctly oriented.
810         setLayoutDirection(qApp->layoutDirection());
811
812         // Allow the toc and view-source dock widget to be restored if needed.
813         Dialog * dialog;
814         if ((dialog = findOrBuild("toc", true)))
815                 // see bug 5082. At least setup title and enabled state.
816                 // Visibility will be adjusted by restoreState below.
817                 dialog->prepareView();
818         if ((dialog = findOrBuild("view-source", true)))
819                 dialog->prepareView();
820         if ((dialog = findOrBuild("progress", true)))
821                 dialog->prepareView();
822
823         if (!restoreState(settings.value("layout").toByteArray(), 0))
824                 initToolbars();
825
826         // init the toolbars that have not been restored
827         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
828         Toolbars::Infos::iterator end = guiApp->toolbars().end();
829         for (; cit != end; ++cit) {
830                 GuiToolbar * tb = toolbar(cit->name);
831                 if (tb && !tb->isRestored())
832                         initToolbar(cit->name);
833         }
834
835         // update lock (all) toolbars positions
836         updateLockToolbars();
837
838         updateDialogs();
839         return true;
840 }
841
842
843 GuiToolbar * GuiView::toolbar(string const & name)
844 {
845         ToolbarMap::iterator it = d.toolbars_.find(name);
846         if (it != d.toolbars_.end())
847                 return it->second;
848
849         LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
850         return 0;
851 }
852
853
854 void GuiView::updateLockToolbars()
855 {
856         toolbarsMovable_ = false;
857         for (ToolbarInfo const & info : guiApp->toolbars()) {
858                 GuiToolbar * tb = toolbar(info.name);
859                 if (tb && tb->isMovable())
860                         toolbarsMovable_ = true;
861         }
862 }
863
864
865 void GuiView::constructToolbars()
866 {
867         ToolbarMap::iterator it = d.toolbars_.begin();
868         for (; it != d.toolbars_.end(); ++it)
869                 delete it->second;
870         d.toolbars_.clear();
871
872         // I don't like doing this here, but the standard toolbar
873         // destroys this object when it's destroyed itself (vfr)
874         d.layout_ = new LayoutBox(*this);
875         d.stack_widget_->addWidget(d.layout_);
876         d.layout_->move(0,0);
877
878         // extracts the toolbars from the backend
879         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
880         Toolbars::Infos::iterator end = guiApp->toolbars().end();
881         for (; cit != end; ++cit)
882                 d.toolbars_[cit->name] =  new GuiToolbar(*cit, *this);
883 }
884
885
886 void GuiView::initToolbars()
887 {
888         // extracts the toolbars from the backend
889         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
890         Toolbars::Infos::iterator end = guiApp->toolbars().end();
891         for (; cit != end; ++cit)
892                 initToolbar(cit->name);
893 }
894
895
896 void GuiView::initToolbar(string const & name)
897 {
898         GuiToolbar * tb = toolbar(name);
899         if (!tb)
900                 return;
901         int const visibility = guiApp->toolbars().defaultVisibility(name);
902         bool newline = !(visibility & Toolbars::SAMEROW);
903         tb->setVisible(false);
904         tb->setVisibility(visibility);
905
906         if (visibility & Toolbars::TOP) {
907                 if (newline)
908                         addToolBarBreak(Qt::TopToolBarArea);
909                 addToolBar(Qt::TopToolBarArea, tb);
910         }
911
912         if (visibility & Toolbars::BOTTOM) {
913                 if (newline)
914                         addToolBarBreak(Qt::BottomToolBarArea);
915                 addToolBar(Qt::BottomToolBarArea, tb);
916         }
917
918         if (visibility & Toolbars::LEFT) {
919                 if (newline)
920                         addToolBarBreak(Qt::LeftToolBarArea);
921                 addToolBar(Qt::LeftToolBarArea, tb);
922         }
923
924         if (visibility & Toolbars::RIGHT) {
925                 if (newline)
926                         addToolBarBreak(Qt::RightToolBarArea);
927                 addToolBar(Qt::RightToolBarArea, tb);
928         }
929
930         if (visibility & Toolbars::ON)
931                 tb->setVisible(true);
932
933         tb->setMovable(true);
934 }
935
936
937 TocModels & GuiView::tocModels()
938 {
939         return d.toc_models_;
940 }
941
942
943 void GuiView::setFocus()
944 {
945         LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
946         QMainWindow::setFocus();
947 }
948
949
950 bool GuiView::hasFocus() const
951 {
952         if (currentWorkArea())
953                 return currentWorkArea()->hasFocus();
954         if (currentMainWorkArea())
955                 return currentMainWorkArea()->hasFocus();
956         return d.bg_widget_->hasFocus();
957 }
958
959
960 void GuiView::focusInEvent(QFocusEvent * e)
961 {
962         LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
963         QMainWindow::focusInEvent(e);
964         // Make sure guiApp points to the correct view.
965         guiApp->setCurrentView(this);
966         if (currentWorkArea())
967                 currentWorkArea()->setFocus();
968         else if (currentMainWorkArea())
969                 currentMainWorkArea()->setFocus();
970         else
971                 d.bg_widget_->setFocus();
972 }
973
974
975 void GuiView::showEvent(QShowEvent * e)
976 {
977         LYXERR(Debug::GUI, "Passed Geometry "
978                 << size().height() << "x" << size().width()
979                 << "+" << pos().x() << "+" << pos().y());
980
981         if (d.splitter_->count() == 0)
982                 // No work area, switch to the background widget.
983                 d.setBackground();
984
985         updateToolbars();
986         QMainWindow::showEvent(e);
987 }
988
989
990 bool GuiView::closeScheduled()
991 {
992         closing_ = true;
993         return close();
994 }
995
996
997 bool GuiView::prepareAllBuffersForLogout()
998 {
999         Buffer * first = theBufferList().first();
1000         if (!first)
1001                 return true;
1002
1003         // First, iterate over all buffers and ask the users if unsaved
1004         // changes should be saved.
1005         // We cannot use a for loop as the buffer list cycles.
1006         Buffer * b = first;
1007         do {
1008                 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1009                         return false;
1010                 b = theBufferList().next(b);
1011         } while (b != first);
1012
1013         // Next, save session state
1014         // When a view/window was closed before without quitting LyX, there
1015         // are already entries in the lastOpened list.
1016         theSession().lastOpened().clear();
1017         writeSession();
1018
1019         return true;
1020 }
1021
1022
1023 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1024  ** is responsibility of the container (e.g., dialog)
1025  **/
1026 void GuiView::closeEvent(QCloseEvent * close_event)
1027 {
1028         LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1029
1030         if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1031                 Alert::warning(_("Exit LyX"),
1032                         _("LyX could not be closed because documents are being processed by LyX."));
1033                 close_event->setAccepted(false);
1034                 return;
1035         }
1036
1037         // If the user pressed the x (so we didn't call closeView
1038         // programmatically), we want to clear all existing entries.
1039         if (!closing_)
1040                 theSession().lastOpened().clear();
1041         closing_ = true;
1042
1043         writeSession();
1044
1045         // it can happen that this event arrives without selecting the view,
1046         // e.g. when clicking the close button on a background window.
1047         setFocus();
1048         if (!closeWorkAreaAll()) {
1049                 closing_ = false;
1050                 close_event->ignore();
1051                 return;
1052         }
1053
1054         // Make sure that nothing will use this to be closed View.
1055         guiApp->unregisterView(this);
1056
1057         if (isFullScreen()) {
1058                 // Switch off fullscreen before closing.
1059                 toggleFullScreen();
1060                 updateDialogs();
1061         }
1062
1063         // Make sure the timer time out will not trigger a statusbar update.
1064         d.statusbar_timer_.stop();
1065
1066         // Saving fullscreen requires additional tweaks in the toolbar code.
1067         // It wouldn't also work under linux natively.
1068         if (lyxrc.allow_geometry_session) {
1069                 saveLayout();
1070                 saveUISettings();
1071         }
1072
1073         close_event->accept();
1074 }
1075
1076
1077 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1078 {
1079         if (event->mimeData()->hasUrls())
1080                 event->accept();
1081         /// \todo Ask lyx-devel is this is enough:
1082         /// if (event->mimeData()->hasFormat("text/plain"))
1083         ///     event->acceptProposedAction();
1084 }
1085
1086
1087 void GuiView::dropEvent(QDropEvent * event)
1088 {
1089         QList<QUrl> files = event->mimeData()->urls();
1090         if (files.isEmpty())
1091                 return;
1092
1093         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1094         for (int i = 0; i != files.size(); ++i) {
1095                 string const file = os::internal_path(fromqstr(
1096                         files.at(i).toLocalFile()));
1097                 if (file.empty())
1098                         continue;
1099
1100                 string const ext = support::getExtension(file);
1101                 vector<const Format *> found_formats;
1102
1103                 // Find all formats that have the correct extension.
1104                 vector<const Format *> const & import_formats
1105                         = theConverters().importableFormats();
1106                 vector<const Format *>::const_iterator it = import_formats.begin();
1107                 for (; it != import_formats.end(); ++it)
1108                         if ((*it)->hasExtension(ext))
1109                                 found_formats.push_back(*it);
1110
1111                 FuncRequest cmd;
1112                 if (found_formats.size() >= 1) {
1113                         if (found_formats.size() > 1) {
1114                                 //FIXME: show a dialog to choose the correct importable format
1115                                 LYXERR(Debug::FILES,
1116                                         "Multiple importable formats found, selecting first");
1117                         }
1118                         string const arg = found_formats[0]->name() + " " + file;
1119                         cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1120                 }
1121                 else {
1122                         //FIXME: do we have to explicitly check whether it's a lyx file?
1123                         LYXERR(Debug::FILES,
1124                                 "No formats found, trying to open it as a lyx file");
1125                         cmd = FuncRequest(LFUN_FILE_OPEN, file);
1126                 }
1127                 // add the functions to the queue
1128                 guiApp->addToFuncRequestQueue(cmd);
1129                 event->accept();
1130         }
1131         // now process the collected functions. We perform the events
1132         // asynchronously. This prevents potential problems in case the
1133         // BufferView is closed within an event.
1134         guiApp->processFuncRequestQueueAsync();
1135 }
1136
1137
1138 void GuiView::message(docstring const & str)
1139 {
1140         if (ForkedProcess::iAmAChild())
1141                 return;
1142
1143         // call is moved to GUI-thread by GuiProgress
1144         d.progress_->appendMessage(toqstr(str));
1145 }
1146
1147
1148 void GuiView::clearMessageText()
1149 {
1150         message(docstring());
1151 }
1152
1153
1154 void GuiView::updateStatusBarMessage(QString const & str)
1155 {
1156         statusBar()->showMessage(str);
1157         d.statusbar_timer_.stop();
1158         d.statusbar_timer_.start(3000);
1159 }
1160
1161
1162 void GuiView::clearMessage()
1163 {
1164         // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1165         // the hasFocus function mostly returns false, even if the focus is on
1166         // a workarea in this view.
1167         //if (!hasFocus())
1168         //      return;
1169         showMessage();
1170         d.statusbar_timer_.stop();
1171 }
1172
1173
1174 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1175 {
1176         if (wa != d.current_work_area_
1177                 || wa->bufferView().buffer().isInternal())
1178                 return;
1179         Buffer const & buf = wa->bufferView().buffer();
1180         // Set the windows title
1181         docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1182         if (buf.notifiesExternalModification()) {
1183                 title = bformat(_("%1$s (modified externally)"), title);
1184                 // If the external modification status has changed, then maybe the status of
1185                 // buffer-save has changed too.
1186                 updateToolbars();
1187         }
1188 #ifndef Q_WS_MAC
1189         title += from_ascii(" - LyX");
1190 #endif
1191         setWindowTitle(toqstr(title));
1192         // Sets the path for the window: this is used by OSX to
1193         // allow a context click on the title bar showing a menu
1194         // with the path up to the file
1195         setWindowFilePath(toqstr(buf.absFileName()));
1196         // Tell Qt whether the current document is changed
1197         setWindowModified(!buf.isClean());
1198
1199         if (buf.params().shell_escape)
1200                 shell_escape_->show();
1201         else
1202                 shell_escape_->hide();
1203
1204         if (buf.hasReadonlyFlag())
1205                 read_only_->show();
1206         else
1207                 read_only_->hide();
1208
1209         if (buf.lyxvc().inUse()) {
1210                 version_control_->show();
1211                 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1212         } else
1213                 version_control_->hide();
1214 }
1215
1216
1217 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1218 {
1219         if (d.current_work_area_)
1220                 // disconnect the current work area from all slots
1221                 QObject::disconnect(d.current_work_area_, 0, this, 0);
1222         disconnectBuffer();
1223         disconnectBufferView();
1224         connectBufferView(wa->bufferView());
1225         connectBuffer(wa->bufferView().buffer());
1226         d.current_work_area_ = wa;
1227         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1228                          this, SLOT(updateWindowTitle(GuiWorkArea *)));
1229         QObject::connect(wa, SIGNAL(busy(bool)),
1230                          this, SLOT(setBusy(bool)));
1231         // connection of a signal to a signal
1232         QObject::connect(wa, SIGNAL(bufferViewChanged()),
1233                          this, SIGNAL(bufferViewChanged()));
1234         Q_EMIT updateWindowTitle(wa);
1235         Q_EMIT bufferViewChanged();
1236 }
1237
1238
1239 void GuiView::onBufferViewChanged()
1240 {
1241         structureChanged();
1242         // Buffer-dependent dialogs must be updated. This is done here because
1243         // some dialogs require buffer()->text.
1244         updateDialogs();
1245 }
1246
1247
1248 void GuiView::on_lastWorkAreaRemoved()
1249 {
1250         if (closing_)
1251                 // We already are in a close event. Nothing more to do.
1252                 return;
1253
1254         if (d.splitter_->count() > 1)
1255                 // We have a splitter so don't close anything.
1256                 return;
1257
1258         // Reset and updates the dialogs.
1259         Q_EMIT bufferViewChanged();
1260
1261         resetWindowTitle();
1262         updateStatusBar();
1263
1264         if (lyxrc.open_buffers_in_tabs)
1265                 // Nothing more to do, the window should stay open.
1266                 return;
1267
1268         if (guiApp->viewIds().size() > 1) {
1269                 close();
1270                 return;
1271         }
1272
1273 #ifdef Q_OS_MAC
1274         // On Mac we also close the last window because the application stay
1275         // resident in memory. On other platforms we don't close the last
1276         // window because this would quit the application.
1277         close();
1278 #endif
1279 }
1280
1281
1282 void GuiView::updateStatusBar()
1283 {
1284         // let the user see the explicit message
1285         if (d.statusbar_timer_.isActive())
1286                 return;
1287
1288         showMessage();
1289 }
1290
1291
1292 void GuiView::showMessage()
1293 {
1294         if (busy_)
1295                 return;
1296         QString msg = toqstr(theGuiApp()->viewStatusMessage());
1297         if (msg.isEmpty()) {
1298                 BufferView const * bv = currentBufferView();
1299                 if (bv)
1300                         msg = toqstr(bv->cursor().currentState(devel_mode_));
1301                 else
1302                         msg = qt_("Welcome to LyX!");
1303         }
1304         statusBar()->showMessage(msg);
1305 }
1306
1307
1308 bool GuiView::event(QEvent * e)
1309 {
1310         switch (e->type())
1311         {
1312         // Useful debug code:
1313         //case QEvent::ActivationChange:
1314         //case QEvent::WindowDeactivate:
1315         //case QEvent::Paint:
1316         //case QEvent::Enter:
1317         //case QEvent::Leave:
1318         //case QEvent::HoverEnter:
1319         //case QEvent::HoverLeave:
1320         //case QEvent::HoverMove:
1321         //case QEvent::StatusTip:
1322         //case QEvent::DragEnter:
1323         //case QEvent::DragLeave:
1324         //case QEvent::Drop:
1325         //      break;
1326
1327         case QEvent::WindowActivate: {
1328                 GuiView * old_view = guiApp->currentView();
1329                 if (this == old_view) {
1330                         setFocus();
1331                         return QMainWindow::event(e);
1332                 }
1333                 if (old_view && old_view->currentBufferView()) {
1334                         // save current selection to the selection buffer to allow
1335                         // middle-button paste in this window.
1336                         cap::saveSelection(old_view->currentBufferView()->cursor());
1337                 }
1338                 guiApp->setCurrentView(this);
1339                 if (d.current_work_area_)
1340                         on_currentWorkAreaChanged(d.current_work_area_);
1341                 else
1342                         resetWindowTitle();
1343                 setFocus();
1344                 return QMainWindow::event(e);
1345         }
1346
1347         case QEvent::ShortcutOverride: {
1348                 // See bug 4888
1349                 if (isFullScreen() && menuBar()->isHidden()) {
1350                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1351                         // FIXME: we should also try to detect special LyX shortcut such as
1352                         // Alt-P and Alt-M. Right now there is a hack in
1353                         // GuiWorkArea::processKeySym() that hides again the menubar for
1354                         // those cases.
1355                         if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1356                                 menuBar()->show();
1357                                 return QMainWindow::event(e);
1358                         }
1359                 }
1360                 return QMainWindow::event(e);
1361         }
1362
1363         default:
1364                 return QMainWindow::event(e);
1365         }
1366 }
1367
1368 void GuiView::resetWindowTitle()
1369 {
1370         setWindowTitle(qt_("LyX"));
1371 }
1372
1373 bool GuiView::focusNextPrevChild(bool /*next*/)
1374 {
1375         setFocus();
1376         return true;
1377 }
1378
1379
1380 bool GuiView::busy() const
1381 {
1382         return busy_ > 0;
1383 }
1384
1385
1386 void GuiView::setBusy(bool busy)
1387 {
1388         bool const busy_before = busy_ > 0;
1389         busy ? ++busy_ : --busy_;
1390         if ((busy_ > 0) == busy_before)
1391                 // busy state didn't change
1392                 return;
1393
1394         if (busy) {
1395                 QApplication::setOverrideCursor(Qt::WaitCursor);
1396                 return;
1397         }
1398         QApplication::restoreOverrideCursor();
1399         updateLayoutList();
1400 }
1401
1402
1403 void GuiView::resetCommandExecute()
1404 {
1405         command_execute_ = false;
1406         updateToolbars();
1407 }
1408
1409
1410 double GuiView::pixelRatio() const
1411 {
1412 #if QT_VERSION >= 0x050000
1413         return qt_scale_factor * devicePixelRatio();
1414 #else
1415         return 1.0;
1416 #endif
1417 }
1418
1419
1420 GuiWorkArea * GuiView::workArea(int index)
1421 {
1422         if (TabWorkArea * twa = d.currentTabWorkArea())
1423                 if (index < twa->count())
1424                         return twa->workArea(index);
1425         return 0;
1426 }
1427
1428
1429 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1430 {
1431         if (currentWorkArea()
1432                 && &currentWorkArea()->bufferView().buffer() == &buffer)
1433                 return currentWorkArea();
1434         if (TabWorkArea * twa = d.currentTabWorkArea())
1435                 return twa->workArea(buffer);
1436         return 0;
1437 }
1438
1439
1440 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1441 {
1442         // Automatically create a TabWorkArea if there are none yet.
1443         TabWorkArea * tab_widget = d.splitter_->count()
1444                 ? d.currentTabWorkArea() : addTabWorkArea();
1445         return tab_widget->addWorkArea(buffer, *this);
1446 }
1447
1448
1449 TabWorkArea * GuiView::addTabWorkArea()
1450 {
1451         TabWorkArea * twa = new TabWorkArea;
1452         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1453                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1454         QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1455                          this, SLOT(on_lastWorkAreaRemoved()));
1456
1457         d.splitter_->addWidget(twa);
1458         d.stack_widget_->setCurrentWidget(d.splitter_);
1459         return twa;
1460 }
1461
1462
1463 GuiWorkArea const * GuiView::currentWorkArea() const
1464 {
1465         return d.current_work_area_;
1466 }
1467
1468
1469 GuiWorkArea * GuiView::currentWorkArea()
1470 {
1471         return d.current_work_area_;
1472 }
1473
1474
1475 GuiWorkArea const * GuiView::currentMainWorkArea() const
1476 {
1477         if (!d.currentTabWorkArea())
1478                 return 0;
1479         return d.currentTabWorkArea()->currentWorkArea();
1480 }
1481
1482
1483 GuiWorkArea * GuiView::currentMainWorkArea()
1484 {
1485         if (!d.currentTabWorkArea())
1486                 return 0;
1487         return d.currentTabWorkArea()->currentWorkArea();
1488 }
1489
1490
1491 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1492 {
1493         LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1494         if (!wa) {
1495                 d.current_work_area_ = 0;
1496                 d.setBackground();
1497                 Q_EMIT bufferViewChanged();
1498                 return;
1499         }
1500
1501         // FIXME: I've no clue why this is here and why it accesses
1502         //  theGuiApp()->currentView, which might be 0 (bug 6464).
1503         //  See also 27525 (vfr).
1504         if (theGuiApp()->currentView() == this
1505                   && theGuiApp()->currentView()->currentWorkArea() == wa)
1506                 return;
1507
1508         if (currentBufferView())
1509                 cap::saveSelection(currentBufferView()->cursor());
1510
1511         theGuiApp()->setCurrentView(this);
1512         d.current_work_area_ = wa;
1513
1514         // We need to reset this now, because it will need to be
1515         // right if the tabWorkArea gets reset in the for loop. We
1516         // will change it back if we aren't in that case.
1517         GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1518         d.current_main_work_area_ = wa;
1519
1520         for (int i = 0; i != d.splitter_->count(); ++i) {
1521                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1522                         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1523                                 << ", Current main wa: " << currentMainWorkArea());
1524                         return;
1525                 }
1526         }
1527
1528         d.current_main_work_area_ = old_cmwa;
1529
1530         LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1531         on_currentWorkAreaChanged(wa);
1532         BufferView & bv = wa->bufferView();
1533         bv.cursor().fixIfBroken();
1534         bv.updateMetrics();
1535         wa->setUpdatesEnabled(true);
1536         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1537 }
1538
1539
1540 void GuiView::removeWorkArea(GuiWorkArea * wa)
1541 {
1542         LASSERT(wa, return);
1543         if (wa == d.current_work_area_) {
1544                 disconnectBuffer();
1545                 disconnectBufferView();
1546                 d.current_work_area_ = 0;
1547                 d.current_main_work_area_ = 0;
1548         }
1549
1550         bool found_twa = false;
1551         for (int i = 0; i != d.splitter_->count(); ++i) {
1552                 TabWorkArea * twa = d.tabWorkArea(i);
1553                 if (twa->removeWorkArea(wa)) {
1554                         // Found in this tab group, and deleted the GuiWorkArea.
1555                         found_twa = true;
1556                         if (twa->count() != 0) {
1557                                 if (d.current_work_area_ == 0)
1558                                         // This means that we are closing the current GuiWorkArea, so
1559                                         // switch to the next GuiWorkArea in the found TabWorkArea.
1560                                         setCurrentWorkArea(twa->currentWorkArea());
1561                         } else {
1562                                 // No more WorkAreas in this tab group, so delete it.
1563                                 delete twa;
1564                         }
1565                         break;
1566                 }
1567         }
1568
1569         // It is not a tabbed work area (i.e., the search work area), so it
1570         // should be deleted by other means.
1571         LASSERT(found_twa, return);
1572
1573         if (d.current_work_area_ == 0) {
1574                 if (d.splitter_->count() != 0) {
1575                         TabWorkArea * twa = d.currentTabWorkArea();
1576                         setCurrentWorkArea(twa->currentWorkArea());
1577                 } else {
1578                         // No more work areas, switch to the background widget.
1579                         setCurrentWorkArea(0);
1580                 }
1581         }
1582 }
1583
1584
1585 LayoutBox * GuiView::getLayoutDialog() const
1586 {
1587         return d.layout_;
1588 }
1589
1590
1591 void GuiView::updateLayoutList()
1592 {
1593         if (d.layout_)
1594                 d.layout_->updateContents(false);
1595 }
1596
1597
1598 void GuiView::updateToolbars()
1599 {
1600         ToolbarMap::iterator end = d.toolbars_.end();
1601         if (d.current_work_area_) {
1602                 int context = 0;
1603                 if (d.current_work_area_->bufferView().cursor().inMathed()
1604                         && !d.current_work_area_->bufferView().cursor().inRegexped())
1605                         context |= Toolbars::MATH;
1606                 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1607                         context |= Toolbars::TABLE;
1608                 if (currentBufferView()->buffer().areChangesPresent()
1609                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1610                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1611                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1612                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1613                         context |= Toolbars::REVIEW;
1614                 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1615                         context |= Toolbars::MATHMACROTEMPLATE;
1616                 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1617                         context |= Toolbars::IPA;
1618                 if (command_execute_)
1619                         context |= Toolbars::MINIBUFFER;
1620                 if (minibuffer_focus_) {
1621                         context |= Toolbars::MINIBUFFER_FOCUS;
1622                         minibuffer_focus_ = false;
1623                 }
1624
1625                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1626                         it->second->update(context);
1627         } else
1628                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1629                         it->second->update();
1630 }
1631
1632
1633 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1634 {
1635         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1636         LASSERT(newBuffer, return);
1637
1638         GuiWorkArea * wa = workArea(*newBuffer);
1639         if (wa == 0) {
1640                 setBusy(true);
1641                 newBuffer->masterBuffer()->updateBuffer();
1642                 setBusy(false);
1643                 wa = addWorkArea(*newBuffer);
1644                 // scroll to the position when the BufferView was last closed
1645                 if (lyxrc.use_lastfilepos) {
1646                         LastFilePosSection::FilePos filepos =
1647                                 theSession().lastFilePos().load(newBuffer->fileName());
1648                         wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1649                 }
1650         } else {
1651                 //Disconnect the old buffer...there's no new one.
1652                 disconnectBuffer();
1653         }
1654         connectBuffer(*newBuffer);
1655         connectBufferView(wa->bufferView());
1656         if (switch_to)
1657                 setCurrentWorkArea(wa);
1658 }
1659
1660
1661 void GuiView::connectBuffer(Buffer & buf)
1662 {
1663         buf.setGuiDelegate(this);
1664 }
1665
1666
1667 void GuiView::disconnectBuffer()
1668 {
1669         if (d.current_work_area_)
1670                 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1671 }
1672
1673
1674 void GuiView::connectBufferView(BufferView & bv)
1675 {
1676         bv.setGuiDelegate(this);
1677 }
1678
1679
1680 void GuiView::disconnectBufferView()
1681 {
1682         if (d.current_work_area_)
1683                 d.current_work_area_->bufferView().setGuiDelegate(0);
1684 }
1685
1686
1687 void GuiView::errors(string const & error_type, bool from_master)
1688 {
1689         BufferView const * const bv = currentBufferView();
1690         if (!bv)
1691                 return;
1692
1693 #if EXPORT_in_THREAD
1694         // We are called with from_master == false by default, so we
1695         // have to figure out whether that is the case or not.
1696         ErrorList & el = bv->buffer().errorList(error_type);
1697         if (el.empty()) {
1698             el = bv->buffer().masterBuffer()->errorList(error_type);
1699             from_master = true;
1700         }
1701 #else
1702         ErrorList const & el = from_master ?
1703                 bv->buffer().masterBuffer()->errorList(error_type) :
1704                 bv->buffer().errorList(error_type);
1705 #endif
1706
1707         if (el.empty())
1708                 return;
1709
1710         string data = error_type;
1711         if (from_master)
1712                 data = "from_master|" + error_type;
1713         showDialog("errorlist", data);
1714 }
1715
1716
1717 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1718 {
1719         d.toc_models_.updateItem(toqstr(type), dit);
1720 }
1721
1722
1723 void GuiView::structureChanged()
1724 {
1725         // This is called from the Buffer, which has no way to ensure that cursors
1726         // in BufferView remain valid.
1727         if (documentBufferView())
1728                 documentBufferView()->cursor().sanitize();
1729         // FIXME: This is slightly expensive, though less than the tocBackend update
1730         // (#9880). This also resets the view in the Toc Widget (#6675).
1731         d.toc_models_.reset(documentBufferView());
1732         // Navigator needs more than a simple update in this case. It needs to be
1733         // rebuilt.
1734         updateDialog("toc", "");
1735 }
1736
1737
1738 void GuiView::updateDialog(string const & name, string const & data)
1739 {
1740         if (!isDialogVisible(name))
1741                 return;
1742
1743         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1744         if (it == d.dialogs_.end())
1745                 return;
1746
1747         Dialog * const dialog = it->second.get();
1748         if (dialog->isVisibleView())
1749                 dialog->initialiseParams(data);
1750 }
1751
1752
1753 BufferView * GuiView::documentBufferView()
1754 {
1755         return currentMainWorkArea()
1756                 ? &currentMainWorkArea()->bufferView()
1757                 : 0;
1758 }
1759
1760
1761 BufferView const * GuiView::documentBufferView() const
1762 {
1763         return currentMainWorkArea()
1764                 ? &currentMainWorkArea()->bufferView()
1765                 : 0;
1766 }
1767
1768
1769 BufferView * GuiView::currentBufferView()
1770 {
1771         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1772 }
1773
1774
1775 BufferView const * GuiView::currentBufferView() const
1776 {
1777         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1778 }
1779
1780
1781 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1782         Buffer const * orig, Buffer * clone)
1783 {
1784         bool const success = clone->autoSave();
1785         delete clone;
1786         busyBuffers.remove(orig);
1787         return success
1788                 ? _("Automatic save done.")
1789                 : _("Automatic save failed!");
1790 }
1791
1792
1793 void GuiView::autoSave()
1794 {
1795         LYXERR(Debug::INFO, "Running autoSave()");
1796
1797         Buffer * buffer = documentBufferView()
1798                 ? &documentBufferView()->buffer() : 0;
1799         if (!buffer) {
1800                 resetAutosaveTimers();
1801                 return;
1802         }
1803
1804         GuiViewPrivate::busyBuffers.insert(buffer);
1805         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1806                 buffer, buffer->cloneBufferOnly());
1807         d.autosave_watcher_.setFuture(f);
1808         resetAutosaveTimers();
1809 }
1810
1811
1812 void GuiView::resetAutosaveTimers()
1813 {
1814         if (lyxrc.autosave)
1815                 d.autosave_timeout_.restart();
1816 }
1817
1818
1819 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1820 {
1821         bool enable = true;
1822         Buffer * buf = currentBufferView()
1823                 ? &currentBufferView()->buffer() : 0;
1824         Buffer * doc_buffer = documentBufferView()
1825                 ? &(documentBufferView()->buffer()) : 0;
1826
1827 #ifdef Q_OS_MAC
1828         /* In LyX/Mac, when a dialog is open, the menus of the
1829            application can still be accessed without giving focus to
1830            the main window. In this case, we want to disable the menu
1831            entries that are buffer-related.
1832            This code must not be used on Linux and Windows, since it
1833            would disable buffer-related entries when hovering over the
1834            menu (see bug #9574).
1835          */
1836         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1837                 buf = 0;
1838                 doc_buffer = 0;
1839         }
1840 #endif
1841
1842         // Check whether we need a buffer
1843         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1844                 // no, exit directly
1845                 flag.message(from_utf8(N_("Command not allowed with"
1846                                         "out any document open")));
1847                 flag.setEnabled(false);
1848                 return true;
1849         }
1850
1851         if (cmd.origin() == FuncRequest::TOC) {
1852                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1853                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1854                         flag.setEnabled(false);
1855                 return true;
1856         }
1857
1858         switch(cmd.action()) {
1859         case LFUN_BUFFER_IMPORT:
1860                 break;
1861
1862         case LFUN_MASTER_BUFFER_UPDATE:
1863         case LFUN_MASTER_BUFFER_VIEW:
1864                 enable = doc_buffer
1865                         && (doc_buffer->parent() != 0
1866                             || doc_buffer->hasChildren())
1867                         && !d.processing_thread_watcher_.isRunning();
1868                 break;
1869
1870         case LFUN_BUFFER_UPDATE:
1871         case LFUN_BUFFER_VIEW: {
1872                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1873                         enable = false;
1874                         break;
1875                 }
1876                 string format = to_utf8(cmd.argument());
1877                 if (cmd.argument().empty())
1878                         format = doc_buffer->params().getDefaultOutputFormat();
1879                 enable = doc_buffer->params().isExportable(format, true);
1880                 break;
1881         }
1882
1883         case LFUN_BUFFER_RELOAD:
1884                 enable = doc_buffer && !doc_buffer->isUnnamed()
1885                         && doc_buffer->fileName().exists()
1886                         && (!doc_buffer->isClean() || doc_buffer->notifiesExternalModification());
1887                 break;
1888
1889         case LFUN_BUFFER_CHILD_OPEN:
1890                 enable = doc_buffer != 0;
1891                 break;
1892
1893         case LFUN_BUFFER_WRITE:
1894                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1895                 break;
1896
1897         //FIXME: This LFUN should be moved to GuiApplication.
1898         case LFUN_BUFFER_WRITE_ALL: {
1899                 // We enable the command only if there are some modified buffers
1900                 Buffer * first = theBufferList().first();
1901                 enable = false;
1902                 if (!first)
1903                         break;
1904                 Buffer * b = first;
1905                 // We cannot use a for loop as the buffer list is a cycle.
1906                 do {
1907                         if (!b->isClean()) {
1908                                 enable = true;
1909                                 break;
1910                         }
1911                         b = theBufferList().next(b);
1912                 } while (b != first);
1913                 break;
1914         }
1915
1916         case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1917                 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1918                 break;
1919
1920         case LFUN_BUFFER_EXPORT: {
1921                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1922                         enable = false;
1923                         break;
1924                 }
1925                 return doc_buffer->getStatus(cmd, flag);
1926                 break;
1927         }
1928
1929         case LFUN_BUFFER_EXPORT_AS:
1930                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1931                         enable = false;
1932                         break;
1933                 }
1934                 // fall through
1935         case LFUN_BUFFER_WRITE_AS:
1936                 enable = doc_buffer != 0;
1937                 break;
1938
1939         case LFUN_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_("Documents|#o#O"), toqstr(lyxrc.document_path));
2232         dlg.setButton2(qt_("Templates|#T#t"), 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_("Documents|#o#O"), toqstr(lyxrc.document_path));
2291                 dlg.setButton2(qt_("Examples|#E#e"), 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_("Documents|#o#O"), toqstr(lyxrc.document_path));
2429                 dlg.setButton2(qt_("Examples|#E#e"), 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_("Documents|#o#O"), toqstr(lyxrc.document_path));
2562                 dlg.setButton2(qt_("Examples|#E#e"), 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_("Documents|#o#O"), toqstr(lyxrc.document_path));
2602                 dlg.setButton2(qt_("Templates|#T#t"), 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_("Documents|#o#O"), 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;
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_BUFFER_SWITCH: {
3768                         string const file_name = to_utf8(cmd.argument());
3769                         if (!FileName::isAbsolute(file_name)) {
3770                                 dr.setError(true);
3771                                 dr.setMessage(_("Absolute filename expected."));
3772                                 break;
3773                         }
3774
3775                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3776                         if (!buffer) {
3777                                 dr.setError(true);
3778                                 dr.setMessage(_("Document not loaded"));
3779                                 break;
3780                         }
3781
3782                         // Do we open or switch to the buffer in this view ?
3783                         if (workArea(*buffer)
3784                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3785                                 setBuffer(buffer);
3786                                 break;
3787                         }
3788
3789                         // Look for the buffer in other views
3790                         QList<int> const ids = guiApp->viewIds();
3791                         int i = 0;
3792                         for (; i != ids.size(); ++i) {
3793                                 GuiView & gv = guiApp->view(ids[i]);
3794                                 if (gv.workArea(*buffer)) {
3795                                         gv.raise();
3796                                         gv.activateWindow();
3797                                         gv.setFocus();
3798                                         gv.setBuffer(buffer);
3799                                         break;
3800                                 }
3801                         }
3802
3803                         // If necessary, open a new window as a last resort
3804                         if (i == ids.size()) {
3805                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3806                                 lyx::dispatch(cmd);
3807                         }
3808                         break;
3809                 }
3810
3811                 case LFUN_BUFFER_NEXT:
3812                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3813                         break;
3814
3815                 case LFUN_BUFFER_MOVE_NEXT:
3816                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3817                         break;
3818
3819                 case LFUN_BUFFER_PREVIOUS:
3820                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3821                         break;
3822
3823                 case LFUN_BUFFER_MOVE_PREVIOUS:
3824                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3825                         break;
3826
3827                 case LFUN_BUFFER_CHKTEX:
3828                         LASSERT(doc_buffer, break);
3829                         doc_buffer->runChktex();
3830                         break;
3831
3832                 case LFUN_COMMAND_EXECUTE: {
3833                         command_execute_ = true;
3834                         minibuffer_focus_ = true;
3835                         break;
3836                 }
3837                 case LFUN_DROP_LAYOUTS_CHOICE:
3838                         d.layout_->showPopup();
3839                         break;
3840
3841                 case LFUN_MENU_OPEN:
3842                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3843                                 menu->exec(QCursor::pos());
3844                         break;
3845
3846                 case LFUN_FILE_INSERT:
3847                         insertLyXFile(cmd.argument());
3848                         break;
3849
3850                 case LFUN_FILE_INSERT_PLAINTEXT:
3851                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3852                         string const fname = to_utf8(cmd.argument());
3853                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3854                                 dr.setMessage(_("Absolute filename expected."));
3855                                 break;
3856                         }
3857
3858                         FileName filename(fname);
3859                         if (fname.empty()) {
3860                                 FileDialog dlg(qt_("Select file to insert"));
3861
3862                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3863                                         QStringList(qt_("All Files (*)")));
3864
3865                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3866                                         dr.setMessage(_("Canceled."));
3867                                         break;
3868                                 }
3869
3870                                 filename.set(fromqstr(result.second));
3871                         }
3872
3873                         if (bv) {
3874                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3875                                 bv->dispatch(new_cmd, dr);
3876                         }
3877                         break;
3878                 }
3879
3880                 case LFUN_BUFFER_RELOAD: {
3881                         LASSERT(doc_buffer, break);
3882
3883                         int ret = 0;
3884                         if (!doc_buffer->isClean()) {
3885                                 docstring const file =
3886                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3887                                 if (doc_buffer->notifiesExternalModification()) {
3888                                         docstring text = _("The current version will be lost. "
3889                                             "Are you sure you want to load the version on disk "
3890                                             "of the document %1$s?");
3891                                         ret = Alert::prompt(_("Reload saved document?"),
3892                                                             bformat(text, file), 1, 1,
3893                                                             _("&Reload"), _("&Cancel"));
3894                                 } else {
3895                                         docstring text = _("Any changes will be lost. "
3896                                             "Are you sure you want to revert to the saved version "
3897                                             "of the document %1$s?");
3898                                         ret = Alert::prompt(_("Revert to saved document?"),
3899                                                             bformat(text, file), 1, 1,
3900                                                             _("&Revert"), _("&Cancel"));
3901                                 }
3902                         }
3903
3904                         if (ret == 0) {
3905                                 doc_buffer->markClean();
3906                                 reloadBuffer(*doc_buffer);
3907                                 dr.forceBufferUpdate();
3908                         }
3909                         break;
3910                 }
3911
3912                 case LFUN_BUFFER_WRITE:
3913                         LASSERT(doc_buffer, break);
3914                         saveBuffer(*doc_buffer);
3915                         break;
3916
3917                 case LFUN_BUFFER_WRITE_AS:
3918                         LASSERT(doc_buffer, break);
3919                         renameBuffer(*doc_buffer, cmd.argument());
3920                         break;
3921
3922                 case LFUN_BUFFER_WRITE_ALL: {
3923                         Buffer * first = theBufferList().first();
3924                         if (!first)
3925                                 break;
3926                         message(_("Saving all documents..."));
3927                         // We cannot use a for loop as the buffer list cycles.
3928                         Buffer * b = first;
3929                         do {
3930                                 if (!b->isClean()) {
3931                                         saveBuffer(*b);
3932                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3933                                 }
3934                                 b = theBufferList().next(b);
3935                         } while (b != first);
3936                         dr.setMessage(_("All documents saved."));
3937                         break;
3938                 }
3939
3940                 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3941                         LASSERT(doc_buffer, break);
3942                         doc_buffer->clearExternalModification();
3943                         break;
3944
3945                 case LFUN_BUFFER_CLOSE:
3946                         closeBuffer();
3947                         break;
3948
3949                 case LFUN_BUFFER_CLOSE_ALL:
3950                         closeBufferAll();
3951                         break;
3952
3953                 case LFUN_DEVEL_MODE_TOGGLE:
3954                         devel_mode_ = !devel_mode_;
3955                         if (devel_mode_)
3956                                 dr.setMessage(_("Developer mode is now enabled."));
3957                         else
3958                                 dr.setMessage(_("Developer mode is now disabled."));
3959                         break;
3960
3961                 case LFUN_TOOLBAR_TOGGLE: {
3962                         string const name = cmd.getArg(0);
3963                         if (GuiToolbar * t = toolbar(name))
3964                                 t->toggle();
3965                         break;
3966                 }
3967
3968                 case LFUN_TOOLBAR_MOVABLE: {
3969                         string const name = cmd.getArg(0);
3970                         if (name == "*") {
3971                                 // toggle (all) toolbars movablility
3972                                 toolbarsMovable_ = !toolbarsMovable_;
3973                                 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3974                                         GuiToolbar * tb = toolbar(ti.name);
3975                                         if (tb && tb->isMovable() != toolbarsMovable_)
3976                                                 // toggle toolbar movablity if it does not fit lock
3977                                                 // (all) toolbars positions state silent = true, since
3978                                                 // status bar notifications are slow
3979                                                 tb->movable(true);
3980                                 }
3981                                 if (toolbarsMovable_)
3982                                         dr.setMessage(_("Toolbars unlocked."));
3983                                 else
3984                                         dr.setMessage(_("Toolbars locked."));
3985                         } else if (GuiToolbar * t = toolbar(name)) {
3986                                 // toggle current toolbar movablity
3987                                 t->movable();
3988                                 // update lock (all) toolbars positions
3989                                 updateLockToolbars();
3990                         }
3991                         break;
3992                 }
3993
3994                 case LFUN_ICON_SIZE: {
3995                         QSize size = d.iconSize(cmd.argument());
3996                         setIconSize(size);
3997                         dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3998                                                 size.width(), size.height()));
3999                         break;
4000                 }
4001
4002                 case LFUN_DIALOG_UPDATE: {
4003                         string const name = to_utf8(cmd.argument());
4004                         if (name == "prefs" || name == "document")
4005                                 updateDialog(name, string());
4006                         else if (name == "paragraph")
4007                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4008                         else if (currentBufferView()) {
4009                                 Inset * inset = currentBufferView()->editedInset(name);
4010                                 // Can only update a dialog connected to an existing inset
4011                                 if (inset) {
4012                                         // FIXME: get rid of this indirection; GuiView ask the inset
4013                                         // if he is kind enough to update itself...
4014                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4015                                         //FIXME: pass DispatchResult here?
4016                                         inset->dispatch(currentBufferView()->cursor(), fr);
4017                                 }
4018                         }
4019                         break;
4020                 }
4021
4022                 case LFUN_DIALOG_TOGGLE: {
4023                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4024                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4025                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
4026                         break;
4027                 }
4028
4029                 case LFUN_DIALOG_DISCONNECT_INSET:
4030                         disconnectDialog(to_utf8(cmd.argument()));
4031                         break;
4032
4033                 case LFUN_DIALOG_HIDE: {
4034                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4035                         break;
4036                 }
4037
4038                 case LFUN_DIALOG_SHOW: {
4039                         string const name = cmd.getArg(0);
4040                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4041
4042                         if (name == "character") {
4043                                 data = freefont2string();
4044                                 if (!data.empty())
4045                                         showDialog("character", data);
4046                         } else if (name == "latexlog") {
4047                                 // getStatus checks that
4048                                 LATTEST(doc_buffer);
4049                                 Buffer::LogType type;
4050                                 string const logfile = doc_buffer->logName(&type);
4051                                 switch (type) {
4052                                 case Buffer::latexlog:
4053                                         data = "latex ";
4054                                         break;
4055                                 case Buffer::buildlog:
4056                                         data = "literate ";
4057                                         break;
4058                                 }
4059                                 data += Lexer::quoteString(logfile);
4060                                 showDialog("log", data);
4061                         } else if (name == "vclog") {
4062                                 // getStatus checks that
4063                                 LATTEST(doc_buffer);
4064                                 string const data = "vc " +
4065                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4066                                 showDialog("log", data);
4067                         } else if (name == "symbols") {
4068                                 data = bv->cursor().getEncoding()->name();
4069                                 if (!data.empty())
4070                                         showDialog("symbols", data);
4071                         // bug 5274
4072                         } else if (name == "prefs" && isFullScreen()) {
4073                                 lfunUiToggle("fullscreen");
4074                                 showDialog("prefs", data);
4075                         } else
4076                                 showDialog(name, data);
4077                         break;
4078                 }
4079
4080                 case LFUN_MESSAGE:
4081                         dr.setMessage(cmd.argument());
4082                         break;
4083
4084                 case LFUN_UI_TOGGLE: {
4085                         string arg = cmd.getArg(0);
4086                         if (!lfunUiToggle(arg)) {
4087                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4088                                 dr.setMessage(bformat(msg, from_utf8(arg)));
4089                         }
4090                         // Make sure the keyboard focus stays in the work area.
4091                         setFocus();
4092                         break;
4093                 }
4094
4095                 case LFUN_VIEW_SPLIT: {
4096                         LASSERT(doc_buffer, break);
4097                         string const orientation = cmd.getArg(0);
4098                         d.splitter_->setOrientation(orientation == "vertical"
4099                                 ? Qt::Vertical : Qt::Horizontal);
4100                         TabWorkArea * twa = addTabWorkArea();
4101                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4102                         setCurrentWorkArea(wa);
4103                         break;
4104                 }
4105                 case LFUN_TAB_GROUP_CLOSE:
4106                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4107                                 closeTabWorkArea(twa);
4108                                 d.current_work_area_ = 0;
4109                                 twa = d.currentTabWorkArea();
4110                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4111                                 if (twa) {
4112                                         // Make sure the work area is up to date.
4113                                         setCurrentWorkArea(twa->currentWorkArea());
4114                                 } else {
4115                                         setCurrentWorkArea(0);
4116                                 }
4117                         }
4118                         break;
4119
4120                 case LFUN_VIEW_CLOSE:
4121                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
4122                                 closeWorkArea(twa->currentWorkArea());
4123                                 d.current_work_area_ = 0;
4124                                 twa = d.currentTabWorkArea();
4125                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
4126                                 if (twa) {
4127                                         // Make sure the work area is up to date.
4128                                         setCurrentWorkArea(twa->currentWorkArea());
4129                                 } else {
4130                                         setCurrentWorkArea(0);
4131                                 }
4132                         }
4133                         break;
4134
4135                 case LFUN_COMPLETION_INLINE:
4136                         if (d.current_work_area_)
4137                                 d.current_work_area_->completer().showInline();
4138                         break;
4139
4140                 case LFUN_COMPLETION_POPUP:
4141                         if (d.current_work_area_)
4142                                 d.current_work_area_->completer().showPopup();
4143                         break;
4144
4145
4146                 case LFUN_COMPLETE:
4147                         if (d.current_work_area_)
4148                                 d.current_work_area_->completer().tab();
4149                         break;
4150
4151                 case LFUN_COMPLETION_CANCEL:
4152                         if (d.current_work_area_) {
4153                                 if (d.current_work_area_->completer().popupVisible())
4154                                         d.current_work_area_->completer().hidePopup();
4155                                 else
4156                                         d.current_work_area_->completer().hideInline();
4157                         }
4158                         break;
4159
4160                 case LFUN_COMPLETION_ACCEPT:
4161                         if (d.current_work_area_)
4162                                 d.current_work_area_->completer().activate();
4163                         break;
4164
4165                 case LFUN_BUFFER_ZOOM_IN:
4166                 case LFUN_BUFFER_ZOOM_OUT:
4167                 case LFUN_BUFFER_ZOOM: {
4168                         if (cmd.argument().empty()) {
4169                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4170                                         zoom_ratio_ = 1.0;
4171                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4172                                         zoom_ratio_ += 0.1;
4173                                 else
4174                                         zoom_ratio_ -= 0.1;
4175                         } else {
4176                                 if (cmd.action() == LFUN_BUFFER_ZOOM)
4177                                         zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4178                                 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4179                                         zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4180                                 else
4181                                         zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4182                         }
4183
4184                         // Actual zoom value: default zoom + fractional extra value
4185                         int zoom = lyxrc.defaultZoom * zoom_ratio_;
4186                         if (zoom < static_cast<int>(zoom_min_))
4187                                 zoom = zoom_min_;
4188
4189                         lyxrc.currentZoom = zoom;
4190
4191                         dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4192                                               lyxrc.currentZoom, lyxrc.defaultZoom));
4193
4194                         // The global QPixmapCache is used in GuiPainter to cache text
4195                         // painting so we must reset it.
4196                         QPixmapCache::clear();
4197                         guiApp->fontLoader().update();
4198                         dr.screenUpdate(Update::Force | Update::FitCursor);
4199                         break;
4200                 }
4201
4202                 case LFUN_VC_REGISTER:
4203                 case LFUN_VC_RENAME:
4204                 case LFUN_VC_COPY:
4205                 case LFUN_VC_CHECK_IN:
4206                 case LFUN_VC_CHECK_OUT:
4207                 case LFUN_VC_REPO_UPDATE:
4208                 case LFUN_VC_LOCKING_TOGGLE:
4209                 case LFUN_VC_REVERT:
4210                 case LFUN_VC_UNDO_LAST:
4211                 case LFUN_VC_COMMAND:
4212                 case LFUN_VC_COMPARE:
4213                         dispatchVC(cmd, dr);
4214                         break;
4215
4216                 case LFUN_SERVER_GOTO_FILE_ROW:
4217                         if(goToFileRow(to_utf8(cmd.argument())))
4218                                 dr.screenUpdate(Update::Force | Update::FitCursor);
4219                         break;
4220
4221                 case LFUN_LYX_ACTIVATE:
4222                         activateWindow();
4223                         break;
4224
4225                 case LFUN_FORWARD_SEARCH: {
4226                         // it seems safe to assume we have a document buffer, since
4227                         // getStatus wants one.
4228                         LATTEST(doc_buffer);
4229                         Buffer const * doc_master = doc_buffer->masterBuffer();
4230                         FileName const path(doc_master->temppath());
4231                         string const texname = doc_master->isChild(doc_buffer)
4232                                 ? DocFileName(changeExtension(
4233                                         doc_buffer->absFileName(),
4234                                                 "tex")).mangledFileName()
4235                                 : doc_buffer->latexName();
4236                         string const fulltexname =
4237                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4238                         string const mastername =
4239                                 removeExtension(doc_master->latexName());
4240                         FileName const dviname(addName(path.absFileName(),
4241                                         addExtension(mastername, "dvi")));
4242                         FileName const pdfname(addName(path.absFileName(),
4243                                         addExtension(mastername, "pdf")));
4244                         bool const have_dvi = dviname.exists();
4245                         bool const have_pdf = pdfname.exists();
4246                         if (!have_dvi && !have_pdf) {
4247                                 dr.setMessage(_("Please, preview the document first."));
4248                                 break;
4249                         }
4250                         string outname = dviname.onlyFileName();
4251                         string command = lyxrc.forward_search_dvi;
4252                         if (!have_dvi || (have_pdf &&
4253                             pdfname.lastModified() > dviname.lastModified())) {
4254                                 outname = pdfname.onlyFileName();
4255                                 command = lyxrc.forward_search_pdf;
4256                         }
4257
4258                         DocIterator cur = bv->cursor();
4259                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4260                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4261                                    << " cur:" << cur);
4262                         if (row == -1 || command.empty()) {
4263                                 dr.setMessage(_("Couldn't proceed."));
4264                                 break;
4265                         }
4266                         string texrow = convert<string>(row);
4267
4268                         command = subst(command, "$$n", texrow);
4269                         command = subst(command, "$$f", fulltexname);
4270                         command = subst(command, "$$t", texname);
4271                         command = subst(command, "$$o", outname);
4272
4273                         PathChanger p(path);
4274                         Systemcall one;
4275                         one.startscript(Systemcall::DontWait, command);
4276                         break;
4277                 }
4278
4279                 case LFUN_SPELLING_CONTINUOUSLY:
4280                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4281                         dr.screenUpdate(Update::Force);
4282                         break;
4283
4284                 default:
4285                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4286                         // let's try that:
4287                         dispatchToBufferView(cmd, dr);
4288                         break;
4289         }
4290
4291         // Part of automatic menu appearance feature.
4292         if (isFullScreen()) {
4293                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4294                         menuBar()->hide();
4295         }
4296
4297         // Need to update bv because many LFUNs here might have destroyed it
4298         bv = currentBufferView();
4299
4300         // Clear non-empty selections
4301         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4302         if (bv) {
4303                 Cursor & cur = bv->cursor();
4304                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4305                         cur.clearSelection();
4306                 }
4307         }
4308 }
4309
4310
4311 bool GuiView::lfunUiToggle(string const & ui_component)
4312 {
4313         if (ui_component == "scrollbar") {
4314                 // hide() is of no help
4315                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4316                         Qt::ScrollBarAlwaysOff)
4317
4318                         d.current_work_area_->setVerticalScrollBarPolicy(
4319                                 Qt::ScrollBarAsNeeded);
4320                 else
4321                         d.current_work_area_->setVerticalScrollBarPolicy(
4322                                 Qt::ScrollBarAlwaysOff);
4323         } else if (ui_component == "statusbar") {
4324                 statusBar()->setVisible(!statusBar()->isVisible());
4325         } else if (ui_component == "menubar") {
4326                 menuBar()->setVisible(!menuBar()->isVisible());
4327         } else
4328         if (ui_component == "frame") {
4329                 int l, t, r, b;
4330                 getContentsMargins(&l, &t, &r, &b);
4331                 //are the frames in default state?
4332                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4333                 if (l == 0) {
4334                         setContentsMargins(-2, -2, -2, -2);
4335                 } else {
4336                         setContentsMargins(0, 0, 0, 0);
4337                 }
4338         } else
4339         if (ui_component == "fullscreen") {
4340                 toggleFullScreen();
4341         } else
4342                 return false;
4343         return true;
4344 }
4345
4346
4347 void GuiView::toggleFullScreen()
4348 {
4349         if (isFullScreen()) {
4350                 for (int i = 0; i != d.splitter_->count(); ++i)
4351                         d.tabWorkArea(i)->setFullScreen(false);
4352                 setContentsMargins(0, 0, 0, 0);
4353                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4354                 restoreLayout();
4355                 menuBar()->show();
4356                 statusBar()->show();
4357         } else {
4358                 // bug 5274
4359                 hideDialogs("prefs", 0);
4360                 for (int i = 0; i != d.splitter_->count(); ++i)
4361                         d.tabWorkArea(i)->setFullScreen(true);
4362                 setContentsMargins(-2, -2, -2, -2);
4363                 saveLayout();
4364                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4365                 if (lyxrc.full_screen_statusbar)
4366                         statusBar()->hide();
4367                 if (lyxrc.full_screen_menubar)
4368                         menuBar()->hide();
4369                 if (lyxrc.full_screen_toolbars) {
4370                         ToolbarMap::iterator end = d.toolbars_.end();
4371                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4372                                 it->second->hide();
4373                 }
4374         }
4375
4376         // give dialogs like the TOC a chance to adapt
4377         updateDialogs();
4378 }
4379
4380
4381 Buffer const * GuiView::updateInset(Inset const * inset)
4382 {
4383         if (!inset)
4384                 return 0;
4385
4386         Buffer const * inset_buffer = &(inset->buffer());
4387
4388         for (int i = 0; i != d.splitter_->count(); ++i) {
4389                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4390                 if (!wa)
4391                         continue;
4392                 Buffer const * buffer = &(wa->bufferView().buffer());
4393                 if (inset_buffer == buffer)
4394                         wa->scheduleRedraw(true);
4395         }
4396         return inset_buffer;
4397 }
4398
4399
4400 void GuiView::restartCaret()
4401 {
4402         /* When we move around, or type, it's nice to be able to see
4403          * the caret immediately after the keypress.
4404          */
4405         if (d.current_work_area_)
4406                 d.current_work_area_->startBlinkingCaret();
4407
4408         // Take this occasion to update the other GUI elements.
4409         updateDialogs();
4410         updateStatusBar();
4411 }
4412
4413
4414 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4415 {
4416         if (d.current_work_area_)
4417                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4418 }
4419
4420 namespace {
4421
4422 // This list should be kept in sync with the list of insets in
4423 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4424 // dialog should have the same name as the inset.
4425 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4426 // docs in LyXAction.cpp.
4427
4428 char const * const dialognames[] = {
4429
4430 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4431 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4432 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4433 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4434 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4435 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4436 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4437 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4438
4439 char const * const * const end_dialognames =
4440         dialognames + (sizeof(dialognames) / sizeof(char *));
4441
4442 class cmpCStr {
4443 public:
4444         cmpCStr(char const * name) : name_(name) {}
4445         bool operator()(char const * other) {
4446                 return strcmp(other, name_) == 0;
4447         }
4448 private:
4449         char const * name_;
4450 };
4451
4452
4453 bool isValidName(string const & name)
4454 {
4455         return find_if(dialognames, end_dialognames,
4456                                 cmpCStr(name.c_str())) != end_dialognames;
4457 }
4458
4459 } // namespace
4460
4461
4462 void GuiView::resetDialogs()
4463 {
4464         // Make sure that no LFUN uses any GuiView.
4465         guiApp->setCurrentView(0);
4466         saveLayout();
4467         saveUISettings();
4468         menuBar()->clear();
4469         constructToolbars();
4470         guiApp->menus().fillMenuBar(menuBar(), this, false);
4471         d.layout_->updateContents(true);
4472         // Now update controls with current buffer.
4473         guiApp->setCurrentView(this);
4474         restoreLayout();
4475         restartCaret();
4476 }
4477
4478
4479 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4480 {
4481         if (!isValidName(name))
4482                 return 0;
4483
4484         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4485
4486         if (it != d.dialogs_.end()) {
4487                 if (hide_it)
4488                         it->second->hideView();
4489                 return it->second.get();
4490         }
4491
4492         Dialog * dialog = build(name);
4493         d.dialogs_[name].reset(dialog);
4494         if (lyxrc.allow_geometry_session)
4495                 dialog->restoreSession();
4496         if (hide_it)
4497                 dialog->hideView();
4498         return dialog;
4499 }
4500
4501
4502 void GuiView::showDialog(string const & name, string const & data,
4503         Inset * inset)
4504 {
4505         triggerShowDialog(toqstr(name), toqstr(data), inset);
4506 }
4507
4508
4509 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4510         Inset * inset)
4511 {
4512         if (d.in_show_)
4513                 return;
4514
4515         const string name = fromqstr(qname);
4516         const string data = fromqstr(qdata);
4517
4518         d.in_show_ = true;
4519         try {
4520                 Dialog * dialog = findOrBuild(name, false);
4521                 if (dialog) {
4522                         bool const visible = dialog->isVisibleView();
4523                         dialog->showData(data);
4524                         if (inset && currentBufferView())
4525                                 currentBufferView()->editInset(name, inset);
4526                         // We only set the focus to the new dialog if it was not yet
4527                         // visible in order not to change the existing previous behaviour
4528                         if (visible) {
4529                                 // activateWindow is needed for floating dockviews
4530                                 dialog->asQWidget()->raise();
4531                                 dialog->asQWidget()->activateWindow();
4532                                 dialog->asQWidget()->setFocus();
4533                         }
4534                 }
4535         }
4536         catch (ExceptionMessage const & ex) {
4537                 d.in_show_ = false;
4538                 throw ex;
4539         }
4540         d.in_show_ = false;
4541 }
4542
4543
4544 bool GuiView::isDialogVisible(string const & name) const
4545 {
4546         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4547         if (it == d.dialogs_.end())
4548                 return false;
4549         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4550 }
4551
4552
4553 void GuiView::hideDialog(string const & name, Inset * inset)
4554 {
4555         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4556         if (it == d.dialogs_.end())
4557                 return;
4558
4559         if (inset) {
4560                 if (!currentBufferView())
4561                         return;
4562                 if (inset != currentBufferView()->editedInset(name))
4563                         return;
4564         }
4565
4566         Dialog * const dialog = it->second.get();
4567         if (dialog->isVisibleView())
4568                 dialog->hideView();
4569         if (currentBufferView())
4570                 currentBufferView()->editInset(name, 0);
4571 }
4572
4573
4574 void GuiView::disconnectDialog(string const & name)
4575 {
4576         if (!isValidName(name))
4577                 return;
4578         if (currentBufferView())
4579                 currentBufferView()->editInset(name, 0);
4580 }
4581
4582
4583 void GuiView::hideAll() const
4584 {
4585         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4586         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4587
4588         for(; it != end; ++it)
4589                 it->second->hideView();
4590 }
4591
4592
4593 void GuiView::updateDialogs()
4594 {
4595         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4596         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4597
4598         for(; it != end; ++it) {
4599                 Dialog * dialog = it->second.get();
4600                 if (dialog) {
4601                         if (dialog->needBufferOpen() && !documentBufferView())
4602                                 hideDialog(fromqstr(dialog->name()), 0);
4603                         else if (dialog->isVisibleView())
4604                                 dialog->checkStatus();
4605                 }
4606         }
4607         updateToolbars();
4608         updateLayoutList();
4609 }
4610
4611 Dialog * createDialog(GuiView & lv, string const & name);
4612
4613 // will be replaced by a proper factory...
4614 Dialog * createGuiAbout(GuiView & lv);
4615 Dialog * createGuiBibtex(GuiView & lv);
4616 Dialog * createGuiChanges(GuiView & lv);
4617 Dialog * createGuiCharacter(GuiView & lv);
4618 Dialog * createGuiCitation(GuiView & lv);
4619 Dialog * createGuiCompare(GuiView & lv);
4620 Dialog * createGuiCompareHistory(GuiView & lv);
4621 Dialog * createGuiDelimiter(GuiView & lv);
4622 Dialog * createGuiDocument(GuiView & lv);
4623 Dialog * createGuiErrorList(GuiView & lv);
4624 Dialog * createGuiExternal(GuiView & lv);
4625 Dialog * createGuiGraphics(GuiView & lv);
4626 Dialog * createGuiInclude(GuiView & lv);
4627 Dialog * createGuiIndex(GuiView & lv);
4628 Dialog * createGuiListings(GuiView & lv);
4629 Dialog * createGuiLog(GuiView & lv);
4630 Dialog * createGuiMathMatrix(GuiView & lv);
4631 Dialog * createGuiNote(GuiView & lv);
4632 Dialog * createGuiParagraph(GuiView & lv);
4633 Dialog * createGuiPhantom(GuiView & lv);
4634 Dialog * createGuiPreferences(GuiView & lv);
4635 Dialog * createGuiPrint(GuiView & lv);
4636 Dialog * createGuiPrintindex(GuiView & lv);
4637 Dialog * createGuiRef(GuiView & lv);
4638 Dialog * createGuiSearch(GuiView & lv);
4639 Dialog * createGuiSearchAdv(GuiView & lv);
4640 Dialog * createGuiSendTo(GuiView & lv);
4641 Dialog * createGuiShowFile(GuiView & lv);
4642 Dialog * createGuiSpellchecker(GuiView & lv);
4643 Dialog * createGuiSymbols(GuiView & lv);
4644 Dialog * createGuiTabularCreate(GuiView & lv);
4645 Dialog * createGuiTexInfo(GuiView & lv);
4646 Dialog * createGuiToc(GuiView & lv);
4647 Dialog * createGuiThesaurus(GuiView & lv);
4648 Dialog * createGuiViewSource(GuiView & lv);
4649 Dialog * createGuiWrap(GuiView & lv);
4650 Dialog * createGuiProgressView(GuiView & lv);
4651
4652
4653
4654 Dialog * GuiView::build(string const & name)
4655 {
4656         LASSERT(isValidName(name), return 0);
4657
4658         Dialog * dialog = createDialog(*this, name);
4659         if (dialog)
4660                 return dialog;
4661
4662         if (name == "aboutlyx")
4663                 return createGuiAbout(*this);
4664         if (name == "bibtex")
4665                 return createGuiBibtex(*this);
4666         if (name == "changes")
4667                 return createGuiChanges(*this);
4668         if (name == "character")
4669                 return createGuiCharacter(*this);
4670         if (name == "citation")
4671                 return createGuiCitation(*this);
4672         if (name == "compare")
4673                 return createGuiCompare(*this);
4674         if (name == "comparehistory")
4675                 return createGuiCompareHistory(*this);
4676         if (name == "document")
4677                 return createGuiDocument(*this);
4678         if (name == "errorlist")
4679                 return createGuiErrorList(*this);
4680         if (name == "external")
4681                 return createGuiExternal(*this);
4682         if (name == "file")
4683                 return createGuiShowFile(*this);
4684         if (name == "findreplace")
4685                 return createGuiSearch(*this);
4686         if (name == "findreplaceadv")
4687                 return createGuiSearchAdv(*this);
4688         if (name == "graphics")
4689                 return createGuiGraphics(*this);
4690         if (name == "include")
4691                 return createGuiInclude(*this);
4692         if (name == "index")
4693                 return createGuiIndex(*this);
4694         if (name == "index_print")
4695                 return createGuiPrintindex(*this);
4696         if (name == "listings")
4697                 return createGuiListings(*this);
4698         if (name == "log")
4699                 return createGuiLog(*this);
4700         if (name == "mathdelimiter")
4701                 return createGuiDelimiter(*this);
4702         if (name == "mathmatrix")
4703                 return createGuiMathMatrix(*this);
4704         if (name == "note")
4705                 return createGuiNote(*this);
4706         if (name == "paragraph")
4707                 return createGuiParagraph(*this);
4708         if (name == "phantom")
4709                 return createGuiPhantom(*this);
4710         if (name == "prefs")
4711                 return createGuiPreferences(*this);
4712         if (name == "ref")
4713                 return createGuiRef(*this);
4714         if (name == "sendto")
4715                 return createGuiSendTo(*this);
4716         if (name == "spellchecker")
4717                 return createGuiSpellchecker(*this);
4718         if (name == "symbols")
4719                 return createGuiSymbols(*this);
4720         if (name == "tabularcreate")
4721                 return createGuiTabularCreate(*this);
4722         if (name == "texinfo")
4723                 return createGuiTexInfo(*this);
4724         if (name == "thesaurus")
4725                 return createGuiThesaurus(*this);
4726         if (name == "toc")
4727                 return createGuiToc(*this);
4728         if (name == "view-source")
4729                 return createGuiViewSource(*this);
4730         if (name == "wrap")
4731                 return createGuiWrap(*this);
4732         if (name == "progress")
4733                 return createGuiProgressView(*this);
4734
4735         return 0;
4736 }
4737
4738
4739 SEMenu::SEMenu(QWidget * parent)
4740 {
4741         QAction * action = addAction(qt_("Disable Shell Escape"));
4742         connect(action, SIGNAL(triggered()),
4743                 parent, SLOT(disableShellEscape()));
4744 }
4745
4746
4747 } // namespace frontend
4748 } // namespace lyx
4749
4750 #include "moc_GuiView.cpp"