]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Factor out magic zoom minimum to a const member
[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         // FIXME: This is slightly expensive, though less than the tocBackend update
1707         // (#9880). This also resets the view in the Toc Widget (#6675).
1708         d.toc_models_.reset(documentBufferView());
1709         // Navigator needs more than a simple update in this case. It needs to be
1710         // rebuilt.
1711         updateDialog("toc", "");
1712 }
1713
1714
1715 void GuiView::updateDialog(string const & name, string const & data)
1716 {
1717         if (!isDialogVisible(name))
1718                 return;
1719
1720         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1721         if (it == d.dialogs_.end())
1722                 return;
1723
1724         Dialog * const dialog = it->second.get();
1725         if (dialog->isVisibleView())
1726                 dialog->initialiseParams(data);
1727 }
1728
1729
1730 BufferView * GuiView::documentBufferView()
1731 {
1732         return currentMainWorkArea()
1733                 ? &currentMainWorkArea()->bufferView()
1734                 : 0;
1735 }
1736
1737
1738 BufferView const * GuiView::documentBufferView() const
1739 {
1740         return currentMainWorkArea()
1741                 ? &currentMainWorkArea()->bufferView()
1742                 : 0;
1743 }
1744
1745
1746 BufferView * GuiView::currentBufferView()
1747 {
1748         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1749 }
1750
1751
1752 BufferView const * GuiView::currentBufferView() const
1753 {
1754         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1755 }
1756
1757
1758 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1759         Buffer const * orig, Buffer * clone)
1760 {
1761         bool const success = clone->autoSave();
1762         delete clone;
1763         busyBuffers.remove(orig);
1764         return success
1765                 ? _("Automatic save done.")
1766                 : _("Automatic save failed!");
1767 }
1768
1769
1770 void GuiView::autoSave()
1771 {
1772         LYXERR(Debug::INFO, "Running autoSave()");
1773
1774         Buffer * buffer = documentBufferView()
1775                 ? &documentBufferView()->buffer() : 0;
1776         if (!buffer) {
1777                 resetAutosaveTimers();
1778                 return;
1779         }
1780
1781         GuiViewPrivate::busyBuffers.insert(buffer);
1782         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1783                 buffer, buffer->cloneBufferOnly());
1784         d.autosave_watcher_.setFuture(f);
1785         resetAutosaveTimers();
1786 }
1787
1788
1789 void GuiView::resetAutosaveTimers()
1790 {
1791         if (lyxrc.autosave)
1792                 d.autosave_timeout_.restart();
1793 }
1794
1795
1796 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1797 {
1798         bool enable = true;
1799         Buffer * buf = currentBufferView()
1800                 ? &currentBufferView()->buffer() : 0;
1801         Buffer * doc_buffer = documentBufferView()
1802                 ? &(documentBufferView()->buffer()) : 0;
1803
1804 #ifdef Q_OS_MAC
1805         /* In LyX/Mac, when a dialog is open, the menus of the
1806            application can still be accessed without giving focus to
1807            the main window. In this case, we want to disable the menu
1808            entries that are buffer-related.
1809            This code must not be used on Linux and Windows, since it
1810            would disable buffer-related entries when hovering over the
1811            menu (see bug #9574).
1812          */
1813         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1814                 buf = 0;
1815                 doc_buffer = 0;
1816         }
1817 #endif
1818
1819         // Check whether we need a buffer
1820         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1821                 // no, exit directly
1822                 flag.message(from_utf8(N_("Command not allowed with"
1823                                         "out any document open")));
1824                 flag.setEnabled(false);
1825                 return true;
1826         }
1827
1828         if (cmd.origin() == FuncRequest::TOC) {
1829                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1830                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1831                         flag.setEnabled(false);
1832                 return true;
1833         }
1834
1835         switch(cmd.action()) {
1836         case LFUN_BUFFER_IMPORT:
1837                 break;
1838
1839         case LFUN_MASTER_BUFFER_UPDATE:
1840         case LFUN_MASTER_BUFFER_VIEW:
1841                 enable = doc_buffer
1842                         && (doc_buffer->parent() != 0
1843                             || doc_buffer->hasChildren())
1844                         && !d.processing_thread_watcher_.isRunning();
1845                 break;
1846
1847         case LFUN_BUFFER_UPDATE:
1848         case LFUN_BUFFER_VIEW: {
1849                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1850                         enable = false;
1851                         break;
1852                 }
1853                 string format = to_utf8(cmd.argument());
1854                 if (cmd.argument().empty())
1855                         format = doc_buffer->params().getDefaultOutputFormat();
1856                 enable = doc_buffer->params().isExportableFormat(format);
1857                 break;
1858         }
1859
1860         case LFUN_BUFFER_RELOAD:
1861                 enable = doc_buffer && !doc_buffer->isUnnamed()
1862                         && doc_buffer->fileName().exists()
1863                         && (!doc_buffer->isClean()
1864                            || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1865                 break;
1866
1867         case LFUN_BUFFER_CHILD_OPEN:
1868                 enable = doc_buffer != 0;
1869                 break;
1870
1871         case LFUN_BUFFER_WRITE:
1872                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1873                 break;
1874
1875         //FIXME: This LFUN should be moved to GuiApplication.
1876         case LFUN_BUFFER_WRITE_ALL: {
1877                 // We enable the command only if there are some modified buffers
1878                 Buffer * first = theBufferList().first();
1879                 enable = false;
1880                 if (!first)
1881                         break;
1882                 Buffer * b = first;
1883                 // We cannot use a for loop as the buffer list is a cycle.
1884                 do {
1885                         if (!b->isClean()) {
1886                                 enable = true;
1887                                 break;
1888                         }
1889                         b = theBufferList().next(b);
1890                 } while (b != first);
1891                 break;
1892         }
1893
1894         case LFUN_BUFFER_WRITE_AS:
1895         case LFUN_BUFFER_EXPORT_AS:
1896                 enable = doc_buffer != 0;
1897                 break;
1898
1899         case LFUN_BUFFER_CLOSE:
1900         case LFUN_VIEW_CLOSE:
1901                 enable = doc_buffer != 0;
1902                 break;
1903
1904         case LFUN_BUFFER_CLOSE_ALL:
1905                 enable = theBufferList().last() != theBufferList().first();
1906                 break;
1907
1908         case LFUN_VIEW_SPLIT:
1909                 if (cmd.getArg(0) == "vertical")
1910                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1911                                          d.splitter_->orientation() == Qt::Vertical);
1912                 else
1913                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1914                                          d.splitter_->orientation() == Qt::Horizontal);
1915                 break;
1916
1917         case LFUN_TAB_GROUP_CLOSE:
1918                 enable = d.tabWorkAreaCount() > 1;
1919                 break;
1920
1921         case LFUN_TOOLBAR_TOGGLE: {
1922                 string const name = cmd.getArg(0);
1923                 if (GuiToolbar * t = toolbar(name))
1924                         flag.setOnOff(t->isVisible());
1925                 else {
1926                         enable = false;
1927                         docstring const msg =
1928                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1929                         flag.message(msg);
1930                 }
1931                 break;
1932         }
1933
1934         case LFUN_DROP_LAYOUTS_CHOICE:
1935                 enable = buf != 0;
1936                 break;
1937
1938         case LFUN_UI_TOGGLE:
1939                 flag.setOnOff(isFullScreen());
1940                 break;
1941
1942         case LFUN_DIALOG_DISCONNECT_INSET:
1943                 break;
1944
1945         case LFUN_DIALOG_HIDE:
1946                 // FIXME: should we check if the dialog is shown?
1947                 break;
1948
1949         case LFUN_DIALOG_TOGGLE:
1950                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1951                 // fall through to set "enable"
1952         case LFUN_DIALOG_SHOW: {
1953                 string const name = cmd.getArg(0);
1954                 if (!doc_buffer)
1955                         enable = name == "aboutlyx"
1956                                 || name == "file" //FIXME: should be removed.
1957                                 || name == "prefs"
1958                                 || name == "texinfo"
1959                                 || name == "progress"
1960                                 || name == "compare";
1961                 else if (name == "character" || name == "symbols"
1962                         || name == "mathdelimiter" || name == "mathmatrix") {
1963                         if (!buf || buf->isReadonly())
1964                                 enable = false;
1965                         else {
1966                                 Cursor const & cur = currentBufferView()->cursor();
1967                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1968                         }
1969                 }
1970                 else if (name == "latexlog")
1971                         enable = FileName(doc_buffer->logName()).isReadableFile();
1972                 else if (name == "spellchecker")
1973                         enable = theSpellChecker() 
1974                                 && !doc_buffer->isReadonly()
1975                                 && !doc_buffer->text().empty();
1976                 else if (name == "vclog")
1977                         enable = doc_buffer->lyxvc().inUse();
1978                 break;
1979         }
1980
1981         case LFUN_DIALOG_UPDATE: {
1982                 string const name = cmd.getArg(0);
1983                 if (!buf)
1984                         enable = name == "prefs";
1985                 break;
1986         }
1987
1988         case LFUN_COMMAND_EXECUTE:
1989         case LFUN_MESSAGE:
1990         case LFUN_MENU_OPEN:
1991                 // Nothing to check.
1992                 break;
1993
1994         case LFUN_COMPLETION_INLINE:
1995                 if (!d.current_work_area_
1996                         || !d.current_work_area_->completer().inlinePossible(
1997                         currentBufferView()->cursor()))
1998                         enable = false;
1999                 break;
2000
2001         case LFUN_COMPLETION_POPUP:
2002                 if (!d.current_work_area_
2003                         || !d.current_work_area_->completer().popupPossible(
2004                         currentBufferView()->cursor()))
2005                         enable = false;
2006                 break;
2007
2008         case LFUN_COMPLETE:
2009                 if (!d.current_work_area_
2010                         || !d.current_work_area_->completer().inlinePossible(
2011                         currentBufferView()->cursor()))
2012                         enable = false;
2013                 break;
2014
2015         case LFUN_COMPLETION_ACCEPT:
2016                 if (!d.current_work_area_
2017                         || (!d.current_work_area_->completer().popupVisible()
2018                         && !d.current_work_area_->completer().inlineVisible()
2019                         && !d.current_work_area_->completer().completionAvailable()))
2020                         enable = false;
2021                 break;
2022
2023         case LFUN_COMPLETION_CANCEL:
2024                 if (!d.current_work_area_
2025                         || (!d.current_work_area_->completer().popupVisible()
2026                         && !d.current_work_area_->completer().inlineVisible()))
2027                         enable = false;
2028                 break;
2029
2030         case LFUN_BUFFER_ZOOM_OUT:
2031         case LFUN_BUFFER_ZOOM_IN: {
2032                 // only diff between these two is that the default for ZOOM_OUT
2033                 // is a neg. number
2034                 bool const neg_zoom =
2035                         convert<int>(cmd.argument()) < 0 ||
2036                         (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2037                 if (lyxrc.zoom <= zoom_min && neg_zoom) {
2038                         docstring const msg =
2039                                 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min);
2040                         flag.message(msg);
2041                         enable = false;
2042                 } else
2043                         enable = doc_buffer;
2044                 break;
2045         }
2046         case LFUN_BUFFER_MOVE_NEXT:
2047         case LFUN_BUFFER_MOVE_PREVIOUS:
2048                 // we do not cycle when moving
2049         case LFUN_BUFFER_NEXT:
2050         case LFUN_BUFFER_PREVIOUS:
2051                 // because we cycle, it doesn't matter whether on first or last
2052                 enable = (d.currentTabWorkArea()->count() > 1);
2053                 break;
2054         case LFUN_BUFFER_SWITCH:
2055                 // toggle on the current buffer, but do not toggle off
2056                 // the other ones (is that a good idea?)
2057                 if (doc_buffer
2058                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2059                         flag.setOnOff(true);
2060                 break;
2061
2062         case LFUN_VC_REGISTER:
2063                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2064                 break;
2065         case LFUN_VC_RENAME:
2066                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2067                 break;
2068         case LFUN_VC_COPY:
2069                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2070                 break;
2071         case LFUN_VC_CHECK_IN:
2072                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2073                 break;
2074         case LFUN_VC_CHECK_OUT:
2075                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2076                 break;
2077         case LFUN_VC_LOCKING_TOGGLE:
2078                 enable = doc_buffer && !doc_buffer->isReadonly()
2079                         && doc_buffer->lyxvc().lockingToggleEnabled();
2080                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2081                 break;
2082         case LFUN_VC_REVERT:
2083                 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2084                 break;
2085         case LFUN_VC_UNDO_LAST:
2086                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2087                 break;
2088         case LFUN_VC_REPO_UPDATE:
2089                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2090                 break;
2091         case LFUN_VC_COMMAND: {
2092                 if (cmd.argument().empty())
2093                         enable = false;
2094                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2095                         enable = false;
2096                 break;
2097         }
2098         case LFUN_VC_COMPARE:
2099                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2100                 break;
2101
2102         case LFUN_SERVER_GOTO_FILE_ROW:
2103         case LFUN_LYX_ACTIVATE:
2104                 break;
2105         case LFUN_FORWARD_SEARCH:
2106                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2107                 break;
2108
2109         case LFUN_FILE_INSERT_PLAINTEXT:
2110         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2111                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2112                 break;
2113
2114         case LFUN_SPELLING_CONTINUOUSLY:
2115                 flag.setOnOff(lyxrc.spellcheck_continuously);
2116                 break;
2117
2118         default:
2119                 return false;
2120         }
2121
2122         if (!enable)
2123                 flag.setEnabled(false);
2124
2125         return true;
2126 }
2127
2128
2129 static FileName selectTemplateFile()
2130 {
2131         FileDialog dlg(qt_("Select template file"));
2132         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2133         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2134
2135         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2136                                  QStringList(qt_("LyX Documents (*.lyx)")));
2137
2138         if (result.first == FileDialog::Later)
2139                 return FileName();
2140         if (result.second.isEmpty())
2141                 return FileName();
2142         return FileName(fromqstr(result.second));
2143 }
2144
2145
2146 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2147 {
2148         setBusy(true);
2149
2150         Buffer * newBuffer = 0;
2151         try {
2152                 newBuffer = checkAndLoadLyXFile(filename);
2153         } catch (ExceptionMessage const & e) {
2154                 setBusy(false);
2155                 throw(e);
2156         }
2157         setBusy(false);
2158
2159         if (!newBuffer) {
2160                 message(_("Document not loaded."));
2161                 return 0;
2162         }
2163
2164         setBuffer(newBuffer);
2165         newBuffer->errors("Parse");
2166
2167         if (tolastfiles)
2168                 theSession().lastFiles().add(filename);
2169
2170         return newBuffer;
2171 }
2172
2173
2174 void GuiView::openDocument(string const & fname)
2175 {
2176         string initpath = lyxrc.document_path;
2177
2178         if (documentBufferView()) {
2179                 string const trypath = documentBufferView()->buffer().filePath();
2180                 // If directory is writeable, use this as default.
2181                 if (FileName(trypath).isDirWritable())
2182                         initpath = trypath;
2183         }
2184
2185         string filename;
2186
2187         if (fname.empty()) {
2188                 FileDialog dlg(qt_("Select document to open"));
2189                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2190                 dlg.setButton2(qt_("Examples|#E#e"),
2191                                 toqstr(addPath(package().system_support().absFileName(), "examples")));
2192
2193                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2194                 FileDialog::Result result =
2195                         dlg.open(toqstr(initpath), filter);
2196
2197                 if (result.first == FileDialog::Later)
2198                         return;
2199
2200                 filename = fromqstr(result.second);
2201
2202                 // check selected filename
2203                 if (filename.empty()) {
2204                         message(_("Canceled."));
2205                         return;
2206                 }
2207         } else
2208                 filename = fname;
2209
2210         // get absolute path of file and add ".lyx" to the filename if
2211         // necessary.
2212         FileName const fullname =
2213                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2214         if (!fullname.empty())
2215                 filename = fullname.absFileName();
2216
2217         if (!fullname.onlyPath().isDirectory()) {
2218                 Alert::warning(_("Invalid filename"),
2219                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2220                                 from_utf8(fullname.absFileName())));
2221                 return;
2222         }
2223
2224         // if the file doesn't exist and isn't already open (bug 6645),
2225         // let the user create one
2226         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2227             !LyXVC::file_not_found_hook(fullname)) {
2228                 // the user specifically chose this name. Believe him.
2229                 Buffer * const b = newFile(filename, string(), true);
2230                 if (b)
2231                         setBuffer(b);
2232                 return;
2233         }
2234
2235         docstring const disp_fn = makeDisplayPath(filename);
2236         message(bformat(_("Opening document %1$s..."), disp_fn));
2237
2238         docstring str2;
2239         Buffer * buf = loadDocument(fullname);
2240         if (buf) {
2241                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2242                 if (buf->lyxvc().inUse())
2243                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2244                                 " " + _("Version control detected.");
2245         } else {
2246                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2247         }
2248         message(str2);
2249 }
2250
2251 // FIXME: clean that
2252 static bool import(GuiView * lv, FileName const & filename,
2253         string const & format, ErrorList & errorList)
2254 {
2255         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2256
2257         string loader_format;
2258         vector<string> loaders = theConverters().loaders();
2259         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2260                 vector<string>::const_iterator it = loaders.begin();
2261                 vector<string>::const_iterator en = loaders.end();
2262                 for (; it != en; ++it) {
2263                         if (!theConverters().isReachable(format, *it))
2264                                 continue;
2265
2266                         string const tofile =
2267                                 support::changeExtension(filename.absFileName(),
2268                                 formats.extension(*it));
2269                         if (!theConverters().convert(0, filename, FileName(tofile),
2270                                 filename, format, *it, errorList))
2271                                 return false;
2272                         loader_format = *it;
2273                         break;
2274                 }
2275                 if (loader_format.empty()) {
2276                         frontend::Alert::error(_("Couldn't import file"),
2277                                          bformat(_("No information for importing the format %1$s."),
2278                                          formats.prettyName(format)));
2279                         return false;
2280                 }
2281         } else
2282                 loader_format = format;
2283
2284         if (loader_format == "lyx") {
2285                 Buffer * buf = lv->loadDocument(lyxfile);
2286                 if (!buf)
2287                         return false;
2288         } else {
2289                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2290                 if (!b)
2291                         return false;
2292                 lv->setBuffer(b);
2293                 bool as_paragraphs = loader_format == "textparagraph";
2294                 string filename2 = (loader_format == format) ? filename.absFileName()
2295                         : support::changeExtension(filename.absFileName(),
2296                                           formats.extension(loader_format));
2297                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2298                         as_paragraphs);
2299                 guiApp->setCurrentView(lv);
2300                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2301         }
2302
2303         return true;
2304 }
2305
2306
2307 void GuiView::importDocument(string const & argument)
2308 {
2309         string format;
2310         string filename = split(argument, format, ' ');
2311
2312         LYXERR(Debug::INFO, format << " file: " << filename);
2313
2314         // need user interaction
2315         if (filename.empty()) {
2316                 string initpath = lyxrc.document_path;
2317                 if (documentBufferView()) {
2318                         string const trypath = documentBufferView()->buffer().filePath();
2319                         // If directory is writeable, use this as default.
2320                         if (FileName(trypath).isDirWritable())
2321                                 initpath = trypath;
2322                 }
2323
2324                 docstring const text = bformat(_("Select %1$s file to import"),
2325                         formats.prettyName(format));
2326
2327                 FileDialog dlg(toqstr(text));
2328                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2329                 dlg.setButton2(qt_("Examples|#E#e"),
2330                         toqstr(addPath(package().system_support().absFileName(), "examples")));
2331
2332                 docstring filter = formats.prettyName(format);
2333                 filter += " (*.{";
2334                 // FIXME UNICODE
2335                 filter += from_utf8(formats.extensions(format));
2336                 filter += "})";
2337
2338                 FileDialog::Result result =
2339                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2340
2341                 if (result.first == FileDialog::Later)
2342                         return;
2343
2344                 filename = fromqstr(result.second);
2345
2346                 // check selected filename
2347                 if (filename.empty())
2348                         message(_("Canceled."));
2349         }
2350
2351         if (filename.empty())
2352                 return;
2353
2354         // get absolute path of file
2355         FileName const fullname(support::makeAbsPath(filename));
2356
2357         // Can happen if the user entered a path into the dialog
2358         // (see bug #7437)
2359         if (fullname.onlyFileName().empty()) {
2360                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2361                                           "Aborting import."),
2362                                         from_utf8(fullname.absFileName()));
2363                 frontend::Alert::error(_("File name error"), msg);
2364                 message(_("Canceled."));
2365                 return;
2366         }
2367
2368
2369         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2370
2371         // Check if the document already is open
2372         Buffer * buf = theBufferList().getBuffer(lyxfile);
2373         if (buf) {
2374                 setBuffer(buf);
2375                 if (!closeBuffer()) {
2376                         message(_("Canceled."));
2377                         return;
2378                 }
2379         }
2380
2381         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2382
2383         // if the file exists already, and we didn't do
2384         // -i lyx thefile.lyx, warn
2385         if (lyxfile.exists() && fullname != lyxfile) {
2386
2387                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2388                         "Do you want to overwrite that document?"), displaypath);
2389                 int const ret = Alert::prompt(_("Overwrite document?"),
2390                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2391
2392                 if (ret == 1) {
2393                         message(_("Canceled."));
2394                         return;
2395                 }
2396         }
2397
2398         message(bformat(_("Importing %1$s..."), displaypath));
2399         ErrorList errorList;
2400         if (import(this, fullname, format, errorList))
2401                 message(_("imported."));
2402         else
2403                 message(_("file not imported!"));
2404
2405         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2406 }
2407
2408
2409 void GuiView::newDocument(string const & filename, bool from_template)
2410 {
2411         FileName initpath(lyxrc.document_path);
2412         if (documentBufferView()) {
2413                 FileName const trypath(documentBufferView()->buffer().filePath());
2414                 // If directory is writeable, use this as default.
2415                 if (trypath.isDirWritable())
2416                         initpath = trypath;
2417         }
2418
2419         string templatefile;
2420         if (from_template) {
2421                 templatefile = selectTemplateFile().absFileName();
2422                 if (templatefile.empty())
2423                         return;
2424         }
2425
2426         Buffer * b;
2427         if (filename.empty())
2428                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2429         else
2430                 b = newFile(filename, templatefile, true);
2431
2432         if (b)
2433                 setBuffer(b);
2434
2435         // If no new document could be created, it is unsure
2436         // whether there is a valid BufferView.
2437         if (currentBufferView())
2438                 // Ensure the cursor is correctly positioned on screen.
2439                 currentBufferView()->showCursor();
2440 }
2441
2442
2443 void GuiView::insertLyXFile(docstring const & fname)
2444 {
2445         BufferView * bv = documentBufferView();
2446         if (!bv)
2447                 return;
2448
2449         // FIXME UNICODE
2450         FileName filename(to_utf8(fname));
2451         if (filename.empty()) {
2452                 // Launch a file browser
2453                 // FIXME UNICODE
2454                 string initpath = lyxrc.document_path;
2455                 string const trypath = bv->buffer().filePath();
2456                 // If directory is writeable, use this as default.
2457                 if (FileName(trypath).isDirWritable())
2458                         initpath = trypath;
2459
2460                 // FIXME UNICODE
2461                 FileDialog dlg(qt_("Select LyX document to insert"));
2462                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2463                 dlg.setButton2(qt_("Examples|#E#e"),
2464                         toqstr(addPath(package().system_support().absFileName(),
2465                         "examples")));
2466
2467                 FileDialog::Result result = dlg.open(toqstr(initpath),
2468                                          QStringList(qt_("LyX Documents (*.lyx)")));
2469
2470                 if (result.first == FileDialog::Later)
2471                         return;
2472
2473                 // FIXME UNICODE
2474                 filename.set(fromqstr(result.second));
2475
2476                 // check selected filename
2477                 if (filename.empty()) {
2478                         // emit message signal.
2479                         message(_("Canceled."));
2480                         return;
2481                 }
2482         }
2483
2484         bv->insertLyXFile(filename);
2485         bv->buffer().errors("Parse");
2486 }
2487
2488
2489 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2490 {
2491         FileName fname = b.fileName();
2492         FileName const oldname = fname;
2493
2494         if (!newname.empty()) {
2495                 // FIXME UNICODE
2496                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2497         } else {
2498                 // Switch to this Buffer.
2499                 setBuffer(&b);
2500
2501                 // No argument? Ask user through dialog.
2502                 // FIXME UNICODE
2503                 FileDialog dlg(qt_("Choose a filename to save document as"));
2504                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2505                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2506
2507                 if (!isLyXFileName(fname.absFileName()))
2508                         fname.changeExtension(".lyx");
2509
2510                 FileDialog::Result result =
2511                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2512                                    QStringList(qt_("LyX Documents (*.lyx)")),
2513                                          toqstr(fname.onlyFileName()));
2514
2515                 if (result.first == FileDialog::Later)
2516                         return false;
2517
2518                 fname.set(fromqstr(result.second));
2519
2520                 if (fname.empty())
2521                         return false;
2522
2523                 if (!isLyXFileName(fname.absFileName()))
2524                         fname.changeExtension(".lyx");
2525         }
2526
2527         // fname is now the new Buffer location.
2528
2529         // if there is already a Buffer open with this name, we do not want
2530         // to have another one. (the second test makes sure we're not just
2531         // trying to overwrite ourselves, which is fine.)
2532         if (theBufferList().exists(fname) && fname != oldname
2533                   && theBufferList().getBuffer(fname) != &b) {
2534                 docstring const text =
2535                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2536                             "Please close it before attempting to overwrite it.\n"
2537                             "Do you want to choose a new filename?"),
2538                                 from_utf8(fname.absFileName()));
2539                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2540                         text, 0, 1, _("&Rename"), _("&Cancel"));
2541                 switch (ret) {
2542                 case 0: return renameBuffer(b, docstring(), kind);
2543                 case 1: return false;
2544                 }
2545                 //return false;
2546         }
2547
2548         bool const existsLocal = fname.exists();
2549         bool const existsInVC = LyXVC::fileInVC(fname);
2550         if (existsLocal || existsInVC) {
2551                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2552                 if (kind != LV_WRITE_AS && existsInVC) {
2553                         // renaming to a name that is already in VC
2554                         // would not work
2555                         docstring text = bformat(_("The document %1$s "
2556                                         "is already registered.\n\n"
2557                                         "Do you want to choose a new name?"),
2558                                 file);
2559                         docstring const title = (kind == LV_VC_RENAME) ?
2560                                 _("Rename document?") : _("Copy document?");
2561                         docstring const button = (kind == LV_VC_RENAME) ?
2562                                 _("&Rename") : _("&Copy");
2563                         int const ret = Alert::prompt(title, text, 0, 1,
2564                                 button, _("&Cancel"));
2565                         switch (ret) {
2566                         case 0: return renameBuffer(b, docstring(), kind);
2567                         case 1: return false;
2568                         }
2569                 }
2570
2571                 if (existsLocal) {
2572                         docstring text = bformat(_("The document %1$s "
2573                                         "already exists.\n\n"
2574                                         "Do you want to overwrite that document?"),
2575                                 file);
2576                         int const ret = Alert::prompt(_("Overwrite document?"),
2577                                         text, 0, 2, _("&Overwrite"),
2578                                         _("&Rename"), _("&Cancel"));
2579                         switch (ret) {
2580                         case 0: break;
2581                         case 1: return renameBuffer(b, docstring(), kind);
2582                         case 2: return false;
2583                         }
2584                 }
2585         }
2586
2587         switch (kind) {
2588         case LV_VC_RENAME: {
2589                 string msg = b.lyxvc().rename(fname);
2590                 if (msg.empty())
2591                         return false;
2592                 message(from_utf8(msg));
2593                 break;
2594         }
2595         case LV_VC_COPY: {
2596                 string msg = b.lyxvc().copy(fname);
2597                 if (msg.empty())
2598                         return false;
2599                 message(from_utf8(msg));
2600                 break;
2601         }
2602         case LV_WRITE_AS:
2603                 break;
2604         }
2605         // LyXVC created the file already in case of LV_VC_RENAME or
2606         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2607         // relative paths of included stuff right if we moved e.g. from
2608         // /a/b.lyx to /a/c/b.lyx.
2609
2610         bool const saved = saveBuffer(b, fname);
2611         if (saved)
2612                 b.reload();
2613         return saved;
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
2628         vector<Format const *> export_formats;
2629         for (Format const & f : formats)
2630                 if (f.documentFormat())
2631                         export_formats.push_back(&f);
2632         sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2633         map<QString, string> fmap;
2634         QString filter;
2635         string ext;
2636         for (Format const * f : export_formats) {
2637                 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2638                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2639                                                      loc_prettyname,
2640                                                      from_ascii(f->extension())));
2641                 types << loc_filter;
2642                 fmap[loc_filter] = f->name();
2643                 if (from_ascii(f->name()) == iformat) {
2644                         filter = loc_filter;
2645                         ext = f->extension();
2646                 }
2647         }
2648         string ofname = fname.onlyFileName();
2649         if (!ext.empty())
2650                 ofname = support::changeExtension(ofname, ext);
2651         FileDialog::Result result =
2652                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2653                          types,
2654                          toqstr(ofname),
2655                          &filter);
2656         if (result.first != FileDialog::Chosen)
2657                 return false;
2658
2659         string fmt_name;
2660         fname.set(fromqstr(result.second));
2661         if (filter == anyformat)
2662                 fmt_name = formats.getFormatFromExtension(fname.extension());
2663         else
2664                 fmt_name = fmap[filter];
2665         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2666                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2667
2668         if (fmt_name.empty() || fname.empty())
2669                 return false;
2670
2671         // fname is now the new Buffer location.
2672         if (FileName(fname).exists()) {
2673                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2674                 docstring text = bformat(_("The document %1$s already "
2675                                            "exists.\n\nDo you want to "
2676                                            "overwrite that document?"),
2677                                          file);
2678                 int const ret = Alert::prompt(_("Overwrite document?"),
2679                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2680                 switch (ret) {
2681                 case 0: break;
2682                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2683                 case 2: return false;
2684                 }
2685         }
2686
2687         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2688         DispatchResult dr;
2689         dispatch(cmd, dr);
2690         return dr.dispatched();
2691 }
2692
2693
2694 bool GuiView::saveBuffer(Buffer & b)
2695 {
2696         return saveBuffer(b, FileName());
2697 }
2698
2699
2700 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2701 {
2702         if (workArea(b) && workArea(b)->inDialogMode())
2703                 return true;
2704
2705         if (fn.empty() && b.isUnnamed())
2706                 return renameBuffer(b, docstring());
2707
2708         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2709         if (success) {
2710                 theSession().lastFiles().add(b.fileName());
2711                 return true;
2712         }
2713
2714         // Switch to this Buffer.
2715         setBuffer(&b);
2716
2717         // FIXME: we don't tell the user *WHY* the save failed !!
2718         docstring const file = makeDisplayPath(b.absFileName(), 30);
2719         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2720                                    "Do you want to rename the document and "
2721                                    "try again?"), file);
2722         int const ret = Alert::prompt(_("Rename and save?"),
2723                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2724         switch (ret) {
2725         case 0:
2726                 if (!renameBuffer(b, docstring()))
2727                         return false;
2728                 break;
2729         case 1:
2730                 break;
2731         case 2:
2732                 return false;
2733         }
2734
2735         return saveBuffer(b, fn);
2736 }
2737
2738
2739 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2740 {
2741         return closeWorkArea(wa, false);
2742 }
2743
2744
2745 // We only want to close the buffer if it is not visible in other workareas
2746 // of the same view, nor in other views, and if this is not a child
2747 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2748 {
2749         Buffer & buf = wa->bufferView().buffer();
2750
2751         bool last_wa = d.countWorkAreasOf(buf) == 1
2752                 && !inOtherView(buf) && !buf.parent();
2753
2754         bool close_buffer = last_wa;
2755
2756         if (last_wa) {
2757                 if (lyxrc.close_buffer_with_last_view == "yes")
2758                         ; // Nothing to do
2759                 else if (lyxrc.close_buffer_with_last_view == "no")
2760                         close_buffer = false;
2761                 else {
2762                         docstring file;
2763                         if (buf.isUnnamed())
2764                                 file = from_utf8(buf.fileName().onlyFileName());
2765                         else
2766                                 file = buf.fileName().displayName(30);
2767                         docstring const text = bformat(
2768                                 _("Last view on document %1$s is being closed.\n"
2769                                   "Would you like to close or hide the document?\n"
2770                                   "\n"
2771                                   "Hidden documents can be displayed back through\n"
2772                                   "the menu: View->Hidden->...\n"
2773                                   "\n"
2774                                   "To remove this question, set your preference in:\n"
2775                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2776                                 ), file);
2777                         int ret = Alert::prompt(_("Close or hide document?"),
2778                                 text, 0, 1, _("&Close"), _("&Hide"));
2779                         close_buffer = (ret == 0);
2780                 }
2781         }
2782
2783         return closeWorkArea(wa, close_buffer);
2784 }
2785
2786
2787 bool GuiView::closeBuffer()
2788 {
2789         GuiWorkArea * wa = currentMainWorkArea();
2790         // coverity complained about this
2791         // it seems unnecessary, but perhaps is worth the check
2792         LASSERT(wa, return false);
2793
2794         setCurrentWorkArea(wa);
2795         Buffer & buf = wa->bufferView().buffer();
2796         return closeWorkArea(wa, !buf.parent());
2797 }
2798
2799
2800 void GuiView::writeSession() const {
2801         GuiWorkArea const * active_wa = currentMainWorkArea();
2802         for (int i = 0; i < d.splitter_->count(); ++i) {
2803                 TabWorkArea * twa = d.tabWorkArea(i);
2804                 for (int j = 0; j < twa->count(); ++j) {
2805                         GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2806                         Buffer & buf = wa->bufferView().buffer();
2807                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2808                 }
2809         }
2810 }
2811
2812
2813 bool GuiView::closeBufferAll()
2814 {
2815         // Close the workareas in all other views
2816         QList<int> const ids = guiApp->viewIds();
2817         for (int i = 0; i != ids.size(); ++i) {
2818                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2819                         return false;
2820         }
2821
2822         // Close our own workareas
2823         if (!closeWorkAreaAll())
2824                 return false;
2825
2826         // Now close the hidden buffers. We prevent hidden buffers from being
2827         // dirty, so we can just close them.
2828         theBufferList().closeAll();
2829         return true;
2830 }
2831
2832
2833 bool GuiView::closeWorkAreaAll()
2834 {
2835         setCurrentWorkArea(currentMainWorkArea());
2836
2837         // We might be in a situation that there is still a tabWorkArea, but
2838         // there are no tabs anymore. This can happen when we get here after a
2839         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2840         // many TabWorkArea's have no documents anymore.
2841         int empty_twa = 0;
2842
2843         // We have to call count() each time, because it can happen that
2844         // more than one splitter will disappear in one iteration (bug 5998).
2845         while (d.splitter_->count() > empty_twa) {
2846                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2847
2848                 if (twa->count() == 0)
2849                         ++empty_twa;
2850                 else {
2851                         setCurrentWorkArea(twa->currentWorkArea());
2852                         if (!closeTabWorkArea(twa))
2853                                 return false;
2854                 }
2855         }
2856         return true;
2857 }
2858
2859
2860 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2861 {
2862         if (!wa)
2863                 return false;
2864
2865         Buffer & buf = wa->bufferView().buffer();
2866
2867         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2868                 Alert::warning(_("Close document"), 
2869                         _("Document could not be closed because it is being processed by LyX."));
2870                 return false;
2871         }
2872
2873         if (close_buffer)
2874                 return closeBuffer(buf);
2875         else {
2876                 if (!inMultiTabs(wa))
2877                         if (!saveBufferIfNeeded(buf, true))
2878                                 return false;
2879                 removeWorkArea(wa);
2880                 return true;
2881         }
2882 }
2883
2884
2885 bool GuiView::closeBuffer(Buffer & buf)
2886 {
2887         // If we are in a close_event all children will be closed in some time,
2888         // so no need to do it here. This will ensure that the children end up
2889         // in the session file in the correct order. If we close the master
2890         // buffer, we can close or release the child buffers here too.
2891         bool success = true;
2892         if (!closing_) {
2893                 ListOfBuffers clist = buf.getChildren();
2894                 ListOfBuffers::const_iterator it = clist.begin();
2895                 ListOfBuffers::const_iterator const bend = clist.end();
2896                 for (; it != bend; ++it) {
2897                         Buffer * child_buf = *it;
2898                         if (theBufferList().isOthersChild(&buf, child_buf)) {
2899                                 child_buf->setParent(0);
2900                                 continue;
2901                         }
2902
2903                         // FIXME: should we look in other tabworkareas?
2904                         // ANSWER: I don't think so. I've tested, and if the child is
2905                         // open in some other window, it closes without a problem.
2906                         GuiWorkArea * child_wa = workArea(*child_buf);
2907                         if (child_wa) {
2908                                 success = closeWorkArea(child_wa, true);
2909                                 if (!success)
2910                                         break;
2911                         } else {
2912                                 // In this case the child buffer is open but hidden.
2913                                 // It therefore should not (MUST NOT) be dirty!
2914                                 LATTEST(child_buf->isClean());
2915                                 theBufferList().release(child_buf);
2916                         }
2917                 }
2918         }
2919         if (success) {
2920                 // goto bookmark to update bookmark pit.
2921                 // FIXME: we should update only the bookmarks related to this buffer!
2922                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2923                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2924                         guiApp->gotoBookmark(i+1, false, false);
2925
2926                 if (saveBufferIfNeeded(buf, false)) {
2927                         buf.removeAutosaveFile();
2928                         theBufferList().release(&buf);
2929                         return true;
2930                 }
2931         }
2932         // open all children again to avoid a crash because of dangling
2933         // pointers (bug 6603)
2934         buf.updateBuffer();
2935         return false;
2936 }
2937
2938
2939 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2940 {
2941         while (twa == d.currentTabWorkArea()) {
2942                 twa->setCurrentIndex(twa->count() - 1);
2943
2944                 GuiWorkArea * wa = twa->currentWorkArea();
2945                 Buffer & b = wa->bufferView().buffer();
2946
2947                 // We only want to close the buffer if the same buffer is not visible
2948                 // in another view, and if this is not a child and if we are closing
2949                 // a view (not a tabgroup).
2950                 bool const close_buffer =
2951                         !inOtherView(b) && !b.parent() && closing_;
2952
2953                 if (!closeWorkArea(wa, close_buffer))
2954                         return false;
2955         }
2956         return true;
2957 }
2958
2959
2960 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2961 {
2962         if (buf.isClean() || buf.paragraphs().empty())
2963                 return true;
2964
2965         // Switch to this Buffer.
2966         setBuffer(&buf);
2967
2968         docstring file;
2969         // FIXME: Unicode?
2970         if (buf.isUnnamed())
2971                 file = from_utf8(buf.fileName().onlyFileName());
2972         else
2973                 file = buf.fileName().displayName(30);
2974
2975         // Bring this window to top before asking questions.
2976         raise();
2977         activateWindow();
2978
2979         int ret;
2980         if (hiding && buf.isUnnamed()) {
2981                 docstring const text = bformat(_("The document %1$s has not been "
2982                                                  "saved yet.\n\nDo you want to save "
2983                                                  "the document?"), file);
2984                 ret = Alert::prompt(_("Save new document?"),
2985                         text, 0, 1, _("&Save"), _("&Cancel"));
2986                 if (ret == 1)
2987                         ++ret;
2988         } else {
2989                 docstring const text = bformat(_("The document %1$s has unsaved changes."
2990                         "\n\nDo you want to save the document or discard the changes?"), file);
2991                 ret = Alert::prompt(_("Save changed document?"),
2992                         text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2993         }
2994
2995         switch (ret) {
2996         case 0:
2997                 if (!saveBuffer(buf))
2998                         return false;
2999                 break;
3000         case 1:
3001                 // If we crash after this we could have no autosave file
3002                 // but I guess this is really improbable (Jug).
3003                 // Sometimes improbable things happen:
3004                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3005                 // buf.removeAutosaveFile();
3006                 if (hiding)
3007                         // revert all changes
3008                         reloadBuffer(buf);
3009                 buf.markClean();
3010                 break;
3011         case 2:
3012                 return false;
3013         }
3014         return true;
3015 }
3016
3017
3018 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3019 {
3020         Buffer & buf = wa->bufferView().buffer();
3021
3022         for (int i = 0; i != d.splitter_->count(); ++i) {
3023                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3024                 if (wa_ && wa_ != wa)
3025                         return true;
3026         }
3027         return inOtherView(buf);
3028 }
3029
3030
3031 bool GuiView::inOtherView(Buffer & buf)
3032 {
3033         QList<int> const ids = guiApp->viewIds();
3034
3035         for (int i = 0; i != ids.size(); ++i) {
3036                 if (id_ == ids[i])
3037                         continue;
3038
3039                 if (guiApp->view(ids[i]).workArea(buf))
3040                         return true;
3041         }
3042         return false;
3043 }
3044
3045
3046 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3047 {
3048         if (!documentBufferView())
3049                 return;
3050         
3051         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3052                 Buffer * const curbuf = &documentBufferView()->buffer();
3053                 int nwa = twa->count();
3054                 for (int i = 0; i < nwa; ++i) {
3055                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3056                                 int next_index;
3057                                 if (np == NEXTBUFFER)
3058                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3059                                 else
3060                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3061                                 if (move)
3062                                         twa->moveTab(i, next_index);
3063                                 else
3064                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3065                                 break;
3066                         }
3067                 }
3068         }
3069 }
3070
3071
3072 /// make sure the document is saved
3073 static bool ensureBufferClean(Buffer * buffer)
3074 {
3075         LASSERT(buffer, return false);
3076         if (buffer->isClean() && !buffer->isUnnamed())
3077                 return true;
3078
3079         docstring const file = buffer->fileName().displayName(30);
3080         docstring title;
3081         docstring text;
3082         if (!buffer->isUnnamed()) {
3083                 text = bformat(_("The document %1$s has unsaved "
3084                                                  "changes.\n\nDo you want to save "
3085                                                  "the document?"), file);
3086                 title = _("Save changed document?");
3087
3088         } else {
3089                 text = bformat(_("The document %1$s has not been "
3090                                                  "saved yet.\n\nDo you want to save "
3091                                                  "the document?"), file);
3092                 title = _("Save new document?");
3093         }
3094         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3095
3096         if (ret == 0)
3097                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3098
3099         return buffer->isClean() && !buffer->isUnnamed();
3100 }
3101
3102
3103 bool GuiView::reloadBuffer(Buffer & buf)
3104 {
3105         Buffer::ReadStatus status = buf.reload();
3106         return status == Buffer::ReadSuccess;
3107 }
3108
3109
3110 void GuiView::checkExternallyModifiedBuffers()
3111 {
3112         BufferList::iterator bit = theBufferList().begin();
3113         BufferList::iterator const bend = theBufferList().end();
3114         for (; bit != bend; ++bit) {
3115                 Buffer * buf = *bit;
3116                 if (buf->fileName().exists()
3117                         && buf->isExternallyModified(Buffer::checksum_method)) {
3118                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3119                                         " Reload now? Any local changes will be lost."),
3120                                         from_utf8(buf->absFileName()));
3121                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3122                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3123                         if (!ret)
3124                                 reloadBuffer(*buf);
3125                 }
3126         }
3127 }
3128
3129
3130 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3131 {
3132         Buffer * buffer = documentBufferView()
3133                 ? &(documentBufferView()->buffer()) : 0;
3134
3135         switch (cmd.action()) {
3136         case LFUN_VC_REGISTER:
3137                 if (!buffer || !ensureBufferClean(buffer))
3138                         break;
3139                 if (!buffer->lyxvc().inUse()) {
3140                         if (buffer->lyxvc().registrer()) {
3141                                 reloadBuffer(*buffer);
3142                                 dr.clearMessageUpdate();
3143                         }
3144                 }
3145                 break;
3146
3147         case LFUN_VC_RENAME:
3148         case LFUN_VC_COPY: {
3149                 if (!buffer || !ensureBufferClean(buffer))
3150                         break;
3151                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3152                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3153                                 // Some changes are not yet committed.
3154                                 // We test here and not in getStatus(), since
3155                                 // this test is expensive.
3156                                 string log;
3157                                 LyXVC::CommandResult ret =
3158                                         buffer->lyxvc().checkIn(log);
3159                                 dr.setMessage(log);
3160                                 if (ret == LyXVC::ErrorCommand ||
3161                                     ret == LyXVC::VCSuccess)
3162                                         reloadBuffer(*buffer);
3163                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3164                                         frontend::Alert::error(
3165                                                 _("Revision control error."),
3166                                                 _("Document could not be checked in."));
3167                                         break;
3168                                 }
3169                         }
3170                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3171                                 LV_VC_RENAME : LV_VC_COPY;
3172                         renameBuffer(*buffer, cmd.argument(), kind);
3173                 }
3174                 break;
3175         }
3176
3177         case LFUN_VC_CHECK_IN:
3178                 if (!buffer || !ensureBufferClean(buffer))
3179                         break;
3180                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3181                         string log;
3182                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3183                         dr.setMessage(log);
3184                         // Only skip reloading if the checkin was cancelled or
3185                         // an error occurred before the real checkin VCS command
3186                         // was executed, since the VCS might have changed the
3187                         // file even if it could not checkin successfully.
3188                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3189                                 reloadBuffer(*buffer);
3190                 }
3191                 break;
3192
3193         case LFUN_VC_CHECK_OUT:
3194                 if (!buffer || !ensureBufferClean(buffer))
3195                         break;
3196                 if (buffer->lyxvc().inUse()) {
3197                         dr.setMessage(buffer->lyxvc().checkOut());
3198                         reloadBuffer(*buffer);
3199                 }
3200                 break;
3201
3202         case LFUN_VC_LOCKING_TOGGLE:
3203                 LASSERT(buffer, return);
3204                 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3205                         break;
3206                 if (buffer->lyxvc().inUse()) {
3207                         string res = buffer->lyxvc().lockingToggle();
3208                         if (res.empty()) {
3209                                 frontend::Alert::error(_("Revision control error."),
3210                                 _("Error when setting the locking property."));
3211                         } else {
3212                                 dr.setMessage(res);
3213                                 reloadBuffer(*buffer);
3214                         }
3215                 }
3216                 break;
3217
3218         case LFUN_VC_REVERT:
3219                 LASSERT(buffer, return);
3220                 if (buffer->lyxvc().revert()) {
3221                         reloadBuffer(*buffer);
3222                         dr.clearMessageUpdate();
3223                 }
3224                 break;
3225
3226         case LFUN_VC_UNDO_LAST:
3227                 LASSERT(buffer, return);
3228                 buffer->lyxvc().undoLast();
3229                 reloadBuffer(*buffer);
3230                 dr.clearMessageUpdate();
3231                 break;
3232
3233         case LFUN_VC_REPO_UPDATE:
3234                 LASSERT(buffer, return);
3235                 if (ensureBufferClean(buffer)) {
3236                         dr.setMessage(buffer->lyxvc().repoUpdate());
3237                         checkExternallyModifiedBuffers();
3238                 }
3239                 break;
3240
3241         case LFUN_VC_COMMAND: {
3242                 string flag = cmd.getArg(0);
3243                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3244                         break;
3245                 docstring message;
3246                 if (contains(flag, 'M')) {
3247                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3248                                 break;
3249                 }
3250                 string path = cmd.getArg(1);
3251                 if (contains(path, "$$p") && buffer)
3252                         path = subst(path, "$$p", buffer->filePath());
3253                 LYXERR(Debug::LYXVC, "Directory: " << path);
3254                 FileName pp(path);
3255                 if (!pp.isReadableDirectory()) {
3256                         lyxerr << _("Directory is not accessible.") << endl;
3257                         break;
3258                 }
3259                 support::PathChanger p(pp);
3260
3261                 string command = cmd.getArg(2);
3262                 if (command.empty())
3263                         break;
3264                 if (buffer) {
3265                         command = subst(command, "$$i", buffer->absFileName());
3266                         command = subst(command, "$$p", buffer->filePath());
3267                 }
3268                 command = subst(command, "$$m", to_utf8(message));
3269                 LYXERR(Debug::LYXVC, "Command: " << command);
3270                 Systemcall one;
3271                 one.startscript(Systemcall::Wait, command);
3272
3273                 if (!buffer)
3274                         break;
3275                 if (contains(flag, 'I'))
3276                         buffer->markDirty();
3277                 if (contains(flag, 'R'))
3278                         reloadBuffer(*buffer);
3279
3280                 break;
3281                 }
3282
3283         case LFUN_VC_COMPARE: {
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                 // it seems safe to assume we have a buffer
3294                 // coverity[FORWARD_NULL]
3295                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3296                         break;
3297
3298                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3299                         f2 = buffer->absFileName();
3300                 } else {
3301                         string rev2 = cmd.getArg(1);
3302                         if (rev2.empty())
3303                                 break;
3304                         // f2
3305                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3306                                 break;
3307                 }
3308
3309                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3310                                         f1 << "\n"  << f2 << "\n" );
3311                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3312                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3313                 break;
3314         }
3315
3316         default:
3317                 break;
3318         }
3319 }
3320
3321
3322 void GuiView::openChildDocument(string const & fname)
3323 {
3324         LASSERT(documentBufferView(), return);
3325         Buffer & buffer = documentBufferView()->buffer();
3326         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3327         documentBufferView()->saveBookmark(false);
3328         Buffer * child = 0;
3329         if (theBufferList().exists(filename)) {
3330                 child = theBufferList().getBuffer(filename);
3331                 setBuffer(child);
3332         } else {
3333                 message(bformat(_("Opening child document %1$s..."),
3334                         makeDisplayPath(filename.absFileName())));
3335                 child = loadDocument(filename, false);
3336         }
3337         // Set the parent name of the child document.
3338         // This makes insertion of citations and references in the child work,
3339         // when the target is in the parent or another child document.
3340         if (child)
3341                 child->setParent(&buffer);
3342 }
3343
3344
3345 bool GuiView::goToFileRow(string const & argument)
3346 {
3347         string file_name;
3348         int row;
3349         size_t i = argument.find_last_of(' ');
3350         if (i != string::npos) {
3351                 file_name = os::internal_path(trim(argument.substr(0, i)));
3352                 istringstream is(argument.substr(i + 1));
3353                 is >> row;
3354                 if (is.fail())
3355                         i = string::npos;
3356         }
3357         if (i == string::npos) {
3358                 LYXERR0("Wrong argument: " << argument);
3359                 return false;
3360         }
3361         Buffer * buf = 0;
3362         string const abstmp = package().temp_dir().absFileName();
3363         string const realtmp = package().temp_dir().realPath();
3364         // We have to use os::path_prefix_is() here, instead of
3365         // simply prefixIs(), because the file name comes from
3366         // an external application and may need case adjustment.
3367         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3368                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3369                 // Needed by inverse dvi search. If it is a file
3370                 // in tmpdir, call the apropriated function.
3371                 // If tmpdir is a symlink, we may have the real
3372                 // path passed back, so we correct for that.
3373                 if (!prefixIs(file_name, abstmp))
3374                         file_name = subst(file_name, realtmp, abstmp);
3375                 buf = theBufferList().getBufferFromTmp(file_name);
3376         } else {
3377                 // Must replace extension of the file to be .lyx
3378                 // and get full path
3379                 FileName const s = fileSearch(string(),
3380                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3381                 // Either change buffer or load the file
3382                 if (theBufferList().exists(s))
3383                         buf = theBufferList().getBuffer(s);
3384                 else if (s.exists()) {
3385                         buf = loadDocument(s);
3386                         if (!buf)
3387                                 return false;
3388                 } else {
3389                         message(bformat(
3390                                         _("File does not exist: %1$s"),
3391                                         makeDisplayPath(file_name)));
3392                         return false;
3393                 }
3394         }
3395         if (!buf) {
3396                 message(bformat(
3397                         _("No buffer for file: %1$s."),
3398                         makeDisplayPath(file_name))
3399                 );
3400                 return false;
3401         }
3402         setBuffer(buf);
3403         documentBufferView()->setCursorFromRow(row);
3404         return true;
3405 }
3406
3407
3408 template<class T>
3409 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3410 {
3411         Buffer::ExportStatus const status = func(format);
3412
3413         // the cloning operation will have produced a clone of the entire set of
3414         // documents, starting from the master. so we must delete those.
3415         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3416         delete mbuf;
3417         busyBuffers.remove(orig);
3418         return status;
3419 }
3420
3421
3422 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3423 {
3424         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3425         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3426 }
3427
3428
3429 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3430 {
3431         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3432         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3433 }
3434
3435
3436 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3437 {
3438         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3439         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3440 }
3441
3442
3443 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3444                            string const & argument,
3445                            Buffer const * used_buffer,
3446                            docstring const & msg,
3447                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3448                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3449                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3450 {
3451         if (!used_buffer)
3452                 return false;
3453
3454         string format = argument;
3455         if (format.empty())
3456                 format = used_buffer->params().getDefaultOutputFormat();
3457         processing_format = format;
3458         if (!msg.empty()) {
3459                 progress_->clearMessages();
3460                 gv_->message(msg);
3461         }
3462 #if EXPORT_in_THREAD
3463         GuiViewPrivate::busyBuffers.insert(used_buffer);
3464         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3465         if (!cloned_buffer) {
3466                 Alert::error(_("Export Error"),
3467                              _("Error cloning the Buffer."));
3468                 return false;
3469         }
3470         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3471                                 asyncFunc,
3472                                 used_buffer,
3473                                 cloned_buffer,
3474                                 format);
3475         setPreviewFuture(f);
3476         last_export_format = used_buffer->params().bufferFormat();
3477         (void) syncFunc;
3478         (void) previewFunc;
3479         // We are asynchronous, so we don't know here anything about the success
3480         return true;
3481 #else
3482         Buffer::ExportStatus status;
3483         if (syncFunc) {
3484                 status = (used_buffer->*syncFunc)(format, true);
3485         } else if (previewFunc) {
3486                 status = (used_buffer->*previewFunc)(format); 
3487         } else
3488                 return false;
3489         handleExportStatus(gv_, status, format);
3490         (void) asyncFunc;
3491         return (status == Buffer::ExportSuccess 
3492                         || status == Buffer::PreviewSuccess);
3493 #endif
3494 }
3495
3496 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3497 {
3498         BufferView * bv = currentBufferView();
3499         LASSERT(bv, return);
3500
3501         // Let the current BufferView dispatch its own actions.
3502         bv->dispatch(cmd, dr);
3503         if (dr.dispatched())
3504                 return;
3505
3506         // Try with the document BufferView dispatch if any.
3507         BufferView * doc_bv = documentBufferView();
3508         if (doc_bv && doc_bv != bv) {
3509                 doc_bv->dispatch(cmd, dr);
3510                 if (dr.dispatched())
3511                         return;
3512         }
3513
3514         // Then let the current Cursor dispatch its own actions.
3515         bv->cursor().dispatch(cmd);
3516
3517         // update completion. We do it here and not in
3518         // processKeySym to avoid another redraw just for a
3519         // changed inline completion
3520         if (cmd.origin() == FuncRequest::KEYBOARD) {
3521                 if (cmd.action() == LFUN_SELF_INSERT
3522                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3523                         updateCompletion(bv->cursor(), true, true);
3524                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3525                         updateCompletion(bv->cursor(), false, true);
3526                 else
3527                         updateCompletion(bv->cursor(), false, false);
3528         }
3529
3530         dr = bv->cursor().result();
3531 }
3532
3533
3534 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3535 {
3536         BufferView * bv = currentBufferView();
3537         // By default we won't need any update.
3538         dr.screenUpdate(Update::None);
3539         // assume cmd will be dispatched
3540         dr.dispatched(true);
3541
3542         Buffer * doc_buffer = documentBufferView()
3543                 ? &(documentBufferView()->buffer()) : 0;
3544
3545         if (cmd.origin() == FuncRequest::TOC) {
3546                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3547                 // FIXME: do we need to pass a DispatchResult object here?
3548                 toc->doDispatch(bv->cursor(), cmd);
3549                 return;
3550         }
3551
3552         string const argument = to_utf8(cmd.argument());
3553
3554         switch(cmd.action()) {
3555                 case LFUN_BUFFER_CHILD_OPEN:
3556                         openChildDocument(to_utf8(cmd.argument()));
3557                         break;
3558
3559                 case LFUN_BUFFER_IMPORT:
3560                         importDocument(to_utf8(cmd.argument()));
3561                         break;
3562
3563                 case LFUN_BUFFER_EXPORT: {
3564                         if (!doc_buffer)
3565                                 break;
3566                         // GCC only sees strfwd.h when building merged
3567                         if (::lyx::operator==(cmd.argument(), "custom")) {
3568                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3569                                 break;
3570                         }
3571
3572                         string const dest = cmd.getArg(1);
3573                         FileName target_dir;
3574                         if (!dest.empty() && FileName::isAbsolute(dest))
3575                                 target_dir = FileName(support::onlyPath(dest));
3576                         else
3577                                 target_dir = doc_buffer->fileName().onlyPath();
3578
3579                         if ((dest.empty() && doc_buffer->isUnnamed())
3580                             || !target_dir.isDirWritable()) {
3581                                 exportBufferAs(*doc_buffer, cmd.argument());
3582                                 break;
3583                         }
3584                         /* TODO/Review: Is it a problem to also export the children?
3585                                         See the update_unincluded flag */
3586                         d.asyncBufferProcessing(argument,
3587                                                 doc_buffer,
3588                                                 _("Exporting ..."),
3589                                                 &GuiViewPrivate::exportAndDestroy,
3590                                                 &Buffer::doExport,
3591                                                 0);
3592                         // TODO Inform user about success
3593                         break;
3594                 }
3595
3596                 case LFUN_BUFFER_EXPORT_AS: {
3597                         LASSERT(doc_buffer, break);
3598                         docstring f = cmd.argument();
3599                         if (f.empty())
3600                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3601                         exportBufferAs(*doc_buffer, f);
3602                         break;
3603                 }
3604
3605                 case LFUN_BUFFER_UPDATE: {
3606                         d.asyncBufferProcessing(argument,
3607                                                 doc_buffer,
3608                                                 _("Exporting ..."),
3609                                                 &GuiViewPrivate::compileAndDestroy,
3610                                                 &Buffer::doExport,
3611                                                 0);
3612                         break;
3613                 }
3614                 case LFUN_BUFFER_VIEW: {
3615                         d.asyncBufferProcessing(argument,
3616                                                 doc_buffer,
3617                                                 _("Previewing ..."),
3618                                                 &GuiViewPrivate::previewAndDestroy,
3619                                                 0,
3620                                                 &Buffer::preview);
3621                         break;
3622                 }
3623                 case LFUN_MASTER_BUFFER_UPDATE: {
3624                         d.asyncBufferProcessing(argument,
3625                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3626                                                 docstring(),
3627                                                 &GuiViewPrivate::compileAndDestroy,
3628                                                 &Buffer::doExport,
3629                                                 0);
3630                         break;
3631                 }
3632                 case LFUN_MASTER_BUFFER_VIEW: {
3633                         d.asyncBufferProcessing(argument,
3634                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3635                                                 docstring(),
3636                                                 &GuiViewPrivate::previewAndDestroy,
3637                                                 0, &Buffer::preview);
3638                         break;
3639                 }
3640                 case LFUN_BUFFER_SWITCH: {
3641                         string const file_name = to_utf8(cmd.argument());
3642                         if (!FileName::isAbsolute(file_name)) {
3643                                 dr.setError(true);
3644                                 dr.setMessage(_("Absolute filename expected."));
3645                                 break;
3646                         }
3647
3648                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3649                         if (!buffer) {
3650                                 dr.setError(true);
3651                                 dr.setMessage(_("Document not loaded"));
3652                                 break;
3653                         }
3654
3655                         // Do we open or switch to the buffer in this view ?
3656                         if (workArea(*buffer)
3657                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3658                                 setBuffer(buffer);
3659                                 break;
3660                         }
3661
3662                         // Look for the buffer in other views
3663                         QList<int> const ids = guiApp->viewIds();
3664                         int i = 0;
3665                         for (; i != ids.size(); ++i) {
3666                                 GuiView & gv = guiApp->view(ids[i]);
3667                                 if (gv.workArea(*buffer)) {
3668                                         gv.raise();
3669                                         gv.activateWindow();
3670                                         gv.setFocus();
3671                                         gv.setBuffer(buffer);
3672                                         break;
3673                                 }
3674                         }
3675
3676                         // If necessary, open a new window as a last resort
3677                         if (i == ids.size()) {
3678                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3679                                 lyx::dispatch(cmd);
3680                         }
3681                         break;
3682                 }
3683
3684                 case LFUN_BUFFER_NEXT:
3685                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3686                         break;
3687
3688                 case LFUN_BUFFER_MOVE_NEXT:
3689                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3690                         break;
3691
3692                 case LFUN_BUFFER_PREVIOUS:
3693                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3694                         break;
3695
3696                 case LFUN_BUFFER_MOVE_PREVIOUS:
3697                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3698                         break;
3699
3700                 case LFUN_COMMAND_EXECUTE: {
3701                         command_execute_ = true;
3702                         minibuffer_focus_ = true;
3703                         break;
3704                 }
3705                 case LFUN_DROP_LAYOUTS_CHOICE:
3706                         d.layout_->showPopup();
3707                         break;
3708
3709                 case LFUN_MENU_OPEN:
3710                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3711                                 menu->exec(QCursor::pos());
3712                         break;
3713
3714                 case LFUN_FILE_INSERT:
3715                         insertLyXFile(cmd.argument());
3716                         break;
3717
3718                 case LFUN_FILE_INSERT_PLAINTEXT:
3719                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3720                         string const fname = to_utf8(cmd.argument());
3721                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3722                                 dr.setMessage(_("Absolute filename expected."));
3723                                 break;
3724                         }
3725                         
3726                         FileName filename(fname);
3727                         if (fname.empty()) {
3728                                 FileDialog dlg(qt_("Select file to insert"));
3729
3730                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3731                                         QStringList(qt_("All Files (*)")));
3732                                 
3733                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3734                                         dr.setMessage(_("Canceled."));
3735                                         break;
3736                                 }
3737
3738                                 filename.set(fromqstr(result.second));
3739                         }
3740
3741                         if (bv) {
3742                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3743                                 bv->dispatch(new_cmd, dr);
3744                         }
3745                         break;
3746                 }
3747
3748                 case LFUN_BUFFER_RELOAD: {
3749                         LASSERT(doc_buffer, break);
3750
3751                         int ret = 0;
3752                         if (!doc_buffer->isClean()) {
3753                                 docstring const file =
3754                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3755                                 docstring text = bformat(_("Any changes will be lost. "
3756                                         "Are you sure you want to revert to the saved version "
3757                                         "of the document %1$s?"), file);
3758                                 ret = Alert::prompt(_("Revert to saved document?"),
3759                                         text, 1, 1, _("&Revert"), _("&Cancel"));
3760                         }
3761
3762                         if (ret == 0) {
3763                                 doc_buffer->markClean();
3764                                 reloadBuffer(*doc_buffer);
3765                                 dr.forceBufferUpdate();
3766                         }
3767                         break;
3768                 }
3769
3770                 case LFUN_BUFFER_WRITE:
3771                         LASSERT(doc_buffer, break);
3772                         saveBuffer(*doc_buffer);
3773                         break;
3774
3775                 case LFUN_BUFFER_WRITE_AS:
3776                         LASSERT(doc_buffer, break);
3777                         renameBuffer(*doc_buffer, cmd.argument());
3778                         break;
3779
3780                 case LFUN_BUFFER_WRITE_ALL: {
3781                         Buffer * first = theBufferList().first();
3782                         if (!first)
3783                                 break;
3784                         message(_("Saving all documents..."));
3785                         // We cannot use a for loop as the buffer list cycles.
3786                         Buffer * b = first;
3787                         do {
3788                                 if (!b->isClean()) {
3789                                         saveBuffer(*b);
3790                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3791                                 }
3792                                 b = theBufferList().next(b);
3793                         } while (b != first);
3794                         dr.setMessage(_("All documents saved."));
3795                         break;
3796                 }
3797
3798                 case LFUN_BUFFER_CLOSE:
3799                         closeBuffer();
3800                         break;
3801
3802                 case LFUN_BUFFER_CLOSE_ALL:
3803                         closeBufferAll();
3804                         break;
3805
3806                 case LFUN_TOOLBAR_TOGGLE: {
3807                         string const name = cmd.getArg(0);
3808                         if (GuiToolbar * t = toolbar(name))
3809                                 t->toggle();
3810                         break;
3811                 }
3812
3813                 case LFUN_DIALOG_UPDATE: {
3814                         string const name = to_utf8(cmd.argument());
3815                         if (name == "prefs" || name == "document")
3816                                 updateDialog(name, string());
3817                         else if (name == "paragraph")
3818                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3819                         else if (currentBufferView()) {
3820                                 Inset * inset = currentBufferView()->editedInset(name);
3821                                 // Can only update a dialog connected to an existing inset
3822                                 if (inset) {
3823                                         // FIXME: get rid of this indirection; GuiView ask the inset
3824                                         // if he is kind enough to update itself...
3825                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3826                                         //FIXME: pass DispatchResult here?
3827                                         inset->dispatch(currentBufferView()->cursor(), fr);
3828                                 }
3829                         }
3830                         break;
3831                 }
3832
3833                 case LFUN_DIALOG_TOGGLE: {
3834                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3835                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3836                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3837                         break;
3838                 }
3839
3840                 case LFUN_DIALOG_DISCONNECT_INSET:
3841                         disconnectDialog(to_utf8(cmd.argument()));
3842                         break;
3843
3844                 case LFUN_DIALOG_HIDE: {
3845                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3846                         break;
3847                 }
3848
3849                 case LFUN_DIALOG_SHOW: {
3850                         string const name = cmd.getArg(0);
3851                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3852
3853                         if (name == "character") {
3854                                 data = freefont2string();
3855                                 if (!data.empty())
3856                                         showDialog("character", data);
3857                         } else if (name == "latexlog") {
3858                                 Buffer::LogType type;
3859                                 string const logfile = doc_buffer->logName(&type);
3860                                 switch (type) {
3861                                 case Buffer::latexlog:
3862                                         data = "latex ";
3863                                         break;
3864                                 case Buffer::buildlog:
3865                                         data = "literate ";
3866                                         break;
3867                                 }
3868                                 data += Lexer::quoteString(logfile);
3869                                 showDialog("log", data);
3870                         } else if (name == "vclog") {
3871                                 string const data = "vc " +
3872                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3873                                 showDialog("log", data);
3874                         } else if (name == "symbols") {
3875                                 data = bv->cursor().getEncoding()->name();
3876                                 if (!data.empty())
3877                                         showDialog("symbols", data);
3878                         // bug 5274
3879                         } else if (name == "prefs" && isFullScreen()) {
3880                                 lfunUiToggle("fullscreen");
3881                                 showDialog("prefs", data);
3882                         } else
3883                                 showDialog(name, data);
3884                         break;
3885                 }
3886
3887                 case LFUN_MESSAGE:
3888                         dr.setMessage(cmd.argument());
3889                         break;
3890
3891                 case LFUN_UI_TOGGLE: {
3892                         string arg = cmd.getArg(0);
3893                         if (!lfunUiToggle(arg)) {
3894                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3895                                 dr.setMessage(bformat(msg, from_utf8(arg)));
3896                         }
3897                         // Make sure the keyboard focus stays in the work area.
3898                         setFocus();
3899                         break;
3900                 }
3901
3902                 case LFUN_VIEW_SPLIT: {
3903                         LASSERT(doc_buffer, break);
3904                         string const orientation = cmd.getArg(0);
3905                         d.splitter_->setOrientation(orientation == "vertical"
3906                                 ? Qt::Vertical : Qt::Horizontal);
3907                         TabWorkArea * twa = addTabWorkArea();
3908                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3909                         setCurrentWorkArea(wa);
3910                         break;
3911                 }
3912                 case LFUN_TAB_GROUP_CLOSE:
3913                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3914                                 closeTabWorkArea(twa);
3915                                 d.current_work_area_ = 0;
3916                                 twa = d.currentTabWorkArea();
3917                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3918                                 if (twa) {
3919                                         // Make sure the work area is up to date.
3920                                         setCurrentWorkArea(twa->currentWorkArea());
3921                                 } else {
3922                                         setCurrentWorkArea(0);
3923                                 }
3924                         }
3925                         break;
3926
3927                 case LFUN_VIEW_CLOSE:
3928                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3929                                 closeWorkArea(twa->currentWorkArea());
3930                                 d.current_work_area_ = 0;
3931                                 twa = d.currentTabWorkArea();
3932                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3933                                 if (twa) {
3934                                         // Make sure the work area is up to date.
3935                                         setCurrentWorkArea(twa->currentWorkArea());
3936                                 } else {
3937                                         setCurrentWorkArea(0);
3938                                 }
3939                         }
3940                         break;
3941
3942                 case LFUN_COMPLETION_INLINE:
3943                         if (d.current_work_area_)
3944                                 d.current_work_area_->completer().showInline();
3945                         break;
3946
3947                 case LFUN_COMPLETION_POPUP:
3948                         if (d.current_work_area_)
3949                                 d.current_work_area_->completer().showPopup();
3950                         break;
3951
3952
3953                 case LFUN_COMPLETE:
3954                         if (d.current_work_area_)
3955                                 d.current_work_area_->completer().tab();
3956                         break;
3957
3958                 case LFUN_COMPLETION_CANCEL:
3959                         if (d.current_work_area_) {
3960                                 if (d.current_work_area_->completer().popupVisible())
3961                                         d.current_work_area_->completer().hidePopup();
3962                                 else
3963                                         d.current_work_area_->completer().hideInline();
3964                         }
3965                         break;
3966
3967                 case LFUN_COMPLETION_ACCEPT:
3968                         if (d.current_work_area_)
3969                                 d.current_work_area_->completer().activate();
3970                         break;
3971
3972                 case LFUN_BUFFER_ZOOM_IN:
3973                 case LFUN_BUFFER_ZOOM_OUT: {
3974                         // use a signed temp to avoid overflow
3975                         int zoom = lyxrc.zoom;
3976                         if (cmd.argument().empty()) {
3977                                 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3978                                         zoom += 20;
3979                                 else
3980                                         zoom -= 20;
3981                         } else
3982                                 zoom += convert<int>(cmd.argument());
3983
3984                         if (zoom < static_cast<int>(zoom_min))
3985                                 zoom = zoom_min;
3986                         lyxrc.zoom = zoom;
3987
3988                         dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3989
3990                         // The global QPixmapCache is used in GuiPainter to cache text
3991                         // painting so we must reset it.
3992                         QPixmapCache::clear();
3993                         guiApp->fontLoader().update();
3994                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3995                         break;
3996                 }
3997
3998                 case LFUN_VC_REGISTER:
3999                 case LFUN_VC_RENAME:
4000                 case LFUN_VC_COPY:
4001                 case LFUN_VC_CHECK_IN:
4002                 case LFUN_VC_CHECK_OUT:
4003                 case LFUN_VC_REPO_UPDATE:
4004                 case LFUN_VC_LOCKING_TOGGLE:
4005                 case LFUN_VC_REVERT:
4006                 case LFUN_VC_UNDO_LAST:
4007                 case LFUN_VC_COMMAND:
4008                 case LFUN_VC_COMPARE:
4009                         dispatchVC(cmd, dr);
4010                         break;
4011
4012                 case LFUN_SERVER_GOTO_FILE_ROW:
4013                         goToFileRow(to_utf8(cmd.argument()));
4014                         break;
4015
4016                 case LFUN_LYX_ACTIVATE:
4017                         activateWindow();
4018                         break;
4019
4020                 case LFUN_FORWARD_SEARCH: {
4021                 // it seems safe to assume we have a document buffer, since
4022                 // getStatus wants one.
4023                 // coverity[FORWARD_NULL]
4024                         Buffer const * doc_master = doc_buffer->masterBuffer();
4025                         FileName const path(doc_master->temppath());
4026                         string const texname = doc_master->isChild(doc_buffer)
4027                                 ? DocFileName(changeExtension(
4028                                         doc_buffer->absFileName(),
4029                                                 "tex")).mangledFileName()
4030                                 : doc_buffer->latexName();
4031                         string const fulltexname = 
4032                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4033                         string const mastername =
4034                                 removeExtension(doc_master->latexName());
4035                         FileName const dviname(addName(path.absFileName(),
4036                                         addExtension(mastername, "dvi")));
4037                         FileName const pdfname(addName(path.absFileName(),
4038                                         addExtension(mastername, "pdf")));
4039                         bool const have_dvi = dviname.exists();
4040                         bool const have_pdf = pdfname.exists();
4041                         if (!have_dvi && !have_pdf) {
4042                                 dr.setMessage(_("Please, preview the document first."));
4043                                 break;
4044                         }
4045                         string outname = dviname.onlyFileName();
4046                         string command = lyxrc.forward_search_dvi;
4047                         if (!have_dvi || (have_pdf &&
4048                             pdfname.lastModified() > dviname.lastModified())) {
4049                                 outname = pdfname.onlyFileName();
4050                                 command = lyxrc.forward_search_pdf;
4051                         }
4052
4053                         DocIterator cur = bv->cursor();
4054                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4055                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4056                                    << " cur:" << cur);
4057                         if (row == -1 || command.empty()) {
4058                                 dr.setMessage(_("Couldn't proceed."));
4059                                 break;
4060                         }
4061                         string texrow = convert<string>(row);
4062
4063                         command = subst(command, "$$n", texrow);
4064                         command = subst(command, "$$f", fulltexname);
4065                         command = subst(command, "$$t", texname);
4066                         command = subst(command, "$$o", outname);
4067
4068                         PathChanger p(path);
4069                         Systemcall one;
4070                         one.startscript(Systemcall::DontWait, command);
4071                         break;
4072                 }
4073
4074                 case LFUN_SPELLING_CONTINUOUSLY:
4075                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4076                         dr.screenUpdate(Update::Force | Update::FitCursor);
4077                         break;
4078
4079                 default:
4080                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4081                         // let's try that:
4082                         dispatchToBufferView(cmd, dr);
4083                         break;
4084         }
4085
4086         // Part of automatic menu appearance feature.
4087         if (isFullScreen()) {
4088                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4089                         menuBar()->hide();
4090         }
4091
4092         // Need to update bv because many LFUNs here might have destroyed it
4093         bv = currentBufferView();
4094
4095         // Clear non-empty selections
4096         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4097         if (bv) {
4098                 Cursor & cur = bv->cursor();
4099                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4100                         cur.clearSelection();
4101                 }
4102         }
4103 }
4104
4105
4106 bool GuiView::lfunUiToggle(string const & ui_component)
4107 {
4108         if (ui_component == "scrollbar") {
4109                 // hide() is of no help
4110                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4111                         Qt::ScrollBarAlwaysOff)
4112
4113                         d.current_work_area_->setVerticalScrollBarPolicy(
4114                                 Qt::ScrollBarAsNeeded);
4115                 else
4116                         d.current_work_area_->setVerticalScrollBarPolicy(
4117                                 Qt::ScrollBarAlwaysOff);
4118         } else if (ui_component == "statusbar") {
4119                 statusBar()->setVisible(!statusBar()->isVisible());
4120         } else if (ui_component == "menubar") {
4121                 menuBar()->setVisible(!menuBar()->isVisible());
4122         } else
4123         if (ui_component == "frame") {
4124                 int l, t, r, b;
4125                 getContentsMargins(&l, &t, &r, &b);
4126                 //are the frames in default state?
4127                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4128                 if (l == 0) {
4129                         setContentsMargins(-2, -2, -2, -2);
4130                 } else {
4131                         setContentsMargins(0, 0, 0, 0);
4132                 }
4133         } else
4134         if (ui_component == "fullscreen") {
4135                 toggleFullScreen();
4136         } else
4137                 return false;
4138         return true;
4139 }
4140
4141
4142 void GuiView::toggleFullScreen()
4143 {
4144         if (isFullScreen()) {
4145                 for (int i = 0; i != d.splitter_->count(); ++i)
4146                         d.tabWorkArea(i)->setFullScreen(false);
4147                 setContentsMargins(0, 0, 0, 0);
4148                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4149                 restoreLayout();
4150                 menuBar()->show();
4151                 statusBar()->show();
4152         } else {
4153                 // bug 5274
4154                 hideDialogs("prefs", 0);
4155                 for (int i = 0; i != d.splitter_->count(); ++i)
4156                         d.tabWorkArea(i)->setFullScreen(true);
4157                 setContentsMargins(-2, -2, -2, -2);
4158                 saveLayout();
4159                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4160                 if (lyxrc.full_screen_statusbar)
4161                         statusBar()->hide();
4162                 if (lyxrc.full_screen_menubar)
4163                         menuBar()->hide();
4164                 if (lyxrc.full_screen_toolbars) {
4165                         ToolbarMap::iterator end = d.toolbars_.end();
4166                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4167                                 it->second->hide();
4168                 }
4169         }
4170
4171         // give dialogs like the TOC a chance to adapt
4172         updateDialogs();
4173 }
4174
4175
4176 Buffer const * GuiView::updateInset(Inset const * inset)
4177 {
4178         if (!inset)
4179                 return 0;
4180
4181         Buffer const * inset_buffer = &(inset->buffer());
4182
4183         for (int i = 0; i != d.splitter_->count(); ++i) {
4184                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4185                 if (!wa)
4186                         continue;
4187                 Buffer const * buffer = &(wa->bufferView().buffer());
4188                 if (inset_buffer == buffer)
4189                         wa->scheduleRedraw();
4190         }
4191         return inset_buffer;
4192 }
4193
4194
4195 void GuiView::restartCursor()
4196 {
4197         /* When we move around, or type, it's nice to be able to see
4198          * the cursor immediately after the keypress.
4199          */
4200         if (d.current_work_area_)
4201                 d.current_work_area_->startBlinkingCursor();
4202
4203         // Take this occasion to update the other GUI elements.
4204         updateDialogs();
4205         updateStatusBar();
4206 }
4207
4208
4209 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4210 {
4211         if (d.current_work_area_)
4212                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4213 }
4214
4215 namespace {
4216
4217 // This list should be kept in sync with the list of insets in
4218 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4219 // dialog should have the same name as the inset.
4220 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4221 // docs in LyXAction.cpp.
4222
4223 char const * const dialognames[] = {
4224
4225 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4226 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4227 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4228 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4229 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4230 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4231 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4232 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4233
4234 char const * const * const end_dialognames =
4235         dialognames + (sizeof(dialognames) / sizeof(char *));
4236
4237 class cmpCStr {
4238 public:
4239         cmpCStr(char const * name) : name_(name) {}
4240         bool operator()(char const * other) {
4241                 return strcmp(other, name_) == 0;
4242         }
4243 private:
4244         char const * name_;
4245 };
4246
4247
4248 bool isValidName(string const & name)
4249 {
4250         return find_if(dialognames, end_dialognames,
4251                                 cmpCStr(name.c_str())) != end_dialognames;
4252 }
4253
4254 } // namespace anon
4255
4256
4257 void GuiView::resetDialogs()
4258 {
4259         // Make sure that no LFUN uses any GuiView.
4260         guiApp->setCurrentView(0);
4261         saveLayout();
4262         saveUISettings();
4263         menuBar()->clear();
4264         constructToolbars();
4265         guiApp->menus().fillMenuBar(menuBar(), this, false);
4266         d.layout_->updateContents(true);
4267         // Now update controls with current buffer.
4268         guiApp->setCurrentView(this);
4269         restoreLayout();
4270         restartCursor();
4271 }
4272
4273
4274 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4275 {
4276         if (!isValidName(name))
4277                 return 0;
4278
4279         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4280
4281         if (it != d.dialogs_.end()) {
4282                 if (hide_it)
4283                         it->second->hideView();
4284                 return it->second.get();
4285         }
4286
4287         Dialog * dialog = build(name);
4288         d.dialogs_[name].reset(dialog);
4289         if (lyxrc.allow_geometry_session)
4290                 dialog->restoreSession();
4291         if (hide_it)
4292                 dialog->hideView();
4293         return dialog;
4294 }
4295
4296
4297 void GuiView::showDialog(string const & name, string const & data,
4298         Inset * inset)
4299 {
4300         triggerShowDialog(toqstr(name), toqstr(data), inset);
4301 }
4302
4303
4304 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4305         Inset * inset)
4306 {
4307         if (d.in_show_)
4308                 return;
4309
4310         const string name = fromqstr(qname);
4311         const string data = fromqstr(qdata);
4312
4313         d.in_show_ = true;
4314         try {
4315                 Dialog * dialog = findOrBuild(name, false);
4316                 if (dialog) {
4317                         bool const visible = dialog->isVisibleView();
4318                         dialog->showData(data);
4319                         if (inset && currentBufferView())
4320                                 currentBufferView()->editInset(name, inset);
4321                         // We only set the focus to the new dialog if it was not yet
4322                         // visible in order not to change the existing previous behaviour
4323                         if (visible) {
4324                                 // activateWindow is needed for floating dockviews
4325                                 dialog->asQWidget()->raise();
4326                                 dialog->asQWidget()->activateWindow();
4327                                 dialog->asQWidget()->setFocus();
4328                         }
4329                 }
4330         }
4331         catch (ExceptionMessage const & ex) {
4332                 d.in_show_ = false;
4333                 throw ex;
4334         }
4335         d.in_show_ = false;
4336 }
4337
4338
4339 bool GuiView::isDialogVisible(string const & name) const
4340 {
4341         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4342         if (it == d.dialogs_.end())
4343                 return false;
4344         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4345 }
4346
4347
4348 void GuiView::hideDialog(string const & name, Inset * inset)
4349 {
4350         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4351         if (it == d.dialogs_.end())
4352                 return;
4353
4354         if (inset) {
4355                 if (!currentBufferView())
4356                         return;
4357                 if (inset != currentBufferView()->editedInset(name))
4358                         return;
4359         }
4360
4361         Dialog * const dialog = it->second.get();
4362         if (dialog->isVisibleView())
4363                 dialog->hideView();
4364         if (currentBufferView())
4365                 currentBufferView()->editInset(name, 0);
4366 }
4367
4368
4369 void GuiView::disconnectDialog(string const & name)
4370 {
4371         if (!isValidName(name))
4372                 return;
4373         if (currentBufferView())
4374                 currentBufferView()->editInset(name, 0);
4375 }
4376
4377
4378 void GuiView::hideAll() const
4379 {
4380         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4381         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4382
4383         for(; it != end; ++it)
4384                 it->second->hideView();
4385 }
4386
4387
4388 void GuiView::updateDialogs()
4389 {
4390         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4391         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4392
4393         for(; it != end; ++it) {
4394                 Dialog * dialog = it->second.get();
4395                 if (dialog) {
4396                         if (dialog->needBufferOpen() && !documentBufferView())
4397                                 hideDialog(fromqstr(dialog->name()), 0);
4398                         else if (dialog->isVisibleView())
4399                                 dialog->checkStatus();
4400                 }
4401         }
4402         updateToolbars();
4403         updateLayoutList();
4404 }
4405
4406 Dialog * createDialog(GuiView & lv, string const & name);
4407
4408 // will be replaced by a proper factory...
4409 Dialog * createGuiAbout(GuiView & lv);
4410 Dialog * createGuiBibtex(GuiView & lv);
4411 Dialog * createGuiChanges(GuiView & lv);
4412 Dialog * createGuiCharacter(GuiView & lv);
4413 Dialog * createGuiCitation(GuiView & lv);
4414 Dialog * createGuiCompare(GuiView & lv);
4415 Dialog * createGuiCompareHistory(GuiView & lv);
4416 Dialog * createGuiDelimiter(GuiView & lv);
4417 Dialog * createGuiDocument(GuiView & lv);
4418 Dialog * createGuiErrorList(GuiView & lv);
4419 Dialog * createGuiExternal(GuiView & lv);
4420 Dialog * createGuiGraphics(GuiView & lv);
4421 Dialog * createGuiInclude(GuiView & lv);
4422 Dialog * createGuiIndex(GuiView & lv);
4423 Dialog * createGuiListings(GuiView & lv);
4424 Dialog * createGuiLog(GuiView & lv);
4425 Dialog * createGuiMathMatrix(GuiView & lv);
4426 Dialog * createGuiNote(GuiView & lv);
4427 Dialog * createGuiParagraph(GuiView & lv);
4428 Dialog * createGuiPhantom(GuiView & lv);
4429 Dialog * createGuiPreferences(GuiView & lv);
4430 Dialog * createGuiPrint(GuiView & lv);
4431 Dialog * createGuiPrintindex(GuiView & lv);
4432 Dialog * createGuiRef(GuiView & lv);
4433 Dialog * createGuiSearch(GuiView & lv);
4434 Dialog * createGuiSearchAdv(GuiView & lv);
4435 Dialog * createGuiSendTo(GuiView & lv);
4436 Dialog * createGuiShowFile(GuiView & lv);
4437 Dialog * createGuiSpellchecker(GuiView & lv);
4438 Dialog * createGuiSymbols(GuiView & lv);
4439 Dialog * createGuiTabularCreate(GuiView & lv);
4440 Dialog * createGuiTexInfo(GuiView & lv);
4441 Dialog * createGuiToc(GuiView & lv);
4442 Dialog * createGuiThesaurus(GuiView & lv);
4443 Dialog * createGuiViewSource(GuiView & lv);
4444 Dialog * createGuiWrap(GuiView & lv);
4445 Dialog * createGuiProgressView(GuiView & lv);
4446
4447
4448
4449 Dialog * GuiView::build(string const & name)
4450 {
4451         LASSERT(isValidName(name), return 0);
4452
4453         Dialog * dialog = createDialog(*this, name);
4454         if (dialog)
4455                 return dialog;
4456
4457         if (name == "aboutlyx")
4458                 return createGuiAbout(*this);
4459         if (name == "bibtex")
4460                 return createGuiBibtex(*this);
4461         if (name == "changes")
4462                 return createGuiChanges(*this);
4463         if (name == "character")
4464                 return createGuiCharacter(*this);
4465         if (name == "citation")
4466                 return createGuiCitation(*this);
4467         if (name == "compare")
4468                 return createGuiCompare(*this);
4469         if (name == "comparehistory")
4470                 return createGuiCompareHistory(*this);
4471         if (name == "document")
4472                 return createGuiDocument(*this);
4473         if (name == "errorlist")
4474                 return createGuiErrorList(*this);
4475         if (name == "external")
4476                 return createGuiExternal(*this);
4477         if (name == "file")
4478                 return createGuiShowFile(*this);
4479         if (name == "findreplace")
4480                 return createGuiSearch(*this);
4481         if (name == "findreplaceadv")
4482                 return createGuiSearchAdv(*this);
4483         if (name == "graphics")
4484                 return createGuiGraphics(*this);
4485         if (name == "include")
4486                 return createGuiInclude(*this);
4487         if (name == "index")
4488                 return createGuiIndex(*this);
4489         if (name == "index_print")
4490                 return createGuiPrintindex(*this);
4491         if (name == "listings")
4492                 return createGuiListings(*this);
4493         if (name == "log")
4494                 return createGuiLog(*this);
4495         if (name == "mathdelimiter")
4496                 return createGuiDelimiter(*this);
4497         if (name == "mathmatrix")
4498                 return createGuiMathMatrix(*this);
4499         if (name == "note")
4500                 return createGuiNote(*this);
4501         if (name == "paragraph")
4502                 return createGuiParagraph(*this);
4503         if (name == "phantom")
4504                 return createGuiPhantom(*this);
4505         if (name == "prefs")
4506                 return createGuiPreferences(*this);
4507         if (name == "ref")
4508                 return createGuiRef(*this);
4509         if (name == "sendto")
4510                 return createGuiSendTo(*this);
4511         if (name == "spellchecker")
4512                 return createGuiSpellchecker(*this);
4513         if (name == "symbols")
4514                 return createGuiSymbols(*this);
4515         if (name == "tabularcreate")
4516                 return createGuiTabularCreate(*this);
4517         if (name == "texinfo")
4518                 return createGuiTexInfo(*this);
4519         if (name == "thesaurus")
4520                 return createGuiThesaurus(*this);
4521         if (name == "toc")
4522                 return createGuiToc(*this);
4523         if (name == "view-source")
4524                 return createGuiViewSource(*this);
4525         if (name == "wrap")
4526                 return createGuiWrap(*this);
4527         if (name == "progress")
4528                 return createGuiProgressView(*this);
4529
4530         return 0;
4531 }
4532
4533
4534 } // namespace frontend
4535 } // namespace lyx
4536
4537 #include "moc_GuiView.cpp"