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