]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Fix long standing initialisation FIXME.
[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 #include "Dialog.h"
18
19 #include <boost/assert.hpp>
20
21 using std::string;
22
23 #include "GuiView.h"
24
25 #include "GuiApplication.h"
26 #include "GuiWorkArea.h"
27 #include "GuiKeySymbol.h"
28 #include "GuiMenubar.h"
29 #include "GuiToolbar.h"
30 #include "GuiToolbars.h"
31
32 #include "qt_helpers.h"
33
34 #include "support/filetools.h"
35 #include "support/convert.h"
36 #include "support/lstrings.h"
37 #include "support/os.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 "Cursor.h"
45 #include "debug.h"
46 #include "ErrorList.h"
47 #include "FuncRequest.h"
48 #include "gettext.h"
49 #include "Intl.h"
50 #include "Layout.h"
51 #include "LyXFunc.h"
52 #include "LyX.h"
53 #include "LyXRC.h"
54 #include "MenuBackend.h"
55 #include "Paragraph.h"
56 #include "TextClass.h"
57 #include "Text.h"
58 #include "ToolbarBackend.h"
59 #include "version.h"
60
61 #include "support/lstrings.h"
62 #include "support/filetools.h" // OnlyFilename()
63 #include "support/Timeout.h"
64
65 #include <QAction>
66 #include <QApplication>
67 #include <QCloseEvent>
68 #include <QDesktopWidget>
69 #include <QDragEnterEvent>
70 #include <QDropEvent>
71 #include <QList>
72 #include <QMenu>
73 #include <QPainter>
74 #include <QPixmap>
75 #include <QPoint>
76 #include <QPushButton>
77 #include <QSettings>
78 #include <QShowEvent>
79 #include <QSplitter>
80 #include <QStackedWidget>
81 #include <QStatusBar>
82 #include <QToolBar>
83 #include <QUrl>
84
85 #include <boost/bind.hpp>
86 #include <boost/current_function.hpp>
87
88 #ifdef HAVE_SYS_TIME_H
89 # include <sys/time.h>
90 #endif
91 #ifdef HAVE_UNISTD_H
92 # include <unistd.h>
93 #endif
94
95 using std::endl;
96 using std::string;
97 using std::vector;
98
99 namespace lyx {
100
101 extern bool quitting;
102
103 namespace frontend {
104
105 using support::bformat;
106 using support::FileName;
107 using support::makeDisplayPath;
108 using support::onlyFilename;
109
110 namespace {
111
112 int const statusbar_timer_value = 3000;
113
114 class BackgroundWidget : public QWidget
115 {
116 public:
117         BackgroundWidget(QString const & file, QString const & text)
118         {
119                 splash_ = new QPixmap(file);
120                 if (!splash_) {
121                         lyxerr << "could not load splash screen: '" << fromqstr(file) << "'" << endl;
122                         return;
123                 }
124
125                 QPainter pain(splash_);
126                 pain.setPen(QColor(255, 255, 0));
127                 QFont font;
128                 // The font used to display the version info
129                 font.setStyleHint(QFont::SansSerif);
130                 font.setWeight(QFont::Bold);
131                 font.setPointSize(convert<int>(lyxrc.font_sizes[FONT_SIZE_LARGE]));
132                 pain.setFont(font);
133                 pain.drawText(260, 270, text);
134         }
135
136         void paintEvent(QPaintEvent *)
137         {
138                 if (!splash_)
139                         return;
140
141                 int x = (width() - splash_->width()) / 2;
142                 int y = (height() - splash_->height()) / 2;
143                 QPainter pain(this);
144                 pain.drawPixmap(x, y, *splash_);
145         }
146
147 private:
148         QPixmap * splash_;
149 };
150
151 } // namespace anon
152
153
154 typedef boost::shared_ptr<Dialog> DialogPtr;
155
156 struct GuiView::GuiViewPrivate
157 {
158         GuiViewPrivate()
159                 : current_work_area_(0), posx_offset(0), posy_offset(0)
160         {}
161
162         ~GuiViewPrivate()
163         {
164                 delete splitter_;
165                 delete bg_widget_;
166                 delete stack_widget_;
167                 delete menubar_;
168                 delete toolbars_;
169         }
170
171         unsigned int smallIconSize;
172         unsigned int normalIconSize;
173         unsigned int bigIconSize;
174         // static needed by "New Window"
175         static unsigned int lastIconSize;
176
177         QMenu * toolBarPopup(GuiView * parent)
178         {
179                 // FIXME: translation
180                 QMenu * menu = new QMenu(parent);
181                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
182
183                 QAction * smallIcons = new QAction(iconSizeGroup);
184                 smallIcons->setText(qt_("Small-sized icons"));
185                 smallIcons->setCheckable(true);
186                 QObject::connect(smallIcons, SIGNAL(triggered()),
187                         parent, SLOT(smallSizedIcons()));
188                 menu->addAction(smallIcons);
189
190                 QAction * normalIcons = new QAction(iconSizeGroup);
191                 normalIcons->setText(qt_("Normal-sized icons"));
192                 normalIcons->setCheckable(true);
193                 QObject::connect(normalIcons, SIGNAL(triggered()),
194                         parent, SLOT(normalSizedIcons()));
195                 menu->addAction(normalIcons);
196
197                 QAction * bigIcons = new QAction(iconSizeGroup);
198                 bigIcons->setText(qt_("Big-sized icons"));
199                 bigIcons->setCheckable(true);
200                 QObject::connect(bigIcons, SIGNAL(triggered()),
201                         parent, SLOT(bigSizedIcons()));
202                 menu->addAction(bigIcons);
203
204                 unsigned int cur = parent->iconSize().width();
205                 if ( cur == parent->d.smallIconSize)
206                         smallIcons->setChecked(true);
207                 else if (cur == parent->d.normalIconSize)
208                         normalIcons->setChecked(true);
209                 else if (cur == parent->d.bigIconSize)
210                         bigIcons->setChecked(true);
211
212                 return menu;
213         }
214
215         void initBackground()
216         {
217                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
218                 /// The text to be written on top of the pixmap
219                 QString const text = lyx_version ? QString(lyx_version) : qt_("unknown version");
220                 bg_widget_ = new BackgroundWidget(":/images/banner.png", text);
221         }
222
223         void setBackground()
224         {
225                 stack_widget_->setCurrentWidget(bg_widget_);
226                 bg_widget_->setUpdatesEnabled(true);
227         }
228
229         TabWorkArea * tabWorkArea(int i)
230         {
231                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
232         }
233
234         TabWorkArea * currentTabWorkArea()
235         {
236                 if (splitter_->count() == 1)
237                         // The first TabWorkArea is always the first one, if any.
238                         return tabWorkArea(0);
239
240                 TabWorkArea * tab_widget = 0;
241                 for (int i = 0; i != splitter_->count(); ++i) {
242                         QWidget * w = splitter_->widget(i);
243                         if (!w->hasFocus())
244                                 continue;
245                         tab_widget = dynamic_cast<TabWorkArea *>(w);
246                         if (tab_widget)
247                                 break;
248                 }
249
250                 return tab_widget;
251         }
252
253 public:
254         ///
255         string cur_title;
256
257         GuiWorkArea * current_work_area_;
258         int posx_offset;
259         int posy_offset;
260
261         QSplitter * splitter_;
262         QStackedWidget * stack_widget_;
263         BackgroundWidget * bg_widget_;
264         /// view's menubar
265         GuiMenubar * menubar_;
266         /// view's toolbars
267         GuiToolbars * toolbars_;
268         ///
269         docstring current_layout;
270
271         ///
272         std::map<std::string, Inset *> open_insets_;
273
274         ///
275         std::map<std::string, DialogPtr> dialogs_;
276         ///
277         /// flag against a race condition due to multiclicks
278         /// see bug #1119
279         bool in_show_;
280 };
281
282
283 unsigned int GuiView::GuiViewPrivate::lastIconSize = 0;
284
285
286 GuiView::GuiView(int id)
287         : QMainWindow(), LyXView(id),
288           d(*new GuiViewPrivate),
289                 quitting_by_menu_(false),
290                 autosave_timeout_(new Timeout(5000)),
291                 in_show_(false)
292 {
293         // Start autosave timer
294         if (lyxrc.autosave) {
295                 autosave_timeout_->timeout.connect(boost::bind(&GuiView::autoSave, this));
296                 autosave_timeout_->setTimeout(lyxrc.autosave * 1000);
297                 autosave_timeout_->start();
298         }
299
300         // Qt bug? signal lastWindowClosed does not work
301         setAttribute(Qt::WA_QuitOnClose, false);
302         setAttribute(Qt::WA_DeleteOnClose, true);
303
304         // hardcode here the platform specific icon size
305         d.smallIconSize = 14;   // scaling problems
306         d.normalIconSize = 20;  // ok, default
307         d.bigIconSize = 26;             // better for some math icons
308
309 #ifndef Q_WS_MACX
310         // assign an icon to main form. We do not do it under Qt/Mac,
311         // since the icon is provided in the application bundle.
312         setWindowIcon(QPixmap(":/images/lyx.png"));
313 #endif
314
315         d.splitter_ = new QSplitter;
316
317         d.initBackground();
318         LYXERR(Debug::GUI, "stack widget!");
319         d.stack_widget_ = new QStackedWidget;
320         d.stack_widget_->addWidget(d.bg_widget_);
321         d.stack_widget_->addWidget(d.splitter_);
322         setCentralWidget(d.stack_widget_);
323
324         // For Drag&Drop.
325         setAcceptDrops(true);
326
327         setMinimumSize(300, 200);
328         // GuiToolbars *must* be initialised before GuiMenubar.
329         d.toolbars_ = new GuiToolbars(*this);
330         d.toolbars_->init();
331         d.menubar_ = new GuiMenubar(this, menubackend);
332
333         statusBar()->setSizeGripEnabled(true);
334
335         QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
336                 this, SLOT(clearMessage()));
337
338         d.setBackground();
339
340         if (!lyxrc.allow_geometry_session)
341                 setGeometry(50, 50, 690, 510);
342
343         // Now take care of session management.
344         QSettings settings;
345         QString const key = "view-" + QString::number(id);
346 #ifdef Q_WS_X11
347         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
348         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
349         resize(size);
350         move(pos);
351 #else
352         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
353                 setGeometry(50, 50, 690, 510);
354 #endif
355         setIconSize(settings.value(key + "/icon_size").toSize());
356 }
357
358
359 GuiView::~GuiView()
360 {
361         delete autosave_timeout_;
362         delete &d;
363 }
364
365
366 void GuiView::close()
367 {
368         quitting_by_menu_ = true;
369         d.current_work_area_ = 0;
370         for (int i = 0; i != d.splitter_->count(); ++i) {
371                 TabWorkArea * twa = d.tabWorkArea(i);
372                 if (twa)
373                         twa->closeAll();
374         }
375         QMainWindow::close();
376         quitting_by_menu_ = false;
377 }
378
379
380 void GuiView::setFocus()
381 {
382         if (d.current_work_area_)
383                 d.current_work_area_->setFocus();
384         else
385                 QWidget::setFocus();
386 }
387
388
389 QMenu* GuiView::createPopupMenu()
390 {
391         return d.toolBarPopup(this);
392 }
393
394
395 void GuiView::showEvent(QShowEvent * e)
396 {
397         LYXERR(Debug::GUI, "Passed Geometry "
398                 << size().height() << "x" << size().width()
399                 << "+" << pos().x() << "+" << pos().y());
400
401         if (d.splitter_->count() == 0)
402                 // No work area, switch to the background widget.
403                 d.setBackground();
404
405         QMainWindow::showEvent(e);
406 }
407
408
409 void GuiView::closeEvent(QCloseEvent * close_event)
410 {
411         // we may have been called through the close window button
412         // which bypasses the LFUN machinery.
413         if (!quitting_by_menu_ && guiApp->viewCount() == 1) {
414                 if (!theBufferList().quitWriteAll()) {
415                         close_event->ignore();
416                         return;
417                 }
418         }
419
420         // Make sure that no LFUN use this close to be closed View.
421         theLyXFunc().setLyXView(0);
422         // Make sure the timer time out will not trigger a statusbar update.
423         statusbar_timer_.stop();
424
425         if (lyxrc.allow_geometry_session) {
426                 QSettings settings;
427                 QString const key = "view-" + QString::number(id());
428 #ifdef Q_WS_X11
429                 settings.setValue(key + "/pos", pos());
430                 settings.setValue(key + "/size", size());
431 #else
432                 settings.setValue(key + "/geometry", saveGeometry());
433 #endif
434                 settings.setValue(key + "/icon_size", iconSize());
435                 d.toolbars_->saveToolbarInfo();
436         }
437
438         guiApp->unregisterView(id());
439         if (guiApp->viewCount() > 0) {
440                 // Just close the window and do nothing else if this is not the
441                 // last window.
442                 close_event->accept();
443                 return;
444         }
445
446         quitting = true;
447
448         // this is the place where we leave the frontend.
449         // it is the only point at which we start quitting.
450         close_event->accept();
451         // quit the event loop
452         qApp->quit();
453 }
454
455
456 void GuiView::dragEnterEvent(QDragEnterEvent * event)
457 {
458         if (event->mimeData()->hasUrls())
459                 event->accept();
460         /// \todo Ask lyx-devel is this is enough:
461         /// if (event->mimeData()->hasFormat("text/plain"))
462         ///     event->acceptProposedAction();
463 }
464
465
466 void GuiView::dropEvent(QDropEvent* event)
467 {
468         QList<QUrl> files = event->mimeData()->urls();
469         if (files.isEmpty())
470                 return;
471
472         LYXERR(Debug::GUI, BOOST_CURRENT_FUNCTION << " got URLs!");
473         for (int i = 0; i != files.size(); ++i) {
474                 string const file = support::os::internal_path(fromqstr(
475                         files.at(i).toLocalFile()));
476                 if (!file.empty())
477                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
478         }
479 }
480
481
482 void GuiView::message(docstring const & str)
483 {
484         statusBar()->showMessage(toqstr(str));
485         statusbar_timer_.stop();
486         statusbar_timer_.start(statusbar_timer_value);
487 }
488
489
490 void GuiView::smallSizedIcons()
491 {
492         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
493 }
494
495
496 void GuiView::normalSizedIcons()
497 {
498         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
499 }
500
501
502 void GuiView::bigSizedIcons()
503 {
504         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
505 }
506
507
508 void GuiView::clearMessage()
509 {
510         if (!hasFocus())
511                 return;
512         theLyXFunc().setLyXView(this);
513         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
514         statusbar_timer_.stop();
515 }
516
517
518 void GuiView::updateWindowTitle(GuiWorkArea * wa)
519 {
520         if (wa != d.current_work_area_)
521                 return;
522         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
523         setWindowIconText(wa->windowIconText());
524 }
525
526
527 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
528 {
529         disconnectBuffer();
530         disconnectBufferView();
531         connectBufferView(wa->bufferView());
532         connectBuffer(wa->bufferView().buffer());
533         d.current_work_area_ = wa;
534         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
535                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
536         updateWindowTitle(wa);
537
538         updateToc();
539         // Buffer-dependent dialogs should be updated or
540         // hidden. This should go here because some dialogs (eg ToC)
541         // require bv_->text.
542         updateBufferDependent(true);
543         updateToolbars();
544         updateLayoutChoice(false);
545         updateStatusBar();
546 }
547
548
549 void GuiView::updateStatusBar()
550 {
551         // let the user see the explicit message
552         if (statusbar_timer_.isActive())
553                 return;
554
555         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
556 }
557
558
559 bool GuiView::hasFocus() const
560 {
561         return qApp->activeWindow() == this;
562 }
563
564
565 bool GuiView::event(QEvent * e)
566 {
567         switch (e->type())
568         {
569         // Useful debug code:
570         //case QEvent::ActivationChange:
571         //case QEvent::WindowDeactivate:
572         //case QEvent::Paint:
573         //case QEvent::Enter:
574         //case QEvent::Leave:
575         //case QEvent::HoverEnter:
576         //case QEvent::HoverLeave:
577         //case QEvent::HoverMove:
578         //case QEvent::StatusTip:
579         //case QEvent::DragEnter:
580         //case QEvent::DragLeave:
581         //case QEvent::Drop:
582         //      break;
583
584         case QEvent::WindowActivate: {
585                 guiApp->setCurrentView(*this);
586                 if (d.current_work_area_) {
587                         BufferView & bv = d.current_work_area_->bufferView();
588                         connectBufferView(bv);
589                         connectBuffer(bv.buffer());
590                         // The document structure, name and dialogs might have
591                         // changed in another view.
592                         updateBufferDependent(true);
593                 } else {
594                         setWindowTitle(qt_("LyX"));
595                         setWindowIconText(qt_("LyX"));
596                 }
597                 return QMainWindow::event(e);
598         }
599         case QEvent::ShortcutOverride: {
600                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
601                 if (!d.current_work_area_) {
602                         theLyXFunc().setLyXView(this);
603                         KeySymbol sym;
604                         setKeySymbol(&sym, ke);
605                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
606                         e->accept();
607                         return true;
608                 }
609                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
610                         KeySymbol sym;
611                         setKeySymbol(&sym, ke);
612                         d.current_work_area_->processKeySym(sym, NoModifier);
613                         e->accept();
614                         return true;
615                 }
616         }
617         default:
618                 return QMainWindow::event(e);
619         }
620 }
621
622
623 bool GuiView::focusNextPrevChild(bool /*next*/)
624 {
625         setFocus();
626         return true;
627 }
628
629
630 void GuiView::setBusy(bool yes)
631 {
632         if (d.current_work_area_) {
633                 d.current_work_area_->setUpdatesEnabled(!yes);
634                 if (yes)
635                         d.current_work_area_->stopBlinkingCursor();
636                 else
637                         d.current_work_area_->startBlinkingCursor();
638         }
639
640         if (yes)
641                 QApplication::setOverrideCursor(Qt::WaitCursor);
642         else
643                 QApplication::restoreOverrideCursor();
644 }
645
646
647 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
648 {
649         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
650
651         if (tbinfo.flags & ToolbarInfo::TOP) {
652                 if (newline)
653                         addToolBarBreak(Qt::TopToolBarArea);
654                 addToolBar(Qt::TopToolBarArea, toolBar);
655         }
656
657         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
658 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
659 #if (QT_VERSION >= 0x040202)
660                 if (newline)
661                         addToolBarBreak(Qt::BottomToolBarArea);
662 #endif
663                 addToolBar(Qt::BottomToolBarArea, toolBar);
664         }
665
666         if (tbinfo.flags & ToolbarInfo::LEFT) {
667 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
668 #if (QT_VERSION >= 0x040202)
669                 if (newline)
670                         addToolBarBreak(Qt::LeftToolBarArea);
671 #endif
672                 addToolBar(Qt::LeftToolBarArea, toolBar);
673         }
674
675         if (tbinfo.flags & ToolbarInfo::RIGHT) {
676 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
677 #if (QT_VERSION >= 0x040202)
678                 if (newline)
679                         addToolBarBreak(Qt::RightToolBarArea);
680 #endif
681                 addToolBar(Qt::RightToolBarArea, toolBar);
682         }
683
684         // The following does not work so I cannot restore to exact toolbar location
685         /*
686         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
687         toolBar->move(tbinfo.posx, tbinfo.posy);
688         */
689
690         return toolBar;
691 }
692
693
694 GuiWorkArea * GuiView::workArea(Buffer & buffer)
695 {
696         for (int i = 0; i != d.splitter_->count(); ++i) {
697                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
698                 if (wa)
699                         return wa;
700         }
701         return 0;
702 }
703
704
705 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
706 {
707         GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
708         wa->setUpdatesEnabled(false);
709
710         // Automatically create a TabWorkArea if there are none yet.
711         if (!d.splitter_->count())
712                 addTabWorkArea();
713
714         TabWorkArea * tab_widget = d.currentTabWorkArea();
715         tab_widget->addTab(wa, wa->windowTitle());
716         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
717                 tab_widget, SLOT(updateTabText(GuiWorkArea *)));
718
719         wa->bufferView().updateMetrics();
720
721         // Hide tabbar if there's only one tab.
722         tab_widget->showBar(tab_widget->count() > 1);
723         return wa;
724 }
725
726
727 void GuiView::addTabWorkArea()
728 {
729         TabWorkArea * twa = new TabWorkArea;
730         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
731                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
732         d.splitter_->addWidget(twa);
733         d.stack_widget_->setCurrentWidget(d.splitter_);
734 }
735
736
737 GuiWorkArea const * GuiView::currentWorkArea() const
738 {
739         return d.current_work_area_;
740 }
741
742
743 void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
744 {
745         BOOST_ASSERT(work_area);
746
747         // Changing work area can result from opening a file so
748         // update the toc in any case.
749         updateToc();
750
751         GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
752         d.current_work_area_ = wa;
753         for (int i = 0; i != d.splitter_->count(); ++i) {
754                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
755                         return;
756         }
757 }
758
759
760 void GuiView::removeWorkArea(GuiWorkArea * work_area)
761 {
762         BOOST_ASSERT(work_area);
763         GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
764         if (gwa == d.current_work_area_) {
765                 disconnectBuffer();
766                 disconnectBufferView();
767                 hideBufferDependent();
768                 d.current_work_area_ = 0;
769         }
770
771         // removing a work area often results from closing a file so
772         // update the toc in any case.
773         updateToc();
774
775         for (int i = 0; i != d.splitter_->count(); ++i) {
776                 TabWorkArea * twa = d.tabWorkArea(i);
777                 if (!twa->removeWorkArea(gwa))
778                         // Not found in this tab group.
779                         continue;
780
781                 // We found and removed the GuiWorkArea.
782                 if (!twa->count()) {
783                         // No more WorkAreas in this tab group, so delete it.
784                         delete twa;
785                         break;
786                 }
787
788                 if (d.current_work_area_)
789                         // This means that we are not closing the current GuiWorkArea;
790                         break;
791
792                 // Switch to the next GuiWorkArea in the found TabWorkArea.
793                 d.current_work_area_ = twa->currentWorkArea();
794                 break;
795         }
796
797         if (d.splitter_->count() == 0)
798                 // No more work area, switch to the background widget.
799                 d.setBackground();
800 }
801
802
803 void GuiView::updateLayoutChoice(bool force)
804 {
805         // Don't show any layouts without a buffer
806         if (!buffer()) {
807                 d.toolbars_->clearLayoutList();
808                 return;
809         }
810
811         // Update the layout display
812         if (d.toolbars_->updateLayoutList(buffer()->params().getTextClassPtr(), force)) {
813                 d.current_layout = buffer()->params().getTextClass().defaultLayoutName();
814         }
815
816         docstring const & layout = d.current_work_area_->bufferView().cursor().
817                 innerParagraph().layout()->name();
818
819         if (layout != d.current_layout) {
820                 d.toolbars_->setLayout(layout);
821                 d.current_layout = layout;
822         }
823 }
824
825
826 bool GuiView::isToolbarVisible(std::string const & id)
827 {
828         return d.toolbars_->visible(id);
829 }
830
831 void GuiView::updateToolbars()
832 {
833         if (d.current_work_area_) {
834                 bool const math =
835                         d.current_work_area_->bufferView().cursor().inMathed();
836                 bool const table =
837                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
838                 bool const review =
839                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
840                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
841
842                 d.toolbars_->update(math, table, review);
843         } else
844                 d.toolbars_->update(false, false, false);
845
846         // update read-only status of open dialogs.
847         checkStatus();
848 }
849
850
851 Buffer * GuiView::buffer()
852 {
853         if (d.current_work_area_)
854                 return &d.current_work_area_->bufferView().buffer();
855         return 0;
856 }
857
858
859 Buffer const * GuiView::buffer() const
860 {
861         if (d.current_work_area_)
862                 return &d.current_work_area_->bufferView().buffer();
863         return 0;
864 }
865
866
867 void GuiView::setBuffer(Buffer * newBuffer)
868 {
869         BOOST_ASSERT(newBuffer);
870         setBusy(true);
871
872         GuiWorkArea * wa = workArea(*newBuffer);
873         if (wa == 0) {
874                 updateLabels(*newBuffer->masterBuffer());
875                 wa = addWorkArea(*newBuffer);
876         } else {
877                 //Disconnect the old buffer...there's no new one.
878                 disconnectBuffer();
879         }
880         connectBuffer(*newBuffer);
881         connectBufferView(wa->bufferView());
882         setCurrentWorkArea(wa);
883
884         setBusy(false);
885 }
886
887
888 Buffer * GuiView::loadLyXFile(FileName const & filename, bool tolastfiles)
889 {
890         setBusy(true);
891
892         Buffer * newBuffer = checkAndLoadLyXFile(filename);
893
894         if (!newBuffer) {
895                 message(_("Document not loaded."));
896                 updateStatusBar();
897                 setBusy(false);
898                 return 0;
899         }
900
901         GuiWorkArea * wa = workArea(*newBuffer);
902         if (wa == 0)
903                 wa = addWorkArea(*newBuffer);
904
905         // scroll to the position when the file was last closed
906         if (lyxrc.use_lastfilepos) {
907                 LastFilePosSection::FilePos filepos =
908                         LyX::ref().session().lastFilePos().load(filename);
909                 // if successfully move to pit (returned par_id is not zero),
910                 // update metrics and reset font
911                 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
912         }
913
914         if (tolastfiles)
915                 LyX::ref().session().lastFiles().add(filename);
916
917         setBusy(false);
918         return newBuffer;
919 }
920
921
922 void GuiView::connectBuffer(Buffer & buf)
923 {
924         buf.setGuiDelegate(this);
925 }
926
927
928 void GuiView::disconnectBuffer()
929 {
930         if (d.current_work_area_)
931                 d.current_work_area_->bufferView().setGuiDelegate(0);
932 }
933
934
935 void GuiView::connectBufferView(BufferView & bv)
936 {
937         bv.setGuiDelegate(this);
938 }
939
940
941 void GuiView::disconnectBufferView()
942 {
943         if (d.current_work_area_)
944                 d.current_work_area_->bufferView().setGuiDelegate(0);
945 }
946
947
948 void GuiView::errors(string const & error_type)
949 {
950         ErrorList & el = buffer()->errorList(error_type);
951         if (!el.empty())
952                 showDialog("errorlist", error_type);
953 }
954
955
956 void GuiView::updateDialog(string const & name, string const & data)
957 {
958         if (!isDialogVisible(name))
959                 return;
960
961         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
962         if (it == d.dialogs_.end())
963                 return;
964
965         Dialog * const dialog = it->second.get();
966         if (dialog->isVisibleView())
967                 dialog->updateData(data);
968 }
969
970
971 BufferView * GuiView::view()
972 {
973         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
974 }
975
976
977 void GuiView::updateToc()
978 {
979         updateDialog("toc", "");
980 }
981
982
983 void GuiView::updateEmbeddedFiles()
984 {
985         updateDialog("embedding", "");
986 }
987
988
989 void GuiView::autoSave()
990 {
991         LYXERR(Debug::INFO, "Running autoSave()");
992
993         if (buffer())
994                 view()->buffer().autoSave();
995 }
996
997
998 void GuiView::resetAutosaveTimers()
999 {
1000         if (lyxrc.autosave)
1001                 autosave_timeout_->restart();
1002 }
1003
1004
1005 void GuiView::dispatch(FuncRequest const & cmd)
1006 {
1007         switch(cmd.action) {
1008                 case LFUN_BUFFER_SWITCH:
1009                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1010                         break;
1011
1012                 case LFUN_COMMAND_EXECUTE: {
1013                         bool const show_it = cmd.argument() != "off";
1014                         d.toolbars_->showCommandBuffer(show_it);
1015                         break;
1016                 }
1017                 case LFUN_DROP_LAYOUTS_CHOICE:
1018                         d.toolbars_->openLayoutList();
1019                         break;
1020
1021                 case LFUN_MENU_OPEN:
1022                         d.menubar_->openByName(toqstr(cmd.argument()));
1023                         break;
1024
1025                 case LFUN_TOOLBAR_TOGGLE: {
1026                         string const name = cmd.getArg(0);
1027                         bool const allowauto = cmd.getArg(1) == "allowauto";
1028                         // it is possible to get current toolbar status like this,...
1029                         // but I decide to obey the order of ToolbarBackend::flags
1030                         // and disregard real toolbar status.
1031                         // toolbars_->saveToolbarInfo();
1032                         //
1033                         // toggle state on/off/auto
1034                         d.toolbars_->toggleToolbarState(name, allowauto);
1035                         // update toolbar
1036                         updateToolbars();
1037
1038                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1039                         if (!tbi) {
1040                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1041                                 break;
1042                         }
1043                         docstring state;
1044                         if (tbi->flags & ToolbarInfo::ON)
1045                                 state = _("on");
1046                         else if (tbi->flags & ToolbarInfo::OFF)
1047                                 state = _("off");
1048                         else if (tbi->flags & ToolbarInfo::AUTO)
1049                                 state = _("auto");
1050
1051                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1052                                            _(tbi->gui_name), state));
1053                         break;
1054                 }
1055
1056                 default:
1057                         theLyXFunc().setLyXView(this);
1058                         lyx::dispatch(cmd);
1059         }
1060 }
1061
1062
1063 Buffer const * GuiView::updateInset(Inset const * inset)
1064 {
1065         if (!d.current_work_area_)
1066                 return 0;
1067
1068         if (inset)
1069                 d.current_work_area_->scheduleRedraw();
1070
1071         return &d.current_work_area_->bufferView().buffer();
1072 }
1073
1074
1075 void GuiView::restartCursor()
1076 {
1077         /* When we move around, or type, it's nice to be able to see
1078          * the cursor immediately after the keypress.
1079          */
1080         if (d.current_work_area_)
1081                 d.current_work_area_->startBlinkingCursor();
1082 }
1083
1084
1085 Dialog * GuiView::find_or_build(string const & name)
1086 {
1087         if (!isValidName(name))
1088                 return 0;
1089
1090         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1091
1092         if (it != d.dialogs_.end())
1093                 return it->second.get();
1094
1095         d.dialogs_[name].reset(build(name));
1096         return d.dialogs_[name].get();
1097 }
1098
1099
1100 void GuiView::showDialog(string const & name, string const & data,
1101         Inset * inset)
1102 {
1103         if (in_show_)
1104                 return;
1105
1106         in_show_ = true;
1107         Dialog * dialog = find_or_build(name);
1108         if (dialog) {
1109                 dialog->showData(data);
1110                 if (inset)
1111                         d.open_insets_[name] = inset;
1112         }
1113         in_show_ = false;
1114 }
1115
1116
1117 bool GuiView::isDialogVisible(string const & name) const
1118 {
1119         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1120         if (it == d.dialogs_.end())
1121                 return false;
1122         return it->second.get()->isVisibleView();
1123 }
1124
1125
1126 void GuiView::hideDialog(string const & name, Inset * inset)
1127 {
1128         // Don't send the signal if we are quitting, because on MSVC it is
1129         // destructed before the cut stack in CutAndPaste.cpp, and this method
1130         // is called from some inset destructor if the cut stack is not empty
1131         // on exit.
1132         if (quitting)
1133                 return;
1134
1135         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1136         if (it == d.dialogs_.end())
1137                 return;
1138
1139         if (inset && inset != getOpenInset(name))
1140                 return;
1141
1142         Dialog * const dialog = it->second.get();
1143         if (dialog->isVisibleView())
1144                 dialog->hide();
1145         d.open_insets_[name] = 0;
1146 }
1147
1148
1149 void GuiView::disconnectDialog(string const & name)
1150 {
1151         if (!isValidName(name))
1152                 return;
1153
1154         if (d.open_insets_.find(name) != d.open_insets_.end())
1155                 d.open_insets_[name] = 0;
1156 }
1157
1158
1159 Inset * GuiView::getOpenInset(string const & name) const
1160 {
1161         if (!isValidName(name))
1162                 return 0;
1163
1164         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1165         return it == d.open_insets_.end() ? 0 : it->second;
1166 }
1167
1168
1169 void GuiView::hideAll() const
1170 {
1171         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1172         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1173
1174         for(; it != end; ++it)
1175                 it->second->hide();
1176 }
1177
1178
1179 void GuiView::hideBufferDependent() const
1180 {
1181         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1182         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1183
1184         for(; it != end; ++it) {
1185                 Dialog * dialog = it->second.get();
1186                 if (dialog->isBufferDependent())
1187                         dialog->hide();
1188         }
1189 }
1190
1191
1192 void GuiView::updateBufferDependent(bool switched) const
1193 {
1194         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1195         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1196
1197         for(; it != end; ++it) {
1198                 Dialog * dialog = it->second.get();
1199                 if (switched && dialog->isBufferDependent()) {
1200                         if (dialog->isVisibleView() && dialog->initialiseParams(""))
1201                                 dialog->updateView();
1202                         else
1203                                 dialog->hide();
1204                 } else {
1205                         // A bit clunky, but the dialog will request
1206                         // that the kernel provides it with the necessary
1207                         // data.
1208                         dialog->slotRestore();
1209                 }
1210         }
1211 }
1212
1213
1214 void GuiView::redrawDialog() const
1215 {
1216         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1217         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1218
1219         for(; it != end; ++it)
1220                 it->second->redraw();
1221 }
1222
1223
1224 void GuiView::checkStatus()
1225 {
1226         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1227         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1228
1229         for(; it != end; ++it) {
1230                 Dialog * const dialog = it->second.get();
1231                 if (dialog && dialog->isVisibleView())
1232                         dialog->checkStatus();
1233         }
1234 }
1235
1236
1237 namespace {
1238
1239 // This list should be kept in sync with the list of insets in
1240 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1241 // dialog should have the same name as the inset.
1242
1243 char const * const dialognames[] = {
1244 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1245 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1246 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1247 "mathdelimiter", "mathmatrix", "note", "paragraph",
1248 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1249
1250 #ifdef HAVE_LIBAIKSAURUS
1251 "thesaurus",
1252 #endif
1253
1254 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1255
1256 char const * const * const end_dialognames =
1257         dialognames + (sizeof(dialognames) / sizeof(char *));
1258
1259 class cmpCStr {
1260 public:
1261         cmpCStr(char const * name) : name_(name) {}
1262         bool operator()(char const * other) {
1263                 return strcmp(other, name_) == 0;
1264         }
1265 private:
1266         char const * name_;
1267 };
1268
1269
1270 } // namespace anon
1271
1272 // will be replaced by a proper factory...
1273 Dialog * createGuiAbout(LyXView & lv);
1274 Dialog * createGuiBibitem(LyXView & lv);
1275 Dialog * createGuiBibtex(LyXView & lv);
1276 Dialog * createGuiBox(LyXView & lv);
1277 Dialog * createGuiBranch(LyXView & lv);
1278 Dialog * createGuiChanges(LyXView & lv);
1279 Dialog * createGuiCharacter(LyXView & lv);
1280 Dialog * createGuiCitation(LyXView & lv);
1281 Dialog * createGuiDelimiter(LyXView & lv);
1282 Dialog * createGuiDocument(LyXView & lv);
1283 Dialog * createGuiErrorList(LyXView & lv);
1284 Dialog * createGuiERT(LyXView & lv);
1285 Dialog * createGuiExternal(LyXView & lv);
1286 Dialog * createGuiFloat(LyXView & lv);
1287 Dialog * createGuiGraphics(LyXView & lv);
1288 Dialog * createGuiInclude(LyXView & lv);
1289 Dialog * createGuiIndex(LyXView & lv);
1290 Dialog * createGuiLabel(LyXView & lv);
1291 Dialog * createGuiListings(LyXView & lv);
1292 Dialog * createGuiLog(LyXView & lv);
1293 Dialog * createGuiMathMatrix(LyXView & lv);
1294 Dialog * createGuiNomenclature(LyXView & lv);
1295 Dialog * createGuiNote(LyXView & lv);
1296 Dialog * createGuiParagraph(LyXView & lv);
1297 Dialog * createGuiPreferences(LyXView & lv);
1298 Dialog * createGuiPrint(LyXView & lv);
1299 Dialog * createGuiRef(LyXView & lv);
1300 Dialog * createGuiSearch(LyXView & lv);
1301 Dialog * createGuiSendTo(LyXView & lv);
1302 Dialog * createGuiShowFile(LyXView & lv);
1303 Dialog * createGuiSpellchecker(LyXView & lv);
1304 Dialog * createGuiTabularCreate(LyXView & lv);
1305 Dialog * createGuiTabular(LyXView & lv);
1306 Dialog * createGuiTexInfo(LyXView & lv);
1307 Dialog * createGuiToc(LyXView & lv);
1308 Dialog * createGuiThesaurus(LyXView & lv);
1309 Dialog * createGuiHyperlink(LyXView & lv);
1310 Dialog * createGuiVSpace(LyXView & lv);
1311 Dialog * createGuiViewSource(LyXView & lv);
1312 Dialog * createGuiWrap(LyXView & lv);
1313
1314
1315 bool GuiView::isValidName(string const & name) const
1316 {
1317         return std::find_if(dialognames, end_dialognames,
1318                             cmpCStr(name.c_str())) != end_dialognames;
1319 }
1320
1321
1322 Dialog * GuiView::build(string const & name)
1323 {
1324         BOOST_ASSERT(isValidName(name));
1325
1326         if (name == "aboutlyx")
1327                 return createGuiAbout(*this);
1328         if (name == "bibitem")
1329                 return createGuiBibitem(*this);
1330         if (name == "bibtex")
1331                 return createGuiBibtex(*this);
1332         if (name == "box")
1333                 return createGuiBox(*this);
1334         if (name == "branch")
1335                 return createGuiBranch(*this);
1336         if (name == "changes")
1337                 return createGuiChanges(*this);
1338         if (name == "character")
1339                 return createGuiCharacter(*this);
1340         if (name == "citation")
1341                 return createGuiCitation(*this);
1342         if (name == "document")
1343                 return createGuiDocument(*this);
1344         if (name == "errorlist")
1345                 return createGuiErrorList(*this);
1346         if (name == "ert")
1347                 return createGuiERT(*this);
1348         if (name == "external")
1349                 return createGuiExternal(*this);
1350         if (name == "file")
1351                 return createGuiShowFile(*this);
1352         if (name == "findreplace")
1353                 return createGuiSearch(*this);
1354         if (name == "float")
1355                 return createGuiFloat(*this);
1356         if (name == "graphics")
1357                 return createGuiGraphics(*this);
1358         if (name == "include")
1359                 return createGuiInclude(*this);
1360         if (name == "index")
1361                 return createGuiIndex(*this);
1362         if (name == "nomenclature")
1363                 return createGuiNomenclature(*this);
1364         if (name == "label")
1365                 return createGuiLabel(*this);
1366         if (name == "log")
1367                 return createGuiLog(*this);
1368         if (name == "view-source")
1369                 return createGuiViewSource(*this);
1370         if (name == "mathdelimiter")
1371                 return createGuiDelimiter(*this);
1372         if (name == "mathmatrix")
1373                 return createGuiMathMatrix(*this);
1374         if (name == "note")
1375                 return createGuiNote(*this);
1376         if (name == "paragraph")
1377                 return createGuiParagraph(*this);
1378         if (name == "prefs")
1379                 return createGuiPreferences(*this);
1380         if (name == "print")
1381                 return createGuiPrint(*this);
1382         if (name == "ref")
1383                 return createGuiRef(*this);
1384         if (name == "sendto")
1385                 return createGuiSendTo(*this);
1386         if (name == "spellchecker")
1387                 return createGuiSpellchecker(*this);
1388         if (name == "tabular")
1389                 return createGuiTabular(*this);
1390         if (name == "tabularcreate")
1391                 return createGuiTabularCreate(*this);
1392         if (name == "texinfo")
1393                 return createGuiTexInfo(*this);
1394 #ifdef HAVE_LIBAIKSAURUS
1395         if (name == "thesaurus")
1396                 return createGuiThesaurus(*this);
1397 #endif
1398         if (name == "toc")
1399                 return createGuiToc(*this);
1400         if (name == "href")
1401                 return createGuiHyperlink(*this);
1402         if (name == "vspace")
1403                 return createGuiVSpace(*this);
1404         if (name == "wrap")
1405                 return createGuiWrap(*this);
1406         if (name == "listings")
1407                 return createGuiListings(*this);
1408
1409         return 0;
1410 }
1411
1412
1413 } // namespace frontend
1414 } // namespace lyx
1415
1416 #include "GuiView_moc.cpp"