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