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