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