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