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