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