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