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