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