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