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