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