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