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