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