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