]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
* cosmetic
[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                 bool const mathmacrotemplate =
791                         lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
792
793                 d.toolbars_->update(math, table, review, mathmacrotemplate);
794         } else
795                 d.toolbars_->update(false, false, false, false);
796
797         // update read-only status of open dialogs.
798         checkStatus();
799 }
800
801
802 Buffer * GuiView::buffer()
803 {
804         if (d.current_work_area_)
805                 return &d.current_work_area_->bufferView().buffer();
806         return 0;
807 }
808
809
810 Buffer const * GuiView::buffer() const
811 {
812         if (d.current_work_area_)
813                 return &d.current_work_area_->bufferView().buffer();
814         return 0;
815 }
816
817
818 void GuiView::setBuffer(Buffer * newBuffer)
819 {
820         BOOST_ASSERT(newBuffer);
821         setBusy(true);
822
823         GuiWorkArea * wa = workArea(*newBuffer);
824         if (wa == 0) {
825                 updateLabels(*newBuffer->masterBuffer());
826                 wa = addWorkArea(*newBuffer);
827         } else {
828                 //Disconnect the old buffer...there's no new one.
829                 disconnectBuffer();
830         }
831         connectBuffer(*newBuffer);
832         connectBufferView(wa->bufferView());
833         setCurrentWorkArea(wa);
834
835         setBusy(false);
836 }
837
838
839 void GuiView::connectBuffer(Buffer & buf)
840 {
841         buf.setGuiDelegate(this);
842 }
843
844
845 void GuiView::disconnectBuffer()
846 {
847         if (d.current_work_area_)
848                 d.current_work_area_->bufferView().setGuiDelegate(0);
849 }
850
851
852 void GuiView::connectBufferView(BufferView & bv)
853 {
854         bv.setGuiDelegate(this);
855 }
856
857
858 void GuiView::disconnectBufferView()
859 {
860         if (d.current_work_area_)
861                 d.current_work_area_->bufferView().setGuiDelegate(0);
862 }
863
864
865 void GuiView::errors(string const & error_type)
866 {
867         ErrorList & el = buffer()->errorList(error_type);
868         if (!el.empty())
869                 showDialog("errorlist", error_type);
870 }
871
872
873 void GuiView::updateDialog(string const & name, string const & data)
874 {
875         if (!isDialogVisible(name))
876                 return;
877
878         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
879         if (it == d.dialogs_.end())
880                 return;
881
882         Dialog * const dialog = it->second.get();
883         if (dialog->isVisibleView())
884                 dialog->updateData(data);
885 }
886
887
888 BufferView * GuiView::view()
889 {
890         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
891 }
892
893
894 void GuiView::updateToc()
895 {
896         updateDialog("toc", "");
897 }
898
899
900 void GuiView::updateEmbeddedFiles()
901 {
902         updateDialog("embedding", "");
903 }
904
905
906 void GuiView::autoSave()
907 {
908         LYXERR(Debug::INFO, "Running autoSave()");
909
910         if (buffer())
911                 view()->buffer().autoSave();
912 }
913
914
915 void GuiView::resetAutosaveTimers()
916 {
917         if (lyxrc.autosave)
918                 d.autosave_timeout_.restart();
919 }
920
921
922 FuncStatus GuiView::getStatus(FuncRequest const & cmd)
923 {
924         FuncStatus flag;
925         bool enable = true;
926         Buffer * buf = buffer();
927
928         /* In LyX/Mac, when a dialog is open, the menus of the
929            application can still be accessed without giving focus to
930            the main window. In this case, we want to disable the menu
931            entries that are buffer-related.
932
933            Note that this code is not perfect, as bug 1941 attests:
934            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
935         */
936         if (cmd.origin == FuncRequest::MENU && !hasFocus())
937                 buf = 0;
938
939         switch(cmd.action) {
940         case LFUN_BUFFER_WRITE:
941                 enable = buf && (buf->isUnnamed() || !buf->isClean());
942                 break;
943
944         case LFUN_BUFFER_WRITE_AS:
945                 enable = buf;
946                 break;
947
948         case LFUN_TOOLBAR_TOGGLE:
949                 flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
950                 break;
951
952         case LFUN_DIALOG_TOGGLE:
953                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
954                 // fall through to set "enable"
955         case LFUN_DIALOG_SHOW: {
956                 string const name = cmd.getArg(0);
957                 if (!buf)
958                         enable = name == "aboutlyx"
959                                 || name == "file" //FIXME: should be removed.
960                                 || name == "prefs"
961                                 || name == "texinfo";
962                 else if (name == "print")
963                         enable = buf->isExportable("dvi")
964                                 && lyxrc.print_command != "none";
965                 else if (name == "character") {
966                         if (!view())
967                                 enable = false;
968                         else {
969                                 InsetCode ic = view()->cursor().inset().lyxCode();
970                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
971                         }
972                 }
973                 else if (name == "latexlog")
974                         enable = FileName(buf->logName()).isReadableFile();
975                 else if (name == "spellchecker")
976 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
977                         enable = !buf->isReadonly();
978 #else
979                         enable = false;
980 #endif
981                 else if (name == "vclog")
982                         enable = buf->lyxvc().inUse();
983                 break;
984         }
985
986         case LFUN_DIALOG_UPDATE: {
987                 string const name = cmd.getArg(0);
988                 if (!buf)
989                         enable = name == "prefs";
990                 break;
991         }
992
993         case LFUN_INSET_APPLY: {
994                 if (!buf) {
995                         enable = false;
996                         break;
997                 }
998                 string const name = cmd.getArg(0);
999                 Inset * inset = getOpenInset(name);
1000                 if (inset) {
1001                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1002                         FuncStatus fs;
1003                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1004                                 // Every inset is supposed to handle this
1005                                 BOOST_ASSERT(false);
1006                         }
1007                         flag |= fs;
1008                 } else {
1009                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1010                         flag |= getStatus(fr);
1011                 }
1012                 enable = flag.enabled();
1013                 break;
1014         }
1015
1016         default:
1017                 if (!view()) {
1018                         enable = false;
1019                         break;
1020                 }
1021         }
1022
1023         if (!enable)
1024                 flag.enabled(false);
1025
1026         return flag;
1027 }
1028
1029
1030 static FileName selectTemplateFile()
1031 {
1032         FileDialog dlg(_("Select template file"));
1033         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1034         dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1035
1036         FileDialog::Result result =
1037                 dlg.open(from_utf8(lyxrc.template_path),
1038                              FileFilterList(_("LyX Documents (*.lyx)")),
1039                              docstring());
1040
1041         if (result.first == FileDialog::Later)
1042                 return FileName();
1043         if (result.second.empty())
1044                 return FileName();
1045         return FileName(to_utf8(result.second));
1046 }
1047
1048
1049 void GuiView::newDocument(string const & filename, bool from_template)
1050 {
1051         FileName initpath(lyxrc.document_path);
1052         Buffer * buf = buffer();
1053         if (buf) {
1054                 FileName const trypath(buf->filePath());
1055                 // If directory is writeable, use this as default.
1056                 if (trypath.isDirWritable())
1057                         initpath = trypath;
1058         }
1059
1060         string templatefile = from_template ?
1061                 selectTemplateFile().absFilename() : string();
1062         Buffer * b;
1063         if (filename.empty())
1064                 b = newUnnamedFile(templatefile, initpath);
1065         else
1066                 b = newFile(filename, templatefile, true);
1067
1068         if (b)
1069                 setBuffer(b);
1070         // Ensure the cursor is correctly positionned on screen.
1071         view()->showCursor();
1072 }
1073
1074
1075 void GuiView::insertLyXFile(docstring const & fname)
1076 {
1077         BufferView * bv = view();
1078         if (!bv)
1079                 return;
1080
1081         // FIXME UNICODE
1082         FileName filename(to_utf8(fname));
1083         
1084         if (!filename.empty()) {
1085                 bv->insertLyXFile(filename);
1086                 return;
1087         }
1088
1089         // Launch a file browser
1090         // FIXME UNICODE
1091         string initpath = lyxrc.document_path;
1092         string const trypath = bv->buffer().filePath();
1093         // If directory is writeable, use this as default.
1094         if (FileName(trypath).isDirWritable())
1095                 initpath = trypath;
1096
1097         // FIXME UNICODE
1098         FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
1099         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1100         dlg.setButton2(_("Examples|#E#e"),
1101                 from_utf8(addPath(package().system_support().absFilename(),
1102                 "examples")));
1103
1104         FileDialog::Result result =
1105                 dlg.open(from_utf8(initpath),
1106                              FileFilterList(_("LyX Documents (*.lyx)")),
1107                              docstring());
1108
1109         if (result.first == FileDialog::Later)
1110                 return;
1111
1112         // FIXME UNICODE
1113         filename.set(to_utf8(result.second));
1114
1115         // check selected filename
1116         if (filename.empty()) {
1117                 // emit message signal.
1118                 message(_("Canceled."));
1119                 return;
1120         }
1121
1122         bv->insertLyXFile(filename);
1123 }
1124
1125
1126 void GuiView::insertPlaintextFile(docstring const & fname,
1127         bool asParagraph)
1128 {
1129         BufferView * bv = view();
1130         if (!bv)
1131                 return;
1132
1133         // FIXME UNICODE
1134         FileName filename(to_utf8(fname));
1135         
1136         if (!filename.empty()) {
1137                 bv->insertPlaintextFile(filename, asParagraph);
1138                 return;
1139         }
1140
1141         FileDialog dlg(_("Select file to insert"), (asParagraph ?
1142                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1143
1144         FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
1145                 FileFilterList(), docstring());
1146
1147         if (result.first == FileDialog::Later)
1148                 return;
1149
1150         // FIXME UNICODE
1151         filename.set(to_utf8(result.second));
1152
1153         // check selected filename
1154         if (filename.empty()) {
1155                 // emit message signal.
1156                 message(_("Canceled."));
1157                 return;
1158         }
1159
1160         bv->insertPlaintextFile(filename, asParagraph);
1161 }
1162
1163
1164 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1165 {
1166         FileName fname = b.fileName();
1167         FileName const oldname = fname;
1168
1169         if (!newname.empty()) {
1170                 // FIXME UNICODE
1171                 fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1172         } else {
1173                 // Switch to this Buffer.
1174                 setBuffer(&b);
1175
1176                 /// No argument? Ask user through dialog.
1177                 // FIXME UNICODE
1178                 FileDialog dlg(_("Choose a filename to save document as"),
1179                                    LFUN_BUFFER_WRITE_AS);
1180                 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1181                 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1182
1183                 if (!isLyXFilename(fname.absFilename()))
1184                         fname.changeExtension(".lyx");
1185
1186                 FileFilterList const filter(_("LyX Documents (*.lyx)"));
1187
1188                 FileDialog::Result result =
1189                         dlg.save(from_utf8(fname.onlyPath().absFilename()),
1190                                      filter,
1191                                      from_utf8(fname.onlyFileName()));
1192
1193                 if (result.first == FileDialog::Later)
1194                         return false;
1195
1196                 fname.set(to_utf8(result.second));
1197
1198                 if (fname.empty())
1199                         return false;
1200
1201                 if (!isLyXFilename(fname.absFilename()))
1202                         fname.changeExtension(".lyx");
1203         }
1204
1205         if (FileName(fname).exists()) {
1206                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1207                 docstring text = bformat(_("The document %1$s already "
1208                                            "exists.\n\nDo you want to "
1209                                            "overwrite that document?"), 
1210                                          file);
1211                 int const ret = Alert::prompt(_("Overwrite document?"),
1212                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1213                 switch (ret) {
1214                 case 0: break;
1215                 case 1: return renameBuffer(b, docstring());
1216                 case 2: return false;
1217                 }
1218         }
1219
1220         // Ok, change the name of the buffer
1221         b.setFileName(fname.absFilename());
1222         b.markDirty();
1223         bool unnamed = b.isUnnamed();
1224         b.setUnnamed(false);
1225         b.saveCheckSum(fname);
1226
1227         if (!saveBuffer(b)) {
1228                 b.setFileName(oldname.absFilename());
1229                 b.setUnnamed(unnamed);
1230                 b.saveCheckSum(oldname);
1231                 return false;
1232         }
1233
1234         return true;
1235 }
1236
1237
1238 bool GuiView::saveBuffer(Buffer & b)
1239 {
1240         if (b.isUnnamed())
1241                 return renameBuffer(b, docstring());
1242
1243         if (b.save()) {
1244                 LyX::ref().session().lastFiles().add(b.fileName());
1245                 return true;
1246         }
1247
1248         // Switch to this Buffer.
1249         setBuffer(&b);
1250
1251         // FIXME: we don't tell the user *WHY* the save failed !!
1252         docstring const file = makeDisplayPath(b.absFileName(), 30);
1253         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1254                                    "Do you want to rename the document and "
1255                                    "try again?"), file);
1256         int const ret = Alert::prompt(_("Rename and save?"),
1257                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1258         switch (ret) {
1259         case 0:
1260                 if (!renameBuffer(b, docstring()))
1261                         return false;
1262                 break;
1263         case 1:
1264                 return false;
1265         case 2:
1266                 break;
1267         }
1268
1269         return saveBuffer(b);
1270 }
1271
1272
1273 bool GuiView::closeBuffer()
1274 {
1275         Buffer * buf = buffer();
1276         return buf && closeBuffer(*buf);
1277 }
1278
1279
1280 bool GuiView::closeBuffer(Buffer & buf)
1281 {
1282         if (buf.isClean() || buf.paragraphs().empty()) {
1283                 theBufferList().release(&buf);
1284                 return true;
1285         }
1286         // Switch to this Buffer.
1287         setBuffer(&buf);
1288
1289         docstring file;
1290         // FIXME: Unicode?
1291         if (buf.isUnnamed())
1292                 file = from_utf8(buf.fileName().onlyFileName());
1293         else
1294                 file = buf.fileName().displayName(30);
1295
1296         docstring const text = bformat(_("The document %1$s has unsaved changes."
1297                 "\n\nDo you want to save the document or discard the changes?"), file);
1298         int const ret = Alert::prompt(_("Save changed document?"),
1299                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1300
1301         switch (ret) {
1302         case 0:
1303                 if (!saveBuffer(buf))
1304                         return false;
1305                 break;
1306         case 1:
1307                 // if we crash after this we could
1308                 // have no autosave file but I guess
1309                 // this is really improbable (Jug)
1310                 removeAutosaveFile(buf.absFileName());
1311                 break;
1312         case 2:
1313                 return false;
1314         }
1315
1316         // save file names to .lyx/session
1317         // if master/slave are both open, do not save slave since it
1318         // will be automatically loaded when the master is loaded
1319         if (buf.masterBuffer() == &buf)
1320                 LyX::ref().session().lastOpened().add(buf.fileName());
1321
1322         theBufferList().release(&buf);
1323         return true;
1324 }
1325
1326
1327 bool GuiView::quitWriteAll()
1328 {
1329         while (!theBufferList().empty()) {
1330                 Buffer * b = theBufferList().first();
1331                 if (!closeBuffer(*b))
1332                         return false;
1333         }
1334         return true;
1335 }
1336
1337
1338 bool GuiView::dispatch(FuncRequest const & cmd)
1339 {
1340         BufferView * bv = view();       
1341         // By default we won't need any update.
1342         if (bv)
1343                 bv->cursor().updateFlags(Update::None);
1344
1345         switch(cmd.action) {
1346                 case LFUN_BUFFER_SWITCH:
1347                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1348                         break;
1349
1350                 case LFUN_BUFFER_NEXT:
1351                         setBuffer(theBufferList().next(buffer()));
1352                         break;
1353
1354                 case LFUN_BUFFER_PREVIOUS:
1355                         setBuffer(theBufferList().previous(buffer()));
1356                         break;
1357
1358                 case LFUN_COMMAND_EXECUTE: {
1359                         bool const show_it = cmd.argument() != "off";
1360                         d.toolbars_->showCommandBuffer(show_it);
1361                         break;
1362                 }
1363                 case LFUN_DROP_LAYOUTS_CHOICE:
1364                         if (d.layout_)
1365                                 d.layout_->showPopup();
1366                         break;
1367
1368                 case LFUN_MENU_OPEN:
1369                         d.menubar_->openByName(toqstr(cmd.argument()));
1370                         break;
1371
1372                 case LFUN_FILE_INSERT:
1373                         insertLyXFile(cmd.argument());
1374                         break;
1375                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1376                         insertPlaintextFile(cmd.argument(), true);
1377                         break;
1378
1379                 case LFUN_FILE_INSERT_PLAINTEXT:
1380                         insertPlaintextFile(cmd.argument(), false);
1381                         break;
1382
1383                 case LFUN_BUFFER_WRITE:
1384                         if (bv)
1385                                 saveBuffer(bv->buffer());
1386                         break;
1387
1388                 case LFUN_BUFFER_WRITE_AS:
1389                         if (bv)
1390                                 renameBuffer(bv->buffer(), cmd.argument());
1391                         break;
1392
1393                 case LFUN_BUFFER_WRITE_ALL: {
1394                         Buffer * first = theBufferList().first();
1395                         if (!first)
1396                                 break;
1397                         message(_("Saving all documents..."));
1398                         // We cannot use a for loop as the buffer list cycles.
1399                         Buffer * b = first;
1400                         do {
1401                                 if (b->isClean())
1402                                         continue;
1403                                 saveBuffer(*b);
1404                                 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1405                                 b = theBufferList().next(b);
1406                         } while (b != first); 
1407                         message(_("All documents saved."));
1408                         break;
1409                 }
1410
1411                 case LFUN_TOOLBAR_TOGGLE: {
1412                         string const name = cmd.getArg(0);
1413                         bool const allowauto = cmd.getArg(1) == "allowauto";
1414                         // it is possible to get current toolbar status like this,...
1415                         // but I decide to obey the order of ToolbarBackend::flags
1416                         // and disregard real toolbar status.
1417                         // toolbars_->saveToolbarInfo();
1418                         //
1419                         // toggle state on/off/auto
1420                         d.toolbars_->toggleToolbarState(name, allowauto);
1421                         // update toolbar
1422                         updateToolbars();
1423
1424                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1425                         if (!tbi) {
1426                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1427                                 break;
1428                         }
1429                         docstring state;
1430                         if (tbi->flags & ToolbarInfo::ON)
1431                                 state = _("on");
1432                         else if (tbi->flags & ToolbarInfo::OFF)
1433                                 state = _("off");
1434                         else if (tbi->flags & ToolbarInfo::AUTO)
1435                                 state = _("auto");
1436
1437                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1438                                            _(tbi->gui_name), state));
1439                         break;
1440                 }
1441
1442                 case LFUN_DIALOG_UPDATE: {
1443                         string const name = to_utf8(cmd.argument());
1444                         // Can only update a dialog connected to an existing inset
1445                         Inset * inset = getOpenInset(name);
1446                         if (inset) {
1447                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1448                                 inset->dispatch(view()->cursor(), fr);
1449                         } else if (name == "paragraph") {
1450                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1451                         } else if (name == "prefs") {
1452                                 updateDialog(name, string());
1453                         }
1454                         break;
1455                 }
1456
1457                 case LFUN_DIALOG_TOGGLE: {
1458                         if (isDialogVisible(cmd.getArg(0)))
1459                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1460                         else
1461                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1462                         break;
1463                 }
1464
1465                 case LFUN_DIALOG_DISCONNECT_INSET:
1466                         disconnectDialog(to_utf8(cmd.argument()));
1467                         break;
1468
1469                 case LFUN_DIALOG_HIDE: {
1470                         if (quitting)
1471                                 break;
1472                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1473                         break;
1474                 }
1475
1476                 case LFUN_DIALOG_SHOW: {
1477                         string const name = cmd.getArg(0);
1478                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1479
1480                         if (name == "character") {
1481                                 data = freefont2string();
1482                                 if (!data.empty())
1483                                         showDialog("character", data);
1484                         } else if (name == "latexlog") {
1485                                 Buffer::LogType type; 
1486                                 string const logfile = buffer()->logName(&type);
1487                                 switch (type) {
1488                                 case Buffer::latexlog:
1489                                         data = "latex ";
1490                                         break;
1491                                 case Buffer::buildlog:
1492                                         data = "literate ";
1493                                         break;
1494                                 }
1495                                 data += Lexer::quoteString(logfile);
1496                                 showDialog("log", data);
1497                         } else if (name == "vclog") {
1498                                 string const data = "vc " +
1499                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1500                                 showDialog("log", data);
1501                         } else
1502                                 showDialog(name, data);
1503                         break;
1504                 }
1505
1506                 case LFUN_INSET_APPLY: {
1507                         string const name = cmd.getArg(0);
1508                         Inset * inset = getOpenInset(name);
1509                         if (inset) {
1510                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1511                                 inset->dispatch(view()->cursor(), fr);
1512                         } else {
1513                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1514                                 lyx::dispatch(fr);
1515                         }
1516                         break;
1517                 }
1518
1519                 default:
1520                         return false;
1521         }
1522
1523         return true;
1524 }
1525
1526
1527 Buffer const * GuiView::updateInset(Inset const * inset)
1528 {
1529         if (!d.current_work_area_)
1530                 return 0;
1531
1532         if (inset)
1533                 d.current_work_area_->scheduleRedraw();
1534
1535         return &d.current_work_area_->bufferView().buffer();
1536 }
1537
1538
1539 void GuiView::restartCursor()
1540 {
1541         /* When we move around, or type, it's nice to be able to see
1542          * the cursor immediately after the keypress.
1543          */
1544         if (d.current_work_area_)
1545                 d.current_work_area_->startBlinkingCursor();
1546
1547         // Take this occasion to update the toobars and layout list.
1548         updateLayoutList();
1549         updateToolbars();
1550 }
1551
1552 namespace {
1553
1554 // This list should be kept in sync with the list of insets in
1555 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1556 // dialog should have the same name as the inset.
1557
1558 char const * const dialognames[] = {
1559 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1560 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1561 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1562 "mathdelimiter", "mathmatrix", "note", "paragraph",
1563 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1564
1565 #ifdef HAVE_LIBAIKSAURUS
1566 "thesaurus",
1567 #endif
1568
1569 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1570
1571 char const * const * const end_dialognames =
1572         dialognames + (sizeof(dialognames) / sizeof(char *));
1573
1574 class cmpCStr {
1575 public:
1576         cmpCStr(char const * name) : name_(name) {}
1577         bool operator()(char const * other) {
1578                 return strcmp(other, name_) == 0;
1579         }
1580 private:
1581         char const * name_;
1582 };
1583
1584
1585 bool isValidName(string const & name)
1586 {
1587         return find_if(dialognames, end_dialognames,
1588                             cmpCStr(name.c_str())) != end_dialognames;
1589 }
1590
1591 } // namespace anon
1592
1593
1594 void GuiView::resetDialogs()
1595 {
1596         // Make sure that no LFUN uses any LyXView.
1597         theLyXFunc().setLyXView(0);
1598         d.toolbars_->init();
1599         d.menubar_->init();
1600         if (d.layout_)
1601                 d.layout_->updateContents(true);
1602         // Now update controls with current buffer.
1603         theLyXFunc().setLyXView(this);
1604         restartCursor();
1605 }
1606
1607
1608 Dialog * GuiView::find_or_build(string const & name)
1609 {
1610         if (!isValidName(name))
1611                 return 0;
1612
1613         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1614
1615         if (it != d.dialogs_.end())
1616                 return it->second.get();
1617
1618         Dialog * dialog = build(name);
1619         d.dialogs_[name].reset(dialog);
1620         if (lyxrc.allow_geometry_session)
1621                 dialog->restoreSession();
1622         return dialog;
1623 }
1624
1625
1626 void GuiView::showDialog(string const & name, string const & data,
1627         Inset * inset)
1628 {
1629         if (d.in_show_)
1630                 return;
1631
1632         d.in_show_ = true;
1633         Dialog * dialog = find_or_build(name);
1634         if (dialog) {
1635                 dialog->showData(data);
1636                 if (inset)
1637                         d.open_insets_[name] = inset;
1638         }
1639         d.in_show_ = false;
1640 }
1641
1642
1643 bool GuiView::isDialogVisible(string const & name) const
1644 {
1645         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1646         if (it == d.dialogs_.end())
1647                 return false;
1648         return it->second.get()->isVisibleView();
1649 }
1650
1651
1652 void GuiView::hideDialog(string const & name, Inset * inset)
1653 {
1654         // Don't send the signal if we are quitting, because on MSVC it is
1655         // destructed before the cut stack in CutAndPaste.cpp, and this method
1656         // is called from some inset destructor if the cut stack is not empty
1657         // on exit.
1658         if (quitting)
1659                 return;
1660
1661         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1662         if (it == d.dialogs_.end())
1663                 return;
1664
1665         if (inset && inset != getOpenInset(name))
1666                 return;
1667
1668         Dialog * const dialog = it->second.get();
1669         if (dialog->isVisibleView())
1670                 dialog->hideView();
1671         d.open_insets_[name] = 0;
1672 }
1673
1674
1675 void GuiView::disconnectDialog(string const & name)
1676 {
1677         if (!isValidName(name))
1678                 return;
1679
1680         if (d.open_insets_.find(name) != d.open_insets_.end())
1681                 d.open_insets_[name] = 0;
1682 }
1683
1684
1685 Inset * GuiView::getOpenInset(string const & name) const
1686 {
1687         if (!isValidName(name))
1688                 return 0;
1689
1690         map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1691         return it == d.open_insets_.end() ? 0 : it->second;
1692 }
1693
1694
1695 void GuiView::hideAll() const
1696 {
1697         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1698         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1699
1700         for(; it != end; ++it)
1701                 it->second->hideView();
1702 }
1703
1704
1705 void GuiView::hideBufferDependent() const
1706 {
1707         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1708         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1709
1710         for(; it != end; ++it) {
1711                 Dialog * dialog = it->second.get();
1712                 if (dialog->isBufferDependent())
1713                         dialog->hideView();
1714         }
1715 }
1716
1717
1718 void GuiView::updateBufferDependent(bool switched) const
1719 {
1720         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1721         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1722
1723         for(; it != end; ++it) {
1724                 Dialog * dialog = it->second.get();
1725                 if (!dialog->isVisibleView())
1726                         continue;
1727                 if (switched && dialog->isBufferDependent()) {
1728                         if (dialog->initialiseParams(""))
1729                                 dialog->updateView();
1730                         else
1731                                 dialog->hideView();
1732                 } else {
1733                         // A bit clunky, but the dialog will request
1734                         // that the kernel provides it with the necessary
1735                         // data.
1736                         dialog->updateDialog();
1737                 }
1738         }
1739 }
1740
1741
1742 void GuiView::checkStatus()
1743 {
1744         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1745         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1746
1747         for(; it != end; ++it) {
1748                 Dialog * const dialog = it->second.get();
1749                 if (dialog && dialog->isVisibleView())
1750                         dialog->checkStatus();
1751         }
1752 }
1753
1754
1755
1756 // will be replaced by a proper factory...
1757 Dialog * createGuiAbout(GuiView & lv);
1758 Dialog * createGuiBibitem(GuiView & lv);
1759 Dialog * createGuiBibtex(GuiView & lv);
1760 Dialog * createGuiBox(GuiView & lv);
1761 Dialog * createGuiBranch(GuiView & lv);
1762 Dialog * createGuiChanges(GuiView & lv);
1763 Dialog * createGuiCharacter(GuiView & lv);
1764 Dialog * createGuiCitation(GuiView & lv);
1765 Dialog * createGuiDelimiter(GuiView & lv);
1766 Dialog * createGuiDocument(GuiView & lv);
1767 Dialog * createGuiErrorList(GuiView & lv);
1768 Dialog * createGuiERT(GuiView & lv);
1769 Dialog * createGuiExternal(GuiView & lv);
1770 Dialog * createGuiFloat(GuiView & lv);
1771 Dialog * createGuiGraphics(GuiView & lv);
1772 Dialog * createGuiInclude(GuiView & lv);
1773 Dialog * createGuiIndex(GuiView & lv);
1774 Dialog * createGuiLabel(GuiView & lv);
1775 Dialog * createGuiListings(GuiView & lv);
1776 Dialog * createGuiLog(GuiView & lv);
1777 Dialog * createGuiMathMatrix(GuiView & lv);
1778 Dialog * createGuiNomenclature(GuiView & lv);
1779 Dialog * createGuiNote(GuiView & lv);
1780 Dialog * createGuiParagraph(GuiView & lv);
1781 Dialog * createGuiPreferences(GuiView & lv);
1782 Dialog * createGuiPrint(GuiView & lv);
1783 Dialog * createGuiRef(GuiView & lv);
1784 Dialog * createGuiSearch(GuiView & lv);
1785 Dialog * createGuiSendTo(GuiView & lv);
1786 Dialog * createGuiShowFile(GuiView & lv);
1787 Dialog * createGuiSpellchecker(GuiView & lv);
1788 Dialog * createGuiTabularCreate(GuiView & lv);
1789 Dialog * createGuiTabular(GuiView & lv);
1790 Dialog * createGuiTexInfo(GuiView & lv);
1791 Dialog * createGuiToc(GuiView & lv);
1792 Dialog * createGuiThesaurus(GuiView & lv);
1793 Dialog * createGuiHyperlink(GuiView & lv);
1794 Dialog * createGuiVSpace(GuiView & lv);
1795 Dialog * createGuiViewSource(GuiView & lv);
1796 Dialog * createGuiWrap(GuiView & lv);
1797
1798
1799 Dialog * GuiView::build(string const & name)
1800 {
1801         BOOST_ASSERT(isValidName(name));
1802
1803         if (name == "aboutlyx")
1804                 return createGuiAbout(*this);
1805         if (name == "bibitem")
1806                 return createGuiBibitem(*this);
1807         if (name == "bibtex")
1808                 return createGuiBibtex(*this);
1809         if (name == "box")
1810                 return createGuiBox(*this);
1811         if (name == "branch")
1812                 return createGuiBranch(*this);
1813         if (name == "changes")
1814                 return createGuiChanges(*this);
1815         if (name == "character")
1816                 return createGuiCharacter(*this);
1817         if (name == "citation")
1818                 return createGuiCitation(*this);
1819         if (name == "document")
1820                 return createGuiDocument(*this);
1821         if (name == "errorlist")
1822                 return createGuiErrorList(*this);
1823         if (name == "ert")
1824                 return createGuiERT(*this);
1825         if (name == "external")
1826                 return createGuiExternal(*this);
1827         if (name == "file")
1828                 return createGuiShowFile(*this);
1829         if (name == "findreplace")
1830                 return createGuiSearch(*this);
1831         if (name == "float")
1832                 return createGuiFloat(*this);
1833         if (name == "graphics")
1834                 return createGuiGraphics(*this);
1835         if (name == "include")
1836                 return createGuiInclude(*this);
1837         if (name == "index")
1838                 return createGuiIndex(*this);
1839         if (name == "nomenclature")
1840                 return createGuiNomenclature(*this);
1841         if (name == "label")
1842                 return createGuiLabel(*this);
1843         if (name == "log")
1844                 return createGuiLog(*this);
1845         if (name == "view-source")
1846                 return createGuiViewSource(*this);
1847         if (name == "mathdelimiter")
1848                 return createGuiDelimiter(*this);
1849         if (name == "mathmatrix")
1850                 return createGuiMathMatrix(*this);
1851         if (name == "note")
1852                 return createGuiNote(*this);
1853         if (name == "paragraph")
1854                 return createGuiParagraph(*this);
1855         if (name == "prefs")
1856                 return createGuiPreferences(*this);
1857         if (name == "print")
1858                 return createGuiPrint(*this);
1859         if (name == "ref")
1860                 return createGuiRef(*this);
1861         if (name == "sendto")
1862                 return createGuiSendTo(*this);
1863         if (name == "spellchecker")
1864                 return createGuiSpellchecker(*this);
1865         if (name == "tabular")
1866                 return createGuiTabular(*this);
1867         if (name == "tabularcreate")
1868                 return createGuiTabularCreate(*this);
1869         if (name == "texinfo")
1870                 return createGuiTexInfo(*this);
1871 #ifdef HAVE_LIBAIKSAURUS
1872         if (name == "thesaurus")
1873                 return createGuiThesaurus(*this);
1874 #endif
1875         if (name == "toc")
1876                 return createGuiToc(*this);
1877         if (name == "href")
1878                 return createGuiHyperlink(*this);
1879         if (name == "vspace")
1880                 return createGuiVSpace(*this);
1881         if (name == "wrap")
1882                 return createGuiWrap(*this);
1883         if (name == "listings")
1884                 return createGuiListings(*this);
1885
1886         return 0;
1887 }
1888
1889
1890 } // namespace frontend
1891 } // namespace lyx
1892
1893 #include "GuiView_moc.cpp"