]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
consolidate showDialog() incarnations
[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 #include "Dialog.h"
18
19 #include <boost/assert.hpp>
20
21 using std::string;
22
23 #include "GuiView.h"
24
25 #include "GuiApplication.h"
26 #include "GuiWorkArea.h"
27 #include "GuiKeySymbol.h"
28 #include "GuiMenubar.h"
29 #include "GuiToolbar.h"
30 #include "GuiToolbars.h"
31
32 #include "qt_helpers.h"
33
34 #include "support/filetools.h"
35 #include "support/convert.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
38
39 #include "buffer_funcs.h"
40 #include "Buffer.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
44 #include "Cursor.h"
45 #include "debug.h"
46 #include "ErrorList.h"
47 #include "FuncRequest.h"
48 #include "gettext.h"
49 #include "Intl.h"
50 #include "Layout.h"
51 #include "LyXFunc.h"
52 #include "LyX.h"
53 #include "LyXRC.h"
54 #include "MenuBackend.h"
55 #include "Paragraph.h"
56 #include "TextClass.h"
57 #include "Text.h"
58 #include "ToolbarBackend.h"
59 #include "version.h"
60
61 #include "support/lstrings.h"
62 #include "support/filetools.h" // OnlyFilename()
63 #include "support/Timeout.h"
64
65 #include <QAction>
66 #include <QApplication>
67 #include <QCloseEvent>
68 #include <QDesktopWidget>
69 #include <QDragEnterEvent>
70 #include <QDropEvent>
71 #include <QList>
72 #include <QMenu>
73 #include <QPainter>
74 #include <QPixmap>
75 #include <QPoint>
76 #include <QPushButton>
77 #include <QSettings>
78 #include <QShowEvent>
79 #include <QSplitter>
80 #include <QStackedWidget>
81 #include <QStatusBar>
82 #include <QToolBar>
83 #include <QUrl>
84
85 #include <boost/bind.hpp>
86 #include <boost/current_function.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::bformat;
106 using support::FileName;
107 using support::makeDisplayPath;
108 using support::onlyFilename;
109
110 namespace {
111
112 int const statusbar_timer_value = 3000;
113
114 class BackgroundWidget : public QWidget
115 {
116 public:
117         BackgroundWidget(QString const & file, QString const & text)
118         {
119                 splash_ = new QPixmap(file);
120                 if (!splash_) {
121                         lyxerr << "could not load splash screen: '" << fromqstr(file) << "'" << endl;
122                         return;
123                 }
124
125                 QPainter pain(splash_);
126                 pain.setPen(QColor(255, 255, 0));
127                 QFont font;
128                 // The font used to display the version info
129                 font.setStyleHint(QFont::SansSerif);
130                 font.setWeight(QFont::Bold);
131                 font.setPointSize(convert<int>(lyxrc.font_sizes[FONT_SIZE_LARGE]));
132                 pain.setFont(font);
133                 pain.drawText(260, 270, text);
134         }
135
136         void paintEvent(QPaintEvent *)
137         {
138                 if (!splash_)
139                         return;
140
141                 int x = (width() - splash_->width()) / 2;
142                 int y = (height() - splash_->height()) / 2;
143                 QPainter pain(this);
144                 pain.drawPixmap(x, y, *splash_);
145         }
146
147 private:
148         QPixmap * splash_;
149 };
150
151 } // namespace anon
152
153
154 typedef boost::shared_ptr<Dialog> DialogPtr;
155
156 struct GuiView::GuiViewPrivate
157 {
158         GuiViewPrivate()
159                 : current_work_area_(0), posx_offset(0), posy_offset(0)
160         {}
161
162         ~GuiViewPrivate()
163         {
164                 delete splitter_;
165                 delete bg_widget_;
166                 delete stack_widget_;
167                 delete menubar_;
168                 delete toolbars_;
169         }
170
171         unsigned int smallIconSize;
172         unsigned int normalIconSize;
173         unsigned int bigIconSize;
174         // static needed by "New Window"
175         static unsigned int lastIconSize;
176
177         QMenu * toolBarPopup(GuiView * parent)
178         {
179                 // FIXME: translation
180                 QMenu * menu = new QMenu(parent);
181                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
182
183                 QAction * smallIcons = new QAction(iconSizeGroup);
184                 smallIcons->setText(qt_("Small-sized icons"));
185                 smallIcons->setCheckable(true);
186                 QObject::connect(smallIcons, SIGNAL(triggered()),
187                         parent, SLOT(smallSizedIcons()));
188                 menu->addAction(smallIcons);
189
190                 QAction * normalIcons = new QAction(iconSizeGroup);
191                 normalIcons->setText(qt_("Normal-sized icons"));
192                 normalIcons->setCheckable(true);
193                 QObject::connect(normalIcons, SIGNAL(triggered()),
194                         parent, SLOT(normalSizedIcons()));
195                 menu->addAction(normalIcons);
196
197                 QAction * bigIcons = new QAction(iconSizeGroup);
198                 bigIcons->setText(qt_("Big-sized icons"));
199                 bigIcons->setCheckable(true);
200                 QObject::connect(bigIcons, SIGNAL(triggered()),
201                         parent, SLOT(bigSizedIcons()));
202                 menu->addAction(bigIcons);
203
204                 unsigned int cur = parent->iconSize().width();
205                 if ( cur == parent->d.smallIconSize)
206                         smallIcons->setChecked(true);
207                 else if (cur == parent->d.normalIconSize)
208                         normalIcons->setChecked(true);
209                 else if (cur == parent->d.bigIconSize)
210                         bigIcons->setChecked(true);
211
212                 return menu;
213         }
214
215         void initBackground()
216         {
217                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
218                 /// The text to be written on top of the pixmap
219                 QString const text = lyx_version ? QString(lyx_version) : qt_("unknown version");
220                 bg_widget_ = new BackgroundWidget(":/images/banner.png", text);
221         }
222
223         void setBackground()
224         {
225                 stack_widget_->setCurrentWidget(bg_widget_);
226                 bg_widget_->setUpdatesEnabled(true);
227         }
228
229         TabWorkArea * tabWorkArea(int i)
230         {
231                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
232         }
233
234         TabWorkArea * currentTabWorkArea()
235         {
236                 if (splitter_->count() == 1)
237                         // The first TabWorkArea is always the first one, if any.
238                         return tabWorkArea(0);
239
240                 TabWorkArea * tab_widget = 0;
241                 for (int i = 0; i != splitter_->count(); ++i) {
242                         QWidget * w = splitter_->widget(i);
243                         if (!w->hasFocus())
244                                 continue;
245                         tab_widget = dynamic_cast<TabWorkArea *>(w);
246                         if (tab_widget)
247                                 break;
248                 }
249
250                 return tab_widget;
251         }
252
253 public:
254         ///
255         string cur_title;
256
257         GuiWorkArea * current_work_area_;
258         int posx_offset;
259         int posy_offset;
260
261         QSplitter * splitter_;
262         QStackedWidget * stack_widget_;
263         BackgroundWidget * bg_widget_;
264         /// view's menubar
265         GuiMenubar * menubar_;
266         /// view's toolbars
267         GuiToolbars * toolbars_;
268         ///
269         docstring current_layout;
270
271         ///
272         std::map<std::string, Inset *> open_insets_;
273
274         ///
275         std::map<std::string, DialogPtr> dialogs_;
276         ///
277         /// flag against a race condition due to multiclicks
278         /// see bug #1119
279         bool in_show_;
280 };
281
282
283 unsigned int GuiView::GuiViewPrivate::lastIconSize = 0;
284
285
286 GuiView::GuiView(int id)
287         : QMainWindow(), LyXView(id),
288           d(*new GuiViewPrivate),
289                 quitting_by_menu_(false),
290                 autosave_timeout_(new Timeout(5000)),
291                 in_show_(false)
292 {
293         // Start autosave timer
294         if (lyxrc.autosave) {
295                 autosave_timeout_->timeout.connect(boost::bind(&GuiView::autoSave, this));
296                 autosave_timeout_->setTimeout(lyxrc.autosave * 1000);
297                 autosave_timeout_->start();
298         }
299
300         // Qt bug? signal lastWindowClosed does not work
301         setAttribute(Qt::WA_QuitOnClose, false);
302         setAttribute(Qt::WA_DeleteOnClose, true);
303
304         // hardcode here the platform specific icon size
305         d.smallIconSize = 14;   // scaling problems
306         d.normalIconSize = 20;  // ok, default
307         d.bigIconSize = 26;             // better for some math icons
308
309 #ifndef Q_WS_MACX
310         // assign an icon to main form. We do not do it under Qt/Mac,
311         // since the icon is provided in the application bundle.
312         setWindowIcon(QPixmap(":/images/lyx.png"));
313 #endif
314
315         d.splitter_ = new QSplitter;
316
317         d.initBackground();
318         LYXERR(Debug::GUI, "stack widget!");
319         d.stack_widget_ = new QStackedWidget;
320         d.stack_widget_->addWidget(d.bg_widget_);
321         d.stack_widget_->addWidget(d.splitter_);
322         setCentralWidget(d.stack_widget_);
323
324         // For Drag&Drop.
325         setAcceptDrops(true);
326 }
327
328
329 GuiView::~GuiView()
330 {
331         delete autosave_timeout_;
332         delete &d;
333 }
334
335
336 void GuiView::close()
337 {
338         quitting_by_menu_ = true;
339         d.current_work_area_ = 0;
340         for (int i = 0; i != d.splitter_->count(); ++i) {
341                 TabWorkArea * twa = d.tabWorkArea(i);
342                 if (twa)
343                         twa->closeAll();
344         }
345         QMainWindow::close();
346         quitting_by_menu_ = false;
347 }
348
349
350 void GuiView::setFocus()
351 {
352         if (d.current_work_area_)
353                 d.current_work_area_->setFocus();
354         else
355                 QWidget::setFocus();
356 }
357
358
359 QMenu* GuiView::createPopupMenu()
360 {
361         return d.toolBarPopup(this);
362 }
363
364
365 void GuiView::init()
366 {
367         setMinimumSize(300, 200);
368         // GuiToolbars *must* be initialised before GuiMenubar.
369         d.toolbars_ = new GuiToolbars(*this);
370         // FIXME: GuiToolbars::init() cannot be integrated in the ctor
371         // because LyXFunc::getStatus() needs a properly initialized
372         // GuiToolbars object (for LFUN_TOOLBAR_TOGGLE).
373         d.toolbars_->init();
374         d.menubar_ = new GuiMenubar(this, menubackend);
375
376         statusBar()->setSizeGripEnabled(true);
377
378         QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
379                 this, SLOT(clearMessage()));
380
381         d.setBackground();
382
383         if (!lyxrc.allow_geometry_session)
384                 setGeometry(50, 50, 690, 510);
385
386         // Now take care of session management.
387         QSettings settings;
388         QString const key = "view-" + QString::number(id());
389 #ifdef Q_WS_X11
390         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
391         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
392         resize(size);
393         move(pos);
394 #else
395         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
396                 setGeometry(50, 50, 690, 510);
397 #endif
398         setIconSize(settings.value(key + "/icon_size").toSize());
399 }
400
401
402 void GuiView::showEvent(QShowEvent * e)
403 {
404         LYXERR(Debug::GUI, "Passed Geometry "
405                 << size().height() << "x" << size().width()
406                 << "+" << pos().x() << "+" << pos().y());
407
408         if (d.splitter_->count() == 0)
409                 // No work area, switch to the background widget.
410                 d.setBackground();
411
412         QMainWindow::showEvent(e);
413 }
414
415
416 void GuiView::closeEvent(QCloseEvent * close_event)
417 {
418         // we may have been called through the close window button
419         // which bypasses the LFUN machinery.
420         if (!quitting_by_menu_ && guiApp->viewCount() == 1) {
421                 if (!theBufferList().quitWriteAll()) {
422                         close_event->ignore();
423                         return;
424                 }
425         }
426
427         // Make sure that no LFUN use this close to be closed View.
428         theLyXFunc().setLyXView(0);
429         // Make sure the timer time out will not trigger a statusbar update.
430         statusbar_timer_.stop();
431
432         if (lyxrc.allow_geometry_session) {
433                 QSettings settings;
434                 QString const key = "view-" + QString::number(id());
435 #ifdef Q_WS_X11
436                 settings.setValue(key + "/pos", pos());
437                 settings.setValue(key + "/size", size());
438 #else
439                 settings.setValue(key + "/geometry", saveGeometry());
440 #endif
441                 settings.setValue(key + "/icon_size", iconSize());
442                 d.toolbars_->saveToolbarInfo();
443         }
444
445         guiApp->unregisterView(id());
446         if (guiApp->viewCount() > 0) {
447                 // Just close the window and do nothing else if this is not the
448                 // last window.
449                 close_event->accept();
450                 return;
451         }
452
453         quitting = true;
454
455         // this is the place where we leave the frontend.
456         // it is the only point at which we start quitting.
457         close_event->accept();
458         // quit the event loop
459         qApp->quit();
460 }
461
462
463 void GuiView::dragEnterEvent(QDragEnterEvent * event)
464 {
465         if (event->mimeData()->hasUrls())
466                 event->accept();
467         /// \todo Ask lyx-devel is this is enough:
468         /// if (event->mimeData()->hasFormat("text/plain"))
469         ///     event->acceptProposedAction();
470 }
471
472
473 void GuiView::dropEvent(QDropEvent* event)
474 {
475         QList<QUrl> files = event->mimeData()->urls();
476         if (files.isEmpty())
477                 return;
478
479         LYXERR(Debug::GUI, BOOST_CURRENT_FUNCTION << " got URLs!");
480         for (int i = 0; i != files.size(); ++i) {
481                 string const file = support::os::internal_path(fromqstr(
482                         files.at(i).toLocalFile()));
483                 if (!file.empty())
484                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
485         }
486 }
487
488
489 void GuiView::message(docstring const & str)
490 {
491         statusBar()->showMessage(toqstr(str));
492         statusbar_timer_.stop();
493         statusbar_timer_.start(statusbar_timer_value);
494 }
495
496
497 void GuiView::smallSizedIcons()
498 {
499         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
500 }
501
502
503 void GuiView::normalSizedIcons()
504 {
505         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
506 }
507
508
509 void GuiView::bigSizedIcons()
510 {
511         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
512 }
513
514
515 void GuiView::clearMessage()
516 {
517         if (!hasFocus())
518                 return;
519         theLyXFunc().setLyXView(this);
520         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
521         statusbar_timer_.stop();
522 }
523
524
525 void GuiView::updateWindowTitle(GuiWorkArea * wa)
526 {
527         if (wa != d.current_work_area_)
528                 return;
529         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
530         setWindowIconText(wa->windowIconText());
531 }
532
533
534 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
535 {
536         disconnectBuffer();
537         disconnectBufferView();
538         connectBufferView(wa->bufferView());
539         connectBuffer(wa->bufferView().buffer());
540         d.current_work_area_ = wa;
541         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
542                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
543         updateWindowTitle(wa);
544
545         updateToc();
546         // Buffer-dependent dialogs should be updated or
547         // hidden. This should go here because some dialogs (eg ToC)
548         // require bv_->text.
549         updateBufferDependent(true);
550         updateToolbars();
551         updateLayoutChoice(false);
552         updateStatusBar();
553 }
554
555
556 void GuiView::updateStatusBar()
557 {
558         // let the user see the explicit message
559         if (statusbar_timer_.isActive())
560                 return;
561
562         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
563 }
564
565
566 bool GuiView::hasFocus() const
567 {
568         return qApp->activeWindow() == this;
569 }
570
571
572 bool GuiView::event(QEvent * e)
573 {
574         switch (e->type())
575         {
576         // Useful debug code:
577         //case QEvent::ActivationChange:
578         //case QEvent::WindowDeactivate:
579         //case QEvent::Paint:
580         //case QEvent::Enter:
581         //case QEvent::Leave:
582         //case QEvent::HoverEnter:
583         //case QEvent::HoverLeave:
584         //case QEvent::HoverMove:
585         //case QEvent::StatusTip:
586         //case QEvent::DragEnter:
587         //case QEvent::DragLeave:
588         //case QEvent::Drop:
589         //      break;
590
591         case QEvent::WindowActivate: {
592                 guiApp->setCurrentView(*this);
593                 if (d.current_work_area_) {
594                         BufferView & bv = d.current_work_area_->bufferView();
595                         connectBufferView(bv);
596                         connectBuffer(bv.buffer());
597                         // The document structure, name and dialogs might have
598                         // changed in another view.
599                         updateBufferDependent(true);
600                 } else {
601                         setWindowTitle(qt_("LyX"));
602                         setWindowIconText(qt_("LyX"));
603                 }
604                 return QMainWindow::event(e);
605         }
606         case QEvent::ShortcutOverride: {
607                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
608                 if (!d.current_work_area_) {
609                         theLyXFunc().setLyXView(this);
610                         KeySymbol sym;
611                         setKeySymbol(&sym, ke);
612                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
613                         e->accept();
614                         return true;
615                 }
616                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
617                         KeySymbol sym;
618                         setKeySymbol(&sym, ke);
619                         d.current_work_area_->processKeySym(sym, NoModifier);
620                         e->accept();
621                         return true;
622                 }
623         }
624         default:
625                 return QMainWindow::event(e);
626         }
627 }
628
629
630 bool GuiView::focusNextPrevChild(bool /*next*/)
631 {
632         setFocus();
633         return true;
634 }
635
636
637 void GuiView::setBusy(bool yes)
638 {
639         if (d.current_work_area_) {
640                 d.current_work_area_->setUpdatesEnabled(!yes);
641                 if (yes)
642                         d.current_work_area_->stopBlinkingCursor();
643                 else
644                         d.current_work_area_->startBlinkingCursor();
645         }
646
647         if (yes)
648                 QApplication::setOverrideCursor(Qt::WaitCursor);
649         else
650                 QApplication::restoreOverrideCursor();
651 }
652
653
654 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
655 {
656         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
657
658         if (tbinfo.flags & ToolbarInfo::TOP) {
659                 if (newline)
660                         addToolBarBreak(Qt::TopToolBarArea);
661                 addToolBar(Qt::TopToolBarArea, toolBar);
662         }
663
664         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
665 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
666 #if (QT_VERSION >= 0x040202)
667                 if (newline)
668                         addToolBarBreak(Qt::BottomToolBarArea);
669 #endif
670                 addToolBar(Qt::BottomToolBarArea, toolBar);
671         }
672
673         if (tbinfo.flags & ToolbarInfo::LEFT) {
674 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
675 #if (QT_VERSION >= 0x040202)
676                 if (newline)
677                         addToolBarBreak(Qt::LeftToolBarArea);
678 #endif
679                 addToolBar(Qt::LeftToolBarArea, toolBar);
680         }
681
682         if (tbinfo.flags & ToolbarInfo::RIGHT) {
683 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
684 #if (QT_VERSION >= 0x040202)
685                 if (newline)
686                         addToolBarBreak(Qt::RightToolBarArea);
687 #endif
688                 addToolBar(Qt::RightToolBarArea, toolBar);
689         }
690
691         // The following does not work so I cannot restore to exact toolbar location
692         /*
693         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
694         toolBar->move(tbinfo.posx, tbinfo.posy);
695         */
696
697         return toolBar;
698 }
699
700
701 GuiWorkArea * GuiView::workArea(Buffer & buffer)
702 {
703         for (int i = 0; i != d.splitter_->count(); ++i) {
704                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
705                 if (wa)
706                         return wa;
707         }
708         return 0;
709 }
710
711
712 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
713 {
714         GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
715         wa->setUpdatesEnabled(false);
716
717         // Automatically create a TabWorkArea if there are none yet.
718         if (!d.splitter_->count())
719                 addTabWorkArea();
720
721         TabWorkArea * tab_widget = d.currentTabWorkArea();
722         tab_widget->addTab(wa, wa->windowTitle());
723         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
724                 tab_widget, SLOT(updateTabText(GuiWorkArea *)));
725
726         wa->bufferView().updateMetrics();
727
728         // Hide tabbar if there's only one tab.
729         tab_widget->showBar(tab_widget->count() > 1);
730         return wa;
731 }
732
733
734 void GuiView::addTabWorkArea()
735 {
736         TabWorkArea * twa = new TabWorkArea;
737         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
738                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
739         d.splitter_->addWidget(twa);
740         d.stack_widget_->setCurrentWidget(d.splitter_);
741 }
742
743
744 GuiWorkArea const * GuiView::currentWorkArea() const
745 {
746         return d.current_work_area_;
747 }
748
749
750 void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
751 {
752         BOOST_ASSERT(work_area);
753
754         // Changing work area can result from opening a file so
755         // update the toc in any case.
756         updateToc();
757
758         GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
759         d.current_work_area_ = wa;
760         for (int i = 0; i != d.splitter_->count(); ++i) {
761                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
762                         return;
763         }
764 }
765
766
767 void GuiView::removeWorkArea(GuiWorkArea * work_area)
768 {
769         BOOST_ASSERT(work_area);
770         GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
771         if (gwa == d.current_work_area_) {
772                 disconnectBuffer();
773                 disconnectBufferView();
774                 hideBufferDependent();
775                 d.current_work_area_ = 0;
776         }
777
778         // removing a work area often results from closing a file so
779         // update the toc in any case.
780         updateToc();
781
782         for (int i = 0; i != d.splitter_->count(); ++i) {
783                 TabWorkArea * twa = d.tabWorkArea(i);
784                 if (!twa->removeWorkArea(gwa))
785                         // Not found in this tab group.
786                         continue;
787
788                 // We found and removed the GuiWorkArea.
789                 if (!twa->count()) {
790                         // No more WorkAreas in this tab group, so delete it.
791                         delete twa;
792                         break;
793                 }
794
795                 if (d.current_work_area_)
796                         // This means that we are not closing the current GuiWorkArea;
797                         break;
798
799                 // Switch to the next GuiWorkArea in the found TabWorkArea.
800                 d.current_work_area_ = twa->currentWorkArea();
801                 break;
802         }
803
804         if (d.splitter_->count() == 0)
805                 // No more work area, switch to the background widget.
806                 d.setBackground();
807 }
808
809
810 void GuiView::updateLayoutChoice(bool force)
811 {
812         // Don't show any layouts without a buffer
813         if (!buffer()) {
814                 d.toolbars_->clearLayoutList();
815                 return;
816         }
817
818         // Update the layout display
819         if (d.toolbars_->updateLayoutList(buffer()->params().getTextClassPtr(), force)) {
820                 d.current_layout = buffer()->params().getTextClass().defaultLayoutName();
821         }
822
823         docstring const & layout = d.current_work_area_->bufferView().cursor().
824                 innerParagraph().layout()->name();
825
826         if (layout != d.current_layout) {
827                 d.toolbars_->setLayout(layout);
828                 d.current_layout = layout;
829         }
830 }
831
832
833 bool GuiView::isToolbarVisible(std::string const & id)
834 {
835         return d.toolbars_->visible(id);
836 }
837
838 void GuiView::updateToolbars()
839 {
840         if (d.current_work_area_) {
841                 bool const math =
842                         d.current_work_area_->bufferView().cursor().inMathed();
843                 bool const table =
844                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
845                 bool const review =
846                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
847                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
848
849                 d.toolbars_->update(math, table, review);
850         } else
851                 d.toolbars_->update(false, false, false);
852
853         // update read-only status of open dialogs.
854         checkStatus();
855 }
856
857
858 Buffer * GuiView::buffer()
859 {
860         if (d.current_work_area_)
861                 return &d.current_work_area_->bufferView().buffer();
862         return 0;
863 }
864
865
866 Buffer const * GuiView::buffer() const
867 {
868         if (d.current_work_area_)
869                 return &d.current_work_area_->bufferView().buffer();
870         return 0;
871 }
872
873
874 void GuiView::setBuffer(Buffer * newBuffer)
875 {
876         BOOST_ASSERT(newBuffer);
877         setBusy(true);
878
879         GuiWorkArea * wa = workArea(*newBuffer);
880         if (wa == 0) {
881                 updateLabels(*newBuffer->masterBuffer());
882                 wa = addWorkArea(*newBuffer);
883         } else {
884                 //Disconnect the old buffer...there's no new one.
885                 disconnectBuffer();
886         }
887         connectBuffer(*newBuffer);
888         connectBufferView(wa->bufferView());
889         setCurrentWorkArea(wa);
890
891         setBusy(false);
892 }
893
894
895 Buffer * GuiView::loadLyXFile(FileName const & filename, bool tolastfiles)
896 {
897         setBusy(true);
898
899         Buffer * newBuffer = checkAndLoadLyXFile(filename);
900
901         if (!newBuffer) {
902                 message(_("Document not loaded."));
903                 updateStatusBar();
904                 setBusy(false);
905                 return 0;
906         }
907
908         GuiWorkArea * wa = workArea(*newBuffer);
909         if (wa == 0)
910                 wa = addWorkArea(*newBuffer);
911
912         // scroll to the position when the file was last closed
913         if (lyxrc.use_lastfilepos) {
914                 LastFilePosSection::FilePos filepos =
915                         LyX::ref().session().lastFilePos().load(filename);
916                 // if successfully move to pit (returned par_id is not zero),
917                 // update metrics and reset font
918                 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
919         }
920
921         if (tolastfiles)
922                 LyX::ref().session().lastFiles().add(filename);
923
924         setBusy(false);
925         return newBuffer;
926 }
927
928
929 void GuiView::connectBuffer(Buffer & buf)
930 {
931         buf.setGuiDelegate(this);
932 }
933
934
935 void GuiView::disconnectBuffer()
936 {
937         if (d.current_work_area_)
938                 d.current_work_area_->bufferView().setGuiDelegate(0);
939 }
940
941
942 void GuiView::connectBufferView(BufferView & bv)
943 {
944         bv.setGuiDelegate(this);
945 }
946
947
948 void GuiView::disconnectBufferView()
949 {
950         if (d.current_work_area_)
951                 d.current_work_area_->bufferView().setGuiDelegate(0);
952 }
953
954
955 void GuiView::errors(string const & error_type)
956 {
957         ErrorList & el = buffer()->errorList(error_type);
958         if (!el.empty())
959                 showDialog("errorlist", error_type);
960 }
961
962
963 void GuiView::updateDialog(string const & name, string const & data)
964 {
965         if (!isDialogVisible(name))
966                 return;
967
968         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
969         if (it == d.dialogs_.end())
970                 return;
971
972         Dialog * const dialog = it->second.get();
973         if (dialog->isVisibleView())
974                 dialog->updateData(data);
975 }
976
977
978 BufferView * GuiView::view()
979 {
980         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
981 }
982
983
984 void GuiView::updateToc()
985 {
986         updateDialog("toc", "");
987 }
988
989
990 void GuiView::updateEmbeddedFiles()
991 {
992         updateDialog("embedding", "");
993 }
994
995
996 void GuiView::autoSave()
997 {
998         LYXERR(Debug::INFO, "Running autoSave()");
999
1000         if (buffer())
1001                 view()->buffer().autoSave();
1002 }
1003
1004
1005 void GuiView::resetAutosaveTimers()
1006 {
1007         if (lyxrc.autosave)
1008                 autosave_timeout_->restart();
1009 }
1010
1011
1012 void GuiView::dispatch(FuncRequest const & cmd)
1013 {
1014         switch(cmd.action) {
1015                 case LFUN_BUFFER_SWITCH:
1016                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1017                         break;
1018
1019                 case LFUN_COMMAND_EXECUTE: {
1020                         bool const show_it = cmd.argument() != "off";
1021                         d.toolbars_->showCommandBuffer(show_it);
1022                         break;
1023                 }
1024                 case LFUN_DROP_LAYOUTS_CHOICE:
1025                         d.toolbars_->openLayoutList();
1026                         break;
1027
1028                 case LFUN_MENU_OPEN:
1029                         d.menubar_->openByName(toqstr(cmd.argument()));
1030                         break;
1031
1032                 case LFUN_TOOLBAR_TOGGLE: {
1033                         string const name = cmd.getArg(0);
1034                         bool const allowauto = cmd.getArg(1) == "allowauto";
1035                         // it is possible to get current toolbar status like this,...
1036                         // but I decide to obey the order of ToolbarBackend::flags
1037                         // and disregard real toolbar status.
1038                         // toolbars_->saveToolbarInfo();
1039                         //
1040                         // toggle state on/off/auto
1041                         d.toolbars_->toggleToolbarState(name, allowauto);
1042                         // update toolbar
1043                         updateToolbars();
1044
1045                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1046                         if (!tbi) {
1047                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1048                                 break;
1049                         }
1050                         docstring state;
1051                         if (tbi->flags & ToolbarInfo::ON)
1052                                 state = _("on");
1053                         else if (tbi->flags & ToolbarInfo::OFF)
1054                                 state = _("off");
1055                         else if (tbi->flags & ToolbarInfo::AUTO)
1056                                 state = _("auto");
1057
1058                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1059                                            _(tbi->gui_name), state));
1060                         break;
1061                 }
1062
1063                 default:
1064                         theLyXFunc().setLyXView(this);
1065                         lyx::dispatch(cmd);
1066         }
1067 }
1068
1069
1070 Buffer const * GuiView::updateInset(Inset const * inset)
1071 {
1072         if (!d.current_work_area_)
1073                 return 0;
1074
1075         if (inset)
1076                 d.current_work_area_->scheduleRedraw();
1077
1078         return &d.current_work_area_->bufferView().buffer();
1079 }
1080
1081
1082 void GuiView::restartCursor()
1083 {
1084         /* When we move around, or type, it's nice to be able to see
1085          * the cursor immediately after the keypress.
1086          */
1087         if (d.current_work_area_)
1088                 d.current_work_area_->startBlinkingCursor();
1089 }
1090
1091
1092 Dialog * GuiView::find_or_build(string const & name)
1093 {
1094         if (!isValidName(name))
1095                 return 0;
1096
1097         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1098
1099         if (it != d.dialogs_.end())
1100                 return it->second.get();
1101
1102         d.dialogs_[name].reset(build(name));
1103         return d.dialogs_[name].get();
1104 }
1105
1106
1107 void GuiView::showDialog(string const & name, string const & data,
1108         Inset * inset)
1109 {
1110         if (in_show_)
1111                 return;
1112
1113         in_show_ = true;
1114         Dialog * dialog = find_or_build(name);
1115         if (dialog) {
1116                 dialog->showData(data);
1117                 if (inset)
1118                         d.open_insets_[name] = inset;
1119         }
1120         in_show_ = false;
1121 }
1122
1123
1124 bool GuiView::isDialogVisible(string const & name) const
1125 {
1126         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1127         if (it == d.dialogs_.end())
1128                 return false;
1129         return it->second.get()->isVisibleView();
1130 }
1131
1132
1133 void GuiView::hideDialog(string const & name, Inset * inset)
1134 {
1135         // Don't send the signal if we are quitting, because on MSVC it is
1136         // destructed before the cut stack in CutAndPaste.cpp, and this method
1137         // is called from some inset destructor if the cut stack is not empty
1138         // on exit.
1139         if (quitting)
1140                 return;
1141
1142         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1143         if (it == d.dialogs_.end())
1144                 return;
1145
1146         if (inset && inset != getOpenInset(name))
1147                 return;
1148
1149         Dialog * const dialog = it->second.get();
1150         if (dialog->isVisibleView())
1151                 dialog->hide();
1152         d.open_insets_[name] = 0;
1153 }
1154
1155
1156 void GuiView::disconnectDialog(string const & name)
1157 {
1158         if (!isValidName(name))
1159                 return;
1160
1161         if (d.open_insets_.find(name) != d.open_insets_.end())
1162                 d.open_insets_[name] = 0;
1163 }
1164
1165
1166 Inset * GuiView::getOpenInset(string const & name) const
1167 {
1168         if (!isValidName(name))
1169                 return 0;
1170
1171         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1172         return it == d.open_insets_.end() ? 0 : it->second;
1173 }
1174
1175
1176 void GuiView::hideAll() const
1177 {
1178         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1179         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1180
1181         for(; it != end; ++it)
1182                 it->second->hide();
1183 }
1184
1185
1186 void GuiView::hideBufferDependent() const
1187 {
1188         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1189         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1190
1191         for(; it != end; ++it) {
1192                 Dialog * dialog = it->second.get();
1193                 if (dialog->isBufferDependent())
1194                         dialog->hide();
1195         }
1196 }
1197
1198
1199 void GuiView::updateBufferDependent(bool switched) const
1200 {
1201         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1202         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1203
1204         for(; it != end; ++it) {
1205                 Dialog * dialog = it->second.get();
1206                 if (switched && dialog->isBufferDependent()) {
1207                         if (dialog->isVisibleView() && dialog->initialiseParams(""))
1208                                 dialog->updateView();
1209                         else
1210                                 dialog->hide();
1211                 } else {
1212                         // A bit clunky, but the dialog will request
1213                         // that the kernel provides it with the necessary
1214                         // data.
1215                         dialog->slotRestore();
1216                 }
1217         }
1218 }
1219
1220
1221 void GuiView::redrawDialog() const
1222 {
1223         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1224         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1225
1226         for(; it != end; ++it)
1227                 it->second->redraw();
1228 }
1229
1230
1231 void GuiView::checkStatus()
1232 {
1233         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1234         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1235
1236         for(; it != end; ++it) {
1237                 Dialog * const dialog = it->second.get();
1238                 if (dialog && dialog->isVisibleView())
1239                         dialog->checkStatus();
1240         }
1241 }
1242
1243
1244 namespace {
1245
1246 // This list should be kept in sync with the list of insets in
1247 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1248 // dialog should have the same name as the inset.
1249
1250 char const * const dialognames[] = {
1251 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1252 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1253 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1254 "mathdelimiter", "mathmatrix", "note", "paragraph",
1255 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1256
1257 #ifdef HAVE_LIBAIKSAURUS
1258 "thesaurus",
1259 #endif
1260
1261 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1262
1263 char const * const * const end_dialognames =
1264         dialognames + (sizeof(dialognames) / sizeof(char *));
1265
1266 class cmpCStr {
1267 public:
1268         cmpCStr(char const * name) : name_(name) {}
1269         bool operator()(char const * other) {
1270                 return strcmp(other, name_) == 0;
1271         }
1272 private:
1273         char const * name_;
1274 };
1275
1276
1277 } // namespace anon
1278
1279 // will be replaced by a proper factory...
1280 Dialog * createGuiAbout(LyXView & lv);
1281 Dialog * createGuiBibitem(LyXView & lv);
1282 Dialog * createGuiBibtex(LyXView & lv);
1283 Dialog * createGuiBox(LyXView & lv);
1284 Dialog * createGuiBranch(LyXView & lv);
1285 Dialog * createGuiChanges(LyXView & lv);
1286 Dialog * createGuiCharacter(LyXView & lv);
1287 Dialog * createGuiCitation(LyXView & lv);
1288 Dialog * createGuiDelimiter(LyXView & lv);
1289 Dialog * createGuiDocument(LyXView & lv);
1290 Dialog * createGuiErrorList(LyXView & lv);
1291 Dialog * createGuiERT(LyXView & lv);
1292 Dialog * createGuiExternal(LyXView & lv);
1293 Dialog * createGuiFloat(LyXView & lv);
1294 Dialog * createGuiGraphics(LyXView & lv);
1295 Dialog * createGuiInclude(LyXView & lv);
1296 Dialog * createGuiIndex(LyXView & lv);
1297 Dialog * createGuiLabel(LyXView & lv);
1298 Dialog * createGuiListings(LyXView & lv);
1299 Dialog * createGuiLog(LyXView & lv);
1300 Dialog * createGuiMathMatrix(LyXView & lv);
1301 Dialog * createGuiNomenclature(LyXView & lv);
1302 Dialog * createGuiNote(LyXView & lv);
1303 Dialog * createGuiParagraph(LyXView & lv);
1304 Dialog * createGuiPreferences(LyXView & lv);
1305 Dialog * createGuiPrint(LyXView & lv);
1306 Dialog * createGuiRef(LyXView & lv);
1307 Dialog * createGuiSearch(LyXView & lv);
1308 Dialog * createGuiSendTo(LyXView & lv);
1309 Dialog * createGuiShowFile(LyXView & lv);
1310 Dialog * createGuiSpellchecker(LyXView & lv);
1311 Dialog * createGuiTabularCreate(LyXView & lv);
1312 Dialog * createGuiTabular(LyXView & lv);
1313 Dialog * createGuiTexInfo(LyXView & lv);
1314 Dialog * createGuiToc(LyXView & lv);
1315 Dialog * createGuiThesaurus(LyXView & lv);
1316 Dialog * createGuiHyperlink(LyXView & lv);
1317 Dialog * createGuiVSpace(LyXView & lv);
1318 Dialog * createGuiViewSource(LyXView & lv);
1319 Dialog * createGuiWrap(LyXView & lv);
1320
1321
1322 bool GuiView::isValidName(string const & name) const
1323 {
1324         return std::find_if(dialognames, end_dialognames,
1325                             cmpCStr(name.c_str())) != end_dialognames;
1326 }
1327
1328
1329 Dialog * GuiView::build(string const & name)
1330 {
1331         BOOST_ASSERT(isValidName(name));
1332
1333         if (name == "aboutlyx")
1334                 return createGuiAbout(*this);
1335         if (name == "bibitem")
1336                 return createGuiBibitem(*this);
1337         if (name == "bibtex")
1338                 return createGuiBibtex(*this);
1339         if (name == "box")
1340                 return createGuiBox(*this);
1341         if (name == "branch")
1342                 return createGuiBranch(*this);
1343         if (name == "changes")
1344                 return createGuiChanges(*this);
1345         if (name == "character")
1346                 return createGuiCharacter(*this);
1347         if (name == "citation")
1348                 return createGuiCitation(*this);
1349         if (name == "document")
1350                 return createGuiDocument(*this);
1351         if (name == "errorlist")
1352                 return createGuiErrorList(*this);
1353         if (name == "ert")
1354                 return createGuiERT(*this);
1355         if (name == "external")
1356                 return createGuiExternal(*this);
1357         if (name == "file")
1358                 return createGuiShowFile(*this);
1359         if (name == "findreplace")
1360                 return createGuiSearch(*this);
1361         if (name == "float")
1362                 return createGuiFloat(*this);
1363         if (name == "graphics")
1364                 return createGuiGraphics(*this);
1365         if (name == "include")
1366                 return createGuiInclude(*this);
1367         if (name == "index")
1368                 return createGuiIndex(*this);
1369         if (name == "nomenclature")
1370                 return createGuiNomenclature(*this);
1371         if (name == "label")
1372                 return createGuiLabel(*this);
1373         if (name == "log")
1374                 return createGuiLog(*this);
1375         if (name == "view-source")
1376                 return createGuiViewSource(*this);
1377         if (name == "mathdelimiter")
1378                 return createGuiDelimiter(*this);
1379         if (name == "mathmatrix")
1380                 return createGuiMathMatrix(*this);
1381         if (name == "note")
1382                 return createGuiNote(*this);
1383         if (name == "paragraph")
1384                 return createGuiParagraph(*this);
1385         if (name == "prefs")
1386                 return createGuiPreferences(*this);
1387         if (name == "print")
1388                 return createGuiPrint(*this);
1389         if (name == "ref")
1390                 return createGuiRef(*this);
1391         if (name == "sendto")
1392                 return createGuiSendTo(*this);
1393         if (name == "spellchecker")
1394                 return createGuiSpellchecker(*this);
1395         if (name == "tabular")
1396                 return createGuiTabular(*this);
1397         if (name == "tabularcreate")
1398                 return createGuiTabularCreate(*this);
1399         if (name == "texinfo")
1400                 return createGuiTexInfo(*this);
1401 #ifdef HAVE_LIBAIKSAURUS
1402         if (name == "thesaurus")
1403                 return createGuiThesaurus(*this);
1404 #endif
1405         if (name == "toc")
1406                 return createGuiToc(*this);
1407         if (name == "href")
1408                 return createGuiHyperlink(*this);
1409         if (name == "vspace")
1410                 return createGuiVSpace(*this);
1411         if (name == "wrap")
1412                 return createGuiWrap(*this);
1413         if (name == "listings")
1414                 return createGuiListings(*this);
1415
1416         return 0;
1417 }
1418
1419
1420 /// Are the tooltips on or off?
1421 bool GuiView::tooltipsEnabled()
1422 {
1423         return false;
1424 }
1425
1426 } // namespace frontend
1427 } // namespace lyx
1428
1429 #include "GuiView_moc.cpp"