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