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