]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
rev 21667: Forgot this!
[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         init();
328 }
329
330
331 GuiView::~GuiView()
332 {
333         delete autosave_timeout_;
334         delete &d;
335 }
336
337
338 void GuiView::close()
339 {
340         quitting_by_menu_ = true;
341         d.current_work_area_ = 0;
342         for (int i = 0; i != d.splitter_->count(); ++i) {
343                 TabWorkArea * twa = d.tabWorkArea(i);
344                 if (twa)
345                         twa->closeAll();
346         }
347         QMainWindow::close();
348         quitting_by_menu_ = false;
349 }
350
351
352 void GuiView::setFocus()
353 {
354         if (d.current_work_area_)
355                 d.current_work_area_->setFocus();
356         else
357                 QWidget::setFocus();
358 }
359
360
361 QMenu* GuiView::createPopupMenu()
362 {
363         return d.toolBarPopup(this);
364 }
365
366
367 void GuiView::init()
368 {
369         setMinimumSize(300, 200);
370         // GuiToolbars *must* be initialised before GuiMenubar.
371         d.toolbars_ = new GuiToolbars(*this);
372         // FIXME: GuiToolbars::init() cannot be integrated in the ctor
373         // because LyXFunc::getStatus() needs a properly initialized
374         // GuiToolbars object (for LFUN_TOOLBAR_TOGGLE).
375         d.toolbars_->init();
376         d.menubar_ = new GuiMenubar(this, menubackend);
377
378         statusBar()->setSizeGripEnabled(true);
379
380         QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
381                 this, SLOT(clearMessage()));
382
383         d.setBackground();
384
385         if (!lyxrc.allow_geometry_session)
386                 setGeometry(50, 50, 690, 510);
387
388         // Now take care of session management.
389         QSettings settings;
390         QString const key = "view-" + QString::number(id());
391 #ifdef Q_WS_X11
392         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
393         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
394         resize(size);
395         move(pos);
396 #else
397         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
398                 setGeometry(50, 50, 690, 510);
399 #endif
400         setIconSize(settings.value(key + "/icon_size").toSize());
401 }
402
403
404 void GuiView::showEvent(QShowEvent * e)
405 {
406         LYXERR(Debug::GUI, "Passed Geometry "
407                 << size().height() << "x" << size().width()
408                 << "+" << pos().x() << "+" << pos().y());
409
410         if (d.splitter_->count() == 0)
411                 // No work area, switch to the background widget.
412                 d.setBackground();
413
414         QMainWindow::showEvent(e);
415 }
416
417
418 void GuiView::closeEvent(QCloseEvent * close_event)
419 {
420         // we may have been called through the close window button
421         // which bypasses the LFUN machinery.
422         if (!quitting_by_menu_ && guiApp->viewCount() == 1) {
423                 if (!theBufferList().quitWriteAll()) {
424                         close_event->ignore();
425                         return;
426                 }
427         }
428
429         // Make sure that no LFUN use this close to be closed View.
430         theLyXFunc().setLyXView(0);
431         // Make sure the timer time out will not trigger a statusbar update.
432         statusbar_timer_.stop();
433
434         if (lyxrc.allow_geometry_session) {
435                 QSettings settings;
436                 QString const key = "view-" + QString::number(id());
437 #ifdef Q_WS_X11
438                 settings.setValue(key + "/pos", pos());
439                 settings.setValue(key + "/size", size());
440 #else
441                 settings.setValue(key + "/geometry", saveGeometry());
442 #endif
443                 settings.setValue(key + "/icon_size", iconSize());
444                 d.toolbars_->saveToolbarInfo();
445         }
446
447         guiApp->unregisterView(id());
448         if (guiApp->viewCount() > 0) {
449                 // Just close the window and do nothing else if this is not the
450                 // last window.
451                 close_event->accept();
452                 return;
453         }
454
455         quitting = true;
456
457         // this is the place where we leave the frontend.
458         // it is the only point at which we start quitting.
459         close_event->accept();
460         // quit the event loop
461         qApp->quit();
462 }
463
464
465 void GuiView::dragEnterEvent(QDragEnterEvent * event)
466 {
467         if (event->mimeData()->hasUrls())
468                 event->accept();
469         /// \todo Ask lyx-devel is this is enough:
470         /// if (event->mimeData()->hasFormat("text/plain"))
471         ///     event->acceptProposedAction();
472 }
473
474
475 void GuiView::dropEvent(QDropEvent* event)
476 {
477         QList<QUrl> files = event->mimeData()->urls();
478         if (files.isEmpty())
479                 return;
480
481         LYXERR(Debug::GUI, BOOST_CURRENT_FUNCTION << " got URLs!");
482         for (int i = 0; i != files.size(); ++i) {
483                 string const file = support::os::internal_path(fromqstr(
484                         files.at(i).toLocalFile()));
485                 if (!file.empty())
486                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
487         }
488 }
489
490
491 void GuiView::message(docstring const & str)
492 {
493         statusBar()->showMessage(toqstr(str));
494         statusbar_timer_.stop();
495         statusbar_timer_.start(statusbar_timer_value);
496 }
497
498
499 void GuiView::smallSizedIcons()
500 {
501         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
502 }
503
504
505 void GuiView::normalSizedIcons()
506 {
507         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
508 }
509
510
511 void GuiView::bigSizedIcons()
512 {
513         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
514 }
515
516
517 void GuiView::clearMessage()
518 {
519         if (!hasFocus())
520                 return;
521         theLyXFunc().setLyXView(this);
522         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
523         statusbar_timer_.stop();
524 }
525
526
527 void GuiView::updateWindowTitle(GuiWorkArea * wa)
528 {
529         if (wa != d.current_work_area_)
530                 return;
531         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
532         setWindowIconText(wa->windowIconText());
533 }
534
535
536 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
537 {
538         disconnectBuffer();
539         disconnectBufferView();
540         connectBufferView(wa->bufferView());
541         connectBuffer(wa->bufferView().buffer());
542         d.current_work_area_ = wa;
543         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
544                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
545         updateWindowTitle(wa);
546
547         updateToc();
548         // Buffer-dependent dialogs should be updated or
549         // hidden. This should go here because some dialogs (eg ToC)
550         // require bv_->text.
551         updateBufferDependent(true);
552         updateToolbars();
553         updateLayoutChoice(false);
554         updateStatusBar();
555 }
556
557
558 void GuiView::updateStatusBar()
559 {
560         // let the user see the explicit message
561         if (statusbar_timer_.isActive())
562                 return;
563
564         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
565 }
566
567
568 bool GuiView::hasFocus() const
569 {
570         return qApp->activeWindow() == this;
571 }
572
573
574 bool GuiView::event(QEvent * e)
575 {
576         switch (e->type())
577         {
578         // Useful debug code:
579         //case QEvent::ActivationChange:
580         //case QEvent::WindowDeactivate:
581         //case QEvent::Paint:
582         //case QEvent::Enter:
583         //case QEvent::Leave:
584         //case QEvent::HoverEnter:
585         //case QEvent::HoverLeave:
586         //case QEvent::HoverMove:
587         //case QEvent::StatusTip:
588         //case QEvent::DragEnter:
589         //case QEvent::DragLeave:
590         //case QEvent::Drop:
591         //      break;
592
593         case QEvent::WindowActivate: {
594                 guiApp->setCurrentView(*this);
595                 if (d.current_work_area_) {
596                         BufferView & bv = d.current_work_area_->bufferView();
597                         connectBufferView(bv);
598                         connectBuffer(bv.buffer());
599                         // The document structure, name and dialogs might have
600                         // changed in another view.
601                         updateBufferDependent(true);
602                 } else {
603                         setWindowTitle(qt_("LyX"));
604                         setWindowIconText(qt_("LyX"));
605                 }
606                 return QMainWindow::event(e);
607         }
608         case QEvent::ShortcutOverride: {
609                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
610                 if (!d.current_work_area_) {
611                         theLyXFunc().setLyXView(this);
612                         KeySymbol sym;
613                         setKeySymbol(&sym, ke);
614                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
615                         e->accept();
616                         return true;
617                 }
618                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
619                         KeySymbol sym;
620                         setKeySymbol(&sym, ke);
621                         d.current_work_area_->processKeySym(sym, NoModifier);
622                         e->accept();
623                         return true;
624                 }
625         }
626         default:
627                 return QMainWindow::event(e);
628         }
629 }
630
631
632 bool GuiView::focusNextPrevChild(bool /*next*/)
633 {
634         setFocus();
635         return true;
636 }
637
638
639 void GuiView::setBusy(bool yes)
640 {
641         if (d.current_work_area_) {
642                 d.current_work_area_->setUpdatesEnabled(!yes);
643                 if (yes)
644                         d.current_work_area_->stopBlinkingCursor();
645                 else
646                         d.current_work_area_->startBlinkingCursor();
647         }
648
649         if (yes)
650                 QApplication::setOverrideCursor(Qt::WaitCursor);
651         else
652                 QApplication::restoreOverrideCursor();
653 }
654
655
656 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
657 {
658         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
659
660         if (tbinfo.flags & ToolbarInfo::TOP) {
661                 if (newline)
662                         addToolBarBreak(Qt::TopToolBarArea);
663                 addToolBar(Qt::TopToolBarArea, toolBar);
664         }
665
666         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
667 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
668 #if (QT_VERSION >= 0x040202)
669                 if (newline)
670                         addToolBarBreak(Qt::BottomToolBarArea);
671 #endif
672                 addToolBar(Qt::BottomToolBarArea, toolBar);
673         }
674
675         if (tbinfo.flags & ToolbarInfo::LEFT) {
676 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
677 #if (QT_VERSION >= 0x040202)
678                 if (newline)
679                         addToolBarBreak(Qt::LeftToolBarArea);
680 #endif
681                 addToolBar(Qt::LeftToolBarArea, toolBar);
682         }
683
684         if (tbinfo.flags & ToolbarInfo::RIGHT) {
685 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
686 #if (QT_VERSION >= 0x040202)
687                 if (newline)
688                         addToolBarBreak(Qt::RightToolBarArea);
689 #endif
690                 addToolBar(Qt::RightToolBarArea, toolBar);
691         }
692
693         // The following does not work so I cannot restore to exact toolbar location
694         /*
695         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
696         toolBar->move(tbinfo.posx, tbinfo.posy);
697         */
698
699         return toolBar;
700 }
701
702
703 GuiWorkArea * GuiView::workArea(Buffer & buffer)
704 {
705         for (int i = 0; i != d.splitter_->count(); ++i) {
706                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
707                 if (wa)
708                         return wa;
709         }
710         return 0;
711 }
712
713
714 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
715 {
716         GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
717         wa->setUpdatesEnabled(false);
718
719         // Automatically create a TabWorkArea if there are none yet.
720         if (!d.splitter_->count())
721                 addTabWorkArea();
722
723         TabWorkArea * tab_widget = d.currentTabWorkArea();
724         tab_widget->addTab(wa, wa->windowTitle());
725         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
726                 tab_widget, SLOT(updateTabText(GuiWorkArea *)));
727
728         wa->bufferView().updateMetrics();
729
730         // Hide tabbar if there's only one tab.
731         tab_widget->showBar(tab_widget->count() > 1);
732         return wa;
733 }
734
735
736 void GuiView::addTabWorkArea()
737 {
738         TabWorkArea * twa = new TabWorkArea;
739         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
740                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
741         d.splitter_->addWidget(twa);
742         d.stack_widget_->setCurrentWidget(d.splitter_);
743 }
744
745
746 GuiWorkArea const * GuiView::currentWorkArea() const
747 {
748         return d.current_work_area_;
749 }
750
751
752 void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
753 {
754         BOOST_ASSERT(work_area);
755
756         // Changing work area can result from opening a file so
757         // update the toc in any case.
758         updateToc();
759
760         GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
761         d.current_work_area_ = wa;
762         for (int i = 0; i != d.splitter_->count(); ++i) {
763                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
764                         return;
765         }
766 }
767
768
769 void GuiView::removeWorkArea(GuiWorkArea * work_area)
770 {
771         BOOST_ASSERT(work_area);
772         GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
773         if (gwa == d.current_work_area_) {
774                 disconnectBuffer();
775                 disconnectBufferView();
776                 hideBufferDependent();
777                 d.current_work_area_ = 0;
778         }
779
780         // removing a work area often results from closing a file so
781         // update the toc in any case.
782         updateToc();
783
784         for (int i = 0; i != d.splitter_->count(); ++i) {
785                 TabWorkArea * twa = d.tabWorkArea(i);
786                 if (!twa->removeWorkArea(gwa))
787                         // Not found in this tab group.
788                         continue;
789
790                 // We found and removed the GuiWorkArea.
791                 if (!twa->count()) {
792                         // No more WorkAreas in this tab group, so delete it.
793                         delete twa;
794                         break;
795                 }
796
797                 if (d.current_work_area_)
798                         // This means that we are not closing the current GuiWorkArea;
799                         break;
800
801                 // Switch to the next GuiWorkArea in the found TabWorkArea.
802                 d.current_work_area_ = twa->currentWorkArea();
803                 break;
804         }
805
806         if (d.splitter_->count() == 0)
807                 // No more work area, switch to the background widget.
808                 d.setBackground();
809 }
810
811
812 void GuiView::updateLayoutChoice(bool force)
813 {
814         // Don't show any layouts without a buffer
815         if (!buffer()) {
816                 d.toolbars_->clearLayoutList();
817                 return;
818         }
819
820         // Update the layout display
821         if (d.toolbars_->updateLayoutList(buffer()->params().getTextClassPtr(), force)) {
822                 d.current_layout = buffer()->params().getTextClass().defaultLayoutName();
823         }
824
825         docstring const & layout = d.current_work_area_->bufferView().cursor().
826                 innerParagraph().layout()->name();
827
828         if (layout != d.current_layout) {
829                 d.toolbars_->setLayout(layout);
830                 d.current_layout = layout;
831         }
832 }
833
834
835 bool GuiView::isToolbarVisible(std::string const & id)
836 {
837         return d.toolbars_->visible(id);
838 }
839
840 void GuiView::updateToolbars()
841 {
842         if (d.current_work_area_) {
843                 bool const math =
844                         d.current_work_area_->bufferView().cursor().inMathed();
845                 bool const table =
846                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
847                 bool const review =
848                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
849                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
850
851                 d.toolbars_->update(math, table, review);
852         } else
853                 d.toolbars_->update(false, false, false);
854
855         // update read-only status of open dialogs.
856         checkStatus();
857 }
858
859
860 Buffer * GuiView::buffer()
861 {
862         if (d.current_work_area_)
863                 return &d.current_work_area_->bufferView().buffer();
864         return 0;
865 }
866
867
868 Buffer const * GuiView::buffer() const
869 {
870         if (d.current_work_area_)
871                 return &d.current_work_area_->bufferView().buffer();
872         return 0;
873 }
874
875
876 void GuiView::setBuffer(Buffer * newBuffer)
877 {
878         BOOST_ASSERT(newBuffer);
879         setBusy(true);
880
881         GuiWorkArea * wa = workArea(*newBuffer);
882         if (wa == 0) {
883                 updateLabels(*newBuffer->masterBuffer());
884                 wa = addWorkArea(*newBuffer);
885         } else {
886                 //Disconnect the old buffer...there's no new one.
887                 disconnectBuffer();
888         }
889         connectBuffer(*newBuffer);
890         connectBufferView(wa->bufferView());
891         setCurrentWorkArea(wa);
892
893         setBusy(false);
894 }
895
896
897 Buffer * GuiView::loadLyXFile(FileName const & filename, bool tolastfiles)
898 {
899         setBusy(true);
900
901         Buffer * newBuffer = checkAndLoadLyXFile(filename);
902
903         if (!newBuffer) {
904                 message(_("Document not loaded."));
905                 updateStatusBar();
906                 setBusy(false);
907                 return 0;
908         }
909
910         GuiWorkArea * wa = workArea(*newBuffer);
911         if (wa == 0)
912                 wa = addWorkArea(*newBuffer);
913
914         // scroll to the position when the file was last closed
915         if (lyxrc.use_lastfilepos) {
916                 LastFilePosSection::FilePos filepos =
917                         LyX::ref().session().lastFilePos().load(filename);
918                 // if successfully move to pit (returned par_id is not zero),
919                 // update metrics and reset font
920                 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
921         }
922
923         if (tolastfiles)
924                 LyX::ref().session().lastFiles().add(filename);
925
926         setBusy(false);
927         return newBuffer;
928 }
929
930
931 void GuiView::connectBuffer(Buffer & buf)
932 {
933         buf.setGuiDelegate(this);
934 }
935
936
937 void GuiView::disconnectBuffer()
938 {
939         if (d.current_work_area_)
940                 d.current_work_area_->bufferView().setGuiDelegate(0);
941 }
942
943
944 void GuiView::connectBufferView(BufferView & bv)
945 {
946         bv.setGuiDelegate(this);
947 }
948
949
950 void GuiView::disconnectBufferView()
951 {
952         if (d.current_work_area_)
953                 d.current_work_area_->bufferView().setGuiDelegate(0);
954 }
955
956
957 void GuiView::errors(string const & error_type)
958 {
959         ErrorList & el = buffer()->errorList(error_type);
960         if (!el.empty())
961                 showDialog("errorlist", error_type);
962 }
963
964
965 void GuiView::updateDialog(string const & name, string const & data)
966 {
967         if (!isDialogVisible(name))
968                 return;
969
970         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
971         if (it == d.dialogs_.end())
972                 return;
973
974         Dialog * const dialog = it->second.get();
975         if (dialog->isVisibleView())
976                 dialog->updateData(data);
977 }
978
979
980 BufferView * GuiView::view()
981 {
982         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
983 }
984
985
986 void GuiView::updateToc()
987 {
988         updateDialog("toc", "");
989 }
990
991
992 void GuiView::updateEmbeddedFiles()
993 {
994         updateDialog("embedding", "");
995 }
996
997
998 void GuiView::autoSave()
999 {
1000         LYXERR(Debug::INFO, "Running autoSave()");
1001
1002         if (buffer())
1003                 view()->buffer().autoSave();
1004 }
1005
1006
1007 void GuiView::resetAutosaveTimers()
1008 {
1009         if (lyxrc.autosave)
1010                 autosave_timeout_->restart();
1011 }
1012
1013
1014 void GuiView::dispatch(FuncRequest const & cmd)
1015 {
1016         switch(cmd.action) {
1017                 case LFUN_BUFFER_SWITCH:
1018                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1019                         break;
1020
1021                 case LFUN_COMMAND_EXECUTE: {
1022                         bool const show_it = cmd.argument() != "off";
1023                         d.toolbars_->showCommandBuffer(show_it);
1024                         break;
1025                 }
1026                 case LFUN_DROP_LAYOUTS_CHOICE:
1027                         d.toolbars_->openLayoutList();
1028                         break;
1029
1030                 case LFUN_MENU_OPEN:
1031                         d.menubar_->openByName(toqstr(cmd.argument()));
1032                         break;
1033
1034                 case LFUN_TOOLBAR_TOGGLE: {
1035                         string const name = cmd.getArg(0);
1036                         bool const allowauto = cmd.getArg(1) == "allowauto";
1037                         // it is possible to get current toolbar status like this,...
1038                         // but I decide to obey the order of ToolbarBackend::flags
1039                         // and disregard real toolbar status.
1040                         // toolbars_->saveToolbarInfo();
1041                         //
1042                         // toggle state on/off/auto
1043                         d.toolbars_->toggleToolbarState(name, allowauto);
1044                         // update toolbar
1045                         updateToolbars();
1046
1047                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1048                         if (!tbi) {
1049                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1050                                 break;
1051                         }
1052                         docstring state;
1053                         if (tbi->flags & ToolbarInfo::ON)
1054                                 state = _("on");
1055                         else if (tbi->flags & ToolbarInfo::OFF)
1056                                 state = _("off");
1057                         else if (tbi->flags & ToolbarInfo::AUTO)
1058                                 state = _("auto");
1059
1060                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1061                                            _(tbi->gui_name), state));
1062                         break;
1063                 }
1064
1065                 default:
1066                         theLyXFunc().setLyXView(this);
1067                         lyx::dispatch(cmd);
1068         }
1069 }
1070
1071
1072 Buffer const * GuiView::updateInset(Inset const * inset)
1073 {
1074         if (!d.current_work_area_)
1075                 return 0;
1076
1077         if (inset)
1078                 d.current_work_area_->scheduleRedraw();
1079
1080         return &d.current_work_area_->bufferView().buffer();
1081 }
1082
1083
1084 void GuiView::restartCursor()
1085 {
1086         /* When we move around, or type, it's nice to be able to see
1087          * the cursor immediately after the keypress.
1088          */
1089         if (d.current_work_area_)
1090                 d.current_work_area_->startBlinkingCursor();
1091 }
1092
1093
1094 Dialog * GuiView::find_or_build(string const & name)
1095 {
1096         if (!isValidName(name))
1097                 return 0;
1098
1099         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1100
1101         if (it != d.dialogs_.end())
1102                 return it->second.get();
1103
1104         d.dialogs_[name].reset(build(name));
1105         return d.dialogs_[name].get();
1106 }
1107
1108
1109 void GuiView::showDialog(string const & name, string const & data,
1110         Inset * inset)
1111 {
1112         if (in_show_)
1113                 return;
1114
1115         in_show_ = true;
1116         Dialog * dialog = find_or_build(name);
1117         if (dialog) {
1118                 dialog->showData(data);
1119                 if (inset)
1120                         d.open_insets_[name] = inset;
1121         }
1122         in_show_ = false;
1123 }
1124
1125
1126 bool GuiView::isDialogVisible(string const & name) const
1127 {
1128         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1129         if (it == d.dialogs_.end())
1130                 return false;
1131         return it->second.get()->isVisibleView();
1132 }
1133
1134
1135 void GuiView::hideDialog(string const & name, Inset * inset)
1136 {
1137         // Don't send the signal if we are quitting, because on MSVC it is
1138         // destructed before the cut stack in CutAndPaste.cpp, and this method
1139         // is called from some inset destructor if the cut stack is not empty
1140         // on exit.
1141         if (quitting)
1142                 return;
1143
1144         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1145         if (it == d.dialogs_.end())
1146                 return;
1147
1148         if (inset && inset != getOpenInset(name))
1149                 return;
1150
1151         Dialog * const dialog = it->second.get();
1152         if (dialog->isVisibleView())
1153                 dialog->hide();
1154         d.open_insets_[name] = 0;
1155 }
1156
1157
1158 void GuiView::disconnectDialog(string const & name)
1159 {
1160         if (!isValidName(name))
1161                 return;
1162
1163         if (d.open_insets_.find(name) != d.open_insets_.end())
1164                 d.open_insets_[name] = 0;
1165 }
1166
1167
1168 Inset * GuiView::getOpenInset(string const & name) const
1169 {
1170         if (!isValidName(name))
1171                 return 0;
1172
1173         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1174         return it == d.open_insets_.end() ? 0 : it->second;
1175 }
1176
1177
1178 void GuiView::hideAll() const
1179 {
1180         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1181         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1182
1183         for(; it != end; ++it)
1184                 it->second->hide();
1185 }
1186
1187
1188 void GuiView::hideBufferDependent() const
1189 {
1190         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1191         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1192
1193         for(; it != end; ++it) {
1194                 Dialog * dialog = it->second.get();
1195                 if (dialog->isBufferDependent())
1196                         dialog->hide();
1197         }
1198 }
1199
1200
1201 void GuiView::updateBufferDependent(bool switched) const
1202 {
1203         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1204         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1205
1206         for(; it != end; ++it) {
1207                 Dialog * dialog = it->second.get();
1208                 if (switched && dialog->isBufferDependent()) {
1209                         if (dialog->isVisibleView() && dialog->initialiseParams(""))
1210                                 dialog->updateView();
1211                         else
1212                                 dialog->hide();
1213                 } else {
1214                         // A bit clunky, but the dialog will request
1215                         // that the kernel provides it with the necessary
1216                         // data.
1217                         dialog->slotRestore();
1218                 }
1219         }
1220 }
1221
1222
1223 void GuiView::redrawDialog() const
1224 {
1225         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1226         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1227
1228         for(; it != end; ++it)
1229                 it->second->redraw();
1230 }
1231
1232
1233 void GuiView::checkStatus()
1234 {
1235         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1236         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1237
1238         for(; it != end; ++it) {
1239                 Dialog * const dialog = it->second.get();
1240                 if (dialog && dialog->isVisibleView())
1241                         dialog->checkStatus();
1242         }
1243 }
1244
1245
1246 namespace {
1247
1248 // This list should be kept in sync with the list of insets in
1249 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1250 // dialog should have the same name as the inset.
1251
1252 char const * const dialognames[] = {
1253 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1254 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1255 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1256 "mathdelimiter", "mathmatrix", "note", "paragraph",
1257 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1258
1259 #ifdef HAVE_LIBAIKSAURUS
1260 "thesaurus",
1261 #endif
1262
1263 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1264
1265 char const * const * const end_dialognames =
1266         dialognames + (sizeof(dialognames) / sizeof(char *));
1267
1268 class cmpCStr {
1269 public:
1270         cmpCStr(char const * name) : name_(name) {}
1271         bool operator()(char const * other) {
1272                 return strcmp(other, name_) == 0;
1273         }
1274 private:
1275         char const * name_;
1276 };
1277
1278
1279 } // namespace anon
1280
1281 // will be replaced by a proper factory...
1282 Dialog * createGuiAbout(LyXView & lv);
1283 Dialog * createGuiBibitem(LyXView & lv);
1284 Dialog * createGuiBibtex(LyXView & lv);
1285 Dialog * createGuiBox(LyXView & lv);
1286 Dialog * createGuiBranch(LyXView & lv);
1287 Dialog * createGuiChanges(LyXView & lv);
1288 Dialog * createGuiCharacter(LyXView & lv);
1289 Dialog * createGuiCitation(LyXView & lv);
1290 Dialog * createGuiDelimiter(LyXView & lv);
1291 Dialog * createGuiDocument(LyXView & lv);
1292 Dialog * createGuiErrorList(LyXView & lv);
1293 Dialog * createGuiERT(LyXView & lv);
1294 Dialog * createGuiExternal(LyXView & lv);
1295 Dialog * createGuiFloat(LyXView & lv);
1296 Dialog * createGuiGraphics(LyXView & lv);
1297 Dialog * createGuiInclude(LyXView & lv);
1298 Dialog * createGuiIndex(LyXView & lv);
1299 Dialog * createGuiLabel(LyXView & lv);
1300 Dialog * createGuiListings(LyXView & lv);
1301 Dialog * createGuiLog(LyXView & lv);
1302 Dialog * createGuiMathMatrix(LyXView & lv);
1303 Dialog * createGuiNomenclature(LyXView & lv);
1304 Dialog * createGuiNote(LyXView & lv);
1305 Dialog * createGuiParagraph(LyXView & lv);
1306 Dialog * createGuiPreferences(LyXView & lv);
1307 Dialog * createGuiPrint(LyXView & lv);
1308 Dialog * createGuiRef(LyXView & lv);
1309 Dialog * createGuiSearch(LyXView & lv);
1310 Dialog * createGuiSendTo(LyXView & lv);
1311 Dialog * createGuiShowFile(LyXView & lv);
1312 Dialog * createGuiSpellchecker(LyXView & lv);
1313 Dialog * createGuiTabularCreate(LyXView & lv);
1314 Dialog * createGuiTabular(LyXView & lv);
1315 Dialog * createGuiTexInfo(LyXView & lv);
1316 Dialog * createGuiToc(LyXView & lv);
1317 Dialog * createGuiThesaurus(LyXView & lv);
1318 Dialog * createGuiHyperlink(LyXView & lv);
1319 Dialog * createGuiVSpace(LyXView & lv);
1320 Dialog * createGuiViewSource(LyXView & lv);
1321 Dialog * createGuiWrap(LyXView & lv);
1322
1323
1324 bool GuiView::isValidName(string const & name) const
1325 {
1326         return std::find_if(dialognames, end_dialognames,
1327                             cmpCStr(name.c_str())) != end_dialognames;
1328 }
1329
1330
1331 Dialog * GuiView::build(string const & name)
1332 {
1333         BOOST_ASSERT(isValidName(name));
1334
1335         if (name == "aboutlyx")
1336                 return createGuiAbout(*this);
1337         if (name == "bibitem")
1338                 return createGuiBibitem(*this);
1339         if (name == "bibtex")
1340                 return createGuiBibtex(*this);
1341         if (name == "box")
1342                 return createGuiBox(*this);
1343         if (name == "branch")
1344                 return createGuiBranch(*this);
1345         if (name == "changes")
1346                 return createGuiChanges(*this);
1347         if (name == "character")
1348                 return createGuiCharacter(*this);
1349         if (name == "citation")
1350                 return createGuiCitation(*this);
1351         if (name == "document")
1352                 return createGuiDocument(*this);
1353         if (name == "errorlist")
1354                 return createGuiErrorList(*this);
1355         if (name == "ert")
1356                 return createGuiERT(*this);
1357         if (name == "external")
1358                 return createGuiExternal(*this);
1359         if (name == "file")
1360                 return createGuiShowFile(*this);
1361         if (name == "findreplace")
1362                 return createGuiSearch(*this);
1363         if (name == "float")
1364                 return createGuiFloat(*this);
1365         if (name == "graphics")
1366                 return createGuiGraphics(*this);
1367         if (name == "include")
1368                 return createGuiInclude(*this);
1369         if (name == "index")
1370                 return createGuiIndex(*this);
1371         if (name == "nomenclature")
1372                 return createGuiNomenclature(*this);
1373         if (name == "label")
1374                 return createGuiLabel(*this);
1375         if (name == "log")
1376                 return createGuiLog(*this);
1377         if (name == "view-source")
1378                 return createGuiViewSource(*this);
1379         if (name == "mathdelimiter")
1380                 return createGuiDelimiter(*this);
1381         if (name == "mathmatrix")
1382                 return createGuiMathMatrix(*this);
1383         if (name == "note")
1384                 return createGuiNote(*this);
1385         if (name == "paragraph")
1386                 return createGuiParagraph(*this);
1387         if (name == "prefs")
1388                 return createGuiPreferences(*this);
1389         if (name == "print")
1390                 return createGuiPrint(*this);
1391         if (name == "ref")
1392                 return createGuiRef(*this);
1393         if (name == "sendto")
1394                 return createGuiSendTo(*this);
1395         if (name == "spellchecker")
1396                 return createGuiSpellchecker(*this);
1397         if (name == "tabular")
1398                 return createGuiTabular(*this);
1399         if (name == "tabularcreate")
1400                 return createGuiTabularCreate(*this);
1401         if (name == "texinfo")
1402                 return createGuiTexInfo(*this);
1403 #ifdef HAVE_LIBAIKSAURUS
1404         if (name == "thesaurus")
1405                 return createGuiThesaurus(*this);
1406 #endif
1407         if (name == "toc")
1408                 return createGuiToc(*this);
1409         if (name == "href")
1410                 return createGuiHyperlink(*this);
1411         if (name == "vspace")
1412                 return createGuiVSpace(*this);
1413         if (name == "wrap")
1414                 return createGuiWrap(*this);
1415         if (name == "listings")
1416                 return createGuiListings(*this);
1417
1418         return 0;
1419 }
1420
1421
1422 } // namespace frontend
1423 } // namespace lyx
1424
1425 #include "GuiView_moc.cpp"