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