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