]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
21a323efb608c8e30f1a8ddbe6a981e39fecf2b8
[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 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 = support::os::internal_path(fromqstr(
455                         files.at(i).toLocalFile()));
456                 if (!file.empty())
457                         lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
458         }
459 }
460
461
462 void GuiView::message(docstring const & str)
463 {
464         statusBar()->showMessage(toqstr(str));
465         d.statusbar_timer_.stop();
466         d.statusbar_timer_.start(3000);
467 }
468
469
470 void GuiView::smallSizedIcons()
471 {
472         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
473 }
474
475
476 void GuiView::normalSizedIcons()
477 {
478         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
479 }
480
481
482 void GuiView::bigSizedIcons()
483 {
484         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
485 }
486
487
488 void GuiView::clearMessage()
489 {
490         if (!hasFocus())
491                 return;
492         theLyXFunc().setLyXView(this);
493         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
494         d.statusbar_timer_.stop();
495 }
496
497
498 void GuiView::updateWindowTitle(GuiWorkArea * wa)
499 {
500         if (wa != d.current_work_area_)
501                 return;
502         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
503         setWindowIconText(wa->windowIconText());
504 }
505
506
507 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
508 {
509         disconnectBuffer();
510         disconnectBufferView();
511         connectBufferView(wa->bufferView());
512         connectBuffer(wa->bufferView().buffer());
513         d.current_work_area_ = wa;
514         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
515                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
516         updateWindowTitle(wa);
517
518         updateToc();
519         // Buffer-dependent dialogs should be updated or
520         // hidden. This should go here because some dialogs (eg ToC)
521         // require bv_->text.
522         updateBufferDependent(true);
523         updateToolbars();
524         updateLayoutList();
525         updateStatusBar();
526 }
527
528
529 void GuiView::updateStatusBar()
530 {
531         // let the user see the explicit message
532         if (d.statusbar_timer_.isActive())
533                 return;
534
535         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
536 }
537
538
539 bool GuiView::hasFocus() const
540 {
541         return qApp->activeWindow() == this;
542 }
543
544
545 bool GuiView::event(QEvent * e)
546 {
547         switch (e->type())
548         {
549         // Useful debug code:
550         //case QEvent::ActivationChange:
551         //case QEvent::WindowDeactivate:
552         //case QEvent::Paint:
553         //case QEvent::Enter:
554         //case QEvent::Leave:
555         //case QEvent::HoverEnter:
556         //case QEvent::HoverLeave:
557         //case QEvent::HoverMove:
558         //case QEvent::StatusTip:
559         //case QEvent::DragEnter:
560         //case QEvent::DragLeave:
561         //case QEvent::Drop:
562         //      break;
563
564         case QEvent::WindowActivate: {
565                 guiApp->setCurrentView(*this);
566                 if (d.current_work_area_) {
567                         BufferView & bv = d.current_work_area_->bufferView();
568                         connectBufferView(bv);
569                         connectBuffer(bv.buffer());
570                         // The document structure, name and dialogs might have
571                         // changed in another view.
572                         updateBufferDependent(true);
573                 } else {
574                         setWindowTitle(qt_("LyX"));
575                         setWindowIconText(qt_("LyX"));
576                 }
577                 return QMainWindow::event(e);
578         }
579         case QEvent::ShortcutOverride: {
580                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
581                 if (!d.current_work_area_) {
582                         theLyXFunc().setLyXView(this);
583                         KeySymbol sym;
584                         setKeySymbol(&sym, ke);
585                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
586                         e->accept();
587                         return true;
588                 }
589                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
590                         KeySymbol sym;
591                         setKeySymbol(&sym, ke);
592                         d.current_work_area_->processKeySym(sym, NoModifier);
593                         e->accept();
594                         return true;
595                 }
596         }
597         default:
598                 return QMainWindow::event(e);
599         }
600 }
601
602
603 bool GuiView::focusNextPrevChild(bool /*next*/)
604 {
605         setFocus();
606         return true;
607 }
608
609
610 void GuiView::setBusy(bool yes)
611 {
612         if (d.current_work_area_) {
613                 d.current_work_area_->setUpdatesEnabled(!yes);
614                 if (yes)
615                         d.current_work_area_->stopBlinkingCursor();
616                 else
617                         d.current_work_area_->startBlinkingCursor();
618         }
619
620         if (yes)
621                 QApplication::setOverrideCursor(Qt::WaitCursor);
622         else
623                 QApplication::restoreOverrideCursor();
624 }
625
626
627 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
628 {
629         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
630
631         if (tbinfo.flags & ToolbarInfo::TOP) {
632                 if (newline)
633                         addToolBarBreak(Qt::TopToolBarArea);
634                 addToolBar(Qt::TopToolBarArea, toolBar);
635         }
636
637         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
638 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
639 #if (QT_VERSION >= 0x040202)
640                 if (newline)
641                         addToolBarBreak(Qt::BottomToolBarArea);
642 #endif
643                 addToolBar(Qt::BottomToolBarArea, toolBar);
644         }
645
646         if (tbinfo.flags & ToolbarInfo::LEFT) {
647 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
648 #if (QT_VERSION >= 0x040202)
649                 if (newline)
650                         addToolBarBreak(Qt::LeftToolBarArea);
651 #endif
652                 addToolBar(Qt::LeftToolBarArea, toolBar);
653         }
654
655         if (tbinfo.flags & ToolbarInfo::RIGHT) {
656 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
657 #if (QT_VERSION >= 0x040202)
658                 if (newline)
659                         addToolBarBreak(Qt::RightToolBarArea);
660 #endif
661                 addToolBar(Qt::RightToolBarArea, toolBar);
662         }
663
664         // The following does not work so I cannot restore to exact toolbar location
665         /*
666         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
667         toolBar->move(tbinfo.posx, tbinfo.posy);
668         */
669
670         return toolBar;
671 }
672
673
674 GuiWorkArea * GuiView::workArea(Buffer & buffer)
675 {
676         for (int i = 0; i != d.splitter_->count(); ++i) {
677                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
678                 if (wa)
679                         return wa;
680         }
681         return 0;
682 }
683
684
685 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
686 {
687
688         // Automatically create a TabWorkArea if there are none yet.
689         if (!d.splitter_->count())
690                 addTabWorkArea();
691
692         TabWorkArea * tab_widget = d.currentTabWorkArea();
693         return tab_widget->addWorkArea(buffer, *this);
694 }
695
696
697 void GuiView::addTabWorkArea()
698 {
699         TabWorkArea * twa = new TabWorkArea;
700         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
701                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
702         d.splitter_->addWidget(twa);
703         d.stack_widget_->setCurrentWidget(d.splitter_);
704 }
705
706
707 GuiWorkArea const * GuiView::currentWorkArea() const
708 {
709         return d.current_work_area_;
710 }
711
712
713 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
714 {
715         BOOST_ASSERT(wa);
716
717         // Changing work area can result from opening a file so
718         // update the toc in any case.
719         updateToc();
720
721         d.current_work_area_ = wa;
722         for (int i = 0; i != d.splitter_->count(); ++i) {
723                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
724                         return;
725         }
726 }
727
728
729 void GuiView::removeWorkArea(GuiWorkArea * wa)
730 {
731         BOOST_ASSERT(wa);
732         if (wa == d.current_work_area_) {
733                 disconnectBuffer();
734                 disconnectBufferView();
735                 hideBufferDependent();
736                 d.current_work_area_ = 0;
737         }
738
739         for (int i = 0; i != d.splitter_->count(); ++i) {
740                 TabWorkArea * twa = d.tabWorkArea(i);
741                 if (!twa->removeWorkArea(wa))
742                         // Not found in this tab group.
743                         continue;
744
745                 // We found and removed the GuiWorkArea.
746                 if (!twa->count()) {
747                         // No more WorkAreas in this tab group, so delete it.
748                         delete twa;
749                         break;
750                 }
751
752                 if (d.current_work_area_)
753                         // This means that we are not closing the current GuiWorkArea;
754                         break;
755
756                 // Switch to the next GuiWorkArea in the found TabWorkArea.
757                 d.current_work_area_ = twa->currentWorkArea();
758                 break;
759         }
760
761         if (d.splitter_->count() == 0)
762                 // No more work area, switch to the background widget.
763                 d.setBackground();
764 }
765
766
767 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
768 {
769         d.layout_ = layout;
770 }
771
772
773 void GuiView::updateLayoutList()
774 {
775         if (d.layout_)
776                 d.layout_->updateContents(false);
777 }
778
779
780 void GuiView::updateToolbars()
781 {
782         if (d.current_work_area_) {
783                 bool const math =
784                         d.current_work_area_->bufferView().cursor().inMathed();
785                 bool const table =
786                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
787                 bool const review =
788                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
789                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
790
791                 d.toolbars_->update(math, table, review);
792         } else
793                 d.toolbars_->update(false, false, false);
794
795         // update read-only status of open dialogs.
796         checkStatus();
797 }
798
799
800 Buffer * GuiView::buffer()
801 {
802         if (d.current_work_area_)
803                 return &d.current_work_area_->bufferView().buffer();
804         return 0;
805 }
806
807
808 Buffer const * GuiView::buffer() const
809 {
810         if (d.current_work_area_)
811                 return &d.current_work_area_->bufferView().buffer();
812         return 0;
813 }
814
815
816 void GuiView::setBuffer(Buffer * newBuffer)
817 {
818         BOOST_ASSERT(newBuffer);
819         setBusy(true);
820
821         GuiWorkArea * wa = workArea(*newBuffer);
822         if (wa == 0) {
823                 updateLabels(*newBuffer->masterBuffer());
824                 wa = addWorkArea(*newBuffer);
825         } else {
826                 //Disconnect the old buffer...there's no new one.
827                 disconnectBuffer();
828         }
829         connectBuffer(*newBuffer);
830         connectBufferView(wa->bufferView());
831         setCurrentWorkArea(wa);
832
833         setBusy(false);
834 }
835
836
837 void GuiView::connectBuffer(Buffer & buf)
838 {
839         buf.setGuiDelegate(this);
840 }
841
842
843 void GuiView::disconnectBuffer()
844 {
845         if (d.current_work_area_)
846                 d.current_work_area_->bufferView().setGuiDelegate(0);
847 }
848
849
850 void GuiView::connectBufferView(BufferView & bv)
851 {
852         bv.setGuiDelegate(this);
853 }
854
855
856 void GuiView::disconnectBufferView()
857 {
858         if (d.current_work_area_)
859                 d.current_work_area_->bufferView().setGuiDelegate(0);
860 }
861
862
863 void GuiView::errors(string const & error_type)
864 {
865         ErrorList & el = buffer()->errorList(error_type);
866         if (!el.empty())
867                 showDialog("errorlist", error_type);
868 }
869
870
871 void GuiView::updateDialog(string const & name, string const & data)
872 {
873         if (!isDialogVisible(name))
874                 return;
875
876         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
877         if (it == d.dialogs_.end())
878                 return;
879
880         Dialog * const dialog = it->second.get();
881         if (dialog->isVisibleView())
882                 dialog->updateData(data);
883 }
884
885
886 BufferView * GuiView::view()
887 {
888         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
889 }
890
891
892 void GuiView::updateToc()
893 {
894         updateDialog("toc", "");
895 }
896
897
898 void GuiView::updateEmbeddedFiles()
899 {
900         updateDialog("embedding", "");
901 }
902
903
904 void GuiView::autoSave()
905 {
906         LYXERR(Debug::INFO, "Running autoSave()");
907
908         if (buffer())
909                 view()->buffer().autoSave();
910 }
911
912
913 void GuiView::resetAutosaveTimers()
914 {
915         if (lyxrc.autosave)
916                 d.autosave_timeout_.restart();
917 }
918
919
920 FuncStatus GuiView::getStatus(FuncRequest const & cmd)
921 {
922         FuncStatus flag;
923         bool enable = true;
924         Buffer * buf = buffer();
925
926         /* In LyX/Mac, when a dialog is open, the menus of the
927            application can still be accessed without giving focus to
928            the main window. In this case, we want to disable the menu
929            entries that are buffer-related.
930
931            Note that this code is not perfect, as bug 1941 attests:
932            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
933         */
934         if (cmd.origin == FuncRequest::MENU && !hasFocus())
935                 buf = 0;
936
937         switch(cmd.action) {
938         case LFUN_BUFFER_WRITE:
939                 enable = buf && (buf->isUnnamed() || !buf->isClean());
940                 break;
941
942         case LFUN_BUFFER_WRITE_AS:
943                 enable = buf;
944                 break;
945
946         case LFUN_TOOLBAR_TOGGLE:
947                 flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
948                 break;
949
950         case LFUN_DIALOG_TOGGLE:
951                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
952                 // fall through to set "enable"
953         case LFUN_DIALOG_SHOW: {
954                 string const name = cmd.getArg(0);
955                 if (!buf)
956                         enable = name == "aboutlyx"
957                                 || name == "file" //FIXME: should be removed.
958                                 || name == "prefs"
959                                 || name == "texinfo";
960                 else if (name == "print")
961                         enable = buf->isExportable("dvi")
962                                 && lyxrc.print_command != "none";
963                 else if (name == "character") {
964                         if (!view())
965                                 enable = false;
966                         else {
967                                 InsetCode ic = view()->cursor().inset().lyxCode();
968                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
969                         }
970                 }
971                 else if (name == "latexlog")
972                         enable = FileName(buf->logName()).isReadableFile();
973                 else if (name == "spellchecker")
974 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
975                         enable = !buf->isReadonly();
976 #else
977                         enable = false;
978 #endif
979                 else if (name == "vclog")
980                         enable = buf->lyxvc().inUse();
981                 break;
982         }
983
984         case LFUN_DIALOG_UPDATE: {
985                 string const name = cmd.getArg(0);
986                 if (!buf)
987                         enable = name == "prefs";
988                 break;
989         }
990
991         case LFUN_INSET_APPLY: {
992                 if (!buf) {
993                         enable = false;
994                         break;
995                 }
996                 string const name = cmd.getArg(0);
997                 Inset * inset = getOpenInset(name);
998                 if (inset) {
999                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1000                         FuncStatus fs;
1001                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1002                                 // Every inset is supposed to handle this
1003                                 BOOST_ASSERT(false);
1004                         }
1005                         flag |= fs;
1006                 } else {
1007                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1008                         flag |= getStatus(fr);
1009                 }
1010                 enable = flag.enabled();
1011                 break;
1012         }
1013
1014         default:
1015                 if (!view()) {
1016                         enable = false;
1017                         break;
1018                 }
1019         }
1020
1021         if (!enable)
1022                 flag.enabled(false);
1023
1024         return flag;
1025 }
1026
1027
1028 static FileName selectTemplateFile()
1029 {
1030         FileDialog dlg(_("Select template file"));
1031         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1032         dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1033
1034         FileDialog::Result result =
1035                 dlg.open(from_utf8(lyxrc.template_path),
1036                              FileFilterList(_("LyX Documents (*.lyx)")),
1037                              docstring());
1038
1039         if (result.first == FileDialog::Later)
1040                 return FileName();
1041         if (result.second.empty())
1042                 return FileName();
1043         return FileName(to_utf8(result.second));
1044 }
1045
1046
1047 void GuiView::newDocument(string const & filename, bool from_template)
1048 {
1049         FileName initpath(lyxrc.document_path);
1050         Buffer * buf = buffer();
1051         if (buf) {
1052                 FileName const trypath(buf->filePath());
1053                 // If directory is writeable, use this as default.
1054                 if (trypath.isDirWritable())
1055                         initpath = trypath;
1056         }
1057
1058         string templatefile = from_template ?
1059                 selectTemplateFile().absFilename() : string();
1060         Buffer * b;
1061         if (filename.empty())
1062                 b = newUnnamedFile(templatefile, initpath);
1063         else
1064                 b = newFile(filename, templatefile, true);
1065
1066         if (b)
1067                 setBuffer(b);
1068 }
1069
1070
1071 void GuiView::insertLyXFile(docstring const & fname)
1072 {
1073         BufferView * bv = view();
1074         if (!bv)
1075                 return;
1076
1077         // FIXME UNICODE
1078         FileName filename(to_utf8(fname));
1079         
1080         if (!filename.empty()) {
1081                 bv->insertLyXFile(filename);
1082                 return;
1083         }
1084
1085         // Launch a file browser
1086         // FIXME UNICODE
1087         string initpath = lyxrc.document_path;
1088         string const trypath = bv->buffer().filePath();
1089         // If directory is writeable, use this as default.
1090         if (FileName(trypath).isDirWritable())
1091                 initpath = trypath;
1092
1093         // FIXME UNICODE
1094         FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
1095         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1096         dlg.setButton2(_("Examples|#E#e"),
1097                 from_utf8(addPath(package().system_support().absFilename(),
1098                 "examples")));
1099
1100         FileDialog::Result result =
1101                 dlg.open(from_utf8(initpath),
1102                              FileFilterList(_("LyX Documents (*.lyx)")),
1103                              docstring());
1104
1105         if (result.first == FileDialog::Later)
1106                 return;
1107
1108         // FIXME UNICODE
1109         filename.set(to_utf8(result.second));
1110
1111         // check selected filename
1112         if (filename.empty()) {
1113                 // emit message signal.
1114                 message(_("Canceled."));
1115                 return;
1116         }
1117
1118         bv->insertLyXFile(filename);
1119 }
1120
1121
1122 void GuiView::insertPlaintextFile(docstring const & fname,
1123         bool asParagraph)
1124 {
1125         BufferView * bv = view();
1126         if (!bv)
1127                 return;
1128
1129         // FIXME UNICODE
1130         FileName filename(to_utf8(fname));
1131         
1132         if (!filename.empty()) {
1133                 bv->insertPlaintextFile(filename, asParagraph);
1134                 return;
1135         }
1136
1137         FileDialog dlg(_("Select file to insert"), (asParagraph ?
1138                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1139
1140         FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
1141                 FileFilterList(), docstring());
1142
1143         if (result.first == FileDialog::Later)
1144                 return;
1145
1146         // FIXME UNICODE
1147         filename.set(to_utf8(result.second));
1148
1149         // check selected filename
1150         if (filename.empty()) {
1151                 // emit message signal.
1152                 message(_("Canceled."));
1153                 return;
1154         }
1155
1156         bv->insertPlaintextFile(filename, asParagraph);
1157 }
1158
1159
1160 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1161 {
1162         FileName fname = b.fileName();
1163         FileName const oldname = fname;
1164
1165         if (!newname.empty()) {
1166                 // FIXME UNICODE
1167                 fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1168         } else {
1169                 // Switch to this Buffer.
1170                 setBuffer(&b);
1171
1172                 /// No argument? Ask user through dialog.
1173                 // FIXME UNICODE
1174                 FileDialog dlg(_("Choose a filename to save document as"),
1175                                    LFUN_BUFFER_WRITE_AS);
1176                 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1177                 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1178
1179                 if (!support::isLyXFilename(fname.absFilename()))
1180                         fname.changeExtension(".lyx");
1181
1182                 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
1183
1184                 FileDialog::Result result =
1185                         dlg.save(from_utf8(fname.onlyPath().absFilename()),
1186                                      filter,
1187                                      from_utf8(fname.onlyFileName()));
1188
1189                 if (result.first == FileDialog::Later)
1190                         return false;
1191
1192                 fname.set(to_utf8(result.second));
1193
1194                 if (fname.empty())
1195                         return false;
1196
1197                 if (!support::isLyXFilename(fname.absFilename()))
1198                         fname.changeExtension(".lyx");
1199         }
1200
1201         if (FileName(fname).exists()) {
1202                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1203                 docstring text = bformat(_("The document %1$s already "
1204                                            "exists.\n\nDo you want to "
1205                                            "overwrite that document?"), 
1206                                          file);
1207                 int const ret = Alert::prompt(_("Overwrite document?"),
1208                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1209                 switch (ret) {
1210                 case 0: break;
1211                 case 1: return renameBuffer(b, docstring());
1212                 case 2: return false;
1213                 }
1214         }
1215
1216         // Ok, change the name of the buffer
1217         b.setFileName(fname.absFilename());
1218         b.markDirty();
1219         bool unnamed = b.isUnnamed();
1220         b.setUnnamed(false);
1221         b.saveCheckSum(fname);
1222
1223         if (!saveBuffer(b)) {
1224                 b.setFileName(oldname.absFilename());
1225                 b.setUnnamed(unnamed);
1226                 b.saveCheckSum(oldname);
1227                 return false;
1228         }
1229
1230         return true;
1231 }
1232
1233
1234 bool GuiView::saveBuffer(Buffer & b)
1235 {
1236         if (b.isUnnamed())
1237                 return renameBuffer(b, docstring());
1238
1239         if (b.save()) {
1240                 LyX::ref().session().lastFiles().add(b.fileName());
1241                 return true;
1242         }
1243
1244         // Switch to this Buffer.
1245         setBuffer(&b);
1246
1247         // FIXME: we don't tell the user *WHY* the save failed !!
1248         docstring const file = makeDisplayPath(b.absFileName(), 30);
1249         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1250                                    "Do you want to rename the document and "
1251                                    "try again?"), file);
1252         int const ret = Alert::prompt(_("Rename and save?"),
1253                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1254         switch (ret) {
1255         case 0:
1256                 if (!renameBuffer(b, docstring()))
1257                         return false;
1258                 break;
1259         case 1:
1260                 return false;
1261         case 2:
1262                 break;
1263         }
1264
1265         return saveBuffer(b);
1266 }
1267
1268
1269 bool GuiView::closeBuffer()
1270 {
1271         Buffer * buf = buffer();
1272         return buf && closeBuffer(*buf);
1273 }
1274
1275
1276 bool GuiView::closeBuffer(Buffer & buf)
1277 {
1278         if (buf.isClean() || buf.paragraphs().empty()) {
1279                 theBufferList().release(&buf);
1280                 return true;
1281         }
1282         // Switch to this Buffer.
1283         setBuffer(&buf);
1284
1285         docstring file;
1286         // FIXME: Unicode?
1287         if (buf.isUnnamed())
1288                 file = from_utf8(buf.fileName().onlyFileName());
1289         else
1290                 file = buf.fileName().displayName(30);
1291
1292         docstring const text = bformat(_("The document %1$s has unsaved changes."
1293                 "\n\nDo you want to save the document or discard the changes?"), file);
1294         int const ret = Alert::prompt(_("Save changed document?"),
1295                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1296
1297         switch (ret) {
1298         case 0:
1299                 if (!saveBuffer(buf))
1300                         return false;
1301                 break;
1302         case 1:
1303                 // if we crash after this we could
1304                 // have no autosave file but I guess
1305                 // this is really improbable (Jug)
1306                 removeAutosaveFile(buf.absFileName());
1307                 break;
1308         case 2:
1309                 return false;
1310         }
1311
1312         // save file names to .lyx/session
1313         // if master/slave are both open, do not save slave since it
1314         // will be automatically loaded when the master is loaded
1315         if (buf.masterBuffer() == &buf)
1316                 LyX::ref().session().lastOpened().add(buf.fileName());
1317
1318         theBufferList().release(&buf);
1319         return true;
1320 }
1321
1322
1323 bool GuiView::quitWriteAll()
1324 {
1325         while (!theBufferList().empty()) {
1326                 Buffer * b = theBufferList().first();
1327                 if (!closeBuffer(*b))
1328                         return false;
1329         }
1330         return true;
1331 }
1332
1333
1334 bool GuiView::dispatch(FuncRequest const & cmd)
1335 {
1336         BufferView * bv = view();       
1337         // By default we won't need any update.
1338         if (bv)
1339                 bv->cursor().updateFlags(Update::None);
1340
1341         switch(cmd.action) {
1342                 case LFUN_BUFFER_SWITCH:
1343                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1344                         break;
1345
1346                 case LFUN_BUFFER_NEXT:
1347                         setBuffer(theBufferList().next(buffer()));
1348                         break;
1349
1350                 case LFUN_BUFFER_PREVIOUS:
1351                         setBuffer(theBufferList().previous(buffer()));
1352                         break;
1353
1354                 case LFUN_COMMAND_EXECUTE: {
1355                         bool const show_it = cmd.argument() != "off";
1356                         d.toolbars_->showCommandBuffer(show_it);
1357                         break;
1358                 }
1359                 case LFUN_DROP_LAYOUTS_CHOICE:
1360                         if (d.layout_)
1361                                 d.layout_->showPopup();
1362                         break;
1363
1364                 case LFUN_MENU_OPEN:
1365                         d.menubar_->openByName(toqstr(cmd.argument()));
1366                         break;
1367
1368                 case LFUN_FILE_INSERT:
1369                         insertLyXFile(cmd.argument());
1370                         break;
1371                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1372                         insertPlaintextFile(cmd.argument(), true);
1373                         break;
1374
1375                 case LFUN_FILE_INSERT_PLAINTEXT:
1376                         insertPlaintextFile(cmd.argument(), false);
1377                         break;
1378
1379                 case LFUN_BUFFER_WRITE:
1380                         if (bv)
1381                                 saveBuffer(bv->buffer());
1382                         break;
1383
1384                 case LFUN_BUFFER_WRITE_AS:
1385                         if (bv)
1386                                 renameBuffer(bv->buffer(), cmd.argument());
1387                         break;
1388
1389                 case LFUN_BUFFER_WRITE_ALL: {
1390                         Buffer * first = theBufferList().first();
1391                         if (!first)
1392                                 break;
1393                         message(_("Saving all documents..."));
1394                         // We cannot use a for loop as the buffer list cycles.
1395                         Buffer * b = first;
1396                         do {
1397                                 if (b->isClean())
1398                                         continue;
1399                                 saveBuffer(*b);
1400                                 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1401                                 b = theBufferList().next(b);
1402                         } while (b != first); 
1403                         message(_("All documents saved."));
1404                         break;
1405                 }
1406
1407                 case LFUN_TOOLBAR_TOGGLE: {
1408                         string const name = cmd.getArg(0);
1409                         bool const allowauto = cmd.getArg(1) == "allowauto";
1410                         // it is possible to get current toolbar status like this,...
1411                         // but I decide to obey the order of ToolbarBackend::flags
1412                         // and disregard real toolbar status.
1413                         // toolbars_->saveToolbarInfo();
1414                         //
1415                         // toggle state on/off/auto
1416                         d.toolbars_->toggleToolbarState(name, allowauto);
1417                         // update toolbar
1418                         updateToolbars();
1419
1420                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1421                         if (!tbi) {
1422                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1423                                 break;
1424                         }
1425                         docstring state;
1426                         if (tbi->flags & ToolbarInfo::ON)
1427                                 state = _("on");
1428                         else if (tbi->flags & ToolbarInfo::OFF)
1429                                 state = _("off");
1430                         else if (tbi->flags & ToolbarInfo::AUTO)
1431                                 state = _("auto");
1432
1433                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1434                                            _(tbi->gui_name), state));
1435                         break;
1436                 }
1437
1438                 case LFUN_DIALOG_UPDATE: {
1439                         string const name = to_utf8(cmd.argument());
1440                         // Can only update a dialog connected to an existing inset
1441                         Inset * inset = getOpenInset(name);
1442                         if (inset) {
1443                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1444                                 inset->dispatch(view()->cursor(), fr);
1445                         } else if (name == "paragraph") {
1446                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1447                         } else if (name == "prefs") {
1448                                 updateDialog(name, string());
1449                         }
1450                         break;
1451                 }
1452
1453                 case LFUN_DIALOG_TOGGLE: {
1454                         if (isDialogVisible(cmd.getArg(0)))
1455                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1456                         else
1457                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1458                         break;
1459                 }
1460
1461                 case LFUN_DIALOG_DISCONNECT_INSET:
1462                         disconnectDialog(to_utf8(cmd.argument()));
1463                         break;
1464
1465                 case LFUN_DIALOG_HIDE: {
1466                         if (quitting)
1467                                 break;
1468                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1469                         break;
1470                 }
1471
1472                 case LFUN_DIALOG_SHOW: {
1473                         string const name = cmd.getArg(0);
1474                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1475
1476                         if (name == "character") {
1477                                 data = freefont2string();
1478                                 if (!data.empty())
1479                                         showDialog("character", data);
1480                         } else if (name == "latexlog") {
1481                                 Buffer::LogType type; 
1482                                 string const logfile = buffer()->logName(&type);
1483                                 switch (type) {
1484                                 case Buffer::latexlog:
1485                                         data = "latex ";
1486                                         break;
1487                                 case Buffer::buildlog:
1488                                         data = "literate ";
1489                                         break;
1490                                 }
1491                                 data += Lexer::quoteString(logfile);
1492                                 showDialog("log", data);
1493                         } else if (name == "vclog") {
1494                                 string const data = "vc " +
1495                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1496                                 showDialog("log", data);
1497                         } else
1498                                 showDialog(name, data);
1499                         break;
1500                 }
1501
1502                 case LFUN_INSET_APPLY: {
1503                         string const name = cmd.getArg(0);
1504                         Inset * inset = getOpenInset(name);
1505                         if (inset) {
1506                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1507                                 inset->dispatch(view()->cursor(), fr);
1508                         } else {
1509                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1510                                 lyx::dispatch(fr);
1511                         }
1512                         break;
1513                 }
1514
1515                 default:
1516                         return false;
1517         }
1518
1519         return true;
1520 }
1521
1522
1523 Buffer const * GuiView::updateInset(Inset const * inset)
1524 {
1525         if (!d.current_work_area_)
1526                 return 0;
1527
1528         if (inset)
1529                 d.current_work_area_->scheduleRedraw();
1530
1531         return &d.current_work_area_->bufferView().buffer();
1532 }
1533
1534
1535 void GuiView::restartCursor()
1536 {
1537         /* When we move around, or type, it's nice to be able to see
1538          * the cursor immediately after the keypress.
1539          */
1540         if (d.current_work_area_)
1541                 d.current_work_area_->startBlinkingCursor();
1542
1543         // Take this occasion to update the toobars and layout list.
1544         updateLayoutList();
1545         updateToolbars();
1546 }
1547
1548 namespace {
1549
1550 // This list should be kept in sync with the list of insets in
1551 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1552 // dialog should have the same name as the inset.
1553
1554 char const * const dialognames[] = {
1555 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1556 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1557 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1558 "mathdelimiter", "mathmatrix", "note", "paragraph",
1559 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1560
1561 #ifdef HAVE_LIBAIKSAURUS
1562 "thesaurus",
1563 #endif
1564
1565 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1566
1567 char const * const * const end_dialognames =
1568         dialognames + (sizeof(dialognames) / sizeof(char *));
1569
1570 class cmpCStr {
1571 public:
1572         cmpCStr(char const * name) : name_(name) {}
1573         bool operator()(char const * other) {
1574                 return strcmp(other, name_) == 0;
1575         }
1576 private:
1577         char const * name_;
1578 };
1579
1580
1581 bool isValidName(string const & name)
1582 {
1583         return find_if(dialognames, end_dialognames,
1584                             cmpCStr(name.c_str())) != end_dialognames;
1585 }
1586
1587 } // namespace anon
1588
1589
1590 void GuiView::resetDialogs()
1591 {
1592         // Make sure that no LFUN uses any LyXView.
1593         theLyXFunc().setLyXView(0);
1594         d.toolbars_->init();
1595         d.menubar_->init();
1596         if (d.layout_)
1597                 d.layout_->updateContents(true);
1598         // Now update controls with current buffer.
1599         theLyXFunc().setLyXView(this);
1600         restartCursor();
1601 }
1602
1603
1604 Dialog * GuiView::find_or_build(string const & name)
1605 {
1606         if (!isValidName(name))
1607                 return 0;
1608
1609         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1610
1611         if (it != d.dialogs_.end())
1612                 return it->second.get();
1613
1614         Dialog * dialog = build(name);
1615         d.dialogs_[name].reset(dialog);
1616         if (lyxrc.allow_geometry_session)
1617                 dialog->restoreSession();
1618         return dialog;
1619 }
1620
1621
1622 void GuiView::showDialog(string const & name, string const & data,
1623         Inset * inset)
1624 {
1625         if (d.in_show_)
1626                 return;
1627
1628         d.in_show_ = true;
1629         Dialog * dialog = find_or_build(name);
1630         if (dialog) {
1631                 dialog->showData(data);
1632                 if (inset)
1633                         d.open_insets_[name] = inset;
1634         }
1635         d.in_show_ = false;
1636 }
1637
1638
1639 bool GuiView::isDialogVisible(string const & name) const
1640 {
1641         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1642         if (it == d.dialogs_.end())
1643                 return false;
1644         return it->second.get()->isVisibleView();
1645 }
1646
1647
1648 void GuiView::hideDialog(string const & name, Inset * inset)
1649 {
1650         // Don't send the signal if we are quitting, because on MSVC it is
1651         // destructed before the cut stack in CutAndPaste.cpp, and this method
1652         // is called from some inset destructor if the cut stack is not empty
1653         // on exit.
1654         if (quitting)
1655                 return;
1656
1657         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1658         if (it == d.dialogs_.end())
1659                 return;
1660
1661         if (inset && inset != getOpenInset(name))
1662                 return;
1663
1664         Dialog * const dialog = it->second.get();
1665         if (dialog->isVisibleView())
1666                 dialog->hideView();
1667         d.open_insets_[name] = 0;
1668 }
1669
1670
1671 void GuiView::disconnectDialog(string const & name)
1672 {
1673         if (!isValidName(name))
1674                 return;
1675
1676         if (d.open_insets_.find(name) != d.open_insets_.end())
1677                 d.open_insets_[name] = 0;
1678 }
1679
1680
1681 Inset * GuiView::getOpenInset(string const & name) const
1682 {
1683         if (!isValidName(name))
1684                 return 0;
1685
1686         map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1687         return it == d.open_insets_.end() ? 0 : it->second;
1688 }
1689
1690
1691 void GuiView::hideAll() const
1692 {
1693         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1694         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1695
1696         for(; it != end; ++it)
1697                 it->second->hideView();
1698 }
1699
1700
1701 void GuiView::hideBufferDependent() const
1702 {
1703         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1704         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1705
1706         for(; it != end; ++it) {
1707                 Dialog * dialog = it->second.get();
1708                 if (dialog->isBufferDependent())
1709                         dialog->hideView();
1710         }
1711 }
1712
1713
1714 void GuiView::updateBufferDependent(bool switched) const
1715 {
1716         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1717         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1718
1719         for(; it != end; ++it) {
1720                 Dialog * dialog = it->second.get();
1721                 if (!dialog->isVisibleView())
1722                         continue;
1723                 if (switched && dialog->isBufferDependent()) {
1724                         if (dialog->initialiseParams(""))
1725                                 dialog->updateView();
1726                         else
1727                                 dialog->hideView();
1728                 } else {
1729                         // A bit clunky, but the dialog will request
1730                         // that the kernel provides it with the necessary
1731                         // data.
1732                         dialog->updateDialog();
1733                 }
1734         }
1735 }
1736
1737
1738 void GuiView::checkStatus()
1739 {
1740         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1741         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1742
1743         for(; it != end; ++it) {
1744                 Dialog * const dialog = it->second.get();
1745                 if (dialog && dialog->isVisibleView())
1746                         dialog->checkStatus();
1747         }
1748 }
1749
1750
1751
1752 // will be replaced by a proper factory...
1753 Dialog * createGuiAbout(GuiView & lv);
1754 Dialog * createGuiBibitem(GuiView & lv);
1755 Dialog * createGuiBibtex(GuiView & lv);
1756 Dialog * createGuiBox(GuiView & lv);
1757 Dialog * createGuiBranch(GuiView & lv);
1758 Dialog * createGuiChanges(GuiView & lv);
1759 Dialog * createGuiCharacter(GuiView & lv);
1760 Dialog * createGuiCitation(GuiView & lv);
1761 Dialog * createGuiDelimiter(GuiView & lv);
1762 Dialog * createGuiDocument(GuiView & lv);
1763 Dialog * createGuiErrorList(GuiView & lv);
1764 Dialog * createGuiERT(GuiView & lv);
1765 Dialog * createGuiExternal(GuiView & lv);
1766 Dialog * createGuiFloat(GuiView & lv);
1767 Dialog * createGuiGraphics(GuiView & lv);
1768 Dialog * createGuiInclude(GuiView & lv);
1769 Dialog * createGuiIndex(GuiView & lv);
1770 Dialog * createGuiLabel(GuiView & lv);
1771 Dialog * createGuiListings(GuiView & lv);
1772 Dialog * createGuiLog(GuiView & lv);
1773 Dialog * createGuiMathMatrix(GuiView & lv);
1774 Dialog * createGuiNomenclature(GuiView & lv);
1775 Dialog * createGuiNote(GuiView & lv);
1776 Dialog * createGuiParagraph(GuiView & lv);
1777 Dialog * createGuiPreferences(GuiView & lv);
1778 Dialog * createGuiPrint(GuiView & lv);
1779 Dialog * createGuiRef(GuiView & lv);
1780 Dialog * createGuiSearch(GuiView & lv);
1781 Dialog * createGuiSendTo(GuiView & lv);
1782 Dialog * createGuiShowFile(GuiView & lv);
1783 Dialog * createGuiSpellchecker(GuiView & lv);
1784 Dialog * createGuiTabularCreate(GuiView & lv);
1785 Dialog * createGuiTabular(GuiView & lv);
1786 Dialog * createGuiTexInfo(GuiView & lv);
1787 Dialog * createGuiToc(GuiView & lv);
1788 Dialog * createGuiThesaurus(GuiView & lv);
1789 Dialog * createGuiHyperlink(GuiView & lv);
1790 Dialog * createGuiVSpace(GuiView & lv);
1791 Dialog * createGuiViewSource(GuiView & lv);
1792 Dialog * createGuiWrap(GuiView & lv);
1793
1794
1795 Dialog * GuiView::build(string const & name)
1796 {
1797         BOOST_ASSERT(isValidName(name));
1798
1799         if (name == "aboutlyx")
1800                 return createGuiAbout(*this);
1801         if (name == "bibitem")
1802                 return createGuiBibitem(*this);
1803         if (name == "bibtex")
1804                 return createGuiBibtex(*this);
1805         if (name == "box")
1806                 return createGuiBox(*this);
1807         if (name == "branch")
1808                 return createGuiBranch(*this);
1809         if (name == "changes")
1810                 return createGuiChanges(*this);
1811         if (name == "character")
1812                 return createGuiCharacter(*this);
1813         if (name == "citation")
1814                 return createGuiCitation(*this);
1815         if (name == "document")
1816                 return createGuiDocument(*this);
1817         if (name == "errorlist")
1818                 return createGuiErrorList(*this);
1819         if (name == "ert")
1820                 return createGuiERT(*this);
1821         if (name == "external")
1822                 return createGuiExternal(*this);
1823         if (name == "file")
1824                 return createGuiShowFile(*this);
1825         if (name == "findreplace")
1826                 return createGuiSearch(*this);
1827         if (name == "float")
1828                 return createGuiFloat(*this);
1829         if (name == "graphics")
1830                 return createGuiGraphics(*this);
1831         if (name == "include")
1832                 return createGuiInclude(*this);
1833         if (name == "index")
1834                 return createGuiIndex(*this);
1835         if (name == "nomenclature")
1836                 return createGuiNomenclature(*this);
1837         if (name == "label")
1838                 return createGuiLabel(*this);
1839         if (name == "log")
1840                 return createGuiLog(*this);
1841         if (name == "view-source")
1842                 return createGuiViewSource(*this);
1843         if (name == "mathdelimiter")
1844                 return createGuiDelimiter(*this);
1845         if (name == "mathmatrix")
1846                 return createGuiMathMatrix(*this);
1847         if (name == "note")
1848                 return createGuiNote(*this);
1849         if (name == "paragraph")
1850                 return createGuiParagraph(*this);
1851         if (name == "prefs")
1852                 return createGuiPreferences(*this);
1853         if (name == "print")
1854                 return createGuiPrint(*this);
1855         if (name == "ref")
1856                 return createGuiRef(*this);
1857         if (name == "sendto")
1858                 return createGuiSendTo(*this);
1859         if (name == "spellchecker")
1860                 return createGuiSpellchecker(*this);
1861         if (name == "tabular")
1862                 return createGuiTabular(*this);
1863         if (name == "tabularcreate")
1864                 return createGuiTabularCreate(*this);
1865         if (name == "texinfo")
1866                 return createGuiTexInfo(*this);
1867 #ifdef HAVE_LIBAIKSAURUS
1868         if (name == "thesaurus")
1869                 return createGuiThesaurus(*this);
1870 #endif
1871         if (name == "toc")
1872                 return createGuiToc(*this);
1873         if (name == "href")
1874                 return createGuiHyperlink(*this);
1875         if (name == "vspace")
1876                 return createGuiVSpace(*this);
1877         if (name == "wrap")
1878                 return createGuiWrap(*this);
1879         if (name == "listings")
1880                 return createGuiListings(*this);
1881
1882         return 0;
1883 }
1884
1885
1886 } // namespace frontend
1887 } // namespace lyx
1888
1889 #include "GuiView_moc.cpp"