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