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