]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
0217a128374a0d49b7e61495755d05441d82c259
[features.git] / src / frontends / qt4 / GuiView.cpp
1 /**
2  * \file GuiView.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  * \author Abdelrazak Younes
9  * \author Peter Kümmel
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiView.h"
17
18 #include "Dialog.h"
19 #include "frontends/FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiWorkArea.h"
22 #include "GuiKeySymbol.h"
23 #include "GuiMenubar.h"
24 #include "GuiToolbar.h"
25 #include "GuiToolbars.h"
26 #include "GuiProgress.h"
27
28 #include "qt_helpers.h"
29
30 #include "frontends/alert.h"
31
32 #include "buffer_funcs.h"
33 #include "Buffer.h"
34 #include "BufferList.h"
35 #include "BufferParams.h"
36 #include "BufferView.h"
37 #include "Cursor.h"
38 #include "support/debug.h"
39 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "support/gettext.h"
42 #include "Intl.h"
43 #include "Layout.h"
44 #include "Lexer.h"
45 #include "LyXFunc.h"
46 #include "LyX.h"
47 #include "LyXRC.h"
48 #include "LyXVC.h"
49 #include "MenuBackend.h"
50 #include "Paragraph.h"
51 #include "TextClass.h"
52 #include "Text.h"
53 #include "ToolbarBackend.h"
54 #include "version.h"
55
56 #include "support/FileFilterList.h"
57 #include "support/FileName.h"
58 #include "support/filetools.h"
59 #include "support/lstrings.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Timeout.h"
63
64 #include <QAction>
65 #include <QApplication>
66 #include <QCloseEvent>
67 #include <QDebug>
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 <QTimer>
83 #include <QToolBar>
84 #include <QUrl>
85
86 #include <boost/assert.hpp>
87 #include <boost/bind.hpp>
88
89 #ifdef HAVE_SYS_TIME_H
90 # include <sys/time.h>
91 #endif
92 #ifdef HAVE_UNISTD_H
93 # include <unistd.h>
94 #endif
95
96 using namespace std;
97
98 namespace lyx {
99
100 extern bool quitting;
101
102 namespace frontend {
103
104 using support::addPath;
105 using support::bformat;
106 using support::FileFilterList;
107 using support::FileName;
108 using support::makeAbsPath;
109 using support::makeDisplayPath;
110 using support::package;
111 using support::removeAutosaveFile;
112 using support::trim;
113
114 namespace {
115
116 class BackgroundWidget : public QWidget
117 {
118 public:
119         BackgroundWidget()
120         {
121                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
122                 /// The text to be written on top of the pixmap
123                 QString const text = lyx_version ? lyx_version : qt_("unknown version");
124                 splash_ = QPixmap(":/images/banner.png");
125
126                 QPainter pain(&splash_);
127                 pain.setPen(QColor(255, 255, 0));
128                 QFont font;
129                 // The font used to display the version info
130                 font.setStyleHint(QFont::SansSerif);
131                 font.setWeight(QFont::Bold);
132                 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
133                 pain.setFont(font);
134                 pain.drawText(260, 270, text);
135         }
136
137         void paintEvent(QPaintEvent *)
138         {
139                 int x = (width() - splash_.width()) / 2;
140                 int y = (height() - splash_.height()) / 2;
141                 QPainter pain(this);
142                 pain.drawPixmap(x, y, splash_);
143         }
144
145 private:
146         QPixmap splash_;
147 };
148
149 } // namespace anon
150
151
152 typedef boost::shared_ptr<Dialog> DialogPtr;
153
154 struct GuiView::GuiViewPrivate
155 {
156         GuiViewPrivate()
157                 : current_work_area_(0), layout_(0),
158                 quitting_by_menu_(false), autosave_timeout_(5000), in_show_(false)
159         {
160                 // hardcode here the platform specific icon size
161                 smallIconSize = 14;     // scaling problems
162                 normalIconSize = 20;    // ok, default
163                 bigIconSize = 26;               // better for some math icons
164
165                 splitter_ = new QSplitter;
166                 bg_widget_ = new BackgroundWidget;
167                 stack_widget_ = new QStackedWidget;
168                 stack_widget_->addWidget(bg_widget_);
169                 stack_widget_->addWidget(splitter_);
170                 setBackground();
171         }
172
173         ~GuiViewPrivate()
174         {
175                 delete splitter_;
176                 delete bg_widget_;
177                 delete stack_widget_;
178                 delete menubar_;
179                 delete toolbars_;
180         }
181
182         QMenu * toolBarPopup(GuiView * parent)
183         {
184                 // FIXME: translation
185                 QMenu * menu = new QMenu(parent);
186                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
187
188                 QAction * smallIcons = new QAction(iconSizeGroup);
189                 smallIcons->setText(qt_("Small-sized icons"));
190                 smallIcons->setCheckable(true);
191                 QObject::connect(smallIcons, SIGNAL(triggered()),
192                         parent, SLOT(smallSizedIcons()));
193                 menu->addAction(smallIcons);
194
195                 QAction * normalIcons = new QAction(iconSizeGroup);
196                 normalIcons->setText(qt_("Normal-sized icons"));
197                 normalIcons->setCheckable(true);
198                 QObject::connect(normalIcons, SIGNAL(triggered()),
199                         parent, SLOT(normalSizedIcons()));
200                 menu->addAction(normalIcons);
201
202                 QAction * bigIcons = new QAction(iconSizeGroup);
203                 bigIcons->setText(qt_("Big-sized icons"));
204                 bigIcons->setCheckable(true);
205                 QObject::connect(bigIcons, SIGNAL(triggered()),
206                         parent, SLOT(bigSizedIcons()));
207                 menu->addAction(bigIcons);
208
209                 unsigned int cur = parent->iconSize().width();
210                 if ( cur == parent->d.smallIconSize)
211                         smallIcons->setChecked(true);
212                 else if (cur == parent->d.normalIconSize)
213                         normalIcons->setChecked(true);
214                 else if (cur == parent->d.bigIconSize)
215                         bigIcons->setChecked(true);
216
217                 return menu;
218         }
219
220         void setBackground()
221         {
222                 stack_widget_->setCurrentWidget(bg_widget_);
223                 bg_widget_->setUpdatesEnabled(true);
224         }
225
226         TabWorkArea * tabWorkArea(int i)
227         {
228                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
229         }
230
231         TabWorkArea * currentTabWorkArea()
232         {
233                 if (splitter_->count() == 1)
234                         // The first TabWorkArea is always the first one, if any.
235                         return tabWorkArea(0);
236
237                 TabWorkArea * tab_widget = 0;
238                 for (int i = 0; i != splitter_->count(); ++i) {
239                         QWidget * w = splitter_->widget(i);
240                         if (!w->hasFocus())
241                                 continue;
242                         tab_widget = dynamic_cast<TabWorkArea *>(w);
243                         if (tab_widget)
244                                 break;
245                 }
246
247                 return tab_widget;
248         }
249
250 public:
251         GuiWorkArea * current_work_area_;
252         QSplitter * splitter_;
253         QStackedWidget * stack_widget_;
254         BackgroundWidget * bg_widget_;
255         /// view's menubar
256         GuiMenubar * menubar_;
257         /// view's toolbars
258         GuiToolbars * toolbars_;
259         /// The main layout box.
260         /** 
261          * \warning Don't Delete! The layout box is actually owned by
262          * whichever toolbar contains it. All the GuiView class needs is a
263          * means of accessing it.
264          *
265          * FIXME: replace that with a proper model so that we are not limited
266          * to only one dialog.
267          */
268         GuiLayoutBox * layout_;
269
270         ///
271         std::map<std::string, Inset *> open_insets_;
272
273         ///
274         std::map<std::string, DialogPtr> dialogs_;
275
276         unsigned int smallIconSize;
277         unsigned int normalIconSize;
278         unsigned int bigIconSize;
279         ///
280         QTimer statusbar_timer_;
281         /// are we quitting by the menu?
282         bool quitting_by_menu_;
283         /// auto-saving of buffers
284         Timeout autosave_timeout_;
285         /// flag against a race condition due to multiclicks, see bug #1119
286         bool in_show_;
287 };
288
289
290 GuiView::GuiView(int id)
291         : d(*new GuiViewPrivate), id_(id)
292 {
293         // GuiToolbars *must* be initialised before GuiMenubar.
294         d.toolbars_ = new GuiToolbars(*this);
295         d.menubar_ = new GuiMenubar(this, menubackend);
296
297         setCentralWidget(d.stack_widget_);
298
299         // Start autosave timer
300         if (lyxrc.autosave) {
301                 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
302                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
303                 d.autosave_timeout_.start();
304         }
305         connect(&d.statusbar_timer_, SIGNAL(timeout()),
306                 this, SLOT(clearMessage()));
307
308         // Qt bug? signal lastWindowClosed does not work
309         setAttribute(Qt::WA_QuitOnClose, false);
310         setAttribute(Qt::WA_DeleteOnClose, true);
311 #ifndef Q_WS_MACX
312         // assign an icon to main form. We do not do it under Qt/Mac,
313         // since the icon is provided in the application bundle.
314         setWindowIcon(QPixmap(":/images/lyx.png"));
315 #endif
316
317         // For Drag&Drop.
318         setAcceptDrops(true);
319
320         statusBar()->setSizeGripEnabled(true);
321
322         // Forbid too small unresizable window because it can happen
323         // with some window manager under X11.
324         setMinimumSize(300, 200);
325
326         if (!lyxrc.allow_geometry_session)
327                 // No session handling, default to a sane size.
328                 setGeometry(50, 50, 690, 510);
329
330         // Now take care of session management.
331         QSettings settings;
332         QString const key = "view-" + QString::number(id_);
333 #ifdef Q_WS_X11
334         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
335         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
336         resize(size);
337         move(pos);
338 #else
339         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
340                 setGeometry(50, 50, 690, 510);
341 #endif
342         setIconSize(settings.value(key + "/icon_size").toSize());
343 }
344
345
346 GuiView::~GuiView()
347 {
348         delete &d;
349 }
350
351
352 void GuiView::close()
353 {
354         d.quitting_by_menu_ = true;
355         d.current_work_area_ = 0;
356         for (int i = 0; i != d.splitter_->count(); ++i) {
357                 TabWorkArea * twa = d.tabWorkArea(i);
358                 if (twa)
359                         twa->closeAll();
360         }
361         QMainWindow::close();
362         d.quitting_by_menu_ = false;
363 }
364
365
366 void GuiView::setFocus()
367 {
368         if (d.current_work_area_)
369                 d.current_work_area_->setFocus();
370         else
371                 QWidget::setFocus();
372 }
373
374
375 QMenu * GuiView::createPopupMenu()
376 {
377         return d.toolBarPopup(this);
378 }
379
380
381 void GuiView::showEvent(QShowEvent * e)
382 {
383         LYXERR(Debug::GUI, "Passed Geometry "
384                 << size().height() << "x" << size().width()
385                 << "+" << pos().x() << "+" << pos().y());
386
387         if (d.splitter_->count() == 0)
388                 // No work area, switch to the background widget.
389                 d.setBackground();
390
391         QMainWindow::showEvent(e);
392 }
393
394
395 void GuiView::closeEvent(QCloseEvent * close_event)
396 {
397         // we may have been called through the close window button
398         // which bypasses the LFUN machinery.
399         if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
400                 if (!quitWriteAll()) {
401                         close_event->ignore();
402                         return;
403                 }
404         }
405
406         // Make sure that no LFUN use this close to be closed View.
407         theLyXFunc().setLyXView(0);
408         // Make sure the timer time out will not trigger a statusbar update.
409         d.statusbar_timer_.stop();
410
411         if (lyxrc.allow_geometry_session) {
412                 QSettings settings;
413                 QString const key = "view-" + QString::number(id_);
414 #ifdef Q_WS_X11
415                 settings.setValue(key + "/pos", pos());
416                 settings.setValue(key + "/size", size());
417 #else
418                 settings.setValue(key + "/geometry", saveGeometry());
419 #endif
420                 settings.setValue(key + "/icon_size", iconSize());
421                 d.toolbars_->saveToolbarInfo();
422                 // Now take care of all other dialogs:
423                 std::map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
424                 for (; it!= d.dialogs_.end(); ++it)
425                         it->second->saveSession();
426         }
427
428         guiApp->unregisterView(id_);
429         if (guiApp->viewCount() > 0) {
430                 // Just close the window and do nothing else if this is not the
431                 // last window.
432                 close_event->accept();
433                 return;
434         }
435
436         quitting = true;
437
438         // this is the place where we leave the frontend.
439         // it is the only point at which we start quitting.
440         close_event->accept();
441         // quit the event loop
442         qApp->quit();
443 }
444
445
446 void GuiView::dragEnterEvent(QDragEnterEvent * event)
447 {
448         if (event->mimeData()->hasUrls())
449                 event->accept();
450         /// \todo Ask lyx-devel is this is enough:
451         /// if (event->mimeData()->hasFormat("text/plain"))
452         ///     event->acceptProposedAction();
453 }
454
455
456 void GuiView::dropEvent(QDropEvent* event)
457 {
458         QList<QUrl> files = event->mimeData()->urls();
459         if (files.isEmpty())
460                 return;
461
462         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
463         for (int i = 0; i != files.size(); ++i) {
464                 string const file = support::os::internal_path(fromqstr(
465                         files.at(i).toLocalFile()));
466                 if (!file.empty())
467                         lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
468         }
469 }
470
471
472 void GuiView::message(docstring const & str)
473 {
474         statusBar()->showMessage(toqstr(str));
475         d.statusbar_timer_.stop();
476         d.statusbar_timer_.start(3000);
477 }
478
479
480 void GuiView::smallSizedIcons()
481 {
482         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
483 }
484
485
486 void GuiView::normalSizedIcons()
487 {
488         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
489 }
490
491
492 void GuiView::bigSizedIcons()
493 {
494         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
495 }
496
497
498 void GuiView::clearMessage()
499 {
500         if (!hasFocus())
501                 return;
502         theLyXFunc().setLyXView(this);
503         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
504         d.statusbar_timer_.stop();
505 }
506
507
508 void GuiView::updateWindowTitle(GuiWorkArea * wa)
509 {
510         if (wa != d.current_work_area_)
511                 return;
512         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
513         setWindowIconText(wa->windowIconText());
514 }
515
516
517 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
518 {
519         disconnectBuffer();
520         disconnectBufferView();
521         connectBufferView(wa->bufferView());
522         connectBuffer(wa->bufferView().buffer());
523         d.current_work_area_ = wa;
524         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
525                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
526         updateWindowTitle(wa);
527
528         updateToc();
529         // Buffer-dependent dialogs should be updated or
530         // hidden. This should go here because some dialogs (eg ToC)
531         // require bv_->text.
532         updateBufferDependent(true);
533         updateToolbars();
534         updateLayoutList();
535         updateStatusBar();
536 }
537
538
539 void GuiView::updateStatusBar()
540 {
541         // let the user see the explicit message
542         if (d.statusbar_timer_.isActive())
543                 return;
544
545         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
546 }
547
548
549 bool GuiView::hasFocus() const
550 {
551         return qApp->activeWindow() == this;
552 }
553
554
555 bool GuiView::event(QEvent * e)
556 {
557         switch (e->type())
558         {
559         // Useful debug code:
560         //case QEvent::ActivationChange:
561         //case QEvent::WindowDeactivate:
562         //case QEvent::Paint:
563         //case QEvent::Enter:
564         //case QEvent::Leave:
565         //case QEvent::HoverEnter:
566         //case QEvent::HoverLeave:
567         //case QEvent::HoverMove:
568         //case QEvent::StatusTip:
569         //case QEvent::DragEnter:
570         //case QEvent::DragLeave:
571         //case QEvent::Drop:
572         //      break;
573
574         case QEvent::WindowActivate: {
575                 guiApp->setCurrentView(*this);
576                 if (d.current_work_area_) {
577                         BufferView & bv = d.current_work_area_->bufferView();
578                         connectBufferView(bv);
579                         connectBuffer(bv.buffer());
580                         // The document structure, name and dialogs might have
581                         // changed in another view.
582                         updateBufferDependent(true);
583                 } else {
584                         setWindowTitle(qt_("LyX"));
585                         setWindowIconText(qt_("LyX"));
586                 }
587                 return QMainWindow::event(e);
588         }
589         case QEvent::ShortcutOverride: {
590                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
591                 if (!d.current_work_area_) {
592                         theLyXFunc().setLyXView(this);
593                         KeySymbol sym;
594                         setKeySymbol(&sym, ke);
595                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
596                         e->accept();
597                         return true;
598                 }
599                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
600                         KeySymbol sym;
601                         setKeySymbol(&sym, ke);
602                         d.current_work_area_->processKeySym(sym, NoModifier);
603                         e->accept();
604                         return true;
605                 }
606         }
607         default:
608                 return QMainWindow::event(e);
609         }
610 }
611
612
613 bool GuiView::focusNextPrevChild(bool /*next*/)
614 {
615         setFocus();
616         return true;
617 }
618
619
620 void GuiView::setBusy(bool yes)
621 {
622         if (d.current_work_area_) {
623                 d.current_work_area_->setUpdatesEnabled(!yes);
624                 if (yes)
625                         d.current_work_area_->stopBlinkingCursor();
626                 else
627                         d.current_work_area_->startBlinkingCursor();
628         }
629
630         if (yes)
631                 QApplication::setOverrideCursor(Qt::WaitCursor);
632         else
633                 QApplication::restoreOverrideCursor();
634 }
635
636
637 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
638 {
639         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
640
641         if (tbinfo.flags & ToolbarInfo::TOP) {
642                 if (newline)
643                         addToolBarBreak(Qt::TopToolBarArea);
644                 addToolBar(Qt::TopToolBarArea, toolBar);
645         }
646
647         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
648 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
649 #if (QT_VERSION >= 0x040202)
650                 if (newline)
651                         addToolBarBreak(Qt::BottomToolBarArea);
652 #endif
653                 addToolBar(Qt::BottomToolBarArea, toolBar);
654         }
655
656         if (tbinfo.flags & ToolbarInfo::LEFT) {
657 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
658 #if (QT_VERSION >= 0x040202)
659                 if (newline)
660                         addToolBarBreak(Qt::LeftToolBarArea);
661 #endif
662                 addToolBar(Qt::LeftToolBarArea, toolBar);
663         }
664
665         if (tbinfo.flags & ToolbarInfo::RIGHT) {
666 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
667 #if (QT_VERSION >= 0x040202)
668                 if (newline)
669                         addToolBarBreak(Qt::RightToolBarArea);
670 #endif
671                 addToolBar(Qt::RightToolBarArea, toolBar);
672         }
673
674         // The following does not work so I cannot restore to exact toolbar location
675         /*
676         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
677         toolBar->move(tbinfo.posx, tbinfo.posy);
678         */
679
680         return toolBar;
681 }
682
683
684 GuiWorkArea * GuiView::workArea(Buffer & buffer)
685 {
686         for (int i = 0; i != d.splitter_->count(); ++i) {
687                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
688                 if (wa)
689                         return wa;
690         }
691         return 0;
692 }
693
694
695 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
696 {
697
698         // Automatically create a TabWorkArea if there are none yet.
699         if (!d.splitter_->count())
700                 addTabWorkArea();
701
702         TabWorkArea * tab_widget = d.currentTabWorkArea();
703         return tab_widget->addWorkArea(buffer, *this);
704 }
705
706
707 void GuiView::addTabWorkArea()
708 {
709         TabWorkArea * twa = new TabWorkArea;
710         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
711                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
712         d.splitter_->addWidget(twa);
713         d.stack_widget_->setCurrentWidget(d.splitter_);
714 }
715
716
717 GuiWorkArea const * GuiView::currentWorkArea() const
718 {
719         return d.current_work_area_;
720 }
721
722
723 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
724 {
725         BOOST_ASSERT(wa);
726
727         // Changing work area can result from opening a file so
728         // update the toc in any case.
729         updateToc();
730
731         d.current_work_area_ = wa;
732         for (int i = 0; i != d.splitter_->count(); ++i) {
733                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
734                         return;
735         }
736 }
737
738
739 void GuiView::removeWorkArea(GuiWorkArea * wa)
740 {
741         BOOST_ASSERT(wa);
742         if (wa == d.current_work_area_) {
743                 disconnectBuffer();
744                 disconnectBufferView();
745                 hideBufferDependent();
746                 d.current_work_area_ = 0;
747         }
748
749         for (int i = 0; i != d.splitter_->count(); ++i) {
750                 TabWorkArea * twa = d.tabWorkArea(i);
751                 if (!twa->removeWorkArea(wa))
752                         // Not found in this tab group.
753                         continue;
754
755                 // We found and removed the GuiWorkArea.
756                 if (!twa->count()) {
757                         // No more WorkAreas in this tab group, so delete it.
758                         delete twa;
759                         break;
760                 }
761
762                 if (d.current_work_area_)
763                         // This means that we are not closing the current GuiWorkArea;
764                         break;
765
766                 // Switch to the next GuiWorkArea in the found TabWorkArea.
767                 d.current_work_area_ = twa->currentWorkArea();
768                 break;
769         }
770
771         if (d.splitter_->count() == 0)
772                 // No more work area, switch to the background widget.
773                 d.setBackground();
774 }
775
776
777 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
778 {
779         d.layout_ = layout;
780 }
781
782
783 void GuiView::updateLayoutList()
784 {
785         if (d.layout_)
786                 d.layout_->updateContents(false);
787 }
788
789
790 void GuiView::updateToolbars()
791 {
792         if (d.current_work_area_) {
793                 bool const math =
794                         d.current_work_area_->bufferView().cursor().inMathed();
795                 bool const table =
796                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
797                 bool const review =
798                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
799                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
800
801                 d.toolbars_->update(math, table, review);
802         } else
803                 d.toolbars_->update(false, false, false);
804
805         // update read-only status of open dialogs.
806         checkStatus();
807 }
808
809
810 Buffer * GuiView::buffer()
811 {
812         if (d.current_work_area_)
813                 return &d.current_work_area_->bufferView().buffer();
814         return 0;
815 }
816
817
818 Buffer const * GuiView::buffer() const
819 {
820         if (d.current_work_area_)
821                 return &d.current_work_area_->bufferView().buffer();
822         return 0;
823 }
824
825
826 void GuiView::setBuffer(Buffer * newBuffer)
827 {
828         BOOST_ASSERT(newBuffer);
829         setBusy(true);
830
831         GuiWorkArea * wa = workArea(*newBuffer);
832         if (wa == 0) {
833                 updateLabels(*newBuffer->masterBuffer());
834                 wa = addWorkArea(*newBuffer);
835         } else {
836                 //Disconnect the old buffer...there's no new one.
837                 disconnectBuffer();
838         }
839         connectBuffer(*newBuffer);
840         connectBufferView(wa->bufferView());
841         setCurrentWorkArea(wa);
842
843         setBusy(false);
844 }
845
846
847 void GuiView::connectBuffer(Buffer & buf)
848 {
849         buf.setGuiDelegate(this);
850 }
851
852
853 void GuiView::disconnectBuffer()
854 {
855         if (d.current_work_area_)
856                 d.current_work_area_->bufferView().setGuiDelegate(0);
857 }
858
859
860 void GuiView::connectBufferView(BufferView & bv)
861 {
862         bv.setGuiDelegate(this);
863 }
864
865
866 void GuiView::disconnectBufferView()
867 {
868         if (d.current_work_area_)
869                 d.current_work_area_->bufferView().setGuiDelegate(0);
870 }
871
872
873 void GuiView::errors(string const & error_type)
874 {
875         ErrorList & el = buffer()->errorList(error_type);
876         if (!el.empty())
877                 showDialog("errorlist", error_type);
878 }
879
880
881 void GuiView::updateDialog(string const & name, string const & data)
882 {
883         if (!isDialogVisible(name))
884                 return;
885
886         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
887         if (it == d.dialogs_.end())
888                 return;
889
890         Dialog * const dialog = it->second.get();
891         if (dialog->isVisibleView())
892                 dialog->updateData(data);
893 }
894
895
896 BufferView * GuiView::view()
897 {
898         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
899 }
900
901
902 void GuiView::updateToc()
903 {
904         updateDialog("toc", "");
905 }
906
907
908 void GuiView::updateEmbeddedFiles()
909 {
910         updateDialog("embedding", "");
911 }
912
913
914 void GuiView::autoSave()
915 {
916         LYXERR(Debug::INFO, "Running autoSave()");
917
918         if (buffer())
919                 view()->buffer().autoSave();
920 }
921
922
923 void GuiView::resetAutosaveTimers()
924 {
925         if (lyxrc.autosave)
926                 d.autosave_timeout_.restart();
927 }
928
929
930 FuncStatus GuiView::getStatus(FuncRequest const & cmd)
931 {
932         FuncStatus flag;
933         bool enable = true;
934         Buffer * buf = buffer();
935
936         /* In LyX/Mac, when a dialog is open, the menus of the
937            application can still be accessed without giving focus to
938            the main window. In this case, we want to disable the menu
939            entries that are buffer-related.
940
941            Note that this code is not perfect, as bug 1941 attests:
942            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
943         */
944         if (cmd.origin == FuncRequest::MENU && !hasFocus())
945                 buf = 0;
946
947         switch(cmd.action) {
948         case LFUN_BUFFER_WRITE:
949                 enable = buf && (buf->isUnnamed() || !buf->isClean());
950                 break;
951
952         case LFUN_BUFFER_WRITE_AS:
953                 enable = buf;
954                 break;
955
956         case LFUN_TOOLBAR_TOGGLE:
957                 flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
958                 break;
959
960         case LFUN_DIALOG_TOGGLE:
961                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
962                 // fall through to set "enable"
963         case LFUN_DIALOG_SHOW: {
964                 string const name = cmd.getArg(0);
965                 if (!buf)
966                         enable = name == "aboutlyx"
967                                 || name == "file" //FIXME: should be removed.
968                                 || name == "prefs"
969                                 || name == "texinfo";
970                 else if (name == "print")
971                         enable = buf->isExportable("dvi")
972                                 && lyxrc.print_command != "none";
973                 else if (name == "character") {
974                         if (!view())
975                                 enable = false;
976                         else {
977                                 InsetCode ic = view()->cursor().inset().lyxCode();
978                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
979                         }
980                 }
981                 else if (name == "latexlog")
982                         enable = FileName(buf->logName()).isReadableFile();
983                 else if (name == "spellchecker")
984 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
985                         enable = !buf->isReadonly();
986 #else
987                         enable = false;
988 #endif
989                 else if (name == "vclog")
990                         enable = buf->lyxvc().inUse();
991                 break;
992         }
993
994         case LFUN_DIALOG_UPDATE: {
995                 string const name = cmd.getArg(0);
996                 if (!buf)
997                         enable = name == "prefs";
998                 break;
999         }
1000
1001         case LFUN_INSET_APPLY: {
1002                 if (!buf) {
1003                         enable = false;
1004                         break;
1005                 }
1006                 string const name = cmd.getArg(0);
1007                 Inset * inset = getOpenInset(name);
1008                 if (inset) {
1009                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1010                         FuncStatus fs;
1011                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1012                                 // Every inset is supposed to handle this
1013                                 BOOST_ASSERT(false);
1014                         }
1015                         flag |= fs;
1016                 } else {
1017                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1018                         flag |= getStatus(fr);
1019                 }
1020                 enable = flag.enabled();
1021                 break;
1022         }
1023
1024         default:
1025                 if (!view()) {
1026                         enable = false;
1027                         break;
1028                 }
1029         }
1030
1031         if (!enable)
1032                 flag.enabled(false);
1033
1034         return flag;
1035 }
1036
1037
1038 static FileName selectTemplateFile()
1039 {
1040         FileDialog dlg(_("Select template file"));
1041         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1042         dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1043
1044         FileDialog::Result result =
1045                 dlg.open(from_utf8(lyxrc.template_path),
1046                              FileFilterList(_("LyX Documents (*.lyx)")),
1047                              docstring());
1048
1049         if (result.first == FileDialog::Later)
1050                 return FileName();
1051         if (result.second.empty())
1052                 return FileName();
1053         return FileName(to_utf8(result.second));
1054 }
1055
1056
1057 void GuiView::newDocument(string const & filename, bool from_template)
1058 {
1059         FileName initpath(lyxrc.document_path);
1060         Buffer * buf = buffer();
1061         if (buf) {
1062                 FileName const trypath(buf->filePath());
1063                 // If directory is writeable, use this as default.
1064                 if (trypath.isDirWritable())
1065                         initpath = trypath;
1066         }
1067
1068         string templatefile = from_template ?
1069                 selectTemplateFile().absFilename() : string();
1070         Buffer * b;
1071         if (filename.empty())
1072                 b = newUnnamedFile(templatefile, initpath);
1073         else
1074                 b = newFile(filename, templatefile, true);
1075
1076         if (b)
1077                 setBuffer(b);
1078 }
1079
1080
1081 void GuiView::insertLyXFile(docstring const & fname)
1082 {
1083         BufferView * bv = view();
1084         if (!bv)
1085                 return;
1086
1087         // FIXME UNICODE
1088         FileName filename(to_utf8(fname));
1089         
1090         if (!filename.empty()) {
1091                 bv->insertLyXFile(filename);
1092                 return;
1093         }
1094
1095         // Launch a file browser
1096         // FIXME UNICODE
1097         string initpath = lyxrc.document_path;
1098         string const trypath = bv->buffer().filePath();
1099         // If directory is writeable, use this as default.
1100         if (FileName(trypath).isDirWritable())
1101                 initpath = trypath;
1102
1103         // FIXME UNICODE
1104         FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
1105         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1106         dlg.setButton2(_("Examples|#E#e"),
1107                 from_utf8(addPath(package().system_support().absFilename(),
1108                 "examples")));
1109
1110         FileDialog::Result result =
1111                 dlg.open(from_utf8(initpath),
1112                              FileFilterList(_("LyX Documents (*.lyx)")),
1113                              docstring());
1114
1115         if (result.first == FileDialog::Later)
1116                 return;
1117
1118         // FIXME UNICODE
1119         filename.set(to_utf8(result.second));
1120
1121         // check selected filename
1122         if (filename.empty()) {
1123                 // emit message signal.
1124                 message(_("Canceled."));
1125                 return;
1126         }
1127
1128         bv->insertLyXFile(filename);
1129 }
1130
1131
1132 void GuiView::insertPlaintextFile(docstring const & fname,
1133         bool asParagraph)
1134 {
1135         BufferView * bv = view();
1136         if (!bv)
1137                 return;
1138
1139         // FIXME UNICODE
1140         FileName filename(to_utf8(fname));
1141         
1142         if (!filename.empty()) {
1143                 bv->insertPlaintextFile(filename, asParagraph);
1144                 return;
1145         }
1146
1147         FileDialog dlg(_("Select file to insert"), (asParagraph ?
1148                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1149
1150         FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
1151                 FileFilterList(), docstring());
1152
1153         if (result.first == FileDialog::Later)
1154                 return;
1155
1156         // FIXME UNICODE
1157         filename.set(to_utf8(result.second));
1158
1159         // check selected filename
1160         if (filename.empty()) {
1161                 // emit message signal.
1162                 message(_("Canceled."));
1163                 return;
1164         }
1165
1166         bv->insertPlaintextFile(filename, asParagraph);
1167 }
1168
1169
1170 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1171 {
1172         FileName fname = b.fileName();
1173         FileName const oldname = fname;
1174
1175         if (!newname.empty()) {
1176                 // FIXME UNICODE
1177                 fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1178         } else {
1179                 // Switch to this Buffer.
1180                 setBuffer(&b);
1181
1182                 /// No argument? Ask user through dialog.
1183                 // FIXME UNICODE
1184                 FileDialog dlg(_("Choose a filename to save document as"),
1185                                    LFUN_BUFFER_WRITE_AS);
1186                 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1187                 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1188
1189                 if (!support::isLyXFilename(fname.absFilename()))
1190                         fname.changeExtension(".lyx");
1191
1192                 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
1193
1194                 FileDialog::Result result =
1195                         dlg.save(from_utf8(fname.onlyPath().absFilename()),
1196                                      filter,
1197                                      from_utf8(fname.onlyFileName()));
1198
1199                 if (result.first == FileDialog::Later)
1200                         return false;
1201
1202                 fname.set(to_utf8(result.second));
1203
1204                 if (fname.empty())
1205                         return false;
1206
1207                 if (!support::isLyXFilename(fname.absFilename()))
1208                         fname.changeExtension(".lyx");
1209         }
1210
1211         if (FileName(fname).exists()) {
1212                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1213                 docstring text = bformat(_("The document %1$s already "
1214                                            "exists.\n\nDo you want to "
1215                                            "overwrite that document?"), 
1216                                          file);
1217                 int const ret = Alert::prompt(_("Overwrite document?"),
1218                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1219                 switch (ret) {
1220                 case 0: break;
1221                 case 1: return renameBuffer(b, docstring());
1222                 case 2: return false;
1223                 }
1224         }
1225
1226         // Ok, change the name of the buffer
1227         b.setFileName(fname.absFilename());
1228         b.markDirty();
1229         bool unnamed = b.isUnnamed();
1230         b.setUnnamed(false);
1231         b.saveCheckSum(fname);
1232
1233         if (!saveBuffer(b)) {
1234                 b.setFileName(oldname.absFilename());
1235                 b.setUnnamed(unnamed);
1236                 b.saveCheckSum(oldname);
1237                 return false;
1238         }
1239
1240         return true;
1241 }
1242
1243
1244 bool GuiView::saveBuffer(Buffer & b)
1245 {
1246         if (b.isUnnamed())
1247                 return renameBuffer(b, docstring());
1248
1249         if (b.save()) {
1250                 LyX::ref().session().lastFiles().add(b.fileName());
1251                 return true;
1252         }
1253
1254         // Switch to this Buffer.
1255         setBuffer(&b);
1256
1257         // FIXME: we don't tell the user *WHY* the save failed !!
1258         docstring const file = makeDisplayPath(b.absFileName(), 30);
1259         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1260                                    "Do you want to rename the document and "
1261                                    "try again?"), file);
1262         int const ret = Alert::prompt(_("Rename and save?"),
1263                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1264         switch (ret) {
1265         case 0:
1266                 if (!renameBuffer(b, docstring()))
1267                         return false;
1268                 break;
1269         case 1:
1270                 return false;
1271         case 2:
1272                 break;
1273         }
1274
1275         return saveBuffer(b);
1276 }
1277
1278
1279 bool GuiView::closeBuffer()
1280 {
1281         Buffer * buf = buffer();
1282         return buf && closeBuffer(*buf);
1283 }
1284
1285
1286 bool GuiView::closeBuffer(Buffer & buf)
1287 {
1288         if (buf.isClean() || buf.paragraphs().empty()) {
1289                 theBufferList().release(&buf);
1290                 return true;
1291         }
1292         // Switch to this Buffer.
1293         setBuffer(&buf);
1294
1295         docstring file;
1296         // FIXME: Unicode?
1297         if (buf.isUnnamed())
1298                 file = from_utf8(buf.fileName().onlyFileName());
1299         else
1300                 file = buf.fileName().displayName(30);
1301
1302         docstring const text = bformat(_("The document %1$s has unsaved changes."
1303                 "\n\nDo you want to save the document or discard the changes?"), file);
1304         int const ret = Alert::prompt(_("Save changed document?"),
1305                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1306
1307         switch (ret) {
1308         case 0:
1309                 if (!saveBuffer(buf))
1310                         return false;
1311                 break;
1312         case 1:
1313                 // if we crash after this we could
1314                 // have no autosave file but I guess
1315                 // this is really improbable (Jug)
1316                 removeAutosaveFile(buf.absFileName());
1317                 break;
1318         case 2:
1319                 return false;
1320         }
1321
1322         // save file names to .lyx/session
1323         // if master/slave are both open, do not save slave since it
1324         // will be automatically loaded when the master is loaded
1325         if (buf.masterBuffer() == &buf)
1326                 LyX::ref().session().lastOpened().add(buf.fileName());
1327
1328         theBufferList().release(&buf);
1329         return true;
1330 }
1331
1332
1333 bool GuiView::quitWriteAll()
1334 {
1335         while (!theBufferList().empty()) {
1336                 Buffer * b = theBufferList().first();
1337                 if (!closeBuffer(*b))
1338                         return false;
1339         }
1340         return true;
1341 }
1342
1343
1344 bool GuiView::dispatch(FuncRequest const & cmd)
1345 {
1346         BufferView * bv = view();       
1347         // By default we won't need any update.
1348         if (bv)
1349                 bv->cursor().updateFlags(Update::None);
1350
1351         switch(cmd.action) {
1352                 case LFUN_BUFFER_SWITCH:
1353                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1354                         break;
1355
1356                 case LFUN_BUFFER_NEXT:
1357                         setBuffer(theBufferList().next(buffer()));
1358                         break;
1359
1360                 case LFUN_BUFFER_PREVIOUS:
1361                         setBuffer(theBufferList().previous(buffer()));
1362                         break;
1363
1364                 case LFUN_COMMAND_EXECUTE: {
1365                         bool const show_it = cmd.argument() != "off";
1366                         d.toolbars_->showCommandBuffer(show_it);
1367                         break;
1368                 }
1369                 case LFUN_DROP_LAYOUTS_CHOICE:
1370                         if (d.layout_)
1371                                 d.layout_->showPopup();
1372                         break;
1373
1374                 case LFUN_MENU_OPEN:
1375                         d.menubar_->openByName(toqstr(cmd.argument()));
1376                         break;
1377
1378                 case LFUN_FILE_INSERT:
1379                         insertLyXFile(cmd.argument());
1380                         break;
1381                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1382                         insertPlaintextFile(cmd.argument(), true);
1383                         break;
1384
1385                 case LFUN_FILE_INSERT_PLAINTEXT:
1386                         insertPlaintextFile(cmd.argument(), false);
1387                         break;
1388
1389                 case LFUN_BUFFER_WRITE:
1390                         if (bv)
1391                                 saveBuffer(bv->buffer());
1392                         break;
1393
1394                 case LFUN_BUFFER_WRITE_AS:
1395                         if (bv)
1396                                 renameBuffer(bv->buffer(), cmd.argument());
1397                         break;
1398
1399                 case LFUN_BUFFER_WRITE_ALL: {
1400                         Buffer * first = theBufferList().first();
1401                         if (!first)
1402                                 break;
1403                         message(_("Saving all documents..."));
1404                         // We cannot use a for loop as the buffer list cycles.
1405                         Buffer * b = first;
1406                         do {
1407                                 if (b->isClean())
1408                                         continue;
1409                                 saveBuffer(*b);
1410                                 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1411                                 b = theBufferList().next(b);
1412                         } while (b != first); 
1413                         message(_("All documents saved."));
1414                         break;
1415                 }
1416
1417                 case LFUN_TOOLBAR_TOGGLE: {
1418                         string const name = cmd.getArg(0);
1419                         bool const allowauto = cmd.getArg(1) == "allowauto";
1420                         // it is possible to get current toolbar status like this,...
1421                         // but I decide to obey the order of ToolbarBackend::flags
1422                         // and disregard real toolbar status.
1423                         // toolbars_->saveToolbarInfo();
1424                         //
1425                         // toggle state on/off/auto
1426                         d.toolbars_->toggleToolbarState(name, allowauto);
1427                         // update toolbar
1428                         updateToolbars();
1429
1430                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1431                         if (!tbi) {
1432                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1433                                 break;
1434                         }
1435                         docstring state;
1436                         if (tbi->flags & ToolbarInfo::ON)
1437                                 state = _("on");
1438                         else if (tbi->flags & ToolbarInfo::OFF)
1439                                 state = _("off");
1440                         else if (tbi->flags & ToolbarInfo::AUTO)
1441                                 state = _("auto");
1442
1443                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1444                                            _(tbi->gui_name), state));
1445                         break;
1446                 }
1447
1448                 case LFUN_DIALOG_UPDATE: {
1449                         string const name = to_utf8(cmd.argument());
1450                         // Can only update a dialog connected to an existing inset
1451                         Inset * inset = getOpenInset(name);
1452                         if (inset) {
1453                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1454                                 inset->dispatch(view()->cursor(), fr);
1455                         } else if (name == "paragraph") {
1456                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1457                         } else if (name == "prefs") {
1458                                 updateDialog(name, string());
1459                         }
1460                         break;
1461                 }
1462
1463                 case LFUN_DIALOG_TOGGLE: {
1464                         if (isDialogVisible(cmd.getArg(0)))
1465                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1466                         else
1467                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1468                         break;
1469                 }
1470
1471                 case LFUN_DIALOG_DISCONNECT_INSET:
1472                         disconnectDialog(to_utf8(cmd.argument()));
1473                         break;
1474
1475                 case LFUN_DIALOG_HIDE: {
1476                         if (quitting)
1477                                 break;
1478                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1479                         break;
1480                 }
1481
1482                 case LFUN_DIALOG_SHOW: {
1483                         string const name = cmd.getArg(0);
1484                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1485
1486                         if (name == "character") {
1487                                 data = freefont2string();
1488                                 if (!data.empty())
1489                                         showDialog("character", data);
1490                         } else if (name == "latexlog") {
1491                                 Buffer::LogType type; 
1492                                 string const logfile = buffer()->logName(&type);
1493                                 switch (type) {
1494                                 case Buffer::latexlog:
1495                                         data = "latex ";
1496                                         break;
1497                                 case Buffer::buildlog:
1498                                         data = "literate ";
1499                                         break;
1500                                 }
1501                                 data += Lexer::quoteString(logfile);
1502                                 showDialog("log", data);
1503                         } else if (name == "vclog") {
1504                                 string const data = "vc " +
1505                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1506                                 showDialog("log", data);
1507                         } else
1508                                 showDialog(name, data);
1509                         break;
1510                 }
1511
1512                 case LFUN_INSET_APPLY: {
1513                         string const name = cmd.getArg(0);
1514                         Inset * inset = getOpenInset(name);
1515                         if (inset) {
1516                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1517                                 inset->dispatch(view()->cursor(), fr);
1518                         } else {
1519                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1520                                 lyx::dispatch(fr);
1521                         }
1522                         break;
1523                 }
1524
1525                 default:
1526                         return false;
1527         }
1528
1529         return true;
1530 }
1531
1532
1533 Buffer const * GuiView::updateInset(Inset const * inset)
1534 {
1535         if (!d.current_work_area_)
1536                 return 0;
1537
1538         if (inset)
1539                 d.current_work_area_->scheduleRedraw();
1540
1541         return &d.current_work_area_->bufferView().buffer();
1542 }
1543
1544
1545 void GuiView::restartCursor()
1546 {
1547         /* When we move around, or type, it's nice to be able to see
1548          * the cursor immediately after the keypress.
1549          */
1550         if (d.current_work_area_)
1551                 d.current_work_area_->startBlinkingCursor();
1552
1553         // Take this occasion to update the toobars and layout list.
1554         updateLayoutList();
1555         updateToolbars();
1556 }
1557
1558 namespace {
1559
1560 // This list should be kept in sync with the list of insets in
1561 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1562 // dialog should have the same name as the inset.
1563
1564 char const * const dialognames[] = {
1565 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1566 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1567 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1568 "mathdelimiter", "mathmatrix", "note", "paragraph",
1569 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1570
1571 #ifdef HAVE_LIBAIKSAURUS
1572 "thesaurus",
1573 #endif
1574
1575 "texinfo", "toc", "href", "view-source", "view-console", "vspace", "wrap", "listings" };
1576
1577 char const * const * const end_dialognames =
1578         dialognames + (sizeof(dialognames) / sizeof(char *));
1579
1580 class cmpCStr {
1581 public:
1582         cmpCStr(char const * name) : name_(name) {}
1583         bool operator()(char const * other) {
1584                 return strcmp(other, name_) == 0;
1585         }
1586 private:
1587         char const * name_;
1588 };
1589
1590
1591 bool isValidName(string const & name)
1592 {
1593         return std::find_if(dialognames, end_dialognames,
1594                             cmpCStr(name.c_str())) != end_dialognames;
1595 }
1596
1597 } // namespace anon
1598
1599
1600 void GuiView::resetDialogs()
1601 {
1602         // Make sure that no LFUN uses any LyXView.
1603         theLyXFunc().setLyXView(0);
1604         d.toolbars_->init();
1605         d.menubar_->init();
1606         if (d.layout_)
1607                 d.layout_->updateContents(true);
1608         // Now update controls with current buffer.
1609         theLyXFunc().setLyXView(this);
1610         restartCursor();
1611 }
1612
1613
1614 Dialog * GuiView::find_or_build(string const & name)
1615 {
1616         if (!isValidName(name))
1617                 return 0;
1618
1619         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1620
1621         if (it != d.dialogs_.end())
1622                 return it->second.get();
1623
1624         Dialog * dialog = build(name);
1625         d.dialogs_[name].reset(dialog);
1626         if (lyxrc.allow_geometry_session)
1627                 dialog->restoreSession();
1628         return dialog;
1629 }
1630
1631
1632 void GuiView::showDialog(string const & name, string const & data,
1633         Inset * inset)
1634 {
1635         if (d.in_show_)
1636                 return;
1637
1638         d.in_show_ = true;
1639         Dialog * dialog = find_or_build(name);
1640         if (dialog) {
1641                 dialog->showData(data);
1642                 if (inset)
1643                         d.open_insets_[name] = inset;
1644         }
1645         d.in_show_ = false;
1646 }
1647
1648
1649 bool GuiView::isDialogVisible(string const & name) const
1650 {
1651         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1652         if (it == d.dialogs_.end())
1653                 return false;
1654         return it->second.get()->isVisibleView();
1655 }
1656
1657
1658 void GuiView::hideDialog(string const & name, Inset * inset)
1659 {
1660         // Don't send the signal if we are quitting, because on MSVC it is
1661         // destructed before the cut stack in CutAndPaste.cpp, and this method
1662         // is called from some inset destructor if the cut stack is not empty
1663         // on exit.
1664         if (quitting)
1665                 return;
1666
1667         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1668         if (it == d.dialogs_.end())
1669                 return;
1670
1671         if (inset && inset != getOpenInset(name))
1672                 return;
1673
1674         Dialog * const dialog = it->second.get();
1675         if (dialog->isVisibleView())
1676                 dialog->hideView();
1677         d.open_insets_[name] = 0;
1678 }
1679
1680
1681 void GuiView::disconnectDialog(string const & name)
1682 {
1683         if (!isValidName(name))
1684                 return;
1685
1686         if (d.open_insets_.find(name) != d.open_insets_.end())
1687                 d.open_insets_[name] = 0;
1688 }
1689
1690
1691 Inset * GuiView::getOpenInset(string const & name) const
1692 {
1693         if (!isValidName(name))
1694                 return 0;
1695
1696         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1697         return it == d.open_insets_.end() ? 0 : it->second;
1698 }
1699
1700
1701 void GuiView::hideAll() const
1702 {
1703         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1704         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1705
1706         for(; it != end; ++it)
1707                 it->second->hideView();
1708 }
1709
1710
1711 void GuiView::hideBufferDependent() const
1712 {
1713         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1714         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1715
1716         for(; it != end; ++it) {
1717                 Dialog * dialog = it->second.get();
1718                 if (dialog->isBufferDependent())
1719                         dialog->hideView();
1720         }
1721 }
1722
1723
1724 void GuiView::updateBufferDependent(bool switched) const
1725 {
1726         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1727         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1728
1729         for(; it != end; ++it) {
1730                 Dialog * dialog = it->second.get();
1731                 if (!dialog->isVisibleView())
1732                         continue;
1733                 if (switched && dialog->isBufferDependent()) {
1734                         if (dialog->initialiseParams(""))
1735                                 dialog->updateView();
1736                         else
1737                                 dialog->hideView();
1738                 } else {
1739                         // A bit clunky, but the dialog will request
1740                         // that the kernel provides it with the necessary
1741                         // data.
1742                         dialog->updateDialog();
1743                 }
1744         }
1745 }
1746
1747
1748 void GuiView::checkStatus()
1749 {
1750         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1751         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1752
1753         for(; it != end; ++it) {
1754                 Dialog * const dialog = it->second.get();
1755                 if (dialog && dialog->isVisibleView())
1756                         dialog->checkStatus();
1757         }
1758 }
1759
1760
1761
1762 // will be replaced by a proper factory...
1763 Dialog * createGuiAbout(GuiView & lv);
1764 Dialog * createGuiBibitem(GuiView & lv);
1765 Dialog * createGuiBibtex(GuiView & lv);
1766 Dialog * createGuiBox(GuiView & lv);
1767 Dialog * createGuiBranch(GuiView & lv);
1768 Dialog * createGuiChanges(GuiView & lv);
1769 Dialog * createGuiCharacter(GuiView & lv);
1770 Dialog * createGuiCitation(GuiView & lv);
1771 Dialog * createGuiDelimiter(GuiView & lv);
1772 Dialog * createGuiDocument(GuiView & lv);
1773 Dialog * createGuiErrorList(GuiView & lv);
1774 Dialog * createGuiERT(GuiView & lv);
1775 Dialog * createGuiExternal(GuiView & lv);
1776 Dialog * createGuiFloat(GuiView & lv);
1777 Dialog * createGuiGraphics(GuiView & lv);
1778 Dialog * createGuiInclude(GuiView & lv);
1779 Dialog * createGuiIndex(GuiView & lv);
1780 Dialog * createGuiLabel(GuiView & lv);
1781 Dialog * createGuiListings(GuiView & lv);
1782 Dialog * createGuiLog(GuiView & lv);
1783 Dialog * createGuiMathMatrix(GuiView & lv);
1784 Dialog * createGuiNomenclature(GuiView & lv);
1785 Dialog * createGuiNote(GuiView & lv);
1786 Dialog * createGuiParagraph(GuiView & lv);
1787 Dialog * createGuiPreferences(GuiView & lv);
1788 Dialog * createGuiPrint(GuiView & lv);
1789 Dialog * createGuiRef(GuiView & lv);
1790 Dialog * createGuiSearch(GuiView & lv);
1791 Dialog * createGuiSendTo(GuiView & lv);
1792 Dialog * createGuiShowFile(GuiView & lv);
1793 Dialog * createGuiSpellchecker(GuiView & lv);
1794 Dialog * createGuiTabularCreate(GuiView & lv);
1795 Dialog * createGuiTabular(GuiView & lv);
1796 Dialog * createGuiTexInfo(GuiView & lv);
1797 Dialog * createGuiToc(GuiView & lv);
1798 Dialog * createGuiThesaurus(GuiView & lv);
1799 Dialog * createGuiHyperlink(GuiView & lv);
1800 Dialog * createGuiVSpace(GuiView & lv);
1801 Dialog * createGuiViewSource(GuiView & lv);
1802 Dialog * createGuiProgress(GuiView & lv);
1803 Dialog * createGuiWrap(GuiView & lv);
1804
1805
1806 Dialog * GuiView::build(string const & name)
1807 {
1808         BOOST_ASSERT(isValidName(name));
1809
1810         if (name == "aboutlyx")
1811                 return createGuiAbout(*this);
1812         if (name == "bibitem")
1813                 return createGuiBibitem(*this);
1814         if (name == "bibtex")
1815                 return createGuiBibtex(*this);
1816         if (name == "box")
1817                 return createGuiBox(*this);
1818         if (name == "branch")
1819                 return createGuiBranch(*this);
1820         if (name == "changes")
1821                 return createGuiChanges(*this);
1822         if (name == "character")
1823                 return createGuiCharacter(*this);
1824         if (name == "citation")
1825                 return createGuiCitation(*this);
1826         if (name == "document")
1827                 return createGuiDocument(*this);
1828         if (name == "errorlist")
1829                 return createGuiErrorList(*this);
1830         if (name == "ert")
1831                 return createGuiERT(*this);
1832         if (name == "external")
1833                 return createGuiExternal(*this);
1834         if (name == "file")
1835                 return createGuiShowFile(*this);
1836         if (name == "findreplace")
1837                 return createGuiSearch(*this);
1838         if (name == "float")
1839                 return createGuiFloat(*this);
1840         if (name == "graphics")
1841                 return createGuiGraphics(*this);
1842         if (name == "include")
1843                 return createGuiInclude(*this);
1844         if (name == "index")
1845                 return createGuiIndex(*this);
1846         if (name == "nomenclature")
1847                 return createGuiNomenclature(*this);
1848         if (name == "label")
1849                 return createGuiLabel(*this);
1850         if (name == "log")
1851                 return createGuiLog(*this);
1852         if (name == "view-source")
1853                 return createGuiViewSource(*this);
1854         if (name == "view-console")
1855                 return createGuiProgress(*this);
1856         if (name == "mathdelimiter")
1857                 return createGuiDelimiter(*this);
1858         if (name == "mathmatrix")
1859                 return createGuiMathMatrix(*this);
1860         if (name == "note")
1861                 return createGuiNote(*this);
1862         if (name == "paragraph")
1863                 return createGuiParagraph(*this);
1864         if (name == "prefs")
1865                 return createGuiPreferences(*this);
1866         if (name == "print")
1867                 return createGuiPrint(*this);
1868         if (name == "ref")
1869                 return createGuiRef(*this);
1870         if (name == "sendto")
1871                 return createGuiSendTo(*this);
1872         if (name == "spellchecker")
1873                 return createGuiSpellchecker(*this);
1874         if (name == "tabular")
1875                 return createGuiTabular(*this);
1876         if (name == "tabularcreate")
1877                 return createGuiTabularCreate(*this);
1878         if (name == "texinfo")
1879                 return createGuiTexInfo(*this);
1880 #ifdef HAVE_LIBAIKSAURUS
1881         if (name == "thesaurus")
1882                 return createGuiThesaurus(*this);
1883 #endif
1884         if (name == "toc")
1885                 return createGuiToc(*this);
1886         if (name == "href")
1887                 return createGuiHyperlink(*this);
1888         if (name == "vspace")
1889                 return createGuiVSpace(*this);
1890         if (name == "wrap")
1891                 return createGuiWrap(*this);
1892         if (name == "listings")
1893                 return createGuiListings(*this);
1894
1895         return 0;
1896 }
1897
1898
1899 } // namespace frontend
1900 } // namespace lyx
1901
1902 #include "GuiView_moc.cpp"