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