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