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