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