]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
add progress view of system calls
[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
18 #include "Dialog.h"
19 #include "frontends/FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiWorkArea.h"
22 #include "GuiKeySymbol.h"
23 #include "GuiMenubar.h"
24 #include "GuiToolbar.h"
25 #include "GuiToolbars.h"
26 #include "GuiProgress.h"
27
28 #include "qt_helpers.h"
29
30 #include "frontends/alert.h"
31
32 #include "buffer_funcs.h"
33 #include "Buffer.h"
34 #include "BufferList.h"
35 #include "BufferParams.h"
36 #include "BufferView.h"
37 #include "Cursor.h"
38 #include "support/debug.h"
39 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "support/gettext.h"
42 #include "Intl.h"
43 #include "Layout.h"
44 #include "Lexer.h"
45 #include "LyXFunc.h"
46 #include "LyX.h"
47 #include "LyXRC.h"
48 #include "LyXVC.h"
49 #include "MenuBackend.h"
50 #include "Paragraph.h"
51 #include "TextClass.h"
52 #include "Text.h"
53 #include "ToolbarBackend.h"
54 #include "version.h"
55
56 #include "support/FileFilterList.h"
57 #include "support/FileName.h"
58 #include "support/filetools.h"
59 #include "support/lstrings.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Timeout.h"
63
64 #include <QAction>
65 #include <QApplication>
66 #include <QCloseEvent>
67 #include <QDebug>
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/assert.hpp>
87 #include <boost/bind.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::addPath;
107 using support::bformat;
108 using support::FileFilterList;
109 using support::FileName;
110 using support::makeAbsPath;
111 using support::makeDisplayPath;
112 using support::package;
113 using support::removeAutosaveFile;
114 using support::trim;
115
116 namespace {
117
118 class BackgroundWidget : public QWidget
119 {
120 public:
121         BackgroundWidget()
122         {
123                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
124                 /// The text to be written on top of the pixmap
125                 QString const text = lyx_version ? lyx_version : qt_("unknown version");
126                 splash_ = QPixmap(":/images/banner.png");
127
128                 QPainter pain(&splash_);
129                 pain.setPen(QColor(255, 255, 0));
130                 QFont font;
131                 // The font used to display the version info
132                 font.setStyleHint(QFont::SansSerif);
133                 font.setWeight(QFont::Bold);
134                 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
135                 pain.setFont(font);
136                 pain.drawText(260, 270, text);
137         }
138
139         void paintEvent(QPaintEvent *)
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), layout_(0),
160                 quitting_by_menu_(false), autosave_timeout_(5000), in_show_(false)
161         {
162                 // hardcode here the platform specific icon size
163                 smallIconSize = 14;     // scaling problems
164                 normalIconSize = 20;    // ok, default
165                 bigIconSize = 26;               // better for some math icons
166
167                 splitter_ = new QSplitter;
168                 bg_widget_ = new BackgroundWidget;
169                 stack_widget_ = new QStackedWidget;
170                 stack_widget_->addWidget(bg_widget_);
171                 stack_widget_->addWidget(splitter_);
172                 setBackground();
173         }
174
175         ~GuiViewPrivate()
176         {
177                 delete splitter_;
178                 delete bg_widget_;
179                 delete stack_widget_;
180                 delete menubar_;
181                 delete toolbars_;
182         }
183
184         QMenu * toolBarPopup(GuiView * parent)
185         {
186                 // FIXME: translation
187                 QMenu * menu = new QMenu(parent);
188                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
189
190                 QAction * smallIcons = new QAction(iconSizeGroup);
191                 smallIcons->setText(qt_("Small-sized icons"));
192                 smallIcons->setCheckable(true);
193                 QObject::connect(smallIcons, SIGNAL(triggered()),
194                         parent, SLOT(smallSizedIcons()));
195                 menu->addAction(smallIcons);
196
197                 QAction * normalIcons = new QAction(iconSizeGroup);
198                 normalIcons->setText(qt_("Normal-sized icons"));
199                 normalIcons->setCheckable(true);
200                 QObject::connect(normalIcons, SIGNAL(triggered()),
201                         parent, SLOT(normalSizedIcons()));
202                 menu->addAction(normalIcons);
203
204                 QAction * bigIcons = new QAction(iconSizeGroup);
205                 bigIcons->setText(qt_("Big-sized icons"));
206                 bigIcons->setCheckable(true);
207                 QObject::connect(bigIcons, SIGNAL(triggered()),
208                         parent, SLOT(bigSizedIcons()));
209                 menu->addAction(bigIcons);
210
211                 unsigned int cur = parent->iconSize().width();
212                 if ( cur == parent->d.smallIconSize)
213                         smallIcons->setChecked(true);
214                 else if (cur == parent->d.normalIconSize)
215                         normalIcons->setChecked(true);
216                 else if (cur == parent->d.bigIconSize)
217                         bigIcons->setChecked(true);
218
219                 return menu;
220         }
221
222         void setBackground()
223         {
224                 stack_widget_->setCurrentWidget(bg_widget_);
225                 bg_widget_->setUpdatesEnabled(true);
226         }
227
228         TabWorkArea * tabWorkArea(int i)
229         {
230                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
231         }
232
233         TabWorkArea * currentTabWorkArea()
234         {
235                 if (splitter_->count() == 1)
236                         // The first TabWorkArea is always the first one, if any.
237                         return tabWorkArea(0);
238
239                 TabWorkArea * tab_widget = 0;
240                 for (int i = 0; i != splitter_->count(); ++i) {
241                         QWidget * w = splitter_->widget(i);
242                         if (!w->hasFocus())
243                                 continue;
244                         tab_widget = dynamic_cast<TabWorkArea *>(w);
245                         if (tab_widget)
246                                 break;
247                 }
248
249                 return tab_widget;
250         }
251
252 public:
253         GuiWorkArea * current_work_area_;
254         QSplitter * splitter_;
255         QStackedWidget * stack_widget_;
256         BackgroundWidget * bg_widget_;
257         /// view's menubar
258         GuiMenubar * menubar_;
259         /// view's toolbars
260         GuiToolbars * toolbars_;
261         /// The main layout box.
262         /** 
263          * \warning Don't Delete! The layout box is actually owned by
264          * whichever toolbar contains it. All the GuiView class needs is a
265          * means of accessing it.
266          *
267          * FIXME: replace that with a proper model so that we are not limited
268          * to only one dialog.
269          */
270         GuiLayoutBox * layout_;
271
272         ///
273         std::map<std::string, Inset *> open_insets_;
274
275         ///
276         std::map<std::string, DialogPtr> dialogs_;
277
278         unsigned int smallIconSize;
279         unsigned int normalIconSize;
280         unsigned int bigIconSize;
281         ///
282         QTimer statusbar_timer_;
283         /// are we quitting by the menu?
284         bool quitting_by_menu_;
285         /// auto-saving of buffers
286         Timeout autosave_timeout_;
287         /// flag against a race condition due to multiclicks, see bug #1119
288         bool in_show_;
289 };
290
291
292 GuiView::GuiView(int id)
293         : d(*new GuiViewPrivate), id_(id)
294 {
295         // GuiToolbars *must* be initialised before GuiMenubar.
296         d.toolbars_ = new GuiToolbars(*this);
297         d.menubar_ = new GuiMenubar(this, menubackend);
298
299         setCentralWidget(d.stack_widget_);
300
301         // Start autosave timer
302         if (lyxrc.autosave) {
303                 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
304                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
305                 d.autosave_timeout_.start();
306         }
307         connect(&d.statusbar_timer_, SIGNAL(timeout()),
308                 this, SLOT(clearMessage()));
309
310         // Qt bug? signal lastWindowClosed does not work
311         setAttribute(Qt::WA_QuitOnClose, false);
312         setAttribute(Qt::WA_DeleteOnClose, true);
313 #ifndef Q_WS_MACX
314         // assign an icon to main form. We do not do it under Qt/Mac,
315         // since the icon is provided in the application bundle.
316         setWindowIcon(QPixmap(":/images/lyx.png"));
317 #endif
318
319         // For Drag&Drop.
320         setAcceptDrops(true);
321
322         statusBar()->setSizeGripEnabled(true);
323
324         // Forbid too small unresizable window because it can happen
325         // with some window manager under X11.
326         setMinimumSize(300, 200);
327
328         if (!lyxrc.allow_geometry_session)
329                 // No session handling, default to a sane size.
330                 setGeometry(50, 50, 690, 510);
331
332         // Now take care of session management.
333         QSettings settings;
334         QString const key = "view-" + QString::number(id_);
335 #ifdef Q_WS_X11
336         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
337         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
338         resize(size);
339         move(pos);
340 #else
341         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
342                 setGeometry(50, 50, 690, 510);
343 #endif
344         setIconSize(settings.value(key + "/icon_size").toSize());
345 }
346
347
348 GuiView::~GuiView()
349 {
350         delete &d;
351 }
352
353
354 void GuiView::close()
355 {
356         d.quitting_by_menu_ = true;
357         d.current_work_area_ = 0;
358         for (int i = 0; i != d.splitter_->count(); ++i) {
359                 TabWorkArea * twa = d.tabWorkArea(i);
360                 if (twa)
361                         twa->closeAll();
362         }
363         QMainWindow::close();
364         d.quitting_by_menu_ = false;
365 }
366
367
368 void GuiView::setFocus()
369 {
370         if (d.current_work_area_)
371                 d.current_work_area_->setFocus();
372         else
373                 QWidget::setFocus();
374 }
375
376
377 QMenu * GuiView::createPopupMenu()
378 {
379         return d.toolBarPopup(this);
380 }
381
382
383 void GuiView::showEvent(QShowEvent * e)
384 {
385         LYXERR(Debug::GUI, "Passed Geometry "
386                 << size().height() << "x" << size().width()
387                 << "+" << pos().x() << "+" << pos().y());
388
389         if (d.splitter_->count() == 0)
390                 // No work area, switch to the background widget.
391                 d.setBackground();
392
393         QMainWindow::showEvent(e);
394 }
395
396
397 void GuiView::closeEvent(QCloseEvent * close_event)
398 {
399         // we may have been called through the close window button
400         // which bypasses the LFUN machinery.
401         if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
402                 if (!quitWriteAll()) {
403                         close_event->ignore();
404                         return;
405                 }
406         }
407
408         // Make sure that no LFUN use this close to be closed View.
409         theLyXFunc().setLyXView(0);
410         // Make sure the timer time out will not trigger a statusbar update.
411         d.statusbar_timer_.stop();
412
413         if (lyxrc.allow_geometry_session) {
414                 QSettings settings;
415                 QString const key = "view-" + QString::number(id_);
416 #ifdef Q_WS_X11
417                 settings.setValue(key + "/pos", pos());
418                 settings.setValue(key + "/size", size());
419 #else
420                 settings.setValue(key + "/geometry", saveGeometry());
421 #endif
422                 settings.setValue(key + "/icon_size", iconSize());
423                 d.toolbars_->saveToolbarInfo();
424         }
425
426         guiApp->unregisterView(id_);
427         if (guiApp->viewCount() > 0) {
428                 // Just close the window and do nothing else if this is not the
429                 // last window.
430                 close_event->accept();
431                 return;
432         }
433
434         quitting = true;
435
436         // this is the place where we leave the frontend.
437         // it is the only point at which we start quitting.
438         close_event->accept();
439         // quit the event loop
440         qApp->quit();
441 }
442
443
444 void GuiView::dragEnterEvent(QDragEnterEvent * event)
445 {
446         if (event->mimeData()->hasUrls())
447                 event->accept();
448         /// \todo Ask lyx-devel is this is enough:
449         /// if (event->mimeData()->hasFormat("text/plain"))
450         ///     event->acceptProposedAction();
451 }
452
453
454 void GuiView::dropEvent(QDropEvent* event)
455 {
456         QList<QUrl> files = event->mimeData()->urls();
457         if (files.isEmpty())
458                 return;
459
460         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
461         for (int i = 0; i != files.size(); ++i) {
462                 string const file = support::os::internal_path(fromqstr(
463                         files.at(i).toLocalFile()));
464                 if (!file.empty())
465                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
466         }
467 }
468
469
470 void GuiView::message(docstring const & str)
471 {
472         statusBar()->showMessage(toqstr(str));
473         d.statusbar_timer_.stop();
474         d.statusbar_timer_.start(3000);
475 }
476
477
478 void GuiView::smallSizedIcons()
479 {
480         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
481 }
482
483
484 void GuiView::normalSizedIcons()
485 {
486         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
487 }
488
489
490 void GuiView::bigSizedIcons()
491 {
492         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
493 }
494
495
496 void GuiView::clearMessage()
497 {
498         if (!hasFocus())
499                 return;
500         theLyXFunc().setLyXView(this);
501         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
502         d.statusbar_timer_.stop();
503 }
504
505
506 void GuiView::updateWindowTitle(GuiWorkArea * wa)
507 {
508         if (wa != d.current_work_area_)
509                 return;
510         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
511         setWindowIconText(wa->windowIconText());
512 }
513
514
515 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
516 {
517         disconnectBuffer();
518         disconnectBufferView();
519         connectBufferView(wa->bufferView());
520         connectBuffer(wa->bufferView().buffer());
521         d.current_work_area_ = wa;
522         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
523                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
524         updateWindowTitle(wa);
525
526         updateToc();
527         // Buffer-dependent dialogs should be updated or
528         // hidden. This should go here because some dialogs (eg ToC)
529         // require bv_->text.
530         updateBufferDependent(true);
531         updateToolbars();
532         updateLayoutList();
533         updateStatusBar();
534 }
535
536
537 void GuiView::updateStatusBar()
538 {
539         // let the user see the explicit message
540         if (d.statusbar_timer_.isActive())
541                 return;
542
543         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
544 }
545
546
547 bool GuiView::hasFocus() const
548 {
549         return qApp->activeWindow() == this;
550 }
551
552
553 bool GuiView::event(QEvent * e)
554 {
555         switch (e->type())
556         {
557         // Useful debug code:
558         //case QEvent::ActivationChange:
559         //case QEvent::WindowDeactivate:
560         //case QEvent::Paint:
561         //case QEvent::Enter:
562         //case QEvent::Leave:
563         //case QEvent::HoverEnter:
564         //case QEvent::HoverLeave:
565         //case QEvent::HoverMove:
566         //case QEvent::StatusTip:
567         //case QEvent::DragEnter:
568         //case QEvent::DragLeave:
569         //case QEvent::Drop:
570         //      break;
571
572         case QEvent::WindowActivate: {
573                 guiApp->setCurrentView(*this);
574                 if (d.current_work_area_) {
575                         BufferView & bv = d.current_work_area_->bufferView();
576                         connectBufferView(bv);
577                         connectBuffer(bv.buffer());
578                         // The document structure, name and dialogs might have
579                         // changed in another view.
580                         updateBufferDependent(true);
581                 } else {
582                         setWindowTitle(qt_("LyX"));
583                         setWindowIconText(qt_("LyX"));
584                 }
585                 return QMainWindow::event(e);
586         }
587         case QEvent::ShortcutOverride: {
588                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
589                 if (!d.current_work_area_) {
590                         theLyXFunc().setLyXView(this);
591                         KeySymbol sym;
592                         setKeySymbol(&sym, ke);
593                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
594                         e->accept();
595                         return true;
596                 }
597                 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
598                         KeySymbol sym;
599                         setKeySymbol(&sym, ke);
600                         d.current_work_area_->processKeySym(sym, NoModifier);
601                         e->accept();
602                         return true;
603                 }
604         }
605         default:
606                 return QMainWindow::event(e);
607         }
608 }
609
610
611 bool GuiView::focusNextPrevChild(bool /*next*/)
612 {
613         setFocus();
614         return true;
615 }
616
617
618 void GuiView::setBusy(bool yes)
619 {
620         if (d.current_work_area_) {
621                 d.current_work_area_->setUpdatesEnabled(!yes);
622                 if (yes)
623                         d.current_work_area_->stopBlinkingCursor();
624                 else
625                         d.current_work_area_->startBlinkingCursor();
626         }
627
628         if (yes)
629                 QApplication::setOverrideCursor(Qt::WaitCursor);
630         else
631                 QApplication::restoreOverrideCursor();
632 }
633
634
635 GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
636 {
637         GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
638
639         if (tbinfo.flags & ToolbarInfo::TOP) {
640                 if (newline)
641                         addToolBarBreak(Qt::TopToolBarArea);
642                 addToolBar(Qt::TopToolBarArea, toolBar);
643         }
644
645         if (tbinfo.flags & ToolbarInfo::BOTTOM) {
646 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
647 #if (QT_VERSION >= 0x040202)
648                 if (newline)
649                         addToolBarBreak(Qt::BottomToolBarArea);
650 #endif
651                 addToolBar(Qt::BottomToolBarArea, toolBar);
652         }
653
654         if (tbinfo.flags & ToolbarInfo::LEFT) {
655 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
656 #if (QT_VERSION >= 0x040202)
657                 if (newline)
658                         addToolBarBreak(Qt::LeftToolBarArea);
659 #endif
660                 addToolBar(Qt::LeftToolBarArea, toolBar);
661         }
662
663         if (tbinfo.flags & ToolbarInfo::RIGHT) {
664 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
665 #if (QT_VERSION >= 0x040202)
666                 if (newline)
667                         addToolBarBreak(Qt::RightToolBarArea);
668 #endif
669                 addToolBar(Qt::RightToolBarArea, toolBar);
670         }
671
672         // The following does not work so I cannot restore to exact toolbar location
673         /*
674         ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
675         toolBar->move(tbinfo.posx, tbinfo.posy);
676         */
677
678         return toolBar;
679 }
680
681
682 GuiWorkArea * GuiView::workArea(Buffer & buffer)
683 {
684         for (int i = 0; i != d.splitter_->count(); ++i) {
685                 GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
686                 if (wa)
687                         return wa;
688         }
689         return 0;
690 }
691
692
693 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
694 {
695
696         // Automatically create a TabWorkArea if there are none yet.
697         if (!d.splitter_->count())
698                 addTabWorkArea();
699
700         TabWorkArea * tab_widget = d.currentTabWorkArea();
701         return tab_widget->addWorkArea(buffer, *this);
702 }
703
704
705 void GuiView::addTabWorkArea()
706 {
707         TabWorkArea * twa = new TabWorkArea;
708         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
709                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
710         d.splitter_->addWidget(twa);
711         d.stack_widget_->setCurrentWidget(d.splitter_);
712 }
713
714
715 GuiWorkArea const * GuiView::currentWorkArea() const
716 {
717         return d.current_work_area_;
718 }
719
720
721 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
722 {
723         BOOST_ASSERT(wa);
724
725         // Changing work area can result from opening a file so
726         // update the toc in any case.
727         updateToc();
728
729         d.current_work_area_ = wa;
730         for (int i = 0; i != d.splitter_->count(); ++i) {
731                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
732                         return;
733         }
734 }
735
736
737 void GuiView::removeWorkArea(GuiWorkArea * wa)
738 {
739         BOOST_ASSERT(wa);
740         if (wa == d.current_work_area_) {
741                 disconnectBuffer();
742                 disconnectBufferView();
743                 hideBufferDependent();
744                 d.current_work_area_ = 0;
745         }
746
747         for (int i = 0; i != d.splitter_->count(); ++i) {
748                 TabWorkArea * twa = d.tabWorkArea(i);
749                 if (!twa->removeWorkArea(wa))
750                         // Not found in this tab group.
751                         continue;
752
753                 // We found and removed the GuiWorkArea.
754                 if (!twa->count()) {
755                         // No more WorkAreas in this tab group, so delete it.
756                         delete twa;
757                         break;
758                 }
759
760                 if (d.current_work_area_)
761                         // This means that we are not closing the current GuiWorkArea;
762                         break;
763
764                 // Switch to the next GuiWorkArea in the found TabWorkArea.
765                 d.current_work_area_ = twa->currentWorkArea();
766                 break;
767         }
768
769         if (d.splitter_->count() == 0)
770                 // No more work area, switch to the background widget.
771                 d.setBackground();
772 }
773
774
775 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
776 {
777         d.layout_ = layout;
778 }
779
780
781 void GuiView::updateLayoutList()
782 {
783         if (d.layout_)
784                 d.layout_->updateContents(false);
785 }
786
787
788 void GuiView::updateToolbars()
789 {
790         if (d.current_work_area_) {
791                 bool const math =
792                         d.current_work_area_->bufferView().cursor().inMathed();
793                 bool const table =
794                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
795                 bool const review =
796                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
797                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
798
799                 d.toolbars_->update(math, table, review);
800         } else
801                 d.toolbars_->update(false, false, false);
802
803         // update read-only status of open dialogs.
804         checkStatus();
805 }
806
807
808 Buffer * GuiView::buffer()
809 {
810         if (d.current_work_area_)
811                 return &d.current_work_area_->bufferView().buffer();
812         return 0;
813 }
814
815
816 Buffer const * GuiView::buffer() const
817 {
818         if (d.current_work_area_)
819                 return &d.current_work_area_->bufferView().buffer();
820         return 0;
821 }
822
823
824 void GuiView::setBuffer(Buffer * newBuffer)
825 {
826         BOOST_ASSERT(newBuffer);
827         setBusy(true);
828
829         GuiWorkArea * wa = workArea(*newBuffer);
830         if (wa == 0) {
831                 updateLabels(*newBuffer->masterBuffer());
832                 wa = addWorkArea(*newBuffer);
833         } else {
834                 //Disconnect the old buffer...there's no new one.
835                 disconnectBuffer();
836         }
837         connectBuffer(*newBuffer);
838         connectBufferView(wa->bufferView());
839         setCurrentWorkArea(wa);
840
841         setBusy(false);
842 }
843
844
845 void GuiView::connectBuffer(Buffer & buf)
846 {
847         buf.setGuiDelegate(this);
848 }
849
850
851 void GuiView::disconnectBuffer()
852 {
853         if (d.current_work_area_)
854                 d.current_work_area_->bufferView().setGuiDelegate(0);
855 }
856
857
858 void GuiView::connectBufferView(BufferView & bv)
859 {
860         bv.setGuiDelegate(this);
861 }
862
863
864 void GuiView::disconnectBufferView()
865 {
866         if (d.current_work_area_)
867                 d.current_work_area_->bufferView().setGuiDelegate(0);
868 }
869
870
871 void GuiView::errors(string const & error_type)
872 {
873         ErrorList & el = buffer()->errorList(error_type);
874         if (!el.empty())
875                 showDialog("errorlist", error_type);
876 }
877
878
879 void GuiView::updateDialog(string const & name, string const & data)
880 {
881         if (!isDialogVisible(name))
882                 return;
883
884         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
885         if (it == d.dialogs_.end())
886                 return;
887
888         Dialog * const dialog = it->second.get();
889         if (dialog->isVisibleView())
890                 dialog->updateData(data);
891 }
892
893
894 BufferView * GuiView::view()
895 {
896         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
897 }
898
899
900 void GuiView::updateToc()
901 {
902         updateDialog("toc", "");
903 }
904
905
906 void GuiView::updateEmbeddedFiles()
907 {
908         updateDialog("embedding", "");
909 }
910
911
912 void GuiView::autoSave()
913 {
914         LYXERR(Debug::INFO, "Running autoSave()");
915
916         if (buffer())
917                 view()->buffer().autoSave();
918 }
919
920
921 void GuiView::resetAutosaveTimers()
922 {
923         if (lyxrc.autosave)
924                 d.autosave_timeout_.restart();
925 }
926
927
928 FuncStatus GuiView::getStatus(FuncRequest const & cmd)
929 {
930         FuncStatus flag;
931         bool enable = true;
932         Buffer * buf = buffer();
933
934         /* In LyX/Mac, when a dialog is open, the menus of the
935            application can still be accessed without giving focus to
936            the main window. In this case, we want to disable the menu
937            entries that are buffer-related.
938
939            Note that this code is not perfect, as bug 1941 attests:
940            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
941         */
942         if (cmd.origin == FuncRequest::MENU && !hasFocus())
943                 buf = 0;
944
945         switch(cmd.action) {
946         case LFUN_BUFFER_WRITE:
947                 enable = buf && (buf->isUnnamed() || !buf->isClean());
948                 break;
949
950         case LFUN_BUFFER_WRITE_AS:
951                 enable = buf;
952                 break;
953
954         case LFUN_TOOLBAR_TOGGLE:
955                 flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
956                 break;
957
958         case LFUN_DIALOG_TOGGLE:
959                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
960                 // fall through to set "enable"
961         case LFUN_DIALOG_SHOW: {
962                 string const name = cmd.getArg(0);
963                 if (!buf)
964                         enable = name == "aboutlyx"
965                                 || name == "file" //FIXME: should be removed.
966                                 || name == "prefs"
967                                 || name == "texinfo";
968                 else if (name == "print")
969                         enable = buf->isExportable("dvi")
970                                 && lyxrc.print_command != "none";
971                 else if (name == "character") {
972                         if (!view())
973                                 enable = false;
974                         else {
975                                 InsetCode ic = view()->cursor().inset().lyxCode();
976                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
977                         }
978                 }
979                 else if (name == "latexlog")
980                         enable = FileName(buf->logName()).isReadableFile();
981                 else if (name == "spellchecker")
982 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
983                         enable = !buf->isReadonly();
984 #else
985                         enable = false;
986 #endif
987                 else if (name == "vclog")
988                         enable = buf->lyxvc().inUse();
989                 break;
990         }
991
992         case LFUN_DIALOG_UPDATE: {
993                 string const name = cmd.getArg(0);
994                 if (!buf)
995                         enable = name == "prefs";
996                 break;
997         }
998
999         case LFUN_INSET_APPLY: {
1000                 if (!buf) {
1001                         enable = false;
1002                         break;
1003                 }
1004                 string const name = cmd.getArg(0);
1005                 Inset * inset = getOpenInset(name);
1006                 if (inset) {
1007                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1008                         FuncStatus fs;
1009                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1010                                 // Every inset is supposed to handle this
1011                                 BOOST_ASSERT(false);
1012                         }
1013                         flag |= fs;
1014                 } else {
1015                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1016                         flag |= getStatus(fr);
1017                 }
1018                 enable = flag.enabled();
1019                 break;
1020         }
1021
1022         default:
1023                 if (!view()) {
1024                         enable = false;
1025                         break;
1026                 }
1027         }
1028
1029         if (!enable)
1030                 flag.enabled(false);
1031
1032         return flag;
1033 }
1034
1035
1036 static FileName selectTemplateFile()
1037 {
1038         FileDialog dlg(_("Select template file"));
1039         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1040         dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1041
1042         FileDialog::Result result =
1043                 dlg.open(from_utf8(lyxrc.template_path),
1044                              FileFilterList(_("LyX Documents (*.lyx)")),
1045                              docstring());
1046
1047         if (result.first == FileDialog::Later)
1048                 return FileName();
1049         if (result.second.empty())
1050                 return FileName();
1051         return FileName(to_utf8(result.second));
1052 }
1053
1054
1055 void GuiView::newDocument(string const & filename, bool from_template)
1056 {
1057         FileName initpath(lyxrc.document_path);
1058         Buffer * buf = buffer();
1059         if (buf) {
1060                 FileName const trypath(buf->filePath());
1061                 // If directory is writeable, use this as default.
1062                 if (trypath.isDirWritable())
1063                         initpath = trypath;
1064         }
1065
1066         string templatefile = from_template ?
1067                 selectTemplateFile().absFilename() : string();
1068         Buffer * b;
1069         if (filename.empty())
1070                 b = newUnnamedFile(templatefile, initpath);
1071         else
1072                 b = newFile(filename, templatefile, true);
1073
1074         if (b)
1075                 setBuffer(b);
1076 }
1077
1078
1079 void GuiView::insertLyXFile(docstring const & fname)
1080 {
1081         BufferView * bv = view();
1082         if (!bv)
1083                 return;
1084
1085         // FIXME UNICODE
1086         FileName filename(to_utf8(fname));
1087         
1088         if (!filename.empty()) {
1089                 bv->insertLyXFile(filename);
1090                 return;
1091         }
1092
1093         // Launch a file browser
1094         // FIXME UNICODE
1095         string initpath = lyxrc.document_path;
1096         string const trypath = bv->buffer().filePath();
1097         // If directory is writeable, use this as default.
1098         if (FileName(trypath).isDirWritable())
1099                 initpath = trypath;
1100
1101         // FIXME UNICODE
1102         FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
1103         dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1104         dlg.setButton2(_("Examples|#E#e"),
1105                 from_utf8(addPath(package().system_support().absFilename(),
1106                 "examples")));
1107
1108         FileDialog::Result result =
1109                 dlg.open(from_utf8(initpath),
1110                              FileFilterList(_("LyX Documents (*.lyx)")),
1111                              docstring());
1112
1113         if (result.first == FileDialog::Later)
1114                 return;
1115
1116         // FIXME UNICODE
1117         filename.set(to_utf8(result.second));
1118
1119         // check selected filename
1120         if (filename.empty()) {
1121                 // emit message signal.
1122                 message(_("Canceled."));
1123                 return;
1124         }
1125
1126         bv->insertLyXFile(filename);
1127 }
1128
1129
1130 void GuiView::insertPlaintextFile(docstring const & fname,
1131         bool asParagraph)
1132 {
1133         BufferView * bv = view();
1134         if (!bv)
1135                 return;
1136
1137         // FIXME UNICODE
1138         FileName filename(to_utf8(fname));
1139         
1140         if (!filename.empty()) {
1141                 bv->insertPlaintextFile(filename, asParagraph);
1142                 return;
1143         }
1144
1145         FileDialog dlg(_("Select file to insert"), (asParagraph ?
1146                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1147
1148         FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
1149                 FileFilterList(), docstring());
1150
1151         if (result.first == FileDialog::Later)
1152                 return;
1153
1154         // FIXME UNICODE
1155         filename.set(to_utf8(result.second));
1156
1157         // check selected filename
1158         if (filename.empty()) {
1159                 // emit message signal.
1160                 message(_("Canceled."));
1161                 return;
1162         }
1163
1164         bv->insertPlaintextFile(filename, asParagraph);
1165 }
1166
1167
1168 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1169 {
1170         FileName fname = b.fileName();
1171         FileName const oldname = fname;
1172
1173         if (!newname.empty()) {
1174                 // FIXME UNICODE
1175                 fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1176         } else {
1177                 // Switch to this Buffer.
1178                 setBuffer(&b);
1179
1180                 /// No argument? Ask user through dialog.
1181                 // FIXME UNICODE
1182                 FileDialog dlg(_("Choose a filename to save document as"),
1183                                    LFUN_BUFFER_WRITE_AS);
1184                 dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
1185                 dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
1186
1187                 if (!support::isLyXFilename(fname.absFilename()))
1188                         fname.changeExtension(".lyx");
1189
1190                 support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
1191
1192                 FileDialog::Result result =
1193                         dlg.save(from_utf8(fname.onlyPath().absFilename()),
1194                                      filter,
1195                                      from_utf8(fname.onlyFileName()));
1196
1197                 if (result.first == FileDialog::Later)
1198                         return false;
1199
1200                 fname.set(to_utf8(result.second));
1201
1202                 if (fname.empty())
1203                         return false;
1204
1205                 if (!support::isLyXFilename(fname.absFilename()))
1206                         fname.changeExtension(".lyx");
1207         }
1208
1209         if (FileName(fname).exists()) {
1210                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1211                 docstring text = bformat(_("The document %1$s already "
1212                                            "exists.\n\nDo you want to "
1213                                            "overwrite that document?"), 
1214                                          file);
1215                 int const ret = Alert::prompt(_("Overwrite document?"),
1216                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1217                 switch (ret) {
1218                 case 0: break;
1219                 case 1: return renameBuffer(b, docstring());
1220                 case 2: return false;
1221                 }
1222         }
1223
1224         // Ok, change the name of the buffer
1225         b.setFileName(fname.absFilename());
1226         b.markDirty();
1227         bool unnamed = b.isUnnamed();
1228         b.setUnnamed(false);
1229         b.saveCheckSum(fname);
1230
1231         if (!saveBuffer(b)) {
1232                 b.setFileName(oldname.absFilename());
1233                 b.setUnnamed(unnamed);
1234                 b.saveCheckSum(oldname);
1235                 return false;
1236         }
1237
1238         return true;
1239 }
1240
1241
1242 bool GuiView::saveBuffer(Buffer & b)
1243 {
1244         if (b.isUnnamed())
1245                 return renameBuffer(b, docstring());
1246
1247         if (b.save()) {
1248                 LyX::ref().session().lastFiles().add(b.fileName());
1249                 return true;
1250         }
1251
1252         // Switch to this Buffer.
1253         setBuffer(&b);
1254
1255         // FIXME: we don't tell the user *WHY* the save failed !!
1256         docstring const file = makeDisplayPath(b.absFileName(), 30);
1257         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1258                                    "Do you want to rename the document and "
1259                                    "try again?"), file);
1260         int const ret = Alert::prompt(_("Rename and save?"),
1261                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1262         switch (ret) {
1263         case 0:
1264                 if (!renameBuffer(b, docstring()))
1265                         return false;
1266                 break;
1267         case 1:
1268                 return false;
1269         case 2:
1270                 break;
1271         }
1272
1273         return saveBuffer(b);
1274 }
1275
1276
1277 bool GuiView::closeBuffer()
1278 {
1279         Buffer * buf = buffer();
1280         return buf && closeBuffer(*buf);
1281 }
1282
1283
1284 bool GuiView::closeBuffer(Buffer & buf)
1285 {
1286         if (buf.isClean() || buf.paragraphs().empty()) {
1287                 theBufferList().release(&buf);
1288                 return true;
1289         }
1290         // Switch to this Buffer.
1291         setBuffer(&buf);
1292
1293         docstring file;
1294         // FIXME: Unicode?
1295         if (buf.isUnnamed())
1296                 file = from_utf8(buf.fileName().onlyFileName());
1297         else
1298                 file = buf.fileName().displayName(30);
1299
1300         docstring const text = bformat(_("The document %1$s has unsaved changes."
1301                 "\n\nDo you want to save the document or discard the changes?"), file);
1302         int const ret = Alert::prompt(_("Save changed document?"),
1303                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1304
1305         switch (ret) {
1306         case 0:
1307                 if (!saveBuffer(buf))
1308                         return false;
1309                 break;
1310         case 1:
1311                 // if we crash after this we could
1312                 // have no autosave file but I guess
1313                 // this is really improbable (Jug)
1314                 removeAutosaveFile(buf.absFileName());
1315                 break;
1316         case 2:
1317                 return false;
1318         }
1319
1320         // save file names to .lyx/session
1321         // if master/slave are both open, do not save slave since it
1322         // will be automatically loaded when the master is loaded
1323         if (buf.masterBuffer() == &buf)
1324                 LyX::ref().session().lastOpened().add(buf.fileName());
1325
1326         theBufferList().release(&buf);
1327         return true;
1328 }
1329
1330
1331 bool GuiView::quitWriteAll()
1332 {
1333         while (!theBufferList().empty()) {
1334                 Buffer * b = theBufferList().first();
1335                 if (!closeBuffer(*b))
1336                         return false;
1337         }
1338         return true;
1339 }
1340
1341
1342 bool GuiView::dispatch(FuncRequest const & cmd)
1343 {
1344         BufferView * bv = view();       
1345         // By default we won't need any update.
1346         if (bv)
1347                 bv->cursor().updateFlags(Update::None);
1348
1349         switch(cmd.action) {
1350                 case LFUN_BUFFER_SWITCH:
1351                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1352                         break;
1353
1354                 case LFUN_BUFFER_NEXT:
1355                         setBuffer(theBufferList().next(buffer()));
1356                         break;
1357
1358                 case LFUN_BUFFER_PREVIOUS:
1359                         setBuffer(theBufferList().previous(buffer()));
1360                         break;
1361
1362                 case LFUN_COMMAND_EXECUTE: {
1363                         bool const show_it = cmd.argument() != "off";
1364                         d.toolbars_->showCommandBuffer(show_it);
1365                         break;
1366                 }
1367                 case LFUN_DROP_LAYOUTS_CHOICE:
1368                         if (d.layout_)
1369                                 d.layout_->showPopup();
1370                         break;
1371
1372                 case LFUN_MENU_OPEN:
1373                         d.menubar_->openByName(toqstr(cmd.argument()));
1374                         break;
1375
1376                 case LFUN_FILE_INSERT:
1377                         insertLyXFile(cmd.argument());
1378                         break;
1379                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1380                         insertPlaintextFile(cmd.argument(), true);
1381                         break;
1382
1383                 case LFUN_FILE_INSERT_PLAINTEXT:
1384                         insertPlaintextFile(cmd.argument(), false);
1385                         break;
1386
1387                 case LFUN_BUFFER_WRITE:
1388                         if (bv)
1389                                 saveBuffer(bv->buffer());
1390                         break;
1391
1392                 case LFUN_BUFFER_WRITE_AS:
1393                         if (bv)
1394                                 renameBuffer(bv->buffer(), cmd.argument());
1395                         break;
1396
1397                 case LFUN_BUFFER_WRITE_ALL: {
1398                         Buffer * first = theBufferList().first();
1399                         if (!first)
1400                                 break;
1401                         message(_("Saving all documents..."));
1402                         // We cannot use a for loop as the buffer list cycles.
1403                         Buffer * b = first;
1404                         do {
1405                                 if (b->isClean())
1406                                         continue;
1407                                 saveBuffer(*b);
1408                                 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1409                                 b = theBufferList().next(b);
1410                         } while (b != first); 
1411                         message(_("All documents saved."));
1412                         break;
1413                 }
1414
1415                 case LFUN_TOOLBAR_TOGGLE: {
1416                         string const name = cmd.getArg(0);
1417                         bool const allowauto = cmd.getArg(1) == "allowauto";
1418                         // it is possible to get current toolbar status like this,...
1419                         // but I decide to obey the order of ToolbarBackend::flags
1420                         // and disregard real toolbar status.
1421                         // toolbars_->saveToolbarInfo();
1422                         //
1423                         // toggle state on/off/auto
1424                         d.toolbars_->toggleToolbarState(name, allowauto);
1425                         // update toolbar
1426                         updateToolbars();
1427
1428                         ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
1429                         if (!tbi) {
1430                                 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
1431                                 break;
1432                         }
1433                         docstring state;
1434                         if (tbi->flags & ToolbarInfo::ON)
1435                                 state = _("on");
1436                         else if (tbi->flags & ToolbarInfo::OFF)
1437                                 state = _("off");
1438                         else if (tbi->flags & ToolbarInfo::AUTO)
1439                                 state = _("auto");
1440
1441                         message(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1442                                            _(tbi->gui_name), state));
1443                         break;
1444                 }
1445
1446                 case LFUN_DIALOG_UPDATE: {
1447                         string const name = to_utf8(cmd.argument());
1448                         // Can only update a dialog connected to an existing inset
1449                         Inset * inset = getOpenInset(name);
1450                         if (inset) {
1451                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1452                                 inset->dispatch(view()->cursor(), fr);
1453                         } else if (name == "paragraph") {
1454                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1455                         } else if (name == "prefs") {
1456                                 updateDialog(name, string());
1457                         }
1458                         break;
1459                 }
1460
1461                 case LFUN_DIALOG_TOGGLE: {
1462                         if (isDialogVisible(cmd.getArg(0)))
1463                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1464                         else
1465                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1466                         break;
1467                 }
1468
1469                 case LFUN_DIALOG_DISCONNECT_INSET:
1470                         disconnectDialog(to_utf8(cmd.argument()));
1471                         break;
1472
1473                 case LFUN_DIALOG_HIDE: {
1474                         if (quitting)
1475                                 break;
1476                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1477                         break;
1478                 }
1479
1480                 case LFUN_DIALOG_SHOW: {
1481                         string const name = cmd.getArg(0);
1482                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1483
1484                         if (name == "character") {
1485                                 data = freefont2string();
1486                                 if (!data.empty())
1487                                         showDialog("character", data);
1488                         } else if (name == "latexlog") {
1489                                 Buffer::LogType type; 
1490                                 string const logfile = buffer()->logName(&type);
1491                                 switch (type) {
1492                                 case Buffer::latexlog:
1493                                         data = "latex ";
1494                                         break;
1495                                 case Buffer::buildlog:
1496                                         data = "literate ";
1497                                         break;
1498                                 }
1499                                 data += Lexer::quoteString(logfile);
1500                                 showDialog("log", data);
1501                         } else if (name == "vclog") {
1502                                 string const data = "vc " +
1503                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1504                                 showDialog("log", data);
1505                         } else
1506                                 showDialog(name, data);
1507                         break;
1508                 }
1509
1510                 case LFUN_INSET_APPLY: {
1511                         string const name = cmd.getArg(0);
1512                         Inset * inset = getOpenInset(name);
1513                         if (inset) {
1514                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1515                                 inset->dispatch(view()->cursor(), fr);
1516                         } else {
1517                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1518                                 lyx::dispatch(fr);
1519                         }
1520                         break;
1521                 }
1522
1523                 default:
1524                         return false;
1525         }
1526
1527         return true;
1528 }
1529
1530
1531 Buffer const * GuiView::updateInset(Inset const * inset)
1532 {
1533         if (!d.current_work_area_)
1534                 return 0;
1535
1536         if (inset)
1537                 d.current_work_area_->scheduleRedraw();
1538
1539         return &d.current_work_area_->bufferView().buffer();
1540 }
1541
1542
1543 void GuiView::restartCursor()
1544 {
1545         /* When we move around, or type, it's nice to be able to see
1546          * the cursor immediately after the keypress.
1547          */
1548         if (d.current_work_area_)
1549                 d.current_work_area_->startBlinkingCursor();
1550
1551         // Take this occasion to update the toobars and layout list.
1552         updateLayoutList();
1553         updateToolbars();
1554 }
1555
1556 namespace {
1557
1558 // This list should be kept in sync with the list of insets in
1559 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
1560 // dialog should have the same name as the inset.
1561
1562 char const * const dialognames[] = {
1563 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
1564 "citation", "document", "embedding", "errorlist", "ert", "external", "file",
1565 "findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
1566 "mathdelimiter", "mathmatrix", "note", "paragraph",
1567 "prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
1568
1569 #ifdef HAVE_LIBAIKSAURUS
1570 "thesaurus",
1571 #endif
1572
1573 "texinfo", "toc", "href", "view-source", "latex-progress", "vspace", "wrap", "listings" };
1574
1575 char const * const * const end_dialognames =
1576         dialognames + (sizeof(dialognames) / sizeof(char *));
1577
1578 class cmpCStr {
1579 public:
1580         cmpCStr(char const * name) : name_(name) {}
1581         bool operator()(char const * other) {
1582                 return strcmp(other, name_) == 0;
1583         }
1584 private:
1585         char const * name_;
1586 };
1587
1588
1589 bool isValidName(string const & name)
1590 {
1591         return std::find_if(dialognames, end_dialognames,
1592                             cmpCStr(name.c_str())) != end_dialognames;
1593 }
1594
1595 } // namespace anon
1596
1597
1598 void GuiView::resetDialogs()
1599 {
1600         // Make sure that no LFUN uses any LyXView.
1601         theLyXFunc().setLyXView(0);
1602         d.toolbars_->init();
1603         d.menubar_->init();
1604         if (d.layout_)
1605                 d.layout_->updateContents(true);
1606         // Now update controls with current buffer.
1607         theLyXFunc().setLyXView(this);
1608         restartCursor();
1609 }
1610
1611
1612 Dialog * GuiView::find_or_build(string const & name)
1613 {
1614         if (!isValidName(name))
1615                 return 0;
1616
1617         std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
1618
1619         if (it != d.dialogs_.end())
1620                 return it->second.get();
1621
1622         d.dialogs_[name].reset(build(name));
1623         return d.dialogs_[name].get();
1624 }
1625
1626
1627 void GuiView::showDialog(string const & name, string const & data,
1628         Inset * inset)
1629 {
1630         if (d.in_show_)
1631                 return;
1632
1633         d.in_show_ = true;
1634         Dialog * dialog = find_or_build(name);
1635         if (dialog) {
1636                 dialog->showData(data);
1637                 if (inset)
1638                         d.open_insets_[name] = inset;
1639         }
1640         d.in_show_ = false;
1641 }
1642
1643
1644 bool GuiView::isDialogVisible(string const & name) const
1645 {
1646         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1647         if (it == d.dialogs_.end())
1648                 return false;
1649         return it->second.get()->isVisibleView();
1650 }
1651
1652
1653 void GuiView::hideDialog(string const & name, Inset * inset)
1654 {
1655         // Don't send the signal if we are quitting, because on MSVC it is
1656         // destructed before the cut stack in CutAndPaste.cpp, and this method
1657         // is called from some inset destructor if the cut stack is not empty
1658         // on exit.
1659         if (quitting)
1660                 return;
1661
1662         std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1663         if (it == d.dialogs_.end())
1664                 return;
1665
1666         if (inset && inset != getOpenInset(name))
1667                 return;
1668
1669         Dialog * const dialog = it->second.get();
1670         if (dialog->isVisibleView())
1671                 dialog->hide();
1672         d.open_insets_[name] = 0;
1673 }
1674
1675
1676 void GuiView::disconnectDialog(string const & name)
1677 {
1678         if (!isValidName(name))
1679                 return;
1680
1681         if (d.open_insets_.find(name) != d.open_insets_.end())
1682                 d.open_insets_[name] = 0;
1683 }
1684
1685
1686 Inset * GuiView::getOpenInset(string const & name) const
1687 {
1688         if (!isValidName(name))
1689                 return 0;
1690
1691         std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
1692         return it == d.open_insets_.end() ? 0 : it->second;
1693 }
1694
1695
1696 void GuiView::hideAll() const
1697 {
1698         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1699         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1700
1701         for(; it != end; ++it)
1702                 it->second->hide();
1703 }
1704
1705
1706 void GuiView::hideBufferDependent() const
1707 {
1708         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1709         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1710
1711         for(; it != end; ++it) {
1712                 Dialog * dialog = it->second.get();
1713                 if (dialog->isBufferDependent())
1714                         dialog->hide();
1715         }
1716 }
1717
1718
1719 void GuiView::updateBufferDependent(bool switched) const
1720 {
1721         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1722         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1723
1724         for(; it != end; ++it) {
1725                 Dialog * dialog = it->second.get();
1726                 if (!dialog->isVisibleView())
1727                         continue;
1728                 if (switched && dialog->isBufferDependent()) {
1729                         if (dialog->initialiseParams(""))
1730                                 dialog->updateView();
1731                         else
1732                                 dialog->hide();
1733                 } else {
1734                         // A bit clunky, but the dialog will request
1735                         // that the kernel provides it with the necessary
1736                         // data.
1737                         dialog->updateDialog();
1738                 }
1739         }
1740 }
1741
1742
1743 void GuiView::checkStatus()
1744 {
1745         std::map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
1746         std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
1747
1748         for(; it != end; ++it) {
1749                 Dialog * const dialog = it->second.get();
1750                 if (dialog && dialog->isVisibleView())
1751                         dialog->checkStatus();
1752         }
1753 }
1754
1755
1756
1757 // will be replaced by a proper factory...
1758 Dialog * createGuiAbout(GuiView & lv);
1759 Dialog * createGuiBibitem(GuiView & lv);
1760 Dialog * createGuiBibtex(GuiView & lv);
1761 Dialog * createGuiBox(GuiView & lv);
1762 Dialog * createGuiBranch(GuiView & lv);
1763 Dialog * createGuiChanges(GuiView & lv);
1764 Dialog * createGuiCharacter(GuiView & lv);
1765 Dialog * createGuiCitation(GuiView & lv);
1766 Dialog * createGuiDelimiter(GuiView & lv);
1767 Dialog * createGuiDocument(GuiView & lv);
1768 Dialog * createGuiErrorList(GuiView & lv);
1769 Dialog * createGuiERT(GuiView & lv);
1770 Dialog * createGuiExternal(GuiView & lv);
1771 Dialog * createGuiFloat(GuiView & lv);
1772 Dialog * createGuiGraphics(GuiView & lv);
1773 Dialog * createGuiInclude(GuiView & lv);
1774 Dialog * createGuiIndex(GuiView & lv);
1775 Dialog * createGuiLabel(GuiView & lv);
1776 Dialog * createGuiListings(GuiView & lv);
1777 Dialog * createGuiLog(GuiView & lv);
1778 Dialog * createGuiMathMatrix(GuiView & lv);
1779 Dialog * createGuiNomenclature(GuiView & lv);
1780 Dialog * createGuiNote(GuiView & lv);
1781 Dialog * createGuiParagraph(GuiView & lv);
1782 Dialog * createGuiPreferences(GuiView & lv);
1783 Dialog * createGuiPrint(GuiView & lv);
1784 Dialog * createGuiRef(GuiView & lv);
1785 Dialog * createGuiSearch(GuiView & lv);
1786 Dialog * createGuiSendTo(GuiView & lv);
1787 Dialog * createGuiShowFile(GuiView & lv);
1788 Dialog * createGuiSpellchecker(GuiView & lv);
1789 Dialog * createGuiTabularCreate(GuiView & lv);
1790 Dialog * createGuiTabular(GuiView & lv);
1791 Dialog * createGuiTexInfo(GuiView & lv);
1792 Dialog * createGuiToc(GuiView & lv);
1793 Dialog * createGuiThesaurus(GuiView & lv);
1794 Dialog * createGuiHyperlink(GuiView & lv);
1795 Dialog * createGuiVSpace(GuiView & lv);
1796 Dialog * createGuiViewSource(GuiView & lv);
1797 Dialog * createGuiProgress(GuiView & lv);
1798 Dialog * createGuiWrap(GuiView & lv);
1799
1800
1801 Dialog * GuiView::build(string const & name)
1802 {
1803         BOOST_ASSERT(isValidName(name));
1804
1805         if (name == "aboutlyx")
1806                 return createGuiAbout(*this);
1807         if (name == "bibitem")
1808                 return createGuiBibitem(*this);
1809         if (name == "bibtex")
1810                 return createGuiBibtex(*this);
1811         if (name == "box")
1812                 return createGuiBox(*this);
1813         if (name == "branch")
1814                 return createGuiBranch(*this);
1815         if (name == "changes")
1816                 return createGuiChanges(*this);
1817         if (name == "character")
1818                 return createGuiCharacter(*this);
1819         if (name == "citation")
1820                 return createGuiCitation(*this);
1821         if (name == "document")
1822                 return createGuiDocument(*this);
1823         if (name == "errorlist")
1824                 return createGuiErrorList(*this);
1825         if (name == "ert")
1826                 return createGuiERT(*this);
1827         if (name == "external")
1828                 return createGuiExternal(*this);
1829         if (name == "file")
1830                 return createGuiShowFile(*this);
1831         if (name == "findreplace")
1832                 return createGuiSearch(*this);
1833         if (name == "float")
1834                 return createGuiFloat(*this);
1835         if (name == "graphics")
1836                 return createGuiGraphics(*this);
1837         if (name == "include")
1838                 return createGuiInclude(*this);
1839         if (name == "index")
1840                 return createGuiIndex(*this);
1841         if (name == "nomenclature")
1842                 return createGuiNomenclature(*this);
1843         if (name == "label")
1844                 return createGuiLabel(*this);
1845         if (name == "log")
1846                 return createGuiLog(*this);
1847         if (name == "view-source")
1848                 return createGuiViewSource(*this);
1849         if (name == "latex-progress")
1850                 return createGuiProgress(*this);
1851         if (name == "mathdelimiter")
1852                 return createGuiDelimiter(*this);
1853         if (name == "mathmatrix")
1854                 return createGuiMathMatrix(*this);
1855         if (name == "note")
1856                 return createGuiNote(*this);
1857         if (name == "paragraph")
1858                 return createGuiParagraph(*this);
1859         if (name == "prefs")
1860                 return createGuiPreferences(*this);
1861         if (name == "print")
1862                 return createGuiPrint(*this);
1863         if (name == "ref")
1864                 return createGuiRef(*this);
1865         if (name == "sendto")
1866                 return createGuiSendTo(*this);
1867         if (name == "spellchecker")
1868                 return createGuiSpellchecker(*this);
1869         if (name == "tabular")
1870                 return createGuiTabular(*this);
1871         if (name == "tabularcreate")
1872                 return createGuiTabularCreate(*this);
1873         if (name == "texinfo")
1874                 return createGuiTexInfo(*this);
1875 #ifdef HAVE_LIBAIKSAURUS
1876         if (name == "thesaurus")
1877                 return createGuiThesaurus(*this);
1878 #endif
1879         if (name == "toc")
1880                 return createGuiToc(*this);
1881         if (name == "href")
1882                 return createGuiHyperlink(*this);
1883         if (name == "vspace")
1884                 return createGuiVSpace(*this);
1885         if (name == "wrap")
1886                 return createGuiWrap(*this);
1887         if (name == "listings")
1888                 return createGuiListings(*this);
1889
1890         return 0;
1891 }
1892
1893
1894 } // namespace frontend
1895 } // namespace lyx
1896
1897 #include "GuiView_moc.cpp"