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