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