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