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