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