]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Remove some methods from GuiView.
[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         GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
707         wa->setUpdatesEnabled(false);
708
709         // Automatically create a TabWorkArea if there are none yet.
710         if (!d.splitter_->count())
711                 addTabWorkArea();
712
713         TabWorkArea * tab_widget = d.currentTabWorkArea();
714         tab_widget->addTab(wa, wa->windowTitle());
715         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
716                 tab_widget, SLOT(updateTabText(GuiWorkArea *)));
717
718         wa->bufferView().updateMetrics();
719
720         // Hide tabbar if there's only one tab.
721         tab_widget->showBar(tab_widget->count() > 1);
722         return wa;
723 }
724
725
726 void GuiView::addTabWorkArea()
727 {
728         TabWorkArea * twa = new TabWorkArea;
729         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
730                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
731         d.splitter_->addWidget(twa);
732         d.stack_widget_->setCurrentWidget(d.splitter_);
733 }
734
735
736 GuiWorkArea const * GuiView::currentWorkArea() const
737 {
738         return d.current_work_area_;
739 }
740
741
742 void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
743 {
744         BOOST_ASSERT(work_area);
745
746         // Changing work area can result from opening a file so
747         // update the toc in any case.
748         updateToc();
749
750         GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
751         d.current_work_area_ = wa;
752         for (int i = 0; i != d.splitter_->count(); ++i) {
753                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
754                         return;
755         }
756 }
757
758
759 void GuiView::removeWorkArea(GuiWorkArea * work_area)
760 {
761         BOOST_ASSERT(work_area);
762         GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
763         if (gwa == d.current_work_area_) {
764                 disconnectBuffer();
765                 disconnectBufferView();
766                 hideBufferDependent();
767                 d.current_work_area_ = 0;
768         }
769
770         // removing a work area often results from closing a file so
771         // update the toc in any case.
772         updateToc();
773
774         for (int i = 0; i != d.splitter_->count(); ++i) {
775                 TabWorkArea * twa = d.tabWorkArea(i);
776                 if (!twa->removeWorkArea(gwa))
777                         // Not found in this tab group.
778                         continue;
779
780                 // We found and removed the GuiWorkArea.
781                 if (!twa->count()) {
782                         // No more WorkAreas in this tab group, so delete it.
783                         delete twa;
784                         break;
785                 }
786
787                 if (d.current_work_area_)
788                         // This means that we are not closing the current GuiWorkArea;
789                         break;
790
791                 // Switch to the next GuiWorkArea in the found TabWorkArea.
792                 d.current_work_area_ = twa->currentWorkArea();
793                 break;
794         }
795
796         if (d.splitter_->count() == 0)
797                 // No more work area, switch to the background widget.
798                 d.setBackground();
799 }
800
801
802 void GuiView::updateLayoutChoice(bool force)
803 {
804         // Don't show any layouts without a buffer
805         if (!buffer()) {
806                 d.toolbars_->clearLayoutList();
807                 return;
808         }
809
810         // Update the layout display
811         if (d.toolbars_->updateLayoutList(buffer()->params().getTextClassPtr(), force)) {
812                 d.current_layout = buffer()->params().getTextClass().defaultLayoutName();
813         }
814
815         docstring const & layout = d.current_work_area_->bufferView().cursor().
816                 innerParagraph().layout()->name();
817
818         if (layout != d.current_layout) {
819                 d.toolbars_->setLayout(layout);
820                 d.current_layout = layout;
821         }
822 }
823
824
825 bool GuiView::isToolbarVisible(std::string const & id)
826 {
827         return d.toolbars_->visible(id);
828 }
829
830 void GuiView::updateToolbars()
831 {
832         if (d.current_work_area_) {
833                 bool const math =
834                         d.current_work_area_->bufferView().cursor().inMathed();
835                 bool const table =
836                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
837                 bool const review =
838                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
839                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
840
841                 d.toolbars_->update(math, table, review);
842         } else
843                 d.toolbars_->update(false, false, false);
844
845         // update read-only status of open dialogs.
846         checkStatus();
847 }
848
849
850 Buffer * GuiView::buffer()
851 {
852         if (d.current_work_area_)
853                 return &d.current_work_area_->bufferView().buffer();
854         return 0;
855 }
856
857
858 Buffer const * GuiView::buffer() const
859 {
860         if (d.current_work_area_)
861                 return &d.current_work_area_->bufferView().buffer();
862         return 0;
863 }
864
865
866 void GuiView::setBuffer(Buffer * newBuffer)
867 {
868         BOOST_ASSERT(newBuffer);
869         setBusy(true);
870
871         GuiWorkArea * wa = workArea(*newBuffer);
872         if (wa == 0) {
873                 updateLabels(*newBuffer->masterBuffer());
874                 wa = addWorkArea(*newBuffer);
875         } else {
876                 //Disconnect the old buffer...there's no new one.
877                 disconnectBuffer();
878         }
879         connectBuffer(*newBuffer);
880         connectBufferView(wa->bufferView());
881         setCurrentWorkArea(wa);
882
883         setBusy(false);
884 }
885
886
887 Buffer * GuiView::loadLyXFile(FileName const & filename, bool tolastfiles)
888 {
889         setBusy(true);
890
891         Buffer * newBuffer = checkAndLoadLyXFile(filename);
892
893         if (!newBuffer) {
894                 message(_("Document not loaded."));
895                 updateStatusBar();
896                 setBusy(false);
897                 return 0;
898         }
899
900         GuiWorkArea * wa = workArea(*newBuffer);
901         if (wa == 0)
902                 wa = addWorkArea(*newBuffer);
903
904         // scroll to the position when the file was last closed
905         if (lyxrc.use_lastfilepos) {
906                 LastFilePosSection::FilePos filepos =
907                         LyX::ref().session().lastFilePos().load(filename);
908                 // if successfully move to pit (returned par_id is not zero),
909                 // update metrics and reset font
910                 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
911         }
912
913         if (tolastfiles)
914                 LyX::ref().session().lastFiles().add(filename);
915
916         setBusy(false);
917         return newBuffer;
918 }
919
920
921 void GuiView::connectBuffer(Buffer & buf)
922 {
923         buf.setGuiDelegate(this);
924 }
925
926
927 void GuiView::disconnectBuffer()
928 {
929         if (d.current_work_area_)
930                 d.current_work_area_->bufferView().setGuiDelegate(0);
931 }
932
933
934 void GuiView::connectBufferView(BufferView & bv)
935 {
936         bv.setGuiDelegate(this);
937 }
938
939
940 void GuiView::disconnectBufferView()
941 {
942         if (d.current_work_area_)
943                 d.current_work_area_->bufferView().setGuiDelegate(0);
944 }
945
946
947 void GuiView::errors(string const & error_type)
948 {
949         ErrorList & el = buffer()->errorList(error_type);
950         if (!el.empty())
951                 showDialog("errorlist", error_type);
952 }
953
954
955 void GuiView::updateDialog(string const & name, string const & data)
956 {
957         if (!isDialogVisible(name))
958                 return;
959
960         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
961         if (it == d.dialogs_.end())
962                 return;
963
964         Dialog * const dialog = it->second.get();
965         if (dialog->isVisibleView())
966                 dialog->updateData(data);
967 }
968
969
970 BufferView * GuiView::view()
971 {
972         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
973 }
974
975
976 void GuiView::updateToc()
977 {
978         updateDialog("toc", "");
979 }
980
981
982 void GuiView::updateEmbeddedFiles()
983 {
984         updateDialog("embedding", "");
985 }
986
987
988 void GuiView::autoSave()
989 {
990         LYXERR(Debug::INFO, "Running autoSave()");
991
992         if (buffer())
993                 view()->buffer().autoSave();
994 }
995
996
997 void GuiView::resetAutosaveTimers()
998 {
999         if (lyxrc.autosave)
1000                 d.autosave_timeout_->restart();
1001 }
1002
1003
1004 void GuiView::dispatch(FuncRequest const & cmd)
1005 {
1006         switch(cmd.action) {
1007                 case LFUN_BUFFER_SWITCH:
1008                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1009                         break;
1010
1011                 case LFUN_COMMAND_EXECUTE: {
1012                         bool const show_it = cmd.argument() != "off";
1013                         d.toolbars_->showCommandBuffer(show_it);
1014                         break;
1015                 }
1016                 case LFUN_DROP_LAYOUTS_CHOICE:
1017                         d.toolbars_->openLayoutList();
1018                         break;
1019
1020                 case LFUN_MENU_OPEN:
1021                         d.menubar_->openByName(toqstr(cmd.argument()));
1022                         break;
1023
1024                 case LFUN_TOOLBAR_TOGGLE: {
1025                         string const name = cmd.getArg(0);
1026                         bool const allowauto = cmd.getArg(1) == "allowauto";
1027                         // it is possible to get current toolbar status like this,...
1028                         // but I decide to obey the order of ToolbarBackend::flags
1029                         // and disregard real toolbar status.
1030                         // toolbars_->saveToolbarInfo();
1031                         //
1032                         // toggle state on/off/auto
1033                         d.toolbars_->toggleToolbarState(name, allowauto);
1034                         // update toolbar
1035                         updateToolbars();
1036
1037                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1038                         if (!tbi) {
1039                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1040                                 break;
1041                         }
1042                         docstring state;
1043                         if (tbi->flags & ToolbarInfo::ON)
1044                                 state = _("on");
1045                         else if (tbi->flags & ToolbarInfo::OFF)
1046                                 state = _("off");
1047                         else if (tbi->flags & ToolbarInfo::AUTO)
1048                                 state = _("auto");
1049
1050                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1051                                            _(tbi->gui_name), state));
1052                         break;
1053                 }
1054
1055                 default:
1056                         theLyXFunc().setLyXView(this);
1057                         lyx::dispatch(cmd);
1058         }
1059 }
1060
1061
1062 Buffer const * GuiView::updateInset(Inset const * inset)
1063 {
1064         if (!d.current_work_area_)
1065                 return 0;
1066
1067         if (inset)
1068                 d.current_work_area_->scheduleRedraw();
1069
1070         return &d.current_work_area_->bufferView().buffer();
1071 }
1072
1073
1074 void GuiView::restartCursor()
1075 {
1076         /* When we move around, or type, it's nice to be able to see
1077          * the cursor immediately after the keypress.
1078          */
1079         if (d.current_work_area_)
1080                 d.current_work_area_->startBlinkingCursor();
1081 }
1082
1083 namespace {
1084
1085 // This list should be kept in sync with the list of insets in
1086 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1087 // dialog should have the same name as the inset.
1088
1089 char const * const dialognames[] = {
1090 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1091 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1092 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1093 "mathdelimiter", "mathmatrix", "note", "paragraph",
1094 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1095
1096 #ifdef HAVE_LIBAIKSAURUS
1097 "thesaurus",
1098 #endif
1099
1100 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
1101
1102 char const * const * const end_dialognames =
1103         dialognames + (sizeof(dialognames) / sizeof(char *));
1104
1105 class cmpCStr {
1106 public:
1107         cmpCStr(char const * name) : name_(name) {}
1108         bool operator()(char const * other) {
1109                 return strcmp(other, name_) == 0;
1110         }
1111 private:
1112         char const * name_;
1113 };
1114
1115
1116 bool isValidName(string const & name)
1117 {
1118         return std::find_if(dialognames, end_dialognames,
1119                             cmpCStr(name.c_str())) != end_dialognames;
1120 }
1121
1122 } // namespace anon
1123
1124
1125 Dialog * GuiView::find_or_build(string const & name)
1126 {
1127         if (!isValidName(name))
1128                 return 0;
1129
1130         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1131
1132         if (it != d.dialogs_.end())
1133                 return it->second.get();
1134
1135         d.dialogs_[name].reset(build(name));
1136         return d.dialogs_[name].get();
1137 }
1138
1139
1140 void GuiView::showDialog(string const & name, string const & data,
1141         Inset * inset)
1142 {
1143         if (d.in_show_)
1144                 return;
1145
1146         d.in_show_ = true;
1147         Dialog * dialog = find_or_build(name);
1148         if (dialog) {
1149                 dialog->showData(data);
1150                 if (inset)
1151                         d.open_insets_[name] = inset;
1152         }
1153         d.in_show_ = false;
1154 }
1155
1156
1157 bool GuiView::isDialogVisible(string const & name) const
1158 {
1159         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1160         if (it == d.dialogs_.end())
1161                 return false;
1162         return it->second.get()->isVisibleView();
1163 }
1164
1165
1166 void GuiView::hideDialog(string const & name, Inset * inset)
1167 {
1168         // Don't send the signal if we are quitting, because on MSVC it is
1169         // destructed before the cut stack in CutAndPaste.cpp, and this method
1170         // is called from some inset destructor if the cut stack is not empty
1171         // on exit.
1172         if (quitting)
1173                 return;
1174
1175         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1176         if (it == d.dialogs_.end())
1177                 return;
1178
1179         if (inset && inset != getOpenInset(name))
1180                 return;
1181
1182         Dialog * const dialog = it->second.get();
1183         if (dialog->isVisibleView())
1184                 dialog->hide();
1185         d.open_insets_[name] = 0;
1186 }
1187
1188
1189 void GuiView::disconnectDialog(string const & name)
1190 {
1191         if (!isValidName(name))
1192                 return;
1193
1194         if (d.open_insets_.find(name) != d.open_insets_.end())
1195                 d.open_insets_[name] = 0;
1196 }
1197
1198
1199 Inset * GuiView::getOpenInset(string const & name) const
1200 {
1201         if (!isValidName(name))
1202                 return 0;
1203
1204         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1205         return it == d.open_insets_.end() ? 0 : it->second;
1206 }
1207
1208
1209 void GuiView::hideAll() 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                 it->second->hide();
1216 }
1217
1218
1219 void GuiView::hideBufferDependent() const
1220 {
1221         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1222         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1223
1224         for(; it != end; ++it) {
1225                 Dialog * dialog = it->second.get();
1226                 if (dialog->isBufferDependent())
1227                         dialog->hide();
1228         }
1229 }
1230
1231
1232 void GuiView::updateBufferDependent(bool switched) const
1233 {
1234         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1235         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1236
1237         for(; it != end; ++it) {
1238                 Dialog * dialog = it->second.get();
1239                 if (switched && dialog->isBufferDependent()) {
1240                         if (dialog->isVisibleView() && dialog->initialiseParams(""))
1241                                 dialog->updateView();
1242                         else
1243                                 dialog->hide();
1244                 } else {
1245                         // A bit clunky, but the dialog will request
1246                         // that the kernel provides it with the necessary
1247                         // data.
1248                         dialog->slotRestore();
1249                 }
1250         }
1251 }
1252
1253
1254 void GuiView::checkStatus()
1255 {
1256         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1257         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1258
1259         for(; it != end; ++it) {
1260                 Dialog * const dialog = it->second.get();
1261                 if (dialog && dialog->isVisibleView())
1262                         dialog->checkStatus();
1263         }
1264 }
1265
1266
1267
1268 // will be replaced by a proper factory...
1269 Dialog * createGuiAbout(LyXView & lv);
1270 Dialog * createGuiBibitem(LyXView & lv);
1271 Dialog * createGuiBibtex(LyXView & lv);
1272 Dialog * createGuiBox(LyXView & lv);
1273 Dialog * createGuiBranch(LyXView & lv);
1274 Dialog * createGuiChanges(LyXView & lv);
1275 Dialog * createGuiCharacter(LyXView & lv);
1276 Dialog * createGuiCitation(LyXView & lv);
1277 Dialog * createGuiDelimiter(LyXView & lv);
1278 Dialog * createGuiDocument(LyXView & lv);
1279 Dialog * createGuiErrorList(LyXView & lv);
1280 Dialog * createGuiERT(LyXView & lv);
1281 Dialog * createGuiExternal(LyXView & lv);
1282 Dialog * createGuiFloat(LyXView & lv);
1283 Dialog * createGuiGraphics(LyXView & lv);
1284 Dialog * createGuiInclude(LyXView & lv);
1285 Dialog * createGuiIndex(LyXView & lv);
1286 Dialog * createGuiLabel(LyXView & lv);
1287 Dialog * createGuiListings(LyXView & lv);
1288 Dialog * createGuiLog(LyXView & lv);
1289 Dialog * createGuiMathMatrix(LyXView & lv);
1290 Dialog * createGuiNomenclature(LyXView & lv);
1291 Dialog * createGuiNote(LyXView & lv);
1292 Dialog * createGuiParagraph(LyXView & lv);
1293 Dialog * createGuiPreferences(LyXView & lv);
1294 Dialog * createGuiPrint(LyXView & lv);
1295 Dialog * createGuiRef(LyXView & lv);
1296 Dialog * createGuiSearch(LyXView & lv);
1297 Dialog * createGuiSendTo(LyXView & lv);
1298 Dialog * createGuiShowFile(LyXView & lv);
1299 Dialog * createGuiSpellchecker(LyXView & lv);
1300 Dialog * createGuiTabularCreate(LyXView & lv);
1301 Dialog * createGuiTabular(LyXView & lv);
1302 Dialog * createGuiTexInfo(LyXView & lv);
1303 Dialog * createGuiToc(LyXView & lv);
1304 Dialog * createGuiThesaurus(LyXView & lv);
1305 Dialog * createGuiHyperlink(LyXView & lv);
1306 Dialog * createGuiVSpace(LyXView & lv);
1307 Dialog * createGuiViewSource(LyXView & lv);
1308 Dialog * createGuiWrap(LyXView & lv);
1309
1310
1311 Dialog * GuiView::build(string const & name)
1312 {
1313         BOOST_ASSERT(isValidName(name));
1314
1315         if (name == "aboutlyx")
1316                 return createGuiAbout(*this);
1317         if (name == "bibitem")
1318                 return createGuiBibitem(*this);
1319         if (name == "bibtex")
1320                 return createGuiBibtex(*this);
1321         if (name == "box")
1322                 return createGuiBox(*this);
1323         if (name == "branch")
1324                 return createGuiBranch(*this);
1325         if (name == "changes")
1326                 return createGuiChanges(*this);
1327         if (name == "character")
1328                 return createGuiCharacter(*this);
1329         if (name == "citation")
1330                 return createGuiCitation(*this);
1331         if (name == "document")
1332                 return createGuiDocument(*this);
1333         if (name == "errorlist")
1334                 return createGuiErrorList(*this);
1335         if (name == "ert")
1336                 return createGuiERT(*this);
1337         if (name == "external")
1338                 return createGuiExternal(*this);
1339         if (name == "file")
1340                 return createGuiShowFile(*this);
1341         if (name == "findreplace")
1342                 return createGuiSearch(*this);
1343         if (name == "float")
1344                 return createGuiFloat(*this);
1345         if (name == "graphics")
1346                 return createGuiGraphics(*this);
1347         if (name == "include")
1348                 return createGuiInclude(*this);
1349         if (name == "index")
1350                 return createGuiIndex(*this);
1351         if (name == "nomenclature")
1352                 return createGuiNomenclature(*this);
1353         if (name == "label")
1354                 return createGuiLabel(*this);
1355         if (name == "log")
1356                 return createGuiLog(*this);
1357         if (name == "view-source")
1358                 return createGuiViewSource(*this);
1359         if (name == "mathdelimiter")
1360                 return createGuiDelimiter(*this);
1361         if (name == "mathmatrix")
1362                 return createGuiMathMatrix(*this);
1363         if (name == "note")
1364                 return createGuiNote(*this);
1365         if (name == "paragraph")
1366                 return createGuiParagraph(*this);
1367         if (name == "prefs")
1368                 return createGuiPreferences(*this);
1369         if (name == "print")
1370                 return createGuiPrint(*this);
1371         if (name == "ref")
1372                 return createGuiRef(*this);
1373         if (name == "sendto")
1374                 return createGuiSendTo(*this);
1375         if (name == "spellchecker")
1376                 return createGuiSpellchecker(*this);
1377         if (name == "tabular")
1378                 return createGuiTabular(*this);
1379         if (name == "tabularcreate")
1380                 return createGuiTabularCreate(*this);
1381         if (name == "texinfo")
1382                 return createGuiTexInfo(*this);
1383 #ifdef HAVE_LIBAIKSAURUS
1384         if (name == "thesaurus")
1385                 return createGuiThesaurus(*this);
1386 #endif
1387         if (name == "toc")
1388                 return createGuiToc(*this);
1389         if (name == "href")
1390                 return createGuiHyperlink(*this);
1391         if (name == "vspace")
1392                 return createGuiVSpace(*this);
1393         if (name == "wrap")
1394                 return createGuiWrap(*this);
1395         if (name == "listings")
1396                 return createGuiListings(*this);
1397
1398         return 0;
1399 }
1400
1401
1402 } // namespace frontend
1403 } // namespace lyx
1404
1405 #include "GuiView_moc.cpp"