]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Split LyXFunc::menuNew() into LyXView::newDocument() and buffer_funcs::newUnnamedFile().
[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
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 std::endl;
96 using std::string;
97 using std::vector;
98
99 namespace lyx {
100
101 extern bool quitting;
102
103 namespace frontend {
104
105 using support::addPath;
106 using support::bformat;
107 using support::FileFilterList;
108 using support::FileName;
109 using support::makeAbsPath;
110 using support::makeDisplayPath;
111 using support::package;
112 using support::removeAutosaveFile;
113 using support::trim;
114
115 namespace {
116
117 class BackgroundWidget : public QWidget
118 {
119 public:
120         BackgroundWidget()
121         {
122                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
123                 /// The text to be written on top of the pixmap
124                 QString const text = lyx_version ? lyx_version : qt_("unknown version");
125                 splash_ = QPixmap(":/images/banner.png");
126
127                 QPainter pain(&splash_);
128                 pain.setPen(QColor(255, 255, 0));
129                 QFont font;
130                 // The font used to display the version info
131                 font.setStyleHint(QFont::SansSerif);
132                 font.setWeight(QFont::Bold);
133                 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
134                 pain.setFont(font);
135                 pain.drawText(260, 270, text);
136         }
137
138         void paintEvent(QPaintEvent *)
139         {
140                 int x = (width() - splash_.width()) / 2;
141                 int y = (height() - splash_.height()) / 2;
142                 QPainter pain(this);
143                 pain.drawPixmap(x, y, splash_);
144         }
145
146 private:
147         QPixmap splash_;
148 };
149
150 } // namespace anon
151
152
153 typedef boost::shared_ptr<Dialog> DialogPtr;
154
155 struct GuiView::GuiViewPrivate
156 {
157         GuiViewPrivate()
158                 : current_work_area_(0), layout_(0),
159                 quitting_by_menu_(false), autosave_timeout_(5000), in_show_(false)
160         {
161                 // hardcode here the platform specific icon size
162                 smallIconSize = 14;     // scaling problems
163                 normalIconSize = 20;    // ok, default
164                 bigIconSize = 26;               // better for some math icons
165
166                 splitter_ = new QSplitter;
167                 bg_widget_ = new BackgroundWidget;
168                 stack_widget_ = new QStackedWidget;
169                 stack_widget_->addWidget(bg_widget_);
170                 stack_widget_->addWidget(splitter_);
171                 setBackground();
172         }
173
174         ~GuiViewPrivate()
175         {
176                 delete splitter_;
177                 delete bg_widget_;
178                 delete stack_widget_;
179                 delete menubar_;
180                 delete toolbars_;
181         }
182
183         QMenu * toolBarPopup(GuiView * parent)
184         {
185                 // FIXME: translation
186                 QMenu * menu = new QMenu(parent);
187                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
188
189                 QAction * smallIcons = new QAction(iconSizeGroup);
190                 smallIcons->setText(qt_("Small-sized icons"));
191                 smallIcons->setCheckable(true);
192                 QObject::connect(smallIcons, SIGNAL(triggered()),
193                         parent, SLOT(smallSizedIcons()));
194                 menu->addAction(smallIcons);
195
196                 QAction * normalIcons = new QAction(iconSizeGroup);
197                 normalIcons->setText(qt_("Normal-sized icons"));
198                 normalIcons->setCheckable(true);
199                 QObject::connect(normalIcons, SIGNAL(triggered()),
200                         parent, SLOT(normalSizedIcons()));
201                 menu->addAction(normalIcons);
202
203                 QAction * bigIcons = new QAction(iconSizeGroup);
204                 bigIcons->setText(qt_("Big-sized icons"));
205                 bigIcons->setCheckable(true);
206                 QObject::connect(bigIcons, SIGNAL(triggered()),
207                         parent, SLOT(bigSizedIcons()));
208                 menu->addAction(bigIcons);
209
210                 unsigned int cur = parent->iconSize().width();
211                 if ( cur == parent->d.smallIconSize)
212                         smallIcons->setChecked(true);
213                 else if (cur == parent->d.normalIconSize)
214                         normalIcons->setChecked(true);
215                 else if (cur == parent->d.bigIconSize)
216                         bigIcons->setChecked(true);
217
218                 return menu;
219         }
220
221         void setBackground()
222         {
223                 stack_widget_->setCurrentWidget(bg_widget_);
224                 bg_widget_->setUpdatesEnabled(true);
225         }
226
227         TabWorkArea * tabWorkArea(int i)
228         {
229                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
230         }
231
232         TabWorkArea * currentTabWorkArea()
233         {
234                 if (splitter_->count() == 1)
235                         // The first TabWorkArea is always the first one, if any.
236                         return tabWorkArea(0);
237
238                 TabWorkArea * tab_widget = 0;
239                 for (int i = 0; i != splitter_->count(); ++i) {
240                         QWidget * w = splitter_->widget(i);
241                         if (!w->hasFocus())
242                                 continue;
243                         tab_widget = dynamic_cast<TabWorkArea *>(w);
244                         if (tab_widget)
245                                 break;
246                 }
247
248                 return tab_widget;
249         }
250
251 public:
252         GuiWorkArea * current_work_area_;
253         QSplitter * splitter_;
254         QStackedWidget * stack_widget_;
255         BackgroundWidget * bg_widget_;
256         /// view's menubar
257         GuiMenubar * menubar_;
258         /// view's toolbars
259         GuiToolbars * toolbars_;
260         /// The main layout box.
261         /** 
262          * \warning Don't Delete! The layout box is actually owned by
263          * whichever toolbar contains it. All the GuiView class needs is a
264          * means of accessing it.
265          *
266          * FIXME: replace that with a proper model so that we are not limited
267          * to only one dialog.
268          */
269         GuiLayoutBox * layout_;
270
271         ///
272         std::map<std::string, Inset *> open_insets_;
273
274         ///
275         std::map<std::string, DialogPtr> dialogs_;
276
277         unsigned int smallIconSize;
278         unsigned int normalIconSize;
279         unsigned int bigIconSize;
280         ///
281         QTimer statusbar_timer_;
282         /// are we quitting by the menu?
283         bool quitting_by_menu_;
284         /// auto-saving of buffers
285         Timeout autosave_timeout_;
286         /// flag against a race condition due to multiclicks, see bug #1119
287         bool in_show_;
288 };
289
290
291 GuiView::GuiView(int id)
292         : d(*new GuiViewPrivate), id_(id)
293 {
294         // GuiToolbars *must* be initialised before GuiMenubar.
295         d.toolbars_ = new GuiToolbars(*this);
296         d.menubar_ = new GuiMenubar(this, menubackend);
297
298         setCentralWidget(d.stack_widget_);
299
300         // Start autosave timer
301         if (lyxrc.autosave) {
302                 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
303                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
304                 d.autosave_timeout_.start();
305         }
306         connect(&d.statusbar_timer_, SIGNAL(timeout()),
307                 this, SLOT(clearMessage()));
308
309         // Qt bug? signal lastWindowClosed does not work
310         setAttribute(Qt::WA_QuitOnClose, false);
311         setAttribute(Qt::WA_DeleteOnClose, true);
312 #ifndef Q_WS_MACX
313         // assign an icon to main form. We do not do it under Qt/Mac,
314         // since the icon is provided in the application bundle.
315         setWindowIcon(QPixmap(":/images/lyx.png"));
316 #endif
317
318         // For Drag&Drop.
319         setAcceptDrops(true);
320
321         statusBar()->setSizeGripEnabled(true);
322
323         // Forbid too small unresizable window because it can happen
324         // with some window manager under X11.
325         setMinimumSize(300, 200);
326
327         if (!lyxrc.allow_geometry_session)
328                 // No session handling, default to a sane size.
329                 setGeometry(50, 50, 690, 510);
330
331         // Now take care of session management.
332         QSettings settings;
333         QString const key = "view-" + QString::number(id_);
334 #ifdef Q_WS_X11
335         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
336         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
337         resize(size);
338         move(pos);
339 #else
340         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
341                 setGeometry(50, 50, 690, 510);
342 #endif
343         setIconSize(settings.value(key + "/icon_size").toSize());
344 }
345
346
347 GuiView::~GuiView()
348 {
349         delete &d;
350 }
351
352
353 void GuiView::close()
354 {
355         d.quitting_by_menu_ = true;
356         d.current_work_area_ = 0;
357         for (int i = 0; i != d.splitter_->count(); ++i) {
358                 TabWorkArea * twa = d.tabWorkArea(i);
359                 if (twa)
360                         twa->closeAll();
361         }
362         QMainWindow::close();
363         d.quitting_by_menu_ = false;
364 }
365
366
367 void GuiView::setFocus()
368 {
369         if (d.current_work_area_)
370                 d.current_work_area_->setFocus();
371         else
372                 QWidget::setFocus();
373 }
374
375
376 QMenu * GuiView::createPopupMenu()
377 {
378         return d.toolBarPopup(this);
379 }
380
381
382 void GuiView::showEvent(QShowEvent * e)
383 {
384         LYXERR(Debug::GUI, "Passed Geometry "
385                 << size().height() << "x" << size().width()
386                 << "+" << pos().x() << "+" << pos().y());
387
388         if (d.splitter_->count() == 0)
389                 // No work area, switch to the background widget.
390                 d.setBackground();
391
392         QMainWindow::showEvent(e);
393 }
394
395
396 void GuiView::closeEvent(QCloseEvent * close_event)
397 {
398         // we may have been called through the close window button
399         // which bypasses the LFUN machinery.
400         if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
401                 if (!quitWriteAll()) {
402                         close_event->ignore();
403                         return;
404                 }
405         }
406
407         // Make sure that no LFUN use this close to be closed View.
408         theLyXFunc().setLyXView(0);
409         // Make sure the timer time out will not trigger a statusbar update.
410         d.statusbar_timer_.stop();
411
412         if (lyxrc.allow_geometry_session) {
413                 QSettings settings;
414                 QString const key = "view-" + QString::number(id_);
415 #ifdef Q_WS_X11
416                 settings.setValue(key + "/pos", pos());
417                 settings.setValue(key + "/size", size());
418 #else
419                 settings.setValue(key + "/geometry", saveGeometry());
420 #endif
421                 settings.setValue(key + "/icon_size", iconSize());
422                 d.toolbars_->saveToolbarInfo();
423         }
424
425         guiApp->unregisterView(id_);
426         if (guiApp->viewCount() > 0) {
427                 // Just close the window and do nothing else if this is not the
428                 // last window.
429                 close_event->accept();
430                 return;
431         }
432
433         quitting = true;
434
435         // this is the place where we leave the frontend.
436         // it is the only point at which we start quitting.
437         close_event->accept();
438         // quit the event loop
439         qApp->quit();
440 }
441
442
443 void GuiView::dragEnterEvent(QDragEnterEvent * event)
444 {
445         if (event->mimeData()->hasUrls())
446                 event->accept();
447         /// \todo Ask lyx-devel is this is enough:
448         /// if (event->mimeData()->hasFormat("text/plain"))
449         ///     event->acceptProposedAction();
450 }
451
452
453 void GuiView::dropEvent(QDropEvent* event)
454 {
455         QList<QUrl> files = event->mimeData()->urls();
456         if (files.isEmpty())
457                 return;
458
459         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
460         for (int i = 0; i != files.size(); ++i) {
461                 string const file = support::os::internal_path(fromqstr(
462                         files.at(i).toLocalFile()));
463                 if (!file.empty())
464                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
465         }
466 }
467
468
469 void GuiView::message(docstring const & str)
470 {
471         statusBar()->showMessage(toqstr(str));
472         d.statusbar_timer_.stop();
473         d.statusbar_timer_.start(3000);
474 }
475
476
477 void GuiView::smallSizedIcons()
478 {
479         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
480 }
481
482
483 void GuiView::normalSizedIcons()
484 {
485         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
486 }
487
488
489 void GuiView::bigSizedIcons()
490 {
491         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
492 }
493
494
495 void GuiView::clearMessage()
496 {
497         if (!hasFocus())
498                 return;
499         theLyXFunc().setLyXView(this);
500         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
501         d.statusbar_timer_.stop();
502 }
503
504
505 void GuiView::updateWindowTitle(GuiWorkArea * wa)
506 {
507         if (wa != d.current_work_area_)
508                 return;
509         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
510         setWindowIconText(wa->windowIconText());
511 }
512
513
514 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
515 {
516         disconnectBuffer();
517         disconnectBufferView();
518         connectBufferView(wa->bufferView());
519         connectBuffer(wa->bufferView().buffer());
520         d.current_work_area_ = wa;
521         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
522                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
523         updateWindowTitle(wa);
524
525         updateToc();
526         // Buffer-dependent dialogs should be updated or
527         // hidden. This should go here because some dialogs (eg ToC)
528         // require bv_->text.
529         updateBufferDependent(true);
530         updateToolbars();
531         updateLayoutList();
532         updateStatusBar();
533 }
534
535
536 void GuiView::updateStatusBar()
537 {
538         // let the user see the explicit message
539         if (d.statusbar_timer_.isActive())
540                 return;
541
542         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
543 }
544
545
546 bool GuiView::hasFocus() const
547 {
548         return qApp->activeWindow() == this;
549 }
550
551
552 bool GuiView::event(QEvent * e)
553 {
554         switch (e->type())
555         {
556         // Useful debug code:
557         //case QEvent::ActivationChange:
558         //case QEvent::WindowDeactivate:
559         //case QEvent::Paint:
560         //case QEvent::Enter:
561         //case QEvent::Leave:
562         //case QEvent::HoverEnter:
563         //case QEvent::HoverLeave:
564         //case QEvent::HoverMove:
565         //case QEvent::StatusTip:
566         //case QEvent::DragEnter:
567         //case QEvent::DragLeave:
568         //case QEvent::Drop:
569         //      break;
570
571         case QEvent::WindowActivate: {
572                 guiApp->setCurrentView(*this);
573                 if (d.current_work_area_) {
574                         BufferView & bv = d.current_work_area_->bufferView();
575                         connectBufferView(bv);
576                         connectBuffer(bv.buffer());
577                         // The document structure, name and dialogs might have
578                         // changed in another view.
579                         updateBufferDependent(true);
580                 } else {
581                         setWindowTitle(qt_("LyX"));
582                         setWindowIconText(qt_("LyX"));
583                 }
584                 return QMainWindow::event(e);
585         }
586         case QEvent::ShortcutOverride: {
587                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
588                 if (!d.current_work_area_) {
589                         theLyXFunc().setLyXView(this);
590                         KeySymbol sym;
591                         setKeySymbol(&sym, ke);
592                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
593                         e->accept();
594                         return true;
595                 }
596                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
597                         KeySymbol sym;
598                         setKeySymbol(&sym, ke);
599                         d.current_work_area_->processKeySym(sym, NoModifier);
600                         e->accept();
601                         return true;
602                 }
603         }
604         default:
605                 return QMainWindow::event(e);
606         }
607 }
608
609
610 bool GuiView::focusNextPrevChild(bool /*next*/)
611 {
612         setFocus();
613         return true;
614 }
615
616
617 void GuiView::setBusy(bool yes)
618 {
619         if (d.current_work_area_) {
620                 d.current_work_area_->setUpdatesEnabled(!yes);
621                 if (yes)
622                         d.current_work_area_->stopBlinkingCursor();
623                 else
624                         d.current_work_area_->startBlinkingCursor();
625         }
626
627         if (yes)
628                 QApplication::setOverrideCursor(Qt::WaitCursor);
629         else
630                 QApplication::restoreOverrideCursor();
631 }
632
633
634 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
635 {
636         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
637
638         if (tbinfo.flags & ToolbarInfo::TOP) {
639                 if (newline)
640                         addToolBarBreak(Qt::TopToolBarArea);
641                 addToolBar(Qt::TopToolBarArea, toolBar);
642         }
643
644         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
645 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
646 #if (QT_VERSION >= 0x040202)
647                 if (newline)
648                         addToolBarBreak(Qt::BottomToolBarArea);
649 #endif
650                 addToolBar(Qt::BottomToolBarArea, toolBar);
651         }
652
653         if (tbinfo.flags & ToolbarInfo::LEFT) {
654 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
655 #if (QT_VERSION >= 0x040202)
656                 if (newline)
657                         addToolBarBreak(Qt::LeftToolBarArea);
658 #endif
659                 addToolBar(Qt::LeftToolBarArea, toolBar);
660         }
661
662         if (tbinfo.flags & ToolbarInfo::RIGHT) {
663 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
664 #if (QT_VERSION >= 0x040202)
665                 if (newline)
666                         addToolBarBreak(Qt::RightToolBarArea);
667 #endif
668                 addToolBar(Qt::RightToolBarArea, toolBar);
669         }
670
671         // The following does not work so I cannot restore to exact toolbar location
672         /*
673         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
674         toolBar->move(tbinfo.posx, tbinfo.posy);
675         */
676
677         return toolBar;
678 }
679
680
681 GuiWorkArea * GuiView::workArea(Buffer & buffer)
682 {
683         for (int i = 0; i != d.splitter_->count(); ++i) {
684                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
685                 if (wa)
686                         return wa;
687         }
688         return 0;
689 }
690
691
692 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
693 {
694
695         // Automatically create a TabWorkArea if there are none yet.
696         if (!d.splitter_->count())
697                 addTabWorkArea();
698
699         TabWorkArea * tab_widget = d.currentTabWorkArea();
700         return tab_widget->addWorkArea(buffer, *this);
701 }
702
703
704 void GuiView::addTabWorkArea()
705 {
706         TabWorkArea * twa = new TabWorkArea;
707         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
708                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
709         d.splitter_->addWidget(twa);
710         d.stack_widget_->setCurrentWidget(d.splitter_);
711 }
712
713
714 GuiWorkArea const * GuiView::currentWorkArea() const
715 {
716         return d.current_work_area_;
717 }
718
719
720 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
721 {
722         BOOST_ASSERT(wa);
723
724         // Changing work area can result from opening a file so
725         // update the toc in any case.
726         updateToc();
727
728         d.current_work_area_ = wa;
729         for (int i = 0; i != d.splitter_->count(); ++i) {
730                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
731                         return;
732         }
733 }
734
735
736 void GuiView::removeWorkArea(GuiWorkArea * wa)
737 {
738         BOOST_ASSERT(wa);
739         if (wa == d.current_work_area_) {
740                 disconnectBuffer();
741                 disconnectBufferView();
742                 hideBufferDependent();
743                 d.current_work_area_ = 0;
744         }
745
746         for (int i = 0; i != d.splitter_->count(); ++i) {
747                 TabWorkArea * twa = d.tabWorkArea(i);
748                 if (!twa->removeWorkArea(wa))
749                         // Not found in this tab group.
750                         continue;
751
752                 // We found and removed the GuiWorkArea.
753                 if (!twa->count()) {
754                         // No more WorkAreas in this tab group, so delete it.
755                         delete twa;
756                         break;
757                 }
758
759                 if (d.current_work_area_)
760                         // This means that we are not closing the current GuiWorkArea;
761                         break;
762
763                 // Switch to the next GuiWorkArea in the found TabWorkArea.
764                 d.current_work_area_ = twa->currentWorkArea();
765                 break;
766         }
767
768         if (d.splitter_->count() == 0)
769                 // No more work area, switch to the background widget.
770                 d.setBackground();
771 }
772
773
774 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
775 {
776         d.layout_ = layout;
777 }
778
779
780 void GuiView::updateLayoutList()
781 {
782         if (d.layout_)
783                 d.layout_->updateContents(false);
784 }
785
786
787 void GuiView::updateToolbars()
788 {
789         if (d.current_work_area_) {
790                 bool const math =
791                         d.current_work_area_->bufferView().cursor().inMathed();
792                 bool const table =
793                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
794                 bool const review =
795                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
796                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
797
798                 d.toolbars_->update(math, table, review);
799         } else
800                 d.toolbars_->update(false, false, false);
801
802         // update read-only status of open dialogs.
803         checkStatus();
804 }
805
806
807 Buffer * GuiView::buffer()
808 {
809         if (d.current_work_area_)
810                 return &d.current_work_area_->bufferView().buffer();
811         return 0;
812 }
813
814
815 Buffer const * GuiView::buffer() const
816 {
817         if (d.current_work_area_)
818                 return &d.current_work_area_->bufferView().buffer();
819         return 0;
820 }
821
822
823 void GuiView::setBuffer(Buffer * newBuffer)
824 {
825         BOOST_ASSERT(newBuffer);
826         setBusy(true);
827
828         GuiWorkArea * wa = workArea(*newBuffer);
829         if (wa == 0) {
830                 updateLabels(*newBuffer->masterBuffer());
831                 wa = addWorkArea(*newBuffer);
832         } else {
833                 //Disconnect the old buffer...there's no new one.
834                 disconnectBuffer();
835         }
836         connectBuffer(*newBuffer);
837         connectBufferView(wa->bufferView());
838         setCurrentWorkArea(wa);
839
840         setBusy(false);
841 }
842
843
844 void GuiView::connectBuffer(Buffer & buf)
845 {
846         buf.setGuiDelegate(this);
847 }
848
849
850 void GuiView::disconnectBuffer()
851 {
852         if (d.current_work_area_)
853                 d.current_work_area_->bufferView().setGuiDelegate(0);
854 }
855
856
857 void GuiView::connectBufferView(BufferView & bv)
858 {
859         bv.setGuiDelegate(this);
860 }
861
862
863 void GuiView::disconnectBufferView()
864 {
865         if (d.current_work_area_)
866                 d.current_work_area_->bufferView().setGuiDelegate(0);
867 }
868
869
870 void GuiView::errors(string const & error_type)
871 {
872         ErrorList & el = buffer()->errorList(error_type);
873         if (!el.empty())
874                 showDialog("errorlist", error_type);
875 }
876
877
878 void GuiView::updateDialog(string const & name, string const & data)
879 {
880         if (!isDialogVisible(name))
881                 return;
882
883         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
884         if (it == d.dialogs_.end())
885                 return;
886
887         Dialog * const dialog = it->second.get();
888         if (dialog->isVisibleView())
889                 dialog->updateData(data);
890 }
891
892
893 BufferView * GuiView::view()
894 {
895         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
896 }
897
898
899 void GuiView::updateToc()
900 {
901         updateDialog("toc", "");
902 }
903
904
905 void GuiView::updateEmbeddedFiles()
906 {
907         updateDialog("embedding", "");
908 }
909
910
911 void GuiView::autoSave()
912 {
913         LYXERR(Debug::INFO, "Running autoSave()");
914
915         if (buffer())
916                 view()->buffer().autoSave();
917 }
918
919
920 void GuiView::resetAutosaveTimers()
921 {
922         if (lyxrc.autosave)
923                 d.autosave_timeout_.restart();
924 }
925
926
927 FuncStatus GuiView::getStatus(FuncRequest const & cmd)
928 {
929         FuncStatus flag;
930         bool enable = true;
931         Buffer * buf = buffer();
932
933         /* In LyX/Mac, when a dialog is open, the menus of the
934            application can still be accessed without giving focus to
935            the main window. In this case, we want to disable the menu
936            entries that are buffer-related.
937
938            Note that this code is not perfect, as bug 1941 attests:
939            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
940         */
941         if (cmd.origin == FuncRequest::MENU && !hasFocus())
942                 buf = 0;
943
944         switch(cmd.action) {
945         case LFUN_BUFFER_WRITE:
946                 enable = buf && (buf->isUnnamed() || !buf->isClean());
947                 break;
948
949         case LFUN_BUFFER_WRITE_AS:
950                 enable = buf;
951                 break;
952
953         case LFUN_TOOLBAR_TOGGLE:
954                 flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
955                 break;
956
957         case LFUN_DIALOG_TOGGLE:
958                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
959                 // fall through to set "enable"
960         case LFUN_DIALOG_SHOW: {
961                 string const name = cmd.getArg(0);
962                 if (!buf)
963                         enable = name == "aboutlyx"
964                                 || name == "file" //FIXME: should be removed.
965                                 || name == "prefs"
966                                 || name == "texinfo";
967                 else if (name == "print")
968                         enable = buf->isExportable("dvi")
969                                 && lyxrc.print_command != "none";
970                 else if (name == "character") {
971                         if (!view())
972                                 enable = false;
973                         else {
974                                 InsetCode ic = view()->cursor().inset().lyxCode();
975                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
976                         }
977                 }
978                 else if (name == "latexlog")
979                         enable = FileName(buf->logName()).isReadableFile();
980                 else if (name == "spellchecker")
981 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
982                         enable = !buf->isReadonly();
983 #else
984                         enable = false;
985 #endif
986                 else if (name == "vclog")
987                         enable = buf->lyxvc().inUse();
988                 break;
989         }
990
991         case LFUN_DIALOG_UPDATE: {
992                 string const name = cmd.getArg(0);
993                 if (!buf)
994                         enable = name == "prefs";
995                 break;
996         }
997
998         case LFUN_INSET_APPLY: {
999                 if (!buf) {
1000                         enable = false;
1001                         break;
1002                 }
1003                 string const name = cmd.getArg(0);
1004                 Inset * inset = getOpenInset(name);
1005                 if (inset) {
1006                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1007                         FuncStatus fs;
1008                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1009                                 // Every inset is supposed to handle this
1010                                 BOOST_ASSERT(false);
1011                         }
1012                         flag |= fs;
1013                 } else {
1014                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1015                         flag |= getStatus(fr);
1016                 }
1017                 enable = flag.enabled();
1018                 break;
1019         }
1020
1021         default:
1022                 if (!view()) {
1023                         enable = false;
1024                         break;
1025                 }
1026         }
1027
1028         if (!enable)
1029                 flag.enabled(false);
1030
1031         return flag;
1032 }
1033
1034
1035 static FileName selectTemplateFile()
1036 {
1037         FileDialog dlg(_("Select template file"));
1038         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1039         dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1040
1041         FileDialog::Result result =
1042                 dlg.open(from_utf8(lyxrc.template_path),
1043                              FileFilterList(_("LyX Documents (*.lyx)")),
1044                              docstring());
1045
1046         if (result.first == FileDialog::Later)
1047                 return FileName();
1048         if (result.second.empty())
1049                 return FileName();
1050         return FileName(to_utf8(result.second));
1051 }
1052
1053
1054 void GuiView::newDocument(string const & filename, bool from_template)
1055 {
1056         FileName initpath;
1057         Buffer * buf = buffer();
1058         if (buf) {
1059                 FileName const trypath(buf->filePath());
1060                 // If directory is writeable, use this as default.
1061                 if (trypath.isDirWritable())
1062                         initpath = trypath;
1063         } else
1064                 initpath.set(lyxrc.document_path);
1065
1066         // FIXME: Up to now initpath was unconditionally set to the user document
1067         // path. Is it what we want? If yes, erase the code above.
1068         initpath.set(lyxrc.document_path);
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", "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         d.dialogs_[name].reset(build(name));
1627         return d.dialogs_[name].get();
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->hide();
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->hide();
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->hide();
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 (switched && dialog->isBufferDependent()) {
1731                         if (dialog->isVisibleView() && dialog->initialiseParams(""))
1732                                 dialog->updateView();
1733                         else
1734                                 dialog->hide();
1735                 } else {
1736                         // A bit clunky, but the dialog will request
1737                         // that the kernel provides it with the necessary
1738                         // data.
1739                         dialog->updateDialog();
1740                 }
1741         }
1742 }
1743
1744
1745 void GuiView::checkStatus()
1746 {
1747         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1748         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1749
1750         for(; it != end; ++it) {
1751                 Dialog * const dialog = it->second.get();
1752                 if (dialog && dialog->isVisibleView())
1753                         dialog->checkStatus();
1754         }
1755 }
1756
1757
1758
1759 // will be replaced by a proper factory...
1760 Dialog * createGuiAbout(GuiView & lv);
1761 Dialog * createGuiBibitem(GuiView & lv);
1762 Dialog * createGuiBibtex(GuiView & lv);
1763 Dialog * createGuiBox(GuiView & lv);
1764 Dialog * createGuiBranch(GuiView & lv);
1765 Dialog * createGuiChanges(GuiView & lv);
1766 Dialog * createGuiCharacter(GuiView & lv);
1767 Dialog * createGuiCitation(GuiView & lv);
1768 Dialog * createGuiDelimiter(GuiView & lv);
1769 Dialog * createGuiDocument(GuiView & lv);
1770 Dialog * createGuiErrorList(GuiView & lv);
1771 Dialog * createGuiERT(GuiView & lv);
1772 Dialog * createGuiExternal(GuiView & lv);
1773 Dialog * createGuiFloat(GuiView & lv);
1774 Dialog * createGuiGraphics(GuiView & lv);
1775 Dialog * createGuiInclude(GuiView & lv);
1776 Dialog * createGuiIndex(GuiView & lv);
1777 Dialog * createGuiLabel(GuiView & lv);
1778 Dialog * createGuiListings(GuiView & lv);
1779 Dialog * createGuiLog(GuiView & lv);
1780 Dialog * createGuiMathMatrix(GuiView & lv);
1781 Dialog * createGuiNomenclature(GuiView & lv);
1782 Dialog * createGuiNote(GuiView & lv);
1783 Dialog * createGuiParagraph(GuiView & lv);
1784 Dialog * createGuiPreferences(GuiView & lv);
1785 Dialog * createGuiPrint(GuiView & lv);
1786 Dialog * createGuiRef(GuiView & lv);
1787 Dialog * createGuiSearch(GuiView & lv);
1788 Dialog * createGuiSendTo(GuiView & lv);
1789 Dialog * createGuiShowFile(GuiView & lv);
1790 Dialog * createGuiSpellchecker(GuiView & lv);
1791 Dialog * createGuiTabularCreate(GuiView & lv);
1792 Dialog * createGuiTabular(GuiView & lv);
1793 Dialog * createGuiTexInfo(GuiView & lv);
1794 Dialog * createGuiToc(GuiView & lv);
1795 Dialog * createGuiThesaurus(GuiView & lv);
1796 Dialog * createGuiHyperlink(GuiView & lv);
1797 Dialog * createGuiVSpace(GuiView & lv);
1798 Dialog * createGuiViewSource(GuiView & lv);
1799 Dialog * createGuiWrap(GuiView & lv);
1800
1801
1802 Dialog * GuiView::build(string const & name)
1803 {
1804         BOOST_ASSERT(isValidName(name));
1805
1806         if (name == "aboutlyx")
1807                 return createGuiAbout(*this);
1808         if (name == "bibitem")
1809                 return createGuiBibitem(*this);
1810         if (name == "bibtex")
1811                 return createGuiBibtex(*this);
1812         if (name == "box")
1813                 return createGuiBox(*this);
1814         if (name == "branch")
1815                 return createGuiBranch(*this);
1816         if (name == "changes")
1817                 return createGuiChanges(*this);
1818         if (name == "character")
1819                 return createGuiCharacter(*this);
1820         if (name == "citation")
1821                 return createGuiCitation(*this);
1822         if (name == "document")
1823                 return createGuiDocument(*this);
1824         if (name == "errorlist")
1825                 return createGuiErrorList(*this);
1826         if (name == "ert")
1827                 return createGuiERT(*this);
1828         if (name == "external")
1829                 return createGuiExternal(*this);
1830         if (name == "file")
1831                 return createGuiShowFile(*this);
1832         if (name == "findreplace")
1833                 return createGuiSearch(*this);
1834         if (name == "float")
1835                 return createGuiFloat(*this);
1836         if (name == "graphics")
1837                 return createGuiGraphics(*this);
1838         if (name == "include")
1839                 return createGuiInclude(*this);
1840         if (name == "index")
1841                 return createGuiIndex(*this);
1842         if (name == "nomenclature")
1843                 return createGuiNomenclature(*this);
1844         if (name == "label")
1845                 return createGuiLabel(*this);
1846         if (name == "log")
1847                 return createGuiLog(*this);
1848         if (name == "view-source")
1849                 return createGuiViewSource(*this);
1850         if (name == "mathdelimiter")
1851                 return createGuiDelimiter(*this);
1852         if (name == "mathmatrix")
1853                 return createGuiMathMatrix(*this);
1854         if (name == "note")
1855                 return createGuiNote(*this);
1856         if (name == "paragraph")
1857                 return createGuiParagraph(*this);
1858         if (name == "prefs")
1859                 return createGuiPreferences(*this);
1860         if (name == "print")
1861                 return createGuiPrint(*this);
1862         if (name == "ref")
1863                 return createGuiRef(*this);
1864         if (name == "sendto")
1865                 return createGuiSendTo(*this);
1866         if (name == "spellchecker")
1867                 return createGuiSpellchecker(*this);
1868         if (name == "tabular")
1869                 return createGuiTabular(*this);
1870         if (name == "tabularcreate")
1871                 return createGuiTabularCreate(*this);
1872         if (name == "texinfo")
1873                 return createGuiTexInfo(*this);
1874 #ifdef HAVE_LIBAIKSAURUS
1875         if (name == "thesaurus")
1876                 return createGuiThesaurus(*this);
1877 #endif
1878         if (name == "toc")
1879                 return createGuiToc(*this);
1880         if (name == "href")
1881                 return createGuiHyperlink(*this);
1882         if (name == "vspace")
1883                 return createGuiVSpace(*this);
1884         if (name == "wrap")
1885                 return createGuiWrap(*this);
1886         if (name == "listings")
1887                 return createGuiListings(*this);
1888
1889         return 0;
1890 }
1891
1892
1893 } // namespace frontend
1894 } // namespace lyx
1895
1896 #include "GuiView_moc.cpp"