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