]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
Add comment
[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                 bool const math =
1538                         d.current_work_area_->bufferView().cursor().inMathed()
1539                         && !d.current_work_area_->bufferView().cursor().inRegexped();
1540                 bool const table =
1541                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1542                 bool const review =
1543                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1544                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
1545                 bool const mathmacrotemplate =
1546                         lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1547                 bool const ipa =
1548                         lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
1549
1550                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1551                         it->second->update(math, table, review, mathmacrotemplate, ipa);
1552         } else
1553                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1554                         it->second->update(false, false, false, false, false);
1555 }
1556
1557
1558 void GuiView::setBuffer(Buffer * newBuffer)
1559 {
1560         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1561         LASSERT(newBuffer, return);
1562         
1563         GuiWorkArea * wa = workArea(*newBuffer);
1564         if (wa == 0) {
1565                 setBusy(true);
1566                 newBuffer->masterBuffer()->updateBuffer();
1567                 setBusy(false);
1568                 wa = addWorkArea(*newBuffer);
1569                 // scroll to the position when the BufferView was last closed
1570                 if (lyxrc.use_lastfilepos) {
1571                         LastFilePosSection::FilePos filepos =
1572                                 theSession().lastFilePos().load(newBuffer->fileName());
1573                         wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1574                 }
1575         } else {
1576                 //Disconnect the old buffer...there's no new one.
1577                 disconnectBuffer();
1578         }
1579         connectBuffer(*newBuffer);
1580         connectBufferView(wa->bufferView());
1581         setCurrentWorkArea(wa);
1582 }
1583
1584
1585 void GuiView::connectBuffer(Buffer & buf)
1586 {
1587         buf.setGuiDelegate(this);
1588 }
1589
1590
1591 void GuiView::disconnectBuffer()
1592 {
1593         if (d.current_work_area_)
1594                 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1595 }
1596
1597
1598 void GuiView::connectBufferView(BufferView & bv)
1599 {
1600         bv.setGuiDelegate(this);
1601 }
1602
1603
1604 void GuiView::disconnectBufferView()
1605 {
1606         if (d.current_work_area_)
1607                 d.current_work_area_->bufferView().setGuiDelegate(0);
1608 }
1609
1610
1611 void GuiView::errors(string const & error_type, bool from_master)
1612 {
1613         BufferView const * const bv = currentBufferView();
1614         if (!bv)
1615                 return;
1616
1617 #if EXPORT_in_THREAD
1618         // We are called with from_master == false by default, so we
1619         // have to figure out whether that is the case or not.
1620         ErrorList & el = bv->buffer().errorList(error_type);
1621         if (el.empty()) {
1622             el = bv->buffer().masterBuffer()->errorList(error_type);
1623             from_master = true;
1624         }
1625 #else
1626         ErrorList const & el = from_master ?
1627                 bv->buffer().masterBuffer()->errorList(error_type) :
1628                 bv->buffer().errorList(error_type);
1629 #endif
1630
1631         if (el.empty())
1632                 return;
1633
1634         string data = error_type;
1635         if (from_master)
1636                 data = "from_master|" + error_type;
1637         showDialog("errorlist", data);
1638 }
1639
1640
1641 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1642 {
1643         d.toc_models_.updateItem(toqstr(type), dit);
1644 }
1645
1646
1647 void GuiView::structureChanged()
1648 {
1649         d.toc_models_.reset(documentBufferView());
1650         // Navigator needs more than a simple update in this case. It needs to be
1651         // rebuilt.
1652         updateDialog("toc", "");
1653 }
1654
1655
1656 void GuiView::updateDialog(string const & name, string const & data)
1657 {
1658         if (!isDialogVisible(name))
1659                 return;
1660
1661         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1662         if (it == d.dialogs_.end())
1663                 return;
1664
1665         Dialog * const dialog = it->second.get();
1666         if (dialog->isVisibleView())
1667                 dialog->initialiseParams(data);
1668 }
1669
1670
1671 BufferView * GuiView::documentBufferView()
1672 {
1673         return currentMainWorkArea()
1674                 ? &currentMainWorkArea()->bufferView()
1675                 : 0;
1676 }
1677
1678
1679 BufferView const * GuiView::documentBufferView() const
1680 {
1681         return currentMainWorkArea()
1682                 ? &currentMainWorkArea()->bufferView()
1683                 : 0;
1684 }
1685
1686
1687 BufferView * GuiView::currentBufferView()
1688 {
1689         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1690 }
1691
1692
1693 BufferView const * GuiView::currentBufferView() const
1694 {
1695         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1696 }
1697
1698
1699 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1700         Buffer const * orig, Buffer * clone)
1701 {
1702         bool const success = clone->autoSave();
1703         delete clone;
1704         busyBuffers.remove(orig);
1705         return success
1706                 ? _("Automatic save done.")
1707                 : _("Automatic save failed!");
1708 }
1709
1710
1711 void GuiView::autoSave()
1712 {
1713         LYXERR(Debug::INFO, "Running autoSave()");
1714
1715         Buffer * buffer = documentBufferView()
1716                 ? &documentBufferView()->buffer() : 0;
1717         if (!buffer) {
1718                 resetAutosaveTimers();
1719                 return;
1720         }
1721
1722         GuiViewPrivate::busyBuffers.insert(buffer);
1723         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1724                 buffer, buffer->cloneBufferOnly());
1725         d.autosave_watcher_.setFuture(f);
1726         resetAutosaveTimers();
1727 }
1728
1729
1730 void GuiView::resetAutosaveTimers()
1731 {
1732         if (lyxrc.autosave)
1733                 d.autosave_timeout_.restart();
1734 }
1735
1736
1737 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1738 {
1739         bool enable = true;
1740         Buffer * buf = currentBufferView()
1741                 ? &currentBufferView()->buffer() : 0;
1742         Buffer * doc_buffer = documentBufferView()
1743                 ? &(documentBufferView()->buffer()) : 0;
1744
1745 #ifdef Q_OS_MAC
1746         /* In LyX/Mac, when a dialog is open, the menus of the
1747            application can still be accessed without giving focus to
1748            the main window. In this case, we want to disable the menu
1749            entries that are buffer-related.
1750            This code must not be used on Linux and Windows, since it
1751            would disable buffer-related entries when hovering over the
1752            menu (see bug #9574).
1753          */
1754         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1755                 buf = 0;
1756                 doc_buffer = 0;
1757         }
1758 #endif
1759
1760         // Check whether we need a buffer
1761         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1762                 // no, exit directly
1763                 flag.message(from_utf8(N_("Command not allowed with"
1764                                         "out any document open")));
1765                 flag.setEnabled(false);
1766                 return true;
1767         }
1768
1769         if (cmd.origin() == FuncRequest::TOC) {
1770                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1771                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1772                         flag.setEnabled(false);
1773                 return true;
1774         }
1775
1776         switch(cmd.action()) {
1777         case LFUN_BUFFER_IMPORT:
1778                 break;
1779
1780         case LFUN_MASTER_BUFFER_UPDATE:
1781         case LFUN_MASTER_BUFFER_VIEW:
1782                 enable = doc_buffer
1783                         && (doc_buffer->parent() != 0
1784                             || doc_buffer->hasChildren())
1785                         && !d.processing_thread_watcher_.isRunning();
1786                 break;
1787
1788         case LFUN_BUFFER_UPDATE:
1789         case LFUN_BUFFER_VIEW: {
1790                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1791                         enable = false;
1792                         break;
1793                 }
1794                 string format = to_utf8(cmd.argument());
1795                 if (cmd.argument().empty())
1796                         format = doc_buffer->params().getDefaultOutputFormat();
1797                 enable = doc_buffer->params().isExportableFormat(format);
1798                 break;
1799         }
1800
1801         case LFUN_BUFFER_RELOAD:
1802                 enable = doc_buffer && !doc_buffer->isUnnamed()
1803                         && doc_buffer->fileName().exists()
1804                         && (!doc_buffer->isClean()
1805                            || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1806                 break;
1807
1808         case LFUN_BUFFER_CHILD_OPEN:
1809                 enable = doc_buffer;
1810                 break;
1811
1812         case LFUN_BUFFER_WRITE:
1813                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1814                 break;
1815
1816         //FIXME: This LFUN should be moved to GuiApplication.
1817         case LFUN_BUFFER_WRITE_ALL: {
1818                 // We enable the command only if there are some modified buffers
1819                 Buffer * first = theBufferList().first();
1820                 enable = false;
1821                 if (!first)
1822                         break;
1823                 Buffer * b = first;
1824                 // We cannot use a for loop as the buffer list is a cycle.
1825                 do {
1826                         if (!b->isClean()) {
1827                                 enable = true;
1828                                 break;
1829                         }
1830                         b = theBufferList().next(b);
1831                 } while (b != first);
1832                 break;
1833         }
1834
1835         case LFUN_BUFFER_WRITE_AS:
1836         case LFUN_BUFFER_EXPORT_AS:
1837                 enable = doc_buffer;
1838                 break;
1839
1840         case LFUN_BUFFER_CLOSE:
1841         case LFUN_VIEW_CLOSE:
1842                 enable = doc_buffer;
1843                 break;
1844
1845         case LFUN_BUFFER_CLOSE_ALL:
1846                 enable = theBufferList().last() != theBufferList().first();
1847                 break;
1848
1849         case LFUN_VIEW_SPLIT:
1850                 if (cmd.getArg(0) == "vertical")
1851                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1852                                          d.splitter_->orientation() == Qt::Vertical);
1853                 else
1854                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1855                                          d.splitter_->orientation() == Qt::Horizontal);
1856                 break;
1857
1858         case LFUN_TAB_GROUP_CLOSE:
1859                 enable = d.tabWorkAreaCount() > 1;
1860                 break;
1861
1862         case LFUN_TOOLBAR_TOGGLE: {
1863                 string const name = cmd.getArg(0);
1864                 if (GuiToolbar * t = toolbar(name))
1865                         flag.setOnOff(t->isVisible());
1866                 else {
1867                         enable = false;
1868                         docstring const msg =
1869                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1870                         flag.message(msg);
1871                 }
1872                 break;
1873         }
1874
1875         case LFUN_DROP_LAYOUTS_CHOICE:
1876                 enable = buf;
1877                 break;
1878
1879         case LFUN_UI_TOGGLE:
1880                 flag.setOnOff(isFullScreen());
1881                 break;
1882
1883         case LFUN_DIALOG_DISCONNECT_INSET:
1884                 break;
1885
1886         case LFUN_DIALOG_HIDE:
1887                 // FIXME: should we check if the dialog is shown?
1888                 break;
1889
1890         case LFUN_DIALOG_TOGGLE:
1891                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1892                 // fall through to set "enable"
1893         case LFUN_DIALOG_SHOW: {
1894                 string const name = cmd.getArg(0);
1895                 if (!doc_buffer)
1896                         enable = name == "aboutlyx"
1897                                 || name == "file" //FIXME: should be removed.
1898                                 || name == "prefs"
1899                                 || name == "texinfo"
1900                                 || name == "progress"
1901                                 || name == "compare";
1902                 else if (name == "character" || name == "symbols"
1903                         || name == "mathdelimiter" || name == "mathmatrix") {
1904                         if (!buf || buf->isReadonly())
1905                                 enable = false;
1906                         else {
1907                                 Cursor const & cur = currentBufferView()->cursor();
1908                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1909                         }
1910                 }
1911                 else if (name == "latexlog")
1912                         enable = FileName(doc_buffer->logName()).isReadableFile();
1913                 else if (name == "spellchecker")
1914                         enable = theSpellChecker() 
1915                                 && !doc_buffer->isReadonly()
1916                                 && !doc_buffer->text().empty();
1917                 else if (name == "vclog")
1918                         enable = doc_buffer->lyxvc().inUse();
1919                 break;
1920         }
1921
1922         case LFUN_DIALOG_UPDATE: {
1923                 string const name = cmd.getArg(0);
1924                 if (!buf)
1925                         enable = name == "prefs";
1926                 break;
1927         }
1928
1929         case LFUN_COMMAND_EXECUTE:
1930         case LFUN_MESSAGE:
1931         case LFUN_MENU_OPEN:
1932                 // Nothing to check.
1933                 break;
1934
1935         case LFUN_COMPLETION_INLINE:
1936                 if (!d.current_work_area_
1937                         || !d.current_work_area_->completer().inlinePossible(
1938                         currentBufferView()->cursor()))
1939                         enable = false;
1940                 break;
1941
1942         case LFUN_COMPLETION_POPUP:
1943                 if (!d.current_work_area_
1944                         || !d.current_work_area_->completer().popupPossible(
1945                         currentBufferView()->cursor()))
1946                         enable = false;
1947                 break;
1948
1949         case LFUN_COMPLETE:
1950                 if (!d.current_work_area_
1951                         || !d.current_work_area_->completer().inlinePossible(
1952                         currentBufferView()->cursor()))
1953                         enable = false;
1954                 break;
1955
1956         case LFUN_COMPLETION_ACCEPT:
1957                 if (!d.current_work_area_
1958                         || (!d.current_work_area_->completer().popupVisible()
1959                         && !d.current_work_area_->completer().inlineVisible()
1960                         && !d.current_work_area_->completer().completionAvailable()))
1961                         enable = false;
1962                 break;
1963
1964         case LFUN_COMPLETION_CANCEL:
1965                 if (!d.current_work_area_
1966                         || (!d.current_work_area_->completer().popupVisible()
1967                         && !d.current_work_area_->completer().inlineVisible()))
1968                         enable = false;
1969                 break;
1970
1971         case LFUN_BUFFER_ZOOM_OUT:
1972                 enable = doc_buffer && lyxrc.zoom > 10;
1973                 break;
1974
1975         case LFUN_BUFFER_ZOOM_IN:
1976                 enable = doc_buffer;
1977                 break;
1978
1979         case LFUN_BUFFER_MOVE_NEXT:
1980         case LFUN_BUFFER_MOVE_PREVIOUS:
1981                 // we do not cycle when moving
1982         case LFUN_BUFFER_NEXT:
1983         case LFUN_BUFFER_PREVIOUS:
1984                 // because we cycle, it doesn't matter whether on first or last
1985                 enable = (d.currentTabWorkArea()->count() > 1);
1986                 break;
1987         case LFUN_BUFFER_SWITCH:
1988                 // toggle on the current buffer, but do not toggle off
1989                 // the other ones (is that a good idea?)
1990                 if (doc_buffer
1991                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1992                         flag.setOnOff(true);
1993                 break;
1994
1995         case LFUN_VC_REGISTER:
1996                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1997                 break;
1998         case LFUN_VC_RENAME:
1999                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2000                 break;
2001         case LFUN_VC_COPY:
2002                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2003                 break;
2004         case LFUN_VC_CHECK_IN:
2005                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2006                 break;
2007         case LFUN_VC_CHECK_OUT:
2008                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2009                 break;
2010         case LFUN_VC_LOCKING_TOGGLE:
2011                 enable = doc_buffer && !doc_buffer->isReadonly()
2012                         && doc_buffer->lyxvc().lockingToggleEnabled();
2013                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2014                 break;
2015         case LFUN_VC_REVERT:
2016                 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2017                 break;
2018         case LFUN_VC_UNDO_LAST:
2019                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2020                 break;
2021         case LFUN_VC_REPO_UPDATE:
2022                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2023                 break;
2024         case LFUN_VC_COMMAND: {
2025                 if (cmd.argument().empty())
2026                         enable = false;
2027                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2028                         enable = false;
2029                 break;
2030         }
2031         case LFUN_VC_COMPARE:
2032                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2033                 break;
2034
2035         case LFUN_SERVER_GOTO_FILE_ROW:
2036                 break;
2037         case LFUN_FORWARD_SEARCH:
2038                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2039                 break;
2040
2041         case LFUN_FILE_INSERT_PLAINTEXT:
2042         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2043                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2044                 break;
2045
2046         case LFUN_SPELLING_CONTINUOUSLY:
2047                 flag.setOnOff(lyxrc.spellcheck_continuously);
2048                 break;
2049
2050         default:
2051                 return false;
2052         }
2053
2054         if (!enable)
2055                 flag.setEnabled(false);
2056
2057         return true;
2058 }
2059
2060
2061 static FileName selectTemplateFile()
2062 {
2063         FileDialog dlg(qt_("Select template file"));
2064         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2065         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2066
2067         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2068                                  QStringList(qt_("LyX Documents (*.lyx)")));
2069
2070         if (result.first == FileDialog::Later)
2071                 return FileName();
2072         if (result.second.isEmpty())
2073                 return FileName();
2074         return FileName(fromqstr(result.second));
2075 }
2076
2077
2078 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2079 {
2080         setBusy(true);
2081
2082         Buffer * newBuffer = 0;
2083         try {
2084                 newBuffer = checkAndLoadLyXFile(filename);
2085         } catch (ExceptionMessage const & e) {
2086                 setBusy(false);
2087                 throw(e);
2088         }
2089         setBusy(false);
2090
2091         if (!newBuffer) {
2092                 message(_("Document not loaded."));
2093                 return 0;
2094         }
2095
2096         setBuffer(newBuffer);
2097         newBuffer->errors("Parse");
2098
2099         if (tolastfiles)
2100                 theSession().lastFiles().add(filename);
2101
2102         return newBuffer;
2103 }
2104
2105
2106 void GuiView::openDocument(string const & fname)
2107 {
2108         string initpath = lyxrc.document_path;
2109
2110         if (documentBufferView()) {
2111                 string const trypath = documentBufferView()->buffer().filePath();
2112                 // If directory is writeable, use this as default.
2113                 if (FileName(trypath).isDirWritable())
2114                         initpath = trypath;
2115         }
2116
2117         string filename;
2118
2119         if (fname.empty()) {
2120                 FileDialog dlg(qt_("Select document to open"));
2121                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2122                 dlg.setButton2(qt_("Examples|#E#e"),
2123                                 toqstr(addPath(package().system_support().absFileName(), "examples")));
2124
2125                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2126                 FileDialog::Result result =
2127                         dlg.open(toqstr(initpath), filter);
2128
2129                 if (result.first == FileDialog::Later)
2130                         return;
2131
2132                 filename = fromqstr(result.second);
2133
2134                 // check selected filename
2135                 if (filename.empty()) {
2136                         message(_("Canceled."));
2137                         return;
2138                 }
2139         } else
2140                 filename = fname;
2141
2142         // get absolute path of file and add ".lyx" to the filename if
2143         // necessary.
2144         FileName const fullname =
2145                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2146         if (!fullname.empty())
2147                 filename = fullname.absFileName();
2148
2149         if (!fullname.onlyPath().isDirectory()) {
2150                 Alert::warning(_("Invalid filename"),
2151                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2152                                 from_utf8(fullname.absFileName())));
2153                 return;
2154         }
2155
2156         // if the file doesn't exist and isn't already open (bug 6645),
2157         // let the user create one
2158         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2159             !LyXVC::file_not_found_hook(fullname)) {
2160                 // the user specifically chose this name. Believe him.
2161                 Buffer * const b = newFile(filename, string(), true);
2162                 if (b)
2163                         setBuffer(b);
2164                 return;
2165         }
2166
2167         docstring const disp_fn = makeDisplayPath(filename);
2168         message(bformat(_("Opening document %1$s..."), disp_fn));
2169
2170         docstring str2;
2171         Buffer * buf = loadDocument(fullname);
2172         if (buf) {
2173                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2174                 if (buf->lyxvc().inUse())
2175                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2176                                 " " + _("Version control detected.");
2177         } else {
2178                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2179         }
2180         message(str2);
2181 }
2182
2183 // FIXME: clean that
2184 static bool import(GuiView * lv, FileName const & filename,
2185         string const & format, ErrorList & errorList)
2186 {
2187         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2188
2189         string loader_format;
2190         vector<string> loaders = theConverters().loaders();
2191         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2192                 vector<string>::const_iterator it = loaders.begin();
2193                 vector<string>::const_iterator en = loaders.end();
2194                 for (; it != en; ++it) {
2195                         if (!theConverters().isReachable(format, *it))
2196                                 continue;
2197
2198                         string const tofile =
2199                                 support::changeExtension(filename.absFileName(),
2200                                 formats.extension(*it));
2201                         if (!theConverters().convert(0, filename, FileName(tofile),
2202                                 filename, format, *it, errorList))
2203                                 return false;
2204                         loader_format = *it;
2205                         break;
2206                 }
2207                 if (loader_format.empty()) {
2208                         frontend::Alert::error(_("Couldn't import file"),
2209                                          bformat(_("No information for importing the format %1$s."),
2210                                          formats.prettyName(format)));
2211                         return false;
2212                 }
2213         } else
2214                 loader_format = format;
2215
2216         if (loader_format == "lyx") {
2217                 Buffer * buf = lv->loadDocument(lyxfile);
2218                 if (!buf)
2219                         return false;
2220         } else {
2221                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2222                 if (!b)
2223                         return false;
2224                 lv->setBuffer(b);
2225                 bool as_paragraphs = loader_format == "textparagraph";
2226                 string filename2 = (loader_format == format) ? filename.absFileName()
2227                         : support::changeExtension(filename.absFileName(),
2228                                           formats.extension(loader_format));
2229                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2230                         as_paragraphs);
2231                 guiApp->setCurrentView(lv);
2232                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2233         }
2234
2235         return true;
2236 }
2237
2238
2239 void GuiView::importDocument(string const & argument)
2240 {
2241         string format;
2242         string filename = split(argument, format, ' ');
2243
2244         LYXERR(Debug::INFO, format << " file: " << filename);
2245
2246         // need user interaction
2247         if (filename.empty()) {
2248                 string initpath = lyxrc.document_path;
2249                 if (documentBufferView()) {
2250                         string const trypath = documentBufferView()->buffer().filePath();
2251                         // If directory is writeable, use this as default.
2252                         if (FileName(trypath).isDirWritable())
2253                                 initpath = trypath;
2254                 }
2255
2256                 docstring const text = bformat(_("Select %1$s file to import"),
2257                         formats.prettyName(format));
2258
2259                 FileDialog dlg(toqstr(text));
2260                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2261                 dlg.setButton2(qt_("Examples|#E#e"),
2262                         toqstr(addPath(package().system_support().absFileName(), "examples")));
2263
2264                 docstring filter = formats.prettyName(format);
2265                 filter += " (*.{";
2266                 // FIXME UNICODE
2267                 filter += from_utf8(formats.extensions(format));
2268                 filter += "})";
2269
2270                 FileDialog::Result result =
2271                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2272
2273                 if (result.first == FileDialog::Later)
2274                         return;
2275
2276                 filename = fromqstr(result.second);
2277
2278                 // check selected filename
2279                 if (filename.empty())
2280                         message(_("Canceled."));
2281         }
2282
2283         if (filename.empty())
2284                 return;
2285
2286         // get absolute path of file
2287         FileName const fullname(support::makeAbsPath(filename));
2288
2289         // Can happen if the user entered a path into the dialog
2290         // (see bug #7437)
2291         if (fullname.onlyFileName().empty()) {
2292                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2293                                           "Aborting import."),
2294                                         from_utf8(fullname.absFileName()));
2295                 frontend::Alert::error(_("File name error"), msg);
2296                 message(_("Canceled."));
2297                 return;
2298         }
2299
2300
2301         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2302
2303         // Check if the document already is open
2304         Buffer * buf = theBufferList().getBuffer(lyxfile);
2305         if (buf) {
2306                 setBuffer(buf);
2307                 if (!closeBuffer()) {
2308                         message(_("Canceled."));
2309                         return;
2310                 }
2311         }
2312
2313         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2314
2315         // if the file exists already, and we didn't do
2316         // -i lyx thefile.lyx, warn
2317         if (lyxfile.exists() && fullname != lyxfile) {
2318
2319                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2320                         "Do you want to overwrite that document?"), displaypath);
2321                 int const ret = Alert::prompt(_("Overwrite document?"),
2322                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2323
2324                 if (ret == 1) {
2325                         message(_("Canceled."));
2326                         return;
2327                 }
2328         }
2329
2330         message(bformat(_("Importing %1$s..."), displaypath));
2331         ErrorList errorList;
2332         if (import(this, fullname, format, errorList))
2333                 message(_("imported."));
2334         else
2335                 message(_("file not imported!"));
2336
2337         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2338 }
2339
2340
2341 void GuiView::newDocument(string const & filename, bool from_template)
2342 {
2343         FileName initpath(lyxrc.document_path);
2344         if (documentBufferView()) {
2345                 FileName const trypath(documentBufferView()->buffer().filePath());
2346                 // If directory is writeable, use this as default.
2347                 if (trypath.isDirWritable())
2348                         initpath = trypath;
2349         }
2350
2351         string templatefile;
2352         if (from_template) {
2353                 templatefile = selectTemplateFile().absFileName();
2354                 if (templatefile.empty())
2355                         return;
2356         }
2357
2358         Buffer * b;
2359         if (filename.empty())
2360                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2361         else
2362                 b = newFile(filename, templatefile, true);
2363
2364         if (b)
2365                 setBuffer(b);
2366
2367         // If no new document could be created, it is unsure
2368         // whether there is a valid BufferView.
2369         if (currentBufferView())
2370                 // Ensure the cursor is correctly positioned on screen.
2371                 currentBufferView()->showCursor();
2372 }
2373
2374
2375 void GuiView::insertLyXFile(docstring const & fname)
2376 {
2377         BufferView * bv = documentBufferView();
2378         if (!bv)
2379                 return;
2380
2381         // FIXME UNICODE
2382         FileName filename(to_utf8(fname));
2383         if (filename.empty()) {
2384                 // Launch a file browser
2385                 // FIXME UNICODE
2386                 string initpath = lyxrc.document_path;
2387                 string const trypath = bv->buffer().filePath();
2388                 // If directory is writeable, use this as default.
2389                 if (FileName(trypath).isDirWritable())
2390                         initpath = trypath;
2391
2392                 // FIXME UNICODE
2393                 FileDialog dlg(qt_("Select LyX document to insert"));
2394                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2395                 dlg.setButton2(qt_("Examples|#E#e"),
2396                         toqstr(addPath(package().system_support().absFileName(),
2397                         "examples")));
2398
2399                 FileDialog::Result result = dlg.open(toqstr(initpath),
2400                                          QStringList(qt_("LyX Documents (*.lyx)")));
2401
2402                 if (result.first == FileDialog::Later)
2403                         return;
2404
2405                 // FIXME UNICODE
2406                 filename.set(fromqstr(result.second));
2407
2408                 // check selected filename
2409                 if (filename.empty()) {
2410                         // emit message signal.
2411                         message(_("Canceled."));
2412                         return;
2413                 }
2414         }
2415
2416         bv->insertLyXFile(filename);
2417         bv->buffer().errors("Parse");
2418 }
2419
2420
2421 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2422 {
2423         FileName fname = b.fileName();
2424         FileName const oldname = fname;
2425
2426         if (!newname.empty()) {
2427                 // FIXME UNICODE
2428                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2429         } else {
2430                 // Switch to this Buffer.
2431                 setBuffer(&b);
2432
2433                 // No argument? Ask user through dialog.
2434                 // FIXME UNICODE
2435                 FileDialog dlg(qt_("Choose a filename to save document as"));
2436                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2437                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2438
2439                 if (!isLyXFileName(fname.absFileName()))
2440                         fname.changeExtension(".lyx");
2441
2442                 FileDialog::Result result =
2443                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2444                                    QStringList(qt_("LyX Documents (*.lyx)")),
2445                                          toqstr(fname.onlyFileName()));
2446
2447                 if (result.first == FileDialog::Later)
2448                         return false;
2449
2450                 fname.set(fromqstr(result.second));
2451
2452                 if (fname.empty())
2453                         return false;
2454
2455                 if (!isLyXFileName(fname.absFileName()))
2456                         fname.changeExtension(".lyx");
2457         }
2458
2459         // fname is now the new Buffer location.
2460
2461         // if there is already a Buffer open with this name, we do not want
2462         // to have another one. (the second test makes sure we're not just
2463         // trying to overwrite ourselves, which is fine.)
2464         if (theBufferList().exists(fname) && fname != oldname
2465                   && theBufferList().getBuffer(fname) != &b) {
2466                 docstring const text =
2467                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2468                             "Please close it before attempting to overwrite it.\n"
2469                             "Do you want to choose a new filename?"),
2470                                 from_utf8(fname.absFileName()));
2471                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2472                         text, 0, 1, _("&Rename"), _("&Cancel"));
2473                 switch (ret) {
2474                 case 0: return renameBuffer(b, docstring(), kind);
2475                 case 1: return false;
2476                 }
2477                 //return false;
2478         }
2479
2480         bool const existsLocal = fname.exists();
2481         bool const existsInVC = LyXVC::fileInVC(fname);
2482         if (existsLocal || existsInVC) {
2483                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2484                 if (kind != LV_WRITE_AS && existsInVC) {
2485                         // renaming to a name that is already in VC
2486                         // would not work
2487                         docstring text = bformat(_("The document %1$s "
2488                                         "is already registered.\n\n"
2489                                         "Do you want to choose a new name?"),
2490                                 file);
2491                         docstring const title = (kind == LV_VC_RENAME) ?
2492                                 _("Rename document?") : _("Copy document?");
2493                         docstring const button = (kind == LV_VC_RENAME) ?
2494                                 _("&Rename") : _("&Copy");
2495                         int const ret = Alert::prompt(title, text, 0, 1,
2496                                 button, _("&Cancel"));
2497                         switch (ret) {
2498                         case 0: return renameBuffer(b, docstring(), kind);
2499                         case 1: return false;
2500                         }
2501                 }
2502
2503                 if (existsLocal) {
2504                         docstring text = bformat(_("The document %1$s "
2505                                         "already exists.\n\n"
2506                                         "Do you want to overwrite that document?"),
2507                                 file);
2508                         int const ret = Alert::prompt(_("Overwrite document?"),
2509                                         text, 0, 2, _("&Overwrite"),
2510                                         _("&Rename"), _("&Cancel"));
2511                         switch (ret) {
2512                         case 0: break;
2513                         case 1: return renameBuffer(b, docstring(), kind);
2514                         case 2: return false;
2515                         }
2516                 }
2517         }
2518
2519         switch (kind) {
2520         case LV_VC_RENAME: {
2521                 string msg = b.lyxvc().rename(fname);
2522                 if (msg.empty())
2523                         return false;
2524                 message(from_utf8(msg));
2525                 break;
2526         }
2527         case LV_VC_COPY: {
2528                 string msg = b.lyxvc().copy(fname);
2529                 if (msg.empty())
2530                         return false;
2531                 message(from_utf8(msg));
2532                 break;
2533         }
2534         case LV_WRITE_AS:
2535                 break;
2536         }
2537         // LyXVC created the file already in case of LV_VC_RENAME or
2538         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2539         // relative paths of included stuff right if we moved e.g. from
2540         // /a/b.lyx to /a/c/b.lyx.
2541
2542         bool const saved = saveBuffer(b, fname);
2543         if (saved)
2544                 b.reload();
2545         return saved;
2546 }
2547
2548
2549 struct PrettyNameComparator
2550 {
2551         bool operator()(Format const *first, Format const *second) const {
2552                 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2553                                        translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2554         }
2555 };
2556
2557
2558 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2559 {
2560         FileName fname = b.fileName();
2561
2562         FileDialog dlg(qt_("Choose a filename to export the document as"));
2563         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2564
2565         QStringList types;
2566         QString const anyformat = qt_("Guess from extension (*.*)");
2567         types << anyformat;
2568         Formats::const_iterator it = formats.begin();
2569         vector<Format const *> export_formats;
2570         for (; it != formats.end(); ++it)
2571                 if (it->documentFormat())
2572                         export_formats.push_back(&(*it));
2573         PrettyNameComparator cmp;
2574         sort(export_formats.begin(), export_formats.end(), cmp);
2575         vector<Format const *>::const_iterator fit = export_formats.begin();
2576         map<QString, string> fmap;
2577         QString filter;
2578         string ext;
2579         for (; fit != export_formats.end(); ++fit) {
2580                 docstring const loc_prettyname =
2581                         translateIfPossible(from_utf8((*fit)->prettyname()));
2582                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2583                                                      loc_prettyname,
2584                                                      from_ascii((*fit)->extension())));
2585                 types << loc_filter;
2586                 fmap[loc_filter] = (*fit)->name();
2587                 if (from_ascii((*fit)->name()) == iformat) {
2588                         filter = loc_filter;
2589                         ext = (*fit)->extension();
2590                 }
2591         }
2592         string ofname = fname.onlyFileName();
2593         if (!ext.empty())
2594                 ofname = support::changeExtension(ofname, ext);
2595         FileDialog::Result result =
2596                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2597                          types,
2598                          toqstr(ofname),
2599                          &filter);
2600         if (result.first != FileDialog::Chosen)
2601                 return false;
2602
2603         string fmt_name;
2604         fname.set(fromqstr(result.second));
2605         if (filter == anyformat)
2606                 fmt_name = formats.getFormatFromExtension(fname.extension());
2607         else
2608                 fmt_name = fmap[filter];
2609         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2610                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2611
2612         if (fmt_name.empty() || fname.empty())
2613                 return false;
2614
2615         // fname is now the new Buffer location.
2616         if (FileName(fname).exists()) {
2617                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2618                 docstring text = bformat(_("The document %1$s already "
2619                                            "exists.\n\nDo you want to "
2620                                            "overwrite that document?"),
2621                                          file);
2622                 int const ret = Alert::prompt(_("Overwrite document?"),
2623                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2624                 switch (ret) {
2625                 case 0: break;
2626                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2627                 case 2: return false;
2628                 }
2629         }
2630
2631         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2632         DispatchResult dr;
2633         dispatch(cmd, dr);
2634         return dr.dispatched();
2635 }
2636
2637
2638 bool GuiView::saveBuffer(Buffer & b)
2639 {
2640         return saveBuffer(b, FileName());
2641 }
2642
2643
2644 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2645 {
2646         if (workArea(b) && workArea(b)->inDialogMode())
2647                 return true;
2648
2649         if (fn.empty() && b.isUnnamed())
2650                 return renameBuffer(b, docstring());
2651
2652         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2653         if (success) {
2654                 theSession().lastFiles().add(b.fileName());
2655                 return true;
2656         }
2657
2658         // Switch to this Buffer.
2659         setBuffer(&b);
2660
2661         // FIXME: we don't tell the user *WHY* the save failed !!
2662         docstring const file = makeDisplayPath(b.absFileName(), 30);
2663         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2664                                    "Do you want to rename the document and "
2665                                    "try again?"), file);
2666         int const ret = Alert::prompt(_("Rename and save?"),
2667                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2668         switch (ret) {
2669         case 0:
2670                 if (!renameBuffer(b, docstring()))
2671                         return false;
2672                 break;
2673         case 1:
2674                 break;
2675         case 2:
2676                 return false;
2677         }
2678
2679         return saveBuffer(b, fn);
2680 }
2681
2682
2683 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2684 {
2685         return closeWorkArea(wa, false);
2686 }
2687
2688
2689 // We only want to close the buffer if it is not visible in other workareas
2690 // of the same view, nor in other views, and if this is not a child
2691 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2692 {
2693         Buffer & buf = wa->bufferView().buffer();
2694
2695         bool last_wa = d.countWorkAreasOf(buf) == 1
2696                 && !inOtherView(buf) && !buf.parent();
2697
2698         bool close_buffer = last_wa;
2699
2700         if (last_wa) {
2701                 if (lyxrc.close_buffer_with_last_view == "yes")
2702                         ; // Nothing to do
2703                 else if (lyxrc.close_buffer_with_last_view == "no")
2704                         close_buffer = false;
2705                 else {
2706                         docstring file;
2707                         if (buf.isUnnamed())
2708                                 file = from_utf8(buf.fileName().onlyFileName());
2709                         else
2710                                 file = buf.fileName().displayName(30);
2711                         docstring const text = bformat(
2712                                 _("Last view on document %1$s is being closed.\n"
2713                                   "Would you like to close or hide the document?\n"
2714                                   "\n"
2715                                   "Hidden documents can be displayed back through\n"
2716                                   "the menu: View->Hidden->...\n"
2717                                   "\n"
2718                                   "To remove this question, set your preference in:\n"
2719                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2720                                 ), file);
2721                         int ret = Alert::prompt(_("Close or hide document?"),
2722                                 text, 0, 1, _("&Close"), _("&Hide"));
2723                         close_buffer = (ret == 0);
2724                 }
2725         }
2726
2727         return closeWorkArea(wa, close_buffer);
2728 }
2729
2730
2731 bool GuiView::closeBuffer()
2732 {
2733         GuiWorkArea * wa = currentMainWorkArea();
2734         setCurrentWorkArea(wa);
2735         Buffer & buf = wa->bufferView().buffer();
2736         return wa && closeWorkArea(wa, !buf.parent());
2737 }
2738
2739
2740 void GuiView::writeSession() const {
2741         GuiWorkArea const * active_wa = currentMainWorkArea();
2742         for (int i = 0; i < d.splitter_->count(); ++i) {
2743                 TabWorkArea * twa = d.tabWorkArea(i);
2744                 for (int j = 0; j < twa->count(); ++j) {
2745                         GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2746                         Buffer & buf = wa->bufferView().buffer();
2747                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2748                 }
2749         }
2750 }
2751
2752
2753 bool GuiView::closeBufferAll()
2754 {
2755         // Close the workareas in all other views
2756         QList<int> const ids = guiApp->viewIds();
2757         for (int i = 0; i != ids.size(); ++i) {
2758                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2759                         return false;
2760         }
2761
2762         // Close our own workareas
2763         if (!closeWorkAreaAll())
2764                 return false;
2765
2766         // Now close the hidden buffers. We prevent hidden buffers from being
2767         // dirty, so we can just close them.
2768         theBufferList().closeAll();
2769         return true;
2770 }
2771
2772
2773 bool GuiView::closeWorkAreaAll()
2774 {
2775         setCurrentWorkArea(currentMainWorkArea());
2776
2777         // We might be in a situation that there is still a tabWorkArea, but
2778         // there are no tabs anymore. This can happen when we get here after a
2779         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2780         // many TabWorkArea's have no documents anymore.
2781         int empty_twa = 0;
2782
2783         // We have to call count() each time, because it can happen that
2784         // more than one splitter will disappear in one iteration (bug 5998).
2785         for (; d.splitter_->count() > empty_twa; ) {
2786                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2787
2788                 if (twa->count() == 0)
2789                         ++empty_twa;
2790                 else {
2791                         setCurrentWorkArea(twa->currentWorkArea());
2792                         if (!closeTabWorkArea(twa))
2793                                 return false;
2794                 }
2795         }
2796         return true;
2797 }
2798
2799
2800 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2801 {
2802         if (!wa)
2803                 return false;
2804
2805         Buffer & buf = wa->bufferView().buffer();
2806
2807         if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2808                 Alert::warning(_("Close document"), 
2809                         _("Document could not be closed because it is being processed by LyX."));
2810                 return false;
2811         }
2812
2813         if (close_buffer)
2814                 return closeBuffer(buf);
2815         else {
2816                 if (!inMultiTabs(wa))
2817                         if (!saveBufferIfNeeded(buf, true))
2818                                 return false;
2819                 removeWorkArea(wa);
2820                 return true;
2821         }
2822 }
2823
2824
2825 bool GuiView::closeBuffer(Buffer & buf)
2826 {
2827         // If we are in a close_event all children will be closed in some time,
2828         // so no need to do it here. This will ensure that the children end up
2829         // in the session file in the correct order. If we close the master
2830         // buffer, we can close or release the child buffers here too.
2831         bool success = true;
2832         if (!closing_) {
2833                 ListOfBuffers clist = buf.getChildren();
2834                 ListOfBuffers::const_iterator it = clist.begin();
2835                 ListOfBuffers::const_iterator const bend = clist.end();
2836                 for (; it != bend; ++it) {
2837                         // If a child is dirty, do not close
2838                         // without user intervention
2839                         //FIXME: should we look in other tabworkareas?
2840                         Buffer * child_buf = *it;
2841                         GuiWorkArea * child_wa = workArea(*child_buf);
2842                         if (child_wa) {
2843                                 if (!closeWorkArea(child_wa, true)) {
2844                                         success = false;
2845                                         break;
2846                                 }
2847                         } else
2848                                 theBufferList().releaseChild(&buf, child_buf);
2849                 }
2850         }
2851         if (success) {
2852                 // goto bookmark to update bookmark pit.
2853                 //FIXME: we should update only the bookmarks related to this buffer!
2854                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2855                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2856                         guiApp->gotoBookmark(i+1, false, false);
2857
2858                 if (saveBufferIfNeeded(buf, false)) {
2859                         buf.removeAutosaveFile();
2860                         theBufferList().release(&buf);
2861                         return true;
2862                 }
2863         }
2864         // open all children again to avoid a crash because of dangling
2865         // pointers (bug 6603)
2866         buf.updateBuffer();
2867         return false;
2868 }
2869
2870
2871 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2872 {
2873         while (twa == d.currentTabWorkArea()) {
2874                 twa->setCurrentIndex(twa->count()-1);
2875
2876                 GuiWorkArea * wa = twa->currentWorkArea();
2877                 Buffer & b = wa->bufferView().buffer();
2878
2879                 // We only want to close the buffer if the same buffer is not visible
2880                 // in another view, and if this is not a child and if we are closing
2881                 // a view (not a tabgroup).
2882                 bool const close_buffer =
2883                         !inOtherView(b) && !b.parent() && closing_;
2884
2885                 if (!closeWorkArea(wa, close_buffer))
2886                         return false;
2887         }
2888         return true;
2889 }
2890
2891
2892 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2893 {
2894         if (buf.isClean() || buf.paragraphs().empty())
2895                 return true;
2896
2897         // Switch to this Buffer.
2898         setBuffer(&buf);
2899
2900         docstring file;
2901         // FIXME: Unicode?
2902         if (buf.isUnnamed())
2903                 file = from_utf8(buf.fileName().onlyFileName());
2904         else
2905                 file = buf.fileName().displayName(30);
2906
2907         // Bring this window to top before asking questions.
2908         raise();
2909         activateWindow();
2910
2911         int ret;
2912         if (hiding && buf.isUnnamed()) {
2913                 docstring const text = bformat(_("The document %1$s has not been "
2914                                                  "saved yet.\n\nDo you want to save "
2915                                                  "the document?"), file);
2916                 ret = Alert::prompt(_("Save new document?"),
2917                         text, 0, 1, _("&Save"), _("&Cancel"));
2918                 if (ret == 1)
2919                         ++ret;
2920         } else {
2921                 docstring const text = bformat(_("The document %1$s has unsaved changes."
2922                         "\n\nDo you want to save the document or discard the changes?"), file);
2923                 ret = Alert::prompt(_("Save changed document?"),
2924                         text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2925         }
2926
2927         switch (ret) {
2928         case 0:
2929                 if (!saveBuffer(buf))
2930                         return false;
2931                 break;
2932         case 1:
2933                 // If we crash after this we could have no autosave file
2934                 // but I guess this is really improbable (Jug).
2935                 // Sometimes improbable things happen:
2936                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2937                 // buf.removeAutosaveFile();
2938                 if (hiding)
2939                         // revert all changes
2940                         reloadBuffer(buf);
2941                 buf.markClean();
2942                 break;
2943         case 2:
2944                 return false;
2945         }
2946         return true;
2947 }
2948
2949
2950 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2951 {
2952         Buffer & buf = wa->bufferView().buffer();
2953
2954         for (int i = 0; i != d.splitter_->count(); ++i) {
2955                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2956                 if (wa_ && wa_ != wa)
2957                         return true;
2958         }
2959         return inOtherView(buf);
2960 }
2961
2962
2963 bool GuiView::inOtherView(Buffer & buf)
2964 {
2965         QList<int> const ids = guiApp->viewIds();
2966
2967         for (int i = 0; i != ids.size(); ++i) {
2968                 if (id_ == ids[i])
2969                         continue;
2970
2971                 if (guiApp->view(ids[i]).workArea(buf))
2972                         return true;
2973         }
2974         return false;
2975 }
2976
2977
2978 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2979 {
2980         if (!documentBufferView())
2981                 return;
2982         
2983         if (TabWorkArea * twa = d.currentTabWorkArea()) {
2984                 Buffer * const curbuf = &documentBufferView()->buffer();
2985                 int nwa = twa->count();
2986                 for (int i = 0; i < nwa; ++i) {
2987                         if (&workArea(i)->bufferView().buffer() == curbuf) {
2988                                 int next_index;
2989                                 if (np == NEXTBUFFER)
2990                                         next_index = (i == nwa - 1 ? 0 : i + 1);
2991                                 else
2992                                         next_index = (i == 0 ? nwa - 1 : i - 1);
2993                                 if (move)
2994                                         twa->moveTab(i, next_index);
2995                                 else
2996                                         setBuffer(&workArea(next_index)->bufferView().buffer());
2997                                 break;
2998                         }
2999                 }
3000         }
3001 }
3002
3003
3004 /// make sure the document is saved
3005 static bool ensureBufferClean(Buffer * buffer)
3006 {
3007         LASSERT(buffer, return false);
3008         if (buffer->isClean() && !buffer->isUnnamed())
3009                 return true;
3010
3011         docstring const file = buffer->fileName().displayName(30);
3012         docstring title;
3013         docstring text;
3014         if (!buffer->isUnnamed()) {
3015                 text = bformat(_("The document %1$s has unsaved "
3016                                                  "changes.\n\nDo you want to save "
3017                                                  "the document?"), file);
3018                 title = _("Save changed document?");
3019
3020         } else {
3021                 text = bformat(_("The document %1$s has not been "
3022                                                  "saved yet.\n\nDo you want to save "
3023                                                  "the document?"), file);
3024                 title = _("Save new document?");
3025         }
3026         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3027
3028         if (ret == 0)
3029                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3030
3031         return buffer->isClean() && !buffer->isUnnamed();
3032 }
3033
3034
3035 bool GuiView::reloadBuffer(Buffer & buf)
3036 {
3037         Buffer::ReadStatus status = buf.reload();
3038         return status == Buffer::ReadSuccess;
3039 }
3040
3041
3042 void GuiView::checkExternallyModifiedBuffers()
3043 {
3044         BufferList::iterator bit = theBufferList().begin();
3045         BufferList::iterator const bend = theBufferList().end();
3046         for (; bit != bend; ++bit) {
3047                 Buffer * buf = *bit;
3048                 if (buf->fileName().exists()
3049                         && buf->isExternallyModified(Buffer::checksum_method)) {
3050                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3051                                         " Reload now? Any local changes will be lost."),
3052                                         from_utf8(buf->absFileName()));
3053                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3054                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3055                         if (!ret)
3056                                 reloadBuffer(*buf);
3057                 }
3058         }
3059 }
3060
3061
3062 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3063 {
3064         Buffer * buffer = documentBufferView()
3065                 ? &(documentBufferView()->buffer()) : 0;
3066
3067         switch (cmd.action()) {
3068         case LFUN_VC_REGISTER:
3069                 if (!buffer || !ensureBufferClean(buffer))
3070                         break;
3071                 if (!buffer->lyxvc().inUse()) {
3072                         if (buffer->lyxvc().registrer()) {
3073                                 reloadBuffer(*buffer);
3074                                 dr.clearMessageUpdate();
3075                         }
3076                 }
3077                 break;
3078
3079         case LFUN_VC_RENAME:
3080         case LFUN_VC_COPY: {
3081                 if (!buffer || !ensureBufferClean(buffer))
3082                         break;
3083                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3084                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3085                                 // Some changes are not yet committed.
3086                                 // We test here and not in getStatus(), since
3087                                 // this test is expensive.
3088                                 string log;
3089                                 LyXVC::CommandResult ret =
3090                                         buffer->lyxvc().checkIn(log);
3091                                 dr.setMessage(log);
3092                                 if (ret == LyXVC::ErrorCommand ||
3093                                     ret == LyXVC::VCSuccess)
3094                                         reloadBuffer(*buffer);
3095                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3096                                         frontend::Alert::error(
3097                                                 _("Revision control error."),
3098                                                 _("Document could not be checked in."));
3099                                         break;
3100                                 }
3101                         }
3102                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3103                                 LV_VC_RENAME : LV_VC_COPY;
3104                         renameBuffer(*buffer, cmd.argument(), kind);
3105                 }
3106                 break;
3107         }
3108
3109         case LFUN_VC_CHECK_IN:
3110                 if (!buffer || !ensureBufferClean(buffer))
3111                         break;
3112                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3113                         string log;
3114                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3115                         dr.setMessage(log);
3116                         // Only skip reloading if the checkin was cancelled or
3117                         // an error occurred before the real checkin VCS command
3118                         // was executed, since the VCS might have changed the
3119                         // file even if it could not checkin successfully.
3120                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3121                                 reloadBuffer(*buffer);
3122                 }
3123                 break;
3124
3125         case LFUN_VC_CHECK_OUT:
3126                 if (!buffer || !ensureBufferClean(buffer))
3127                         break;
3128                 if (buffer->lyxvc().inUse()) {
3129                         dr.setMessage(buffer->lyxvc().checkOut());
3130                         reloadBuffer(*buffer);
3131                 }
3132                 break;
3133
3134         case LFUN_VC_LOCKING_TOGGLE:
3135                 LASSERT(buffer, return);
3136                 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3137                         break;
3138                 if (buffer->lyxvc().inUse()) {
3139                         string res = buffer->lyxvc().lockingToggle();
3140                         if (res.empty()) {
3141                                 frontend::Alert::error(_("Revision control error."),
3142                                 _("Error when setting the locking property."));
3143                         } else {
3144                                 dr.setMessage(res);
3145                                 reloadBuffer(*buffer);
3146                         }
3147                 }
3148                 break;
3149
3150         case LFUN_VC_REVERT:
3151                 LASSERT(buffer, return);
3152                 if (buffer->lyxvc().revert()) {
3153                         reloadBuffer(*buffer);
3154                         dr.clearMessageUpdate();
3155                 }
3156                 break;
3157
3158         case LFUN_VC_UNDO_LAST:
3159                 LASSERT(buffer, return);
3160                 buffer->lyxvc().undoLast();
3161                 reloadBuffer(*buffer);
3162                 dr.clearMessageUpdate();
3163                 break;
3164
3165         case LFUN_VC_REPO_UPDATE:
3166                 LASSERT(buffer, return);
3167                 if (ensureBufferClean(buffer)) {
3168                         dr.setMessage(buffer->lyxvc().repoUpdate());
3169                         checkExternallyModifiedBuffers();
3170                 }
3171                 break;
3172
3173         case LFUN_VC_COMMAND: {
3174                 string flag = cmd.getArg(0);
3175                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3176                         break;
3177                 docstring message;
3178                 if (contains(flag, 'M')) {
3179                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3180                                 break;
3181                 }
3182                 string path = cmd.getArg(1);
3183                 if (contains(path, "$$p") && buffer)
3184                         path = subst(path, "$$p", buffer->filePath());
3185                 LYXERR(Debug::LYXVC, "Directory: " << path);
3186                 FileName pp(path);
3187                 if (!pp.isReadableDirectory()) {
3188                         lyxerr << _("Directory is not accessible.") << endl;
3189                         break;
3190                 }
3191                 support::PathChanger p(pp);
3192
3193                 string command = cmd.getArg(2);
3194                 if (command.empty())
3195                         break;
3196                 if (buffer) {
3197                         command = subst(command, "$$i", buffer->absFileName());
3198                         command = subst(command, "$$p", buffer->filePath());
3199                 }
3200                 command = subst(command, "$$m", to_utf8(message));
3201                 LYXERR(Debug::LYXVC, "Command: " << command);
3202                 Systemcall one;
3203                 one.startscript(Systemcall::Wait, command);
3204
3205                 if (!buffer)
3206                         break;
3207                 if (contains(flag, 'I'))
3208                         buffer->markDirty();
3209                 if (contains(flag, 'R'))
3210                         reloadBuffer(*buffer);
3211
3212                 break;
3213                 }
3214
3215         case LFUN_VC_COMPARE: {
3216
3217                 if (cmd.argument().empty()) {
3218                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3219                         break;
3220                 }
3221
3222                 string rev1 = cmd.getArg(0);
3223                 string f1, f2;
3224
3225                 // f1
3226                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3227                         break;
3228
3229                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3230                         f2 = buffer->absFileName();
3231                 } else {
3232                         string rev2 = cmd.getArg(1);
3233                         if (rev2.empty())
3234                                 break;
3235                         // f2
3236                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3237                                 break;
3238                 }
3239
3240                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3241                                         f1 << "\n"  << f2 << "\n" );
3242                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3243                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3244                 break;
3245         }
3246
3247         default:
3248                 break;
3249         }
3250 }
3251
3252
3253 void GuiView::openChildDocument(string const & fname)
3254 {
3255         LASSERT(documentBufferView(), return);
3256         Buffer & buffer = documentBufferView()->buffer();
3257         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3258         documentBufferView()->saveBookmark(false);
3259         Buffer * child = 0;
3260         if (theBufferList().exists(filename)) {
3261                 child = theBufferList().getBuffer(filename);
3262                 setBuffer(child);
3263         } else {
3264                 message(bformat(_("Opening child document %1$s..."),
3265                         makeDisplayPath(filename.absFileName())));
3266                 child = loadDocument(filename, false);
3267         }
3268         // Set the parent name of the child document.
3269         // This makes insertion of citations and references in the child work,
3270         // when the target is in the parent or another child document.
3271         if (child)
3272                 child->setParent(&buffer);
3273 }
3274
3275
3276 bool GuiView::goToFileRow(string const & argument)
3277 {
3278         string file_name;
3279         int row;
3280         size_t i = argument.find_last_of(' ');
3281         if (i != string::npos) {
3282                 file_name = os::internal_path(trim(argument.substr(0, i)));
3283                 istringstream is(argument.substr(i + 1));
3284                 is >> row;
3285                 if (is.fail())
3286                         i = string::npos;
3287         }
3288         if (i == string::npos) {
3289                 LYXERR0("Wrong argument: " << argument);
3290                 return false;
3291         }
3292         Buffer * buf = 0;
3293         string const abstmp = package().temp_dir().absFileName();
3294         string const realtmp = package().temp_dir().realPath();
3295         // We have to use os::path_prefix_is() here, instead of
3296         // simply prefixIs(), because the file name comes from
3297         // an external application and may need case adjustment.
3298         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3299                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3300                 // Needed by inverse dvi search. If it is a file
3301                 // in tmpdir, call the apropriated function.
3302                 // If tmpdir is a symlink, we may have the real
3303                 // path passed back, so we correct for that.
3304                 if (!prefixIs(file_name, abstmp))
3305                         file_name = subst(file_name, realtmp, abstmp);
3306                 buf = theBufferList().getBufferFromTmp(file_name);
3307         } else {
3308                 // Must replace extension of the file to be .lyx
3309                 // and get full path
3310                 FileName const s = fileSearch(string(),
3311                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3312                 // Either change buffer or load the file
3313                 if (theBufferList().exists(s))
3314                         buf = theBufferList().getBuffer(s);
3315                 else if (s.exists()) {
3316                         buf = loadDocument(s);
3317                         if (!buf)
3318                                 return false;
3319                 } else {
3320                         message(bformat(
3321                                         _("File does not exist: %1$s"),
3322                                         makeDisplayPath(file_name)));
3323                         return false;
3324                 }
3325         }
3326         if (!buf) {
3327                 message(bformat(
3328                         _("No buffer for file: %1$s."),
3329                         makeDisplayPath(file_name))
3330                 );
3331                 return false;
3332         }
3333         setBuffer(buf);
3334         documentBufferView()->setCursorFromRow(row);
3335         return true;
3336 }
3337
3338
3339 template<class T>
3340 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3341 {
3342         Buffer::ExportStatus const status = func(format);
3343
3344         // the cloning operation will have produced a clone of the entire set of
3345         // documents, starting from the master. so we must delete those.
3346         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3347         delete mbuf;
3348         busyBuffers.remove(orig);
3349         return status;
3350 }
3351
3352
3353 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3354 {
3355         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3356         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3357 }
3358
3359
3360 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3361 {
3362         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3363         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3364 }
3365
3366
3367 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3368 {
3369         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3370         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3371 }
3372
3373
3374 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3375                            string const & argument,
3376                            Buffer const * used_buffer,
3377                            docstring const & msg,
3378                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3379                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3380                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3381 {
3382         if (!used_buffer)
3383                 return false;
3384
3385         string format = argument;
3386         if (format.empty())
3387                 format = used_buffer->params().getDefaultOutputFormat();
3388         processing_format = format;
3389         if (!msg.empty()) {
3390                 progress_->clearMessages();
3391                 gv_->message(msg);
3392         }
3393 #if EXPORT_in_THREAD
3394         GuiViewPrivate::busyBuffers.insert(used_buffer);
3395         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3396         if (!cloned_buffer) {
3397                 Alert::error(_("Export Error"),
3398                              _("Error cloning the Buffer."));
3399                 return false;
3400         }
3401         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3402                                 asyncFunc,
3403                                 used_buffer,
3404                                 cloned_buffer,
3405                                 format);
3406         setPreviewFuture(f);
3407         last_export_format = used_buffer->params().bufferFormat();
3408         (void) syncFunc;
3409         (void) previewFunc;
3410         // We are asynchronous, so we don't know here anything about the success
3411         return true;
3412 #else
3413         Buffer::ExportStatus status;
3414         if (syncFunc) {
3415                 // TODO check here if it breaks exporting with Qt < 4.4
3416                 status = (used_buffer->*syncFunc)(format, true);
3417         } else if (previewFunc) {
3418                 status = (used_buffer->*previewFunc)(format); 
3419         } else
3420                 return false;
3421         handleExportStatus(gv_, status, format);
3422         (void) asyncFunc;
3423         return (status == Buffer::ExportSuccess 
3424                         || status == Buffer::PreviewSuccess);
3425 #endif
3426 }
3427
3428 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3429 {
3430         BufferView * bv = currentBufferView();
3431         LASSERT(bv, return);
3432
3433         // Let the current BufferView dispatch its own actions.
3434         bv->dispatch(cmd, dr);
3435         if (dr.dispatched())
3436                 return;
3437
3438         // Try with the document BufferView dispatch if any.
3439         BufferView * doc_bv = documentBufferView();
3440         if (doc_bv && doc_bv != bv) {
3441                 doc_bv->dispatch(cmd, dr);
3442                 if (dr.dispatched())
3443                         return;
3444         }
3445
3446         // Then let the current Cursor dispatch its own actions.
3447         bv->cursor().dispatch(cmd);
3448
3449         // update completion. We do it here and not in
3450         // processKeySym to avoid another redraw just for a
3451         // changed inline completion
3452         if (cmd.origin() == FuncRequest::KEYBOARD) {
3453                 if (cmd.action() == LFUN_SELF_INSERT
3454                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3455                         updateCompletion(bv->cursor(), true, true);
3456                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3457                         updateCompletion(bv->cursor(), false, true);
3458                 else
3459                         updateCompletion(bv->cursor(), false, false);
3460         }
3461
3462         dr = bv->cursor().result();
3463 }
3464
3465
3466 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3467 {
3468         BufferView * bv = currentBufferView();
3469         // By default we won't need any update.
3470         dr.screenUpdate(Update::None);
3471         // assume cmd will be dispatched
3472         dr.dispatched(true);
3473
3474         Buffer * doc_buffer = documentBufferView()
3475                 ? &(documentBufferView()->buffer()) : 0;
3476
3477         if (cmd.origin() == FuncRequest::TOC) {
3478                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3479                 // FIXME: do we need to pass a DispatchResult object here?
3480                 toc->doDispatch(bv->cursor(), cmd);
3481                 return;
3482         }
3483
3484         string const argument = to_utf8(cmd.argument());
3485
3486         switch(cmd.action()) {
3487                 case LFUN_BUFFER_CHILD_OPEN:
3488                         openChildDocument(to_utf8(cmd.argument()));
3489                         break;
3490
3491                 case LFUN_BUFFER_IMPORT:
3492                         importDocument(to_utf8(cmd.argument()));
3493                         break;
3494
3495                 case LFUN_BUFFER_EXPORT: {
3496                         if (!doc_buffer)
3497                                 break;
3498                         FileName target_dir = doc_buffer->fileName().onlyPath();
3499                         string const dest = cmd.getArg(1);
3500                         if (!dest.empty() && FileName::isAbsolute(dest))
3501                                 target_dir = FileName(support::onlyPath(dest));
3502                         // GCC only sees strfwd.h when building merged
3503                         if (::lyx::operator==(cmd.argument(), "custom")) {
3504                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3505                                 break;
3506                         }
3507                         if (!target_dir.isDirWritable()) {
3508                                 exportBufferAs(*doc_buffer, cmd.argument());
3509                                 break;
3510                         }
3511                         /* TODO/Review: Is it a problem to also export the children?
3512                                         See the update_unincluded flag */
3513                         d.asyncBufferProcessing(argument,
3514                                                 doc_buffer,
3515                                                 _("Exporting ..."),
3516                                                 &GuiViewPrivate::exportAndDestroy,
3517                                                 &Buffer::doExport,
3518                                                 0);
3519                         // TODO Inform user about success
3520                         break;
3521                 }
3522
3523                 case LFUN_BUFFER_EXPORT_AS: {
3524                         LASSERT(doc_buffer, break);
3525                         docstring f = cmd.argument();
3526                         if (f.empty())
3527                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3528                         exportBufferAs(*doc_buffer, f);
3529                         break;
3530                 }
3531
3532                 case LFUN_BUFFER_UPDATE: {
3533                         d.asyncBufferProcessing(argument,
3534                                                 doc_buffer,
3535                                                 _("Exporting ..."),
3536                                                 &GuiViewPrivate::compileAndDestroy,
3537                                                 &Buffer::doExport,
3538                                                 0);
3539                         break;
3540                 }
3541                 case LFUN_BUFFER_VIEW: {
3542                         d.asyncBufferProcessing(argument,
3543                                                 doc_buffer,
3544                                                 _("Previewing ..."),
3545                                                 &GuiViewPrivate::previewAndDestroy,
3546                                                 0,
3547                                                 &Buffer::preview);
3548                         break;
3549                 }
3550                 case LFUN_MASTER_BUFFER_UPDATE: {
3551                         d.asyncBufferProcessing(argument,
3552                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3553                                                 docstring(),
3554                                                 &GuiViewPrivate::compileAndDestroy,
3555                                                 &Buffer::doExport,
3556                                                 0);
3557                         break;
3558                 }
3559                 case LFUN_MASTER_BUFFER_VIEW: {
3560                         d.asyncBufferProcessing(argument,
3561                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3562                                                 docstring(),
3563                                                 &GuiViewPrivate::previewAndDestroy,
3564                                                 0, &Buffer::preview);
3565                         break;
3566                 }
3567                 case LFUN_BUFFER_SWITCH: {
3568                         string const file_name = to_utf8(cmd.argument());
3569                         if (!FileName::isAbsolute(file_name)) {
3570                                 dr.setError(true);
3571                                 dr.setMessage(_("Absolute filename expected."));
3572                                 break;
3573                         }
3574
3575                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3576                         if (!buffer) {
3577                                 dr.setError(true);
3578                                 dr.setMessage(_("Document not loaded"));
3579                                 break;
3580                         }
3581
3582                         // Do we open or switch to the buffer in this view ?
3583                         if (workArea(*buffer)
3584                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3585                                 setBuffer(buffer);
3586                                 break;
3587                         }
3588
3589                         // Look for the buffer in other views
3590                         QList<int> const ids = guiApp->viewIds();
3591                         int i = 0;
3592                         for (; i != ids.size(); ++i) {
3593                                 GuiView & gv = guiApp->view(ids[i]);
3594                                 if (gv.workArea(*buffer)) {
3595                                         gv.activateWindow();
3596                                         gv.setBuffer(buffer);
3597                                         break;
3598                                 }
3599                         }
3600
3601                         // If necessary, open a new window as a last resort
3602                         if (i == ids.size()) {
3603                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3604                                 lyx::dispatch(cmd);
3605                         }
3606                         break;
3607                 }
3608
3609                 case LFUN_BUFFER_NEXT:
3610                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3611                         break;
3612
3613                 case LFUN_BUFFER_MOVE_NEXT:
3614                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3615                         break;
3616
3617                 case LFUN_BUFFER_PREVIOUS:
3618                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3619                         break;
3620
3621                 case LFUN_BUFFER_MOVE_PREVIOUS:
3622                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3623                         break;
3624
3625                 case LFUN_COMMAND_EXECUTE: {
3626                         bool const show_it = cmd.argument() != "off";
3627                         // FIXME: this is a hack, "minibuffer" should not be
3628                         // hardcoded.
3629                         if (GuiToolbar * t = toolbar("minibuffer")) {
3630                                 t->setVisible(show_it);
3631                                 if (show_it && t->commandBuffer())
3632                                         t->commandBuffer()->setFocus();
3633                         }
3634                         break;
3635                 }
3636                 case LFUN_DROP_LAYOUTS_CHOICE:
3637                         d.layout_->showPopup();
3638                         break;
3639
3640                 case LFUN_MENU_OPEN:
3641                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3642                                 menu->exec(QCursor::pos());
3643                         break;
3644
3645                 case LFUN_FILE_INSERT:
3646                         insertLyXFile(cmd.argument());
3647                         break;
3648
3649                 case LFUN_FILE_INSERT_PLAINTEXT:
3650                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3651                         string const fname = to_utf8(cmd.argument());
3652                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3653                                 dr.setMessage(_("Absolute filename expected."));
3654                                 break;
3655                         }
3656                         
3657                         FileName filename(fname);
3658                         if (fname.empty()) {
3659                                 FileDialog dlg(qt_("Select file to insert"));
3660
3661                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3662                                         QStringList(qt_("All Files (*)")));
3663                                 
3664                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3665                                         dr.setMessage(_("Canceled."));
3666                                         break;
3667                                 }
3668
3669                                 filename.set(fromqstr(result.second));
3670                         }
3671
3672                         if (bv) {
3673                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3674                                 bv->dispatch(new_cmd, dr);
3675                         }
3676                         break;
3677                 }
3678
3679                 case LFUN_BUFFER_RELOAD: {
3680                         LASSERT(doc_buffer, break);
3681
3682                         int ret = 0;
3683                         if (!doc_buffer->isClean()) {
3684                                 docstring const file =
3685                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3686                                 docstring text = bformat(_("Any changes will be lost. "
3687                                         "Are you sure you want to revert to the saved version "
3688                                         "of the document %1$s?"), file);
3689                                 ret = Alert::prompt(_("Revert to saved document?"),
3690                                         text, 1, 1, _("&Revert"), _("&Cancel"));
3691                         }
3692
3693                         if (ret == 0) {
3694                                 doc_buffer->markClean();
3695                                 reloadBuffer(*doc_buffer);
3696                                 dr.forceBufferUpdate();
3697                         }
3698                         break;
3699                 }
3700
3701                 case LFUN_BUFFER_WRITE:
3702                         LASSERT(doc_buffer, break);
3703                         saveBuffer(*doc_buffer);
3704                         break;
3705
3706                 case LFUN_BUFFER_WRITE_AS:
3707                         LASSERT(doc_buffer, break);
3708                         renameBuffer(*doc_buffer, cmd.argument());
3709                         break;
3710
3711                 case LFUN_BUFFER_WRITE_ALL: {
3712                         Buffer * first = theBufferList().first();
3713                         if (!first)
3714                                 break;
3715                         message(_("Saving all documents..."));
3716                         // We cannot use a for loop as the buffer list cycles.
3717                         Buffer * b = first;
3718                         do {
3719                                 if (!b->isClean()) {
3720                                         saveBuffer(*b);
3721                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3722                                 }
3723                                 b = theBufferList().next(b);
3724                         } while (b != first);
3725                         dr.setMessage(_("All documents saved."));
3726                         break;
3727                 }
3728
3729                 case LFUN_BUFFER_CLOSE:
3730                         closeBuffer();
3731                         break;
3732
3733                 case LFUN_BUFFER_CLOSE_ALL:
3734                         closeBufferAll();
3735                         break;
3736
3737                 case LFUN_TOOLBAR_TOGGLE: {
3738                         string const name = cmd.getArg(0);
3739                         if (GuiToolbar * t = toolbar(name))
3740                                 t->toggle();
3741                         break;
3742                 }
3743
3744                 case LFUN_DIALOG_UPDATE: {
3745                         string const name = to_utf8(cmd.argument());
3746                         if (name == "prefs" || name == "document")
3747                                 updateDialog(name, string());
3748                         else if (name == "paragraph")
3749                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3750                         else if (currentBufferView()) {
3751                                 Inset * inset = currentBufferView()->editedInset(name);
3752                                 // Can only update a dialog connected to an existing inset
3753                                 if (inset) {
3754                                         // FIXME: get rid of this indirection; GuiView ask the inset
3755                                         // if he is kind enough to update itself...
3756                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3757                                         //FIXME: pass DispatchResult here?
3758                                         inset->dispatch(currentBufferView()->cursor(), fr);
3759                                 }
3760                         }
3761                         break;
3762                 }
3763
3764                 case LFUN_DIALOG_TOGGLE: {
3765                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3766                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3767                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3768                         break;
3769                 }
3770
3771                 case LFUN_DIALOG_DISCONNECT_INSET:
3772                         disconnectDialog(to_utf8(cmd.argument()));
3773                         break;
3774
3775                 case LFUN_DIALOG_HIDE: {
3776                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3777                         break;
3778                 }
3779
3780                 case LFUN_DIALOG_SHOW: {
3781                         string const name = cmd.getArg(0);
3782                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3783
3784                         if (name == "character") {
3785                                 data = freefont2string();
3786                                 if (!data.empty())
3787                                         showDialog("character", data);
3788                         } else if (name == "latexlog") {
3789                                 Buffer::LogType type;
3790                                 string const logfile = doc_buffer->logName(&type);
3791                                 switch (type) {
3792                                 case Buffer::latexlog:
3793                                         data = "latex ";
3794                                         break;
3795                                 case Buffer::buildlog:
3796                                         data = "literate ";
3797                                         break;
3798                                 }
3799                                 data += Lexer::quoteString(logfile);
3800                                 showDialog("log", data);
3801                         } else if (name == "vclog") {
3802                                 string const data = "vc " +
3803                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3804                                 showDialog("log", data);
3805                         } else if (name == "symbols") {
3806                                 data = bv->cursor().getEncoding()->name();
3807                                 if (!data.empty())
3808                                         showDialog("symbols", data);
3809                         // bug 5274
3810                         } else if (name == "prefs" && isFullScreen()) {
3811                                 lfunUiToggle("fullscreen");
3812                                 showDialog("prefs", data);
3813                         } else
3814                                 showDialog(name, data);
3815                         break;
3816                 }
3817
3818                 case LFUN_MESSAGE:
3819                         dr.setMessage(cmd.argument());
3820                         break;
3821
3822                 case LFUN_UI_TOGGLE: {
3823                         string arg = cmd.getArg(0);
3824                         if (!lfunUiToggle(arg)) {
3825                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3826                                 dr.setMessage(bformat(msg, from_utf8(arg)));
3827                         }
3828                         // Make sure the keyboard focus stays in the work area.
3829                         setFocus();
3830                         break;
3831                 }
3832
3833                 case LFUN_VIEW_SPLIT: {
3834                         LASSERT(doc_buffer, break);
3835                         string const orientation = cmd.getArg(0);
3836                         d.splitter_->setOrientation(orientation == "vertical"
3837                                 ? Qt::Vertical : Qt::Horizontal);
3838                         TabWorkArea * twa = addTabWorkArea();
3839                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3840                         setCurrentWorkArea(wa);
3841                         break;
3842                 }
3843                 case LFUN_TAB_GROUP_CLOSE:
3844                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3845                                 closeTabWorkArea(twa);
3846                                 d.current_work_area_ = 0;
3847                                 twa = d.currentTabWorkArea();
3848                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3849                                 if (twa) {
3850                                         // Make sure the work area is up to date.
3851                                         setCurrentWorkArea(twa->currentWorkArea());
3852                                 } else {
3853                                         setCurrentWorkArea(0);
3854                                 }
3855                         }
3856                         break;
3857
3858                 case LFUN_VIEW_CLOSE:
3859                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3860                                 closeWorkArea(twa->currentWorkArea());
3861                                 d.current_work_area_ = 0;
3862                                 twa = d.currentTabWorkArea();
3863                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3864                                 if (twa) {
3865                                         // Make sure the work area is up to date.
3866                                         setCurrentWorkArea(twa->currentWorkArea());
3867                                 } else {
3868                                         setCurrentWorkArea(0);
3869                                 }
3870                         }
3871                         break;
3872
3873                 case LFUN_COMPLETION_INLINE:
3874                         if (d.current_work_area_)
3875                                 d.current_work_area_->completer().showInline();
3876                         break;
3877
3878                 case LFUN_COMPLETION_POPUP:
3879                         if (d.current_work_area_)
3880                                 d.current_work_area_->completer().showPopup();
3881                         break;
3882
3883
3884                 case LFUN_COMPLETE:
3885                         if (d.current_work_area_)
3886                                 d.current_work_area_->completer().tab();
3887                         break;
3888
3889                 case LFUN_COMPLETION_CANCEL:
3890                         if (d.current_work_area_) {
3891                                 if (d.current_work_area_->completer().popupVisible())
3892                                         d.current_work_area_->completer().hidePopup();
3893                                 else
3894                                         d.current_work_area_->completer().hideInline();
3895                         }
3896                         break;
3897
3898                 case LFUN_COMPLETION_ACCEPT:
3899                         if (d.current_work_area_)
3900                                 d.current_work_area_->completer().activate();
3901                         break;
3902
3903                 case LFUN_BUFFER_ZOOM_IN:
3904                 case LFUN_BUFFER_ZOOM_OUT:
3905                         if (cmd.argument().empty()) {
3906                                 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3907                                         lyxrc.zoom += 20;
3908                                 else
3909                                         lyxrc.zoom -= 20;
3910                         } else
3911                                 lyxrc.zoom += convert<int>(cmd.argument());
3912
3913                         if (lyxrc.zoom < 10)
3914                                 lyxrc.zoom = 10;
3915
3916                         // The global QPixmapCache is used in GuiPainter to cache text
3917                         // painting so we must reset it.
3918                         QPixmapCache::clear();
3919                         guiApp->fontLoader().update();
3920                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3921                         break;
3922
3923                 case LFUN_VC_REGISTER:
3924                 case LFUN_VC_RENAME:
3925                 case LFUN_VC_COPY:
3926                 case LFUN_VC_CHECK_IN:
3927                 case LFUN_VC_CHECK_OUT:
3928                 case LFUN_VC_REPO_UPDATE:
3929                 case LFUN_VC_LOCKING_TOGGLE:
3930                 case LFUN_VC_REVERT:
3931                 case LFUN_VC_UNDO_LAST:
3932                 case LFUN_VC_COMMAND:
3933                 case LFUN_VC_COMPARE:
3934                         dispatchVC(cmd, dr);
3935                         break;
3936
3937                 case LFUN_SERVER_GOTO_FILE_ROW:
3938                         goToFileRow(to_utf8(cmd.argument()));
3939                         break;
3940
3941                 case LFUN_FORWARD_SEARCH: {
3942                         Buffer const * doc_master = doc_buffer->masterBuffer();
3943                         FileName const path(doc_master->temppath());
3944                         string const texname = doc_master->isChild(doc_buffer)
3945                                 ? DocFileName(changeExtension(
3946                                         doc_buffer->absFileName(),
3947                                                 "tex")).mangledFileName()
3948                                 : doc_buffer->latexName();
3949                         string const fulltexname = 
3950                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3951                         string const mastername =
3952                                 removeExtension(doc_master->latexName());
3953                         FileName const dviname(addName(path.absFileName(),
3954                                         addExtension(mastername, "dvi")));
3955                         FileName const pdfname(addName(path.absFileName(),
3956                                         addExtension(mastername, "pdf")));
3957                         bool const have_dvi = dviname.exists();
3958                         bool const have_pdf = pdfname.exists();
3959                         if (!have_dvi && !have_pdf) {
3960                                 dr.setMessage(_("Please, preview the document first."));
3961                                 break;
3962                         }
3963                         string outname = dviname.onlyFileName();
3964                         string command = lyxrc.forward_search_dvi;
3965                         if (!have_dvi || (have_pdf &&
3966                             pdfname.lastModified() > dviname.lastModified())) {
3967                                 outname = pdfname.onlyFileName();
3968                                 command = lyxrc.forward_search_pdf;
3969                         }
3970
3971                         DocIterator tmpcur = bv->cursor();
3972                         // Leave math first
3973                         while (tmpcur.inMathed())
3974                                 tmpcur.pop_back();
3975                         int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3976                                                                 tmpcur.paragraph().id(), tmpcur.pos());
3977                         LYXERR(Debug::ACTION, "Forward search: row:" << row
3978                                 << " id:" << tmpcur.paragraph().id());
3979                         if (!row || command.empty()) {
3980                                 dr.setMessage(_("Couldn't proceed."));
3981                                 break;
3982                         }
3983                         string texrow = convert<string>(row);
3984
3985                         command = subst(command, "$$n", texrow);
3986                         command = subst(command, "$$f", fulltexname);
3987                         command = subst(command, "$$t", texname);
3988                         command = subst(command, "$$o", outname);
3989
3990                         PathChanger p(path);
3991                         Systemcall one;
3992                         one.startscript(Systemcall::DontWait, command);
3993                         break;
3994                 }
3995
3996                 case LFUN_SPELLING_CONTINUOUSLY:
3997                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3998                         dr.screenUpdate(Update::Force | Update::FitCursor);
3999                         break;
4000
4001                 default:
4002                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4003                         // let's try that:
4004                         dispatchToBufferView(cmd, dr);
4005                         break;
4006         }
4007
4008         // Part of automatic menu appearance feature.
4009         if (isFullScreen()) {
4010                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4011                         menuBar()->hide();
4012         }
4013
4014         // Need to update bv because many LFUNs here might have destroyed it
4015         bv = currentBufferView();
4016
4017         // Clear non-empty selections
4018         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4019         if (bv) {
4020                 Cursor & cur = bv->cursor();
4021                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4022                         cur.clearSelection();
4023                 }
4024         }
4025 }
4026
4027
4028 bool GuiView::lfunUiToggle(string const & ui_component)
4029 {
4030         if (ui_component == "scrollbar") {
4031                 // hide() is of no help
4032                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4033                         Qt::ScrollBarAlwaysOff)
4034
4035                         d.current_work_area_->setVerticalScrollBarPolicy(
4036                                 Qt::ScrollBarAsNeeded);
4037                 else
4038                         d.current_work_area_->setVerticalScrollBarPolicy(
4039                                 Qt::ScrollBarAlwaysOff);
4040         } else if (ui_component == "statusbar") {
4041                 statusBar()->setVisible(!statusBar()->isVisible());
4042         } else if (ui_component == "menubar") {
4043                 menuBar()->setVisible(!menuBar()->isVisible());
4044         } else
4045         if (ui_component == "frame") {
4046                 int l, t, r, b;
4047                 getContentsMargins(&l, &t, &r, &b);
4048                 //are the frames in default state?
4049                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4050                 if (l == 0) {
4051                         setContentsMargins(-2, -2, -2, -2);
4052                 } else {
4053                         setContentsMargins(0, 0, 0, 0);
4054                 }
4055         } else
4056         if (ui_component == "fullscreen") {
4057                 toggleFullScreen();
4058         } else
4059                 return false;
4060         return true;
4061 }
4062
4063
4064 void GuiView::toggleFullScreen()
4065 {
4066         if (isFullScreen()) {
4067                 for (int i = 0; i != d.splitter_->count(); ++i)
4068                         d.tabWorkArea(i)->setFullScreen(false);
4069                 setContentsMargins(0, 0, 0, 0);
4070                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4071                 restoreLayout();
4072                 menuBar()->show();
4073                 statusBar()->show();
4074         } else {
4075                 // bug 5274
4076                 hideDialogs("prefs", 0);
4077                 for (int i = 0; i != d.splitter_->count(); ++i)
4078                         d.tabWorkArea(i)->setFullScreen(true);
4079                 setContentsMargins(-2, -2, -2, -2);
4080                 saveLayout();
4081                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4082                 if (lyxrc.full_screen_statusbar)
4083                         statusBar()->hide();
4084                 if (lyxrc.full_screen_menubar)
4085                         menuBar()->hide();
4086                 if (lyxrc.full_screen_toolbars) {
4087                         ToolbarMap::iterator end = d.toolbars_.end();
4088                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4089                                 it->second->hide();
4090                 }
4091         }
4092
4093         // give dialogs like the TOC a chance to adapt
4094         updateDialogs();
4095 }
4096
4097
4098 Buffer const * GuiView::updateInset(Inset const * inset)
4099 {
4100         if (!inset)
4101                 return 0;
4102
4103         Buffer const * inset_buffer = &(inset->buffer());
4104
4105         for (int i = 0; i != d.splitter_->count(); ++i) {
4106                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4107                 if (!wa)
4108                         continue;
4109                 Buffer const * buffer = &(wa->bufferView().buffer());
4110                 if (inset_buffer == buffer)
4111                         wa->scheduleRedraw();
4112         }
4113         return inset_buffer;
4114 }
4115
4116
4117 void GuiView::restartCursor()
4118 {
4119         /* When we move around, or type, it's nice to be able to see
4120          * the cursor immediately after the keypress.
4121          */
4122         if (d.current_work_area_)
4123                 d.current_work_area_->startBlinkingCursor();
4124
4125         // Take this occasion to update the other GUI elements.
4126         updateDialogs();
4127         updateStatusBar();
4128 }
4129
4130
4131 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4132 {
4133         if (d.current_work_area_)
4134                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4135 }
4136
4137 namespace {
4138
4139 // This list should be kept in sync with the list of insets in
4140 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4141 // dialog should have the same name as the inset.
4142 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4143 // docs in LyXAction.cpp.
4144
4145 char const * const dialognames[] = {
4146
4147 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4148 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4149 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4150 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4151 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4152 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4153 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4154 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4155
4156 char const * const * const end_dialognames =
4157         dialognames + (sizeof(dialognames) / sizeof(char *));
4158
4159 class cmpCStr {
4160 public:
4161         cmpCStr(char const * name) : name_(name) {}
4162         bool operator()(char const * other) {
4163                 return strcmp(other, name_) == 0;
4164         }
4165 private:
4166         char const * name_;
4167 };
4168
4169
4170 bool isValidName(string const & name)
4171 {
4172         return find_if(dialognames, end_dialognames,
4173                                 cmpCStr(name.c_str())) != end_dialognames;
4174 }
4175
4176 } // namespace anon
4177
4178
4179 void GuiView::resetDialogs()
4180 {
4181         // Make sure that no LFUN uses any GuiView.
4182         guiApp->setCurrentView(0);
4183         saveLayout();
4184         saveUISettings();
4185         menuBar()->clear();
4186         constructToolbars();
4187         guiApp->menus().fillMenuBar(menuBar(), this, false);
4188         d.layout_->updateContents(true);
4189         // Now update controls with current buffer.
4190         guiApp->setCurrentView(this);
4191         restoreLayout();
4192         restartCursor();
4193 }
4194
4195
4196 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4197 {
4198         if (!isValidName(name))
4199                 return 0;
4200
4201         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4202
4203         if (it != d.dialogs_.end()) {
4204                 if (hide_it)
4205                         it->second->hideView();
4206                 return it->second.get();
4207         }
4208
4209         Dialog * dialog = build(name);
4210         d.dialogs_[name].reset(dialog);
4211         if (lyxrc.allow_geometry_session)
4212                 dialog->restoreSession();
4213         if (hide_it)
4214                 dialog->hideView();
4215         return dialog;
4216 }
4217
4218
4219 void GuiView::showDialog(string const & name, string const & data,
4220         Inset * inset)
4221 {
4222         triggerShowDialog(toqstr(name), toqstr(data), inset);
4223 }
4224
4225
4226 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4227         Inset * inset)
4228 {
4229         if (d.in_show_)
4230                 return;
4231
4232         const string name = fromqstr(qname);
4233         const string data = fromqstr(qdata);
4234
4235         d.in_show_ = true;
4236         try {
4237                 Dialog * dialog = findOrBuild(name, false);
4238                 if (dialog) {
4239                         bool const visible = dialog->isVisibleView();
4240                         dialog->showData(data);
4241                         if (inset && currentBufferView())
4242                                 currentBufferView()->editInset(name, inset);
4243                         // We only set the focus to the new dialog if it was not yet
4244                         // visible in order not to change the existing previous behaviour
4245                         if (visible) {
4246                                 // activateWindow is needed for floating dockviews
4247                                 dialog->asQWidget()->raise();
4248                                 dialog->asQWidget()->activateWindow();
4249                                 dialog->asQWidget()->setFocus();
4250                         }
4251                 }
4252         }
4253         catch (ExceptionMessage const & ex) {
4254                 d.in_show_ = false;
4255                 throw ex;
4256         }
4257         d.in_show_ = false;
4258 }
4259
4260
4261 bool GuiView::isDialogVisible(string const & name) const
4262 {
4263         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4264         if (it == d.dialogs_.end())
4265                 return false;
4266         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4267 }
4268
4269
4270 void GuiView::hideDialog(string const & name, Inset * inset)
4271 {
4272         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4273         if (it == d.dialogs_.end())
4274                 return;
4275
4276         if (inset) {
4277                 if (!currentBufferView())
4278                         return;
4279                 if (inset != currentBufferView()->editedInset(name))
4280                         return;
4281         }
4282
4283         Dialog * const dialog = it->second.get();
4284         if (dialog->isVisibleView())
4285                 dialog->hideView();
4286         if (currentBufferView())
4287                 currentBufferView()->editInset(name, 0);
4288 }
4289
4290
4291 void GuiView::disconnectDialog(string const & name)
4292 {
4293         if (!isValidName(name))
4294                 return;
4295         if (currentBufferView())
4296                 currentBufferView()->editInset(name, 0);
4297 }
4298
4299
4300 void GuiView::hideAll() const
4301 {
4302         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4303         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4304
4305         for(; it != end; ++it)
4306                 it->second->hideView();
4307 }
4308
4309
4310 void GuiView::updateDialogs()
4311 {
4312         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4313         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4314
4315         for(; it != end; ++it) {
4316                 Dialog * dialog = it->second.get();
4317                 if (dialog) {
4318                         if (dialog->needBufferOpen() && !documentBufferView())
4319                                 hideDialog(fromqstr(dialog->name()), 0);
4320                         else if (dialog->isVisibleView())
4321                                 dialog->checkStatus();
4322                 }
4323         }
4324         updateToolbars();
4325         updateLayoutList();
4326 }
4327
4328 Dialog * createDialog(GuiView & lv, string const & name);
4329
4330 // will be replaced by a proper factory...
4331 Dialog * createGuiAbout(GuiView & lv);
4332 Dialog * createGuiBibtex(GuiView & lv);
4333 Dialog * createGuiChanges(GuiView & lv);
4334 Dialog * createGuiCharacter(GuiView & lv);
4335 Dialog * createGuiCitation(GuiView & lv);
4336 Dialog * createGuiCompare(GuiView & lv);
4337 Dialog * createGuiCompareHistory(GuiView & lv);
4338 Dialog * createGuiDelimiter(GuiView & lv);
4339 Dialog * createGuiDocument(GuiView & lv);
4340 Dialog * createGuiErrorList(GuiView & lv);
4341 Dialog * createGuiExternal(GuiView & lv);
4342 Dialog * createGuiGraphics(GuiView & lv);
4343 Dialog * createGuiInclude(GuiView & lv);
4344 Dialog * createGuiIndex(GuiView & lv);
4345 Dialog * createGuiListings(GuiView & lv);
4346 Dialog * createGuiLog(GuiView & lv);
4347 Dialog * createGuiMathMatrix(GuiView & lv);
4348 Dialog * createGuiNote(GuiView & lv);
4349 Dialog * createGuiParagraph(GuiView & lv);
4350 Dialog * createGuiPhantom(GuiView & lv);
4351 Dialog * createGuiPreferences(GuiView & lv);
4352 Dialog * createGuiPrint(GuiView & lv);
4353 Dialog * createGuiPrintindex(GuiView & lv);
4354 Dialog * createGuiRef(GuiView & lv);
4355 Dialog * createGuiSearch(GuiView & lv);
4356 Dialog * createGuiSearchAdv(GuiView & lv);
4357 Dialog * createGuiSendTo(GuiView & lv);
4358 Dialog * createGuiShowFile(GuiView & lv);
4359 Dialog * createGuiSpellchecker(GuiView & lv);
4360 Dialog * createGuiSymbols(GuiView & lv);
4361 Dialog * createGuiTabularCreate(GuiView & lv);
4362 Dialog * createGuiTexInfo(GuiView & lv);
4363 Dialog * createGuiToc(GuiView & lv);
4364 Dialog * createGuiThesaurus(GuiView & lv);
4365 Dialog * createGuiViewSource(GuiView & lv);
4366 Dialog * createGuiWrap(GuiView & lv);
4367 Dialog * createGuiProgressView(GuiView & lv);
4368
4369
4370
4371 Dialog * GuiView::build(string const & name)
4372 {
4373         LASSERT(isValidName(name), return 0);
4374
4375         Dialog * dialog = createDialog(*this, name);
4376         if (dialog)
4377                 return dialog;
4378
4379         if (name == "aboutlyx")
4380                 return createGuiAbout(*this);
4381         if (name == "bibtex")
4382                 return createGuiBibtex(*this);
4383         if (name == "changes")
4384                 return createGuiChanges(*this);
4385         if (name == "character")
4386                 return createGuiCharacter(*this);
4387         if (name == "citation")
4388                 return createGuiCitation(*this);
4389         if (name == "compare")
4390                 return createGuiCompare(*this);
4391         if (name == "comparehistory")
4392                 return createGuiCompareHistory(*this);
4393         if (name == "document")
4394                 return createGuiDocument(*this);
4395         if (name == "errorlist")
4396                 return createGuiErrorList(*this);
4397         if (name == "external")
4398                 return createGuiExternal(*this);
4399         if (name == "file")
4400                 return createGuiShowFile(*this);
4401         if (name == "findreplace")
4402                 return createGuiSearch(*this);
4403         if (name == "findreplaceadv")
4404                 return createGuiSearchAdv(*this);
4405         if (name == "graphics")
4406                 return createGuiGraphics(*this);
4407         if (name == "include")
4408                 return createGuiInclude(*this);
4409         if (name == "index")
4410                 return createGuiIndex(*this);
4411         if (name == "index_print")
4412                 return createGuiPrintindex(*this);
4413         if (name == "listings")
4414                 return createGuiListings(*this);
4415         if (name == "log")
4416                 return createGuiLog(*this);
4417         if (name == "mathdelimiter")
4418                 return createGuiDelimiter(*this);
4419         if (name == "mathmatrix")
4420                 return createGuiMathMatrix(*this);
4421         if (name == "note")
4422                 return createGuiNote(*this);
4423         if (name == "paragraph")
4424                 return createGuiParagraph(*this);
4425         if (name == "phantom")
4426                 return createGuiPhantom(*this);
4427         if (name == "prefs")
4428                 return createGuiPreferences(*this);
4429         if (name == "ref")
4430                 return createGuiRef(*this);
4431         if (name == "sendto")
4432                 return createGuiSendTo(*this);
4433         if (name == "spellchecker")
4434                 return createGuiSpellchecker(*this);
4435         if (name == "symbols")
4436                 return createGuiSymbols(*this);
4437         if (name == "tabularcreate")
4438                 return createGuiTabularCreate(*this);
4439         if (name == "texinfo")
4440                 return createGuiTexInfo(*this);
4441         if (name == "thesaurus")
4442                 return createGuiThesaurus(*this);
4443         if (name == "toc")
4444                 return createGuiToc(*this);
4445         if (name == "view-source")
4446                 return createGuiViewSource(*this);
4447         if (name == "wrap")
4448                 return createGuiWrap(*this);
4449         if (name == "progress")
4450                 return createGuiProgressView(*this);
4451
4452         return 0;
4453 }
4454
4455
4456 } // namespace frontend
4457 } // namespace lyx
4458
4459 #include "moc_GuiView.cpp"