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