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