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