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