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