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