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