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