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