]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
Fix crash when tab group is closed while Outline is opened.
[features.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 "FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiCommandBuffer.h"
22 #include "GuiCompleter.h"
23 #include "GuiWorkArea.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToolbar.h"
26 #include "Menus.h"
27 #include "TocModel.h"
28
29 #include "qt_helpers.h"
30
31 #include "frontends/alert.h"
32
33 #include "buffer_funcs.h"
34 #include "Buffer.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
39 #include "Cursor.h"
40 #include "Encoding.h"
41 #include "ErrorList.h"
42 #include "Format.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
45 #include "Intl.h"
46 #include "Layout.h"
47 #include "Lexer.h"
48 #include "LyXFunc.h"
49 #include "LyX.h"
50 #include "LyXRC.h"
51 #include "LyXVC.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
54 #include "Text.h"
55 #include "Toolbars.h"
56 #include "version.h"
57
58 #include "support/lassert.h"
59 #include "support/debug.h"
60 #include "support/FileName.h"
61 #include "support/filetools.h"
62 #include "support/gettext.h"
63 #include "support/ForkedCalls.h"
64 #include "support/lstrings.h"
65 #include "support/os.h"
66 #include "support/Package.h"
67 #include "support/Timeout.h"
68
69 #include <QAction>
70 #include <QApplication>
71 #include <QCloseEvent>
72 #include <QDebug>
73 #include <QDesktopWidget>
74 #include <QDragEnterEvent>
75 #include <QDropEvent>
76 #include <QList>
77 #include <QMenu>
78 #include <QMenuBar>
79 #include <QPainter>
80 #include <QPixmap>
81 #include <QPoint>
82 #include <QPushButton>
83 #include <QSettings>
84 #include <QShowEvent>
85 #include <QSplitter>
86 #include <QStackedWidget>
87 #include <QStatusBar>
88 #include <QTimer>
89 #include <QToolBar>
90 #include <QUrl>
91 #include <QScrollBar>
92
93 #include <boost/bind.hpp>
94
95 #ifdef HAVE_SYS_TIME_H
96 # include <sys/time.h>
97 #endif
98 #ifdef HAVE_UNISTD_H
99 # include <unistd.h>
100 #endif
101
102 using namespace std;
103 using namespace lyx::support;
104
105 namespace lyx {
106 namespace frontend {
107
108 namespace {
109
110 class BackgroundWidget : public QWidget
111 {
112 public:
113         BackgroundWidget()
114         {
115                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
116                 /// The text to be written on top of the pixmap
117                 QString const text = lyx_version ?
118                         qt_("version ") + lyx_version : qt_("unknown version");
119                 splash_ = QPixmap(":/images/banner.png");
120
121                 QPainter pain(&splash_);
122                 pain.setPen(QColor(0, 0, 0));
123                 QFont font;
124                 // The font used to display the version info
125                 font.setStyleHint(QFont::SansSerif);
126                 font.setWeight(QFont::Bold);
127                 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
128                 pain.setFont(font);
129                 pain.drawText(190, 225, text);
130         }
131
132         void paintEvent(QPaintEvent *)
133         {
134                 int x = (width() - splash_.width()) / 2;
135                 int y = (height() - splash_.height()) / 2;
136                 QPainter pain(this);
137                 pain.drawPixmap(x, y, splash_);
138         }
139
140 private:
141         QPixmap splash_;
142 };
143
144 /// Toolbar store providing access to individual toolbars by name.
145 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
146
147 typedef boost::shared_ptr<Dialog> DialogPtr;
148
149 } // namespace anon
150
151
152 struct GuiView::GuiViewPrivate
153 {
154         GuiViewPrivate()
155                 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
156                 in_show_(false)
157         {
158                 // hardcode here the platform specific icon size
159                 smallIconSize = 14;     // scaling problems
160                 normalIconSize = 20;    // ok, default
161                 bigIconSize = 26;               // better for some math icons
162
163                 splitter_ = new QSplitter;
164                 bg_widget_ = new BackgroundWidget;
165                 stack_widget_ = new QStackedWidget;
166                 stack_widget_->addWidget(bg_widget_);
167                 stack_widget_->addWidget(splitter_);
168                 setBackground();
169         }
170
171         ~GuiViewPrivate()
172         {
173                 delete splitter_;
174                 delete bg_widget_;
175                 delete stack_widget_;
176         }
177
178         QMenu * toolBarPopup(GuiView * parent)
179         {
180                 // FIXME: translation
181                 QMenu * menu = new QMenu(parent);
182                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
183
184                 QAction * smallIcons = new QAction(iconSizeGroup);
185                 smallIcons->setText(qt_("Small-sized icons"));
186                 smallIcons->setCheckable(true);
187                 QObject::connect(smallIcons, SIGNAL(triggered()),
188                         parent, SLOT(smallSizedIcons()));
189                 menu->addAction(smallIcons);
190
191                 QAction * normalIcons = new QAction(iconSizeGroup);
192                 normalIcons->setText(qt_("Normal-sized icons"));
193                 normalIcons->setCheckable(true);
194                 QObject::connect(normalIcons, SIGNAL(triggered()),
195                         parent, SLOT(normalSizedIcons()));
196                 menu->addAction(normalIcons);
197
198                 QAction * bigIcons = new QAction(iconSizeGroup);
199                 bigIcons->setText(qt_("Big-sized icons"));
200                 bigIcons->setCheckable(true);
201                 QObject::connect(bigIcons, SIGNAL(triggered()),
202                         parent, SLOT(bigSizedIcons()));
203                 menu->addAction(bigIcons);
204
205                 unsigned int cur = parent->iconSize().width();
206                 if ( cur == parent->d.smallIconSize)
207                         smallIcons->setChecked(true);
208                 else if (cur == parent->d.normalIconSize)
209                         normalIcons->setChecked(true);
210                 else if (cur == parent->d.bigIconSize)
211                         bigIcons->setChecked(true);
212
213                 return menu;
214         }
215
216         void setBackground()
217         {
218                 stack_widget_->setCurrentWidget(bg_widget_);
219                 bg_widget_->setUpdatesEnabled(true);
220         }
221
222         TabWorkArea * tabWorkArea(int i)
223         {
224                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
225         }
226
227         TabWorkArea * currentTabWorkArea()
228         {
229                 if (splitter_->count() == 1)
230                         // The first TabWorkArea is always the first one, if any.
231                         return tabWorkArea(0);
232
233                 for (int i = 0; i != splitter_->count(); ++i) {
234                         TabWorkArea * twa = tabWorkArea(i);
235                         if (current_work_area_ == twa->currentWorkArea())
236                                 return twa;
237                 }
238
239                 // None has the focus so we just take the first one.
240                 return tabWorkArea(0);
241         }
242
243 public:
244         GuiWorkArea * current_work_area_;
245         QSplitter * splitter_;
246         QStackedWidget * stack_widget_;
247         BackgroundWidget * bg_widget_;
248         /// view's toolbars
249         ToolbarMap toolbars_;
250         /// The main layout box.
251         /** 
252          * \warning Don't Delete! The layout box is actually owned by
253          * whichever toolbar contains it. All the GuiView class needs is a
254          * means of accessing it.
255          *
256          * FIXME: replace that with a proper model so that we are not limited
257          * to only one dialog.
258          */
259         GuiLayoutBox * layout_;
260
261         ///
262         map<string, Inset *> open_insets_;
263
264         ///
265         map<string, DialogPtr> dialogs_;
266
267         unsigned int smallIconSize;
268         unsigned int normalIconSize;
269         unsigned int bigIconSize;
270         ///
271         QTimer statusbar_timer_;
272         /// auto-saving of buffers
273         Timeout autosave_timeout_;
274         /// flag against a race condition due to multiclicks, see bug #1119
275         bool in_show_;
276
277         ///
278         TocModels toc_models_;
279 };
280
281
282 GuiView::GuiView(int id)
283         : d(*new GuiViewPrivate), id_(id), closing_(false)
284 {
285         // GuiToolbars *must* be initialised before the menu bar.
286         constructToolbars();
287
288         // set ourself as the current view. This is needed for the menu bar
289         // filling, at least for the static special menu item on Mac. Otherwise
290         // they are greyed out.
291         theLyXFunc().setLyXView(this);
292         
293         // Fill up the menu bar.
294         guiApp->menus().fillMenuBar(menuBar(), this, true);
295
296         setCentralWidget(d.stack_widget_);
297
298         // Start autosave timer
299         if (lyxrc.autosave) {
300                 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
301                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
302                 d.autosave_timeout_.start();
303         }
304         connect(&d.statusbar_timer_, SIGNAL(timeout()),
305                 this, SLOT(clearMessage()));
306
307         // We don't want to keep the window in memory if it is closed.
308         setAttribute(Qt::WA_DeleteOnClose, true);
309
310 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
311         // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
312         // since the icon is provided in the application bundle.
313         setWindowIcon(QPixmap(":/images/lyx.png"));
314 #endif
315
316         // For Drag&Drop.
317         setAcceptDrops(true);
318
319         statusBar()->setSizeGripEnabled(true);
320
321         // Forbid too small unresizable window because it can happen
322         // with some window manager under X11.
323         setMinimumSize(300, 200);
324
325         if (lyxrc.allow_geometry_session) {
326                 // Now take care of session management.
327                 if (restoreLayout())
328                         return;
329         }
330
331         // No session handling, default to a sane size.
332         setGeometry(50, 50, 690, 510);
333         initToolbars();
334         // This enables to clear session data if any.
335         QSettings settings;
336         settings.clear();
337 }
338
339
340 GuiView::~GuiView()
341 {
342         delete &d;
343 }
344
345
346 void GuiView::saveLayout() const
347 {
348         QSettings settings;
349         QString const key = "view-" + QString::number(id_);
350 #ifdef Q_WS_X11
351         settings.setValue(key + "/pos", pos());
352         settings.setValue(key + "/size", size());
353 #else
354         settings.setValue(key + "/geometry", saveGeometry());
355 #endif
356         settings.setValue(key + "/layout", saveState(0));
357         settings.setValue(key + "/icon_size", iconSize());
358 }
359
360
361 bool GuiView::restoreLayout()
362 {
363         QSettings settings;
364         QString const key = "view-" + QString::number(id_);
365         QString const icon_key = key + "/icon_size";
366         if (!settings.contains(icon_key))
367                 return false;
368
369         setIconSize(settings.value(icon_key).toSize());
370 #ifdef Q_WS_X11
371         QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
372         QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
373         resize(size);
374         move(pos);
375 #else
376         if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
377                 setGeometry(50, 50, 690, 510);
378 #endif
379         // Allow the toc and view-source dock widget to be restored if needed.
380         findOrBuild("toc", true);
381         findOrBuild("view-source", true);
382         if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
383                 initToolbars();
384         updateDialogs();
385         return true;
386 }
387
388
389 GuiToolbar * GuiView::toolbar(string const & name)
390 {
391         ToolbarMap::iterator it = d.toolbars_.find(name);
392         if (it != d.toolbars_.end())
393                 return it->second;
394
395         LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
396         message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
397         return 0;
398 }
399
400
401 void GuiView::constructToolbars()
402 {
403         ToolbarMap::iterator it = d.toolbars_.begin();
404         for (; it != d.toolbars_.end(); ++it)
405                 delete it->second;
406         d.toolbars_.clear();
407         d.layout_ = 0;
408
409         // extracts the toolbars from the backend
410         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
411         Toolbars::Infos::iterator end = guiApp->toolbars().end();
412         for (; cit != end; ++cit)
413                 d.toolbars_[cit->name] =  new GuiToolbar(*cit, *this);
414 }
415
416
417 void GuiView::initToolbars()
418 {
419         // extracts the toolbars from the backend
420         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
421         Toolbars::Infos::iterator end = guiApp->toolbars().end();
422         for (; cit != end; ++cit) {
423                 GuiToolbar * tb = toolbar(cit->name);
424                 if (!tb)
425                         continue;
426                 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
427                 bool newline = true;
428                 tb->setVisible(false);
429                 tb->setVisibility(visibility);
430
431                 if (visibility & Toolbars::TOP) {
432                         if (newline)
433                                 addToolBarBreak(Qt::TopToolBarArea);
434                         addToolBar(Qt::TopToolBarArea, tb);
435                 }
436
437                 if (visibility & Toolbars::BOTTOM) {
438                         // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
439 #if (QT_VERSION >= 0x040202)
440                         addToolBarBreak(Qt::BottomToolBarArea);
441 #endif
442                         addToolBar(Qt::BottomToolBarArea, tb);
443                 }
444
445                 if (visibility & Toolbars::LEFT) {
446                         // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
447 #if (QT_VERSION >= 0x040202)
448                         addToolBarBreak(Qt::LeftToolBarArea);
449 #endif
450                         addToolBar(Qt::LeftToolBarArea, tb);
451                 }
452
453                 if (visibility & Toolbars::RIGHT) {
454                         // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456                         addToolBarBreak(Qt::RightToolBarArea);
457 #endif
458                         addToolBar(Qt::RightToolBarArea, tb);
459                 }
460
461                 if (visibility & Toolbars::ON)
462                         tb->setVisible(true);
463         }
464 }
465
466
467 TocModels & GuiView::tocModels()
468 {
469         return d.toc_models_;
470 }
471
472
473 void GuiView::setFocus()
474 {
475         // Make sure LyXFunc points to the correct view.
476         theLyXFunc().setLyXView(this);
477         if (d.current_work_area_)
478                 d.current_work_area_->setFocus();
479         else
480                 QWidget::setFocus();
481 }
482
483
484 QMenu * GuiView::createPopupMenu()
485 {
486         return d.toolBarPopup(this);
487 }
488
489
490 void GuiView::showEvent(QShowEvent * e)
491 {
492         LYXERR(Debug::GUI, "Passed Geometry "
493                 << size().height() << "x" << size().width()
494                 << "+" << pos().x() << "+" << pos().y());
495
496         if (d.splitter_->count() == 0)
497                 // No work area, switch to the background widget.
498                 d.setBackground();
499
500         QMainWindow::showEvent(e);
501 }
502
503
504 void GuiView::closeEvent(QCloseEvent * close_event)
505 {
506         closing_ = true;
507
508         // it can happen that this event arrives without selecting the view,
509         // e.g. when clicking the close button on a background window.
510         theLyXFunc().setLyXView(this);
511
512         while (Buffer * b = buffer()) {
513                 if (b->parent()) {
514                         // This is a child document, just close the tab after saving
515                         // but keep the file loaded.
516                         if (!saveBuffer(*b)) {
517                                 closing_ = false;
518                                 close_event->ignore();
519                                 return;
520                         }
521                         removeWorkArea(d.current_work_area_);
522                         continue;
523                 }
524
525                 QList<int> const ids = guiApp->viewIds();
526                 for (int i = 0; i != ids.size(); ++i) {
527                         if (id_ == ids[i])
528                                 continue;
529                         if (guiApp->view(ids[i]).workArea(*b)) {
530                                 // FIXME 1: should we put an alert box here that the buffer
531                                 // is viewed elsewhere?
532                                 // FIXME 2: should we try to save this buffer in any case?
533                                 //saveBuffer(b);
534
535                                 // This buffer is also opened in another view, so
536                                 // close the associated work area...
537                                 removeWorkArea(d.current_work_area_);
538                                 // ... but don't close the buffer.
539                                 b = 0;
540                                 break;
541                         }
542                 }
543                 if (b && !closeBuffer(*b, true)) {
544                         closing_ = false;
545                         close_event->ignore();
546                         return;
547                 }
548         }
549
550         // Make sure that nothing will use this close to be closed View.
551         guiApp->unregisterView(this);
552
553         if (isFullScreen()) {
554                 // Switch off fullscreen before closing.
555                 toggleFullScreen();
556                 updateDialogs();
557         }
558
559         // Make sure the timer time out will not trigger a statusbar update.
560         d.statusbar_timer_.stop();
561
562         // Saving fullscreen requires additional tweaks in the toolbar code.
563         // It wouldn't also work under linux natively.
564         if (lyxrc.allow_geometry_session) {
565                 // Save this window geometry and layout.
566                 saveLayout();
567                 // Then the toolbar private states.
568                 ToolbarMap::iterator end = d.toolbars_.end();
569                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
570                         it->second->saveSession();
571                 // Now take care of all other dialogs:
572                 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
573                 for (; it!= d.dialogs_.end(); ++it)
574                         it->second->saveSession();
575         }
576
577         close_event->accept();
578 }
579
580
581 void GuiView::dragEnterEvent(QDragEnterEvent * event)
582 {
583         if (event->mimeData()->hasUrls())
584                 event->accept();
585         /// \todo Ask lyx-devel is this is enough:
586         /// if (event->mimeData()->hasFormat("text/plain"))
587         ///     event->acceptProposedAction();
588 }
589
590
591 void GuiView::dropEvent(QDropEvent* event)
592 {
593         QList<QUrl> files = event->mimeData()->urls();
594         if (files.isEmpty())
595                 return;
596
597         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
598         for (int i = 0; i != files.size(); ++i) {
599                 string const file = os::internal_path(fromqstr(
600                         files.at(i).toLocalFile()));
601                 if (!file.empty())
602                         lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
603         }
604 }
605
606
607 void GuiView::message(docstring const & str)
608 {
609         if (ForkedProcess::iAmAChild())
610                 return;
611
612         statusBar()->showMessage(toqstr(str));
613         d.statusbar_timer_.stop();
614         d.statusbar_timer_.start(3000);
615 }
616
617
618 void GuiView::smallSizedIcons()
619 {
620         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
621 }
622
623
624 void GuiView::normalSizedIcons()
625 {
626         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
627 }
628
629
630 void GuiView::bigSizedIcons()
631 {
632         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
633 }
634
635
636 void GuiView::clearMessage()
637 {
638         if (!hasFocus())
639                 return;
640         theLyXFunc().setLyXView(this);
641         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
642         d.statusbar_timer_.stop();
643 }
644
645
646 void GuiView::updateWindowTitle(GuiWorkArea * wa)
647 {
648         if (wa != d.current_work_area_)
649                 return;
650         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
651         setWindowIconText(wa->windowIconText());
652 }
653
654
655 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
656 {
657         disconnectBuffer();
658         disconnectBufferView();
659         connectBufferView(wa->bufferView());
660         connectBuffer(wa->bufferView().buffer());
661         d.current_work_area_ = wa;
662         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
663                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
664         updateWindowTitle(wa);
665
666         structureChanged();
667
668         // The document settings needs to be reinitialised.
669         updateDialog("document", "");
670
671         // Buffer-dependent dialogs must be updated. This is done here because
672         // some dialogs require buffer()->text.
673         updateDialogs();
674 }
675
676
677 void GuiView::on_lastWorkAreaRemoved()
678 {
679         if (closing_)
680                 // We already are in a close event. Nothing more to do.
681                 return;
682
683         if (d.splitter_->count() > 1)
684                 // We have a splitter so don't close anything.
685                 return;
686
687         // Reset and updates the dialogs.
688         d.toc_models_.reset(0);
689         updateDialog("document", "");
690         updateDialogs();
691
692         if (lyxrc.open_buffers_in_tabs)
693                 // Nothing more to do, the window should stay open.
694                 return;
695
696         if (guiApp->viewIds().size() > 1) {
697                 close();
698                 return;
699         }
700
701 #ifdef Q_WS_MACX
702         // On Mac we also close the last window because the application stay
703         // resident in memory. On other platforms we don't close the last
704         // window because this would quit the application.
705         close();
706 #endif
707 }
708
709
710 void GuiView::updateStatusBar()
711 {
712         // let the user see the explicit message
713         if (d.statusbar_timer_.isActive())
714                 return;
715
716         theLyXFunc().setLyXView(this);
717         statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
718 }
719
720
721 bool GuiView::hasFocus() const
722 {
723         return qApp->activeWindow() == this;
724 }
725
726
727 bool GuiView::event(QEvent * e)
728 {
729         switch (e->type())
730         {
731         // Useful debug code:
732         //case QEvent::ActivationChange:
733         //case QEvent::WindowDeactivate:
734         //case QEvent::Paint:
735         //case QEvent::Enter:
736         //case QEvent::Leave:
737         //case QEvent::HoverEnter:
738         //case QEvent::HoverLeave:
739         //case QEvent::HoverMove:
740         //case QEvent::StatusTip:
741         //case QEvent::DragEnter:
742         //case QEvent::DragLeave:
743         //case QEvent::Drop:
744         //      break;
745
746         case QEvent::WindowActivate: {
747                 if (this == guiApp->currentView()) {
748                         setFocus();
749                         return QMainWindow::event(e);
750                 }
751                 guiApp->setCurrentView(this);
752                 theLyXFunc().setLyXView(this);
753                 if (d.current_work_area_) {
754                         BufferView & bv = d.current_work_area_->bufferView();
755                         connectBufferView(bv);
756                         connectBuffer(bv.buffer());
757                         // The document structure, name and dialogs might have
758                         // changed in another view.
759                         structureChanged();
760                         // The document settings needs to be reinitialised.
761                         updateDialog("document", "");
762                         updateDialogs();
763                 } else {
764                         setWindowTitle(qt_("LyX"));
765                         setWindowIconText(qt_("LyX"));
766                 }
767                 setFocus();
768                 return QMainWindow::event(e);
769         }
770
771         case QEvent::ShortcutOverride: {
772
773                 if (isFullScreen() && menuBar()->isHidden()) {
774                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
775                         // FIXME: we should also try to detect special LyX shortcut such as
776                         // Alt-P and Alt-M. Right now there is a hack in
777                         // GuiWorkArea::processKeySym() that hides again the menubar for
778                         // those cases.
779                         if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
780                                 menuBar()->show();
781                         return QMainWindow::event(e);
782                 }
783
784                 if (d.current_work_area_)
785                         // Nothing special to do.
786                         return QMainWindow::event(e);
787
788                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
789                 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
790                 // between controls.
791                 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab 
792                         || ke->key() == Qt::Key_Backtab)
793                         return QMainWindow::event(e);
794
795                 // Allow processing of shortcuts that are allowed even when no Buffer
796                 // is viewed.
797                 theLyXFunc().setLyXView(this);
798                 KeySymbol sym;
799                 setKeySymbol(&sym, ke);
800                 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
801                 e->accept();
802                 return true;
803         }
804
805         default:
806                 return QMainWindow::event(e);
807         }
808 }
809
810
811 bool GuiView::focusNextPrevChild(bool /*next*/)
812 {
813         setFocus();
814         return true;
815 }
816
817
818 void GuiView::setBusy(bool busy)
819 {
820         if (d.current_work_area_) {
821                 d.current_work_area_->setUpdatesEnabled(!busy);
822                 if (busy)
823                         d.current_work_area_->stopBlinkingCursor();
824                 else
825                         d.current_work_area_->startBlinkingCursor();
826         }
827
828         if (busy)
829                 QApplication::setOverrideCursor(Qt::WaitCursor);
830         else
831                 QApplication::restoreOverrideCursor();
832 }
833
834
835 GuiWorkArea * GuiView::workArea(Buffer & buffer)
836 {
837         if (TabWorkArea * twa = d.currentTabWorkArea())
838                 return twa->workArea(buffer);
839         return 0;
840 }
841
842
843 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
844 {
845         // Automatically create a TabWorkArea if there are none yet.
846         TabWorkArea * tab_widget = d.splitter_->count() 
847                 ? d.currentTabWorkArea() : addTabWorkArea();
848         return tab_widget->addWorkArea(buffer, *this);
849 }
850
851
852 TabWorkArea * GuiView::addTabWorkArea()
853 {
854         TabWorkArea * twa = new TabWorkArea;
855         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
856                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
857         QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
858                          this, SLOT(on_lastWorkAreaRemoved()));
859
860         d.splitter_->addWidget(twa);
861         d.stack_widget_->setCurrentWidget(d.splitter_);
862         return twa;
863 }
864
865
866 GuiWorkArea const * GuiView::currentWorkArea() const
867 {
868         return d.current_work_area_;
869 }
870
871
872 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
873 {
874         LASSERT(wa, /**/);
875         d.current_work_area_ = wa;
876         for (int i = 0; i != d.splitter_->count(); ++i) {
877                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
878                         return;
879         }
880 }
881
882
883 void GuiView::removeWorkArea(GuiWorkArea * wa)
884 {
885         LASSERT(wa, /**/);
886         if (wa == d.current_work_area_) {
887                 disconnectBuffer();
888                 disconnectBufferView();
889                 d.current_work_area_ = 0;
890         }
891
892         for (int i = 0; i != d.splitter_->count(); ++i) {
893                 TabWorkArea * twa = d.tabWorkArea(i);
894                 if (!twa->removeWorkArea(wa))
895                         // Not found in this tab group.
896                         continue;
897
898                 // We found and removed the GuiWorkArea.
899                 if (!twa->count()) {
900                         // No more WorkAreas in this tab group, so delete it.
901                         delete twa;
902                         break;
903                 }
904
905                 if (d.current_work_area_)
906                         // This means that we are not closing the current GuiWorkArea;
907                         break;
908
909                 // Switch to the next GuiWorkArea in the found TabWorkArea.
910                 d.current_work_area_ = twa->currentWorkArea();
911                 break;
912         }
913
914         if (d.splitter_->count() == 0)
915                 // No more work area, switch to the background widget.
916                 d.setBackground();
917 }
918
919
920 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
921 {
922         d.layout_ = layout;
923 }
924
925
926 void GuiView::updateLayoutList()
927 {
928         if (d.layout_)
929                 d.layout_->updateContents(false);
930 }
931
932
933 void GuiView::updateToolbars()
934 {
935         ToolbarMap::iterator end = d.toolbars_.end();
936         if (d.current_work_area_) {
937                 bool const math =
938                         d.current_work_area_->bufferView().cursor().inMathed();
939                 bool const table =
940                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
941                 bool const review =
942                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
943                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
944                 bool const mathmacrotemplate =
945                         lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
946
947                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
948                         it->second->update(math, table, review, mathmacrotemplate);
949         } else
950                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
951                         it->second->update(false, false, false, false);
952 }
953
954
955 Buffer * GuiView::buffer()
956 {
957         if (d.current_work_area_)
958                 return &d.current_work_area_->bufferView().buffer();
959         return 0;
960 }
961
962
963 Buffer const * GuiView::buffer() const
964 {
965         if (d.current_work_area_)
966                 return &d.current_work_area_->bufferView().buffer();
967         return 0;
968 }
969
970
971 void GuiView::setBuffer(Buffer * newBuffer)
972 {
973         LASSERT(newBuffer, /**/);
974         setBusy(true);
975
976         GuiWorkArea * wa = workArea(*newBuffer);
977         if (wa == 0) {
978                 updateLabels(*newBuffer->masterBuffer());
979                 wa = addWorkArea(*newBuffer);
980         } else {
981                 //Disconnect the old buffer...there's no new one.
982                 disconnectBuffer();
983         }
984         connectBuffer(*newBuffer);
985         connectBufferView(wa->bufferView());
986         setCurrentWorkArea(wa);
987
988         setBusy(false);
989 }
990
991
992 void GuiView::connectBuffer(Buffer & buf)
993 {
994         buf.setGuiDelegate(this);
995 }
996
997
998 void GuiView::disconnectBuffer()
999 {
1000         if (d.current_work_area_)
1001                 d.current_work_area_->bufferView().setGuiDelegate(0);
1002 }
1003
1004
1005 void GuiView::connectBufferView(BufferView & bv)
1006 {
1007         bv.setGuiDelegate(this);
1008 }
1009
1010
1011 void GuiView::disconnectBufferView()
1012 {
1013         if (d.current_work_area_)
1014                 d.current_work_area_->bufferView().setGuiDelegate(0);
1015 }
1016
1017
1018 void GuiView::errors(string const & error_type)
1019 {
1020         ErrorList & el = buffer()->errorList(error_type);
1021         if (!el.empty())
1022                 showDialog("errorlist", error_type);
1023 }
1024
1025
1026 void GuiView::structureChanged()
1027 {
1028         d.toc_models_.reset(view());
1029         // Navigator needs more than a simple update in this case. It needs to be
1030         // rebuilt.
1031         updateDialog("toc", "");
1032 }
1033
1034
1035 void GuiView::updateDialog(string const & name, string const & data)
1036 {
1037         if (!isDialogVisible(name))
1038                 return;
1039
1040         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1041         if (it == d.dialogs_.end())
1042                 return;
1043
1044         Dialog * const dialog = it->second.get();
1045         if (dialog->isVisibleView())
1046                 dialog->initialiseParams(data);
1047 }
1048
1049
1050 BufferView * GuiView::view()
1051 {
1052         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1053 }
1054
1055
1056 void GuiView::autoSave()
1057 {
1058         LYXERR(Debug::INFO, "Running autoSave()");
1059
1060         if (buffer())
1061                 view()->buffer().autoSave();
1062 }
1063
1064
1065 void GuiView::resetAutosaveTimers()
1066 {
1067         if (lyxrc.autosave)
1068                 d.autosave_timeout_.restart();
1069 }
1070
1071
1072 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1073 {
1074         bool enable = true;
1075         Buffer * buf = buffer();
1076
1077         /* In LyX/Mac, when a dialog is open, the menus of the
1078            application can still be accessed without giving focus to
1079            the main window. In this case, we want to disable the menu
1080            entries that are buffer-related.
1081
1082            Note that this code is not perfect, as bug 1941 attests:
1083            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1084         */
1085         if (cmd.origin == FuncRequest::MENU && !hasFocus())
1086                 buf = 0;
1087
1088         switch(cmd.action) {
1089         case LFUN_BUFFER_WRITE:
1090                 enable = buf && (buf->isUnnamed() || !buf->isClean());
1091                 break;
1092
1093         case LFUN_BUFFER_WRITE_AS:
1094                 enable = buf;
1095                 break;
1096
1097         case LFUN_SPLIT_VIEW:
1098                 enable = buf;
1099                 break;
1100
1101         case LFUN_CLOSE_TAB_GROUP:
1102                 enable = d.currentTabWorkArea();
1103                 break;
1104
1105         case LFUN_TOOLBAR_TOGGLE:
1106                 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1107                         flag.setOnOff(t->isVisible());
1108                 break;
1109
1110         case LFUN_UI_TOGGLE:
1111                 flag.setOnOff(isFullScreen());
1112                 break;
1113
1114         case LFUN_DIALOG_TOGGLE:
1115                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1116                 // fall through to set "enable"
1117         case LFUN_DIALOG_SHOW: {
1118                 string const name = cmd.getArg(0);
1119                 if (!buf)
1120                         enable = name == "aboutlyx"
1121                                 || name == "file" //FIXME: should be removed.
1122                                 || name == "prefs"
1123                                 || name == "texinfo";
1124                 else if (name == "print")
1125                         enable = buf->isExportable("dvi")
1126                                 && lyxrc.print_command != "none";
1127                 else if (name == "character") {
1128                         if (!view())
1129                                 enable = false;
1130                         else {
1131                                 InsetCode ic = view()->cursor().inset().lyxCode();
1132                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1133                         }
1134                 }
1135                 else if (name == "symbols") {
1136                         if (!view() || view()->cursor().inMathed())
1137                                 enable = false;
1138                         else {
1139                                 InsetCode ic = view()->cursor().inset().lyxCode();
1140                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1141                         }
1142                 }
1143                 else if (name == "latexlog")
1144                         enable = FileName(buf->logName()).isReadableFile();
1145                 else if (name == "spellchecker")
1146 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1147                         enable = !buf->isReadonly();
1148 #else
1149                         enable = false;
1150 #endif
1151                 else if (name == "vclog")
1152                         enable = buf->lyxvc().inUse();
1153                 break;
1154         }
1155
1156         case LFUN_DIALOG_UPDATE: {
1157                 string const name = cmd.getArg(0);
1158                 if (!buf)
1159                         enable = name == "prefs";
1160                 break;
1161         }
1162
1163         case LFUN_INSET_APPLY: {
1164                 string const name = cmd.getArg(0);
1165                 Inset * inset = getOpenInset(name);
1166                 if (inset) {
1167                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1168                         FuncStatus fs;
1169                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1170                                 // Every inset is supposed to handle this
1171                                 LASSERT(false, /**/);
1172                         }
1173                         flag |= fs;
1174                 } else {
1175                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1176                         flag |= lyx::getStatus(fr);
1177                 }
1178                 enable = flag.enabled();
1179                 break;
1180         }
1181
1182         case LFUN_COMPLETION_INLINE:
1183                 if (!d.current_work_area_
1184                     || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1185                     enable = false;
1186                 break;
1187
1188         case LFUN_COMPLETION_POPUP:
1189                 if (!d.current_work_area_
1190                     || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1191                     enable = false;
1192                 break;
1193
1194         case LFUN_COMPLETION_COMPLETE:
1195                 if (!d.current_work_area_
1196                         || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1197                     enable = false;
1198                 break;
1199
1200         default:
1201                 return false;
1202         }
1203
1204         if (!enable)
1205                 flag.setEnabled(false);
1206
1207         return true;
1208 }
1209
1210
1211 static FileName selectTemplateFile()
1212 {
1213         FileDialog dlg(qt_("Select template file"));
1214         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1215         dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1216
1217         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1218                              QStringList(qt_("LyX Documents (*.lyx)")));
1219
1220         if (result.first == FileDialog::Later)
1221                 return FileName();
1222         if (result.second.isEmpty())
1223                 return FileName();
1224         return FileName(fromqstr(result.second));
1225 }
1226
1227
1228 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1229 {
1230         setBusy(true);
1231
1232         Buffer * newBuffer = checkAndLoadLyXFile(filename);
1233
1234         if (!newBuffer) {
1235                 message(_("Document not loaded."));
1236                 setBusy(false);
1237                 return 0;
1238         }
1239         
1240         setBuffer(newBuffer);
1241
1242         // scroll to the position when the file was last closed
1243         if (lyxrc.use_lastfilepos) {
1244                 LastFilePosSection::FilePos filepos =
1245                         LyX::ref().session().lastFilePos().load(filename);
1246                 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1247         }
1248
1249         if (tolastfiles)
1250                 LyX::ref().session().lastFiles().add(filename);
1251
1252         setBusy(false);
1253         return newBuffer;
1254 }
1255
1256
1257 void GuiView::openDocument(string const & fname)
1258 {
1259         string initpath = lyxrc.document_path;
1260
1261         if (buffer()) {
1262                 string const trypath = buffer()->filePath();
1263                 // If directory is writeable, use this as default.
1264                 if (FileName(trypath).isDirWritable())
1265                         initpath = trypath;
1266         }
1267
1268         string filename;
1269
1270         if (fname.empty()) {
1271                 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1272                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1273                 dlg.setButton2(qt_("Examples|#E#e"),
1274                                 toqstr(addPath(package().system_support().absFilename(), "examples")));
1275
1276                 QStringList filter(qt_("LyX Documents (*.lyx)"));
1277                 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1278                         << qt_("LyX-1.4.x Documents (*.lyx14)")
1279                         << qt_("LyX-1.5.x Documents (*.lyx15)");
1280                 FileDialog::Result result =
1281                         dlg.open(toqstr(initpath), filter);
1282
1283                 if (result.first == FileDialog::Later)
1284                         return;
1285
1286                 filename = fromqstr(result.second);
1287
1288                 // check selected filename
1289                 if (filename.empty()) {
1290                         message(_("Canceled."));
1291                         return;
1292                 }
1293         } else
1294                 filename = fname;
1295
1296         // get absolute path of file and add ".lyx" to the filename if
1297         // necessary. 
1298         FileName const fullname = 
1299                         fileSearch(string(), filename, "lyx", support::may_not_exist);
1300         if (!fullname.empty())
1301                 filename = fullname.absFilename();
1302
1303         // if the file doesn't exist, let the user create one
1304         if (!fullname.exists()) {
1305                 // the user specifically chose this name. Believe him.
1306                 Buffer * const b = newFile(filename, string(), true);
1307                 if (b)
1308                         setBuffer(b);
1309                 return;
1310         }
1311
1312         docstring const disp_fn = makeDisplayPath(filename);
1313         message(bformat(_("Opening document %1$s..."), disp_fn));
1314
1315         docstring str2;
1316         Buffer * buf = loadDocument(fullname);
1317         if (buf) {
1318                 updateLabels(*buf);
1319                 
1320                 setBuffer(buf);
1321                 buf->errors("Parse");
1322                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1323         } else {
1324                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1325         }
1326         message(str2);
1327 }
1328
1329 // FIXME: clean that
1330 static bool import(GuiView * lv, FileName const & filename,
1331         string const & format, ErrorList & errorList)
1332 {
1333         FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1334
1335         string loader_format;
1336         vector<string> loaders = theConverters().loaders();
1337         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1338                 for (vector<string>::const_iterator it = loaders.begin();
1339                      it != loaders.end(); ++it) {
1340                         if (!theConverters().isReachable(format, *it))
1341                                 continue;
1342
1343                         string const tofile =
1344                                 support::changeExtension(filename.absFilename(),
1345                                 formats.extension(*it));
1346                         if (!theConverters().convert(0, filename, FileName(tofile),
1347                                 filename, format, *it, errorList))
1348                                 return false;
1349                         loader_format = *it;
1350                         break;
1351                 }
1352                 if (loader_format.empty()) {
1353                         frontend::Alert::error(_("Couldn't import file"),
1354                                      bformat(_("No information for importing the format %1$s."),
1355                                          formats.prettyName(format)));
1356                         return false;
1357                 }
1358         } else
1359                 loader_format = format;
1360
1361         if (loader_format == "lyx") {
1362                 Buffer * buf = lv->loadDocument(lyxfile);
1363                 if (!buf)
1364                         return false;
1365                 updateLabels(*buf);
1366                 lv->setBuffer(buf);
1367                 buf->errors("Parse");
1368         } else {
1369                 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1370                 if (!b)
1371                         return false;
1372                 lv->setBuffer(b);
1373                 bool as_paragraphs = loader_format == "textparagraph";
1374                 string filename2 = (loader_format == format) ? filename.absFilename()
1375                         : support::changeExtension(filename.absFilename(),
1376                                           formats.extension(loader_format));
1377                 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1378                 theLyXFunc().setLyXView(lv);
1379                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1380         }
1381
1382         return true;
1383 }
1384
1385
1386 void GuiView::importDocument(string const & argument)
1387 {
1388         string format;
1389         string filename = split(argument, format, ' ');
1390
1391         LYXERR(Debug::INFO, format << " file: " << filename);
1392
1393         // need user interaction
1394         if (filename.empty()) {
1395                 string initpath = lyxrc.document_path;
1396
1397                 Buffer const * buf = buffer();
1398                 if (buf) {
1399                         string const trypath = buf->filePath();
1400                         // If directory is writeable, use this as default.
1401                         if (FileName(trypath).isDirWritable())
1402                                 initpath = trypath;
1403                 }
1404
1405                 docstring const text = bformat(_("Select %1$s file to import"),
1406                         formats.prettyName(format));
1407
1408                 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1409                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1410                 dlg.setButton2(qt_("Examples|#E#e"),
1411                         toqstr(addPath(package().system_support().absFilename(), "examples")));
1412
1413                 docstring filter = formats.prettyName(format);
1414                 filter += " (*.";
1415                 // FIXME UNICODE
1416                 filter += from_utf8(formats.extension(format));
1417                 filter += ')';
1418
1419                 FileDialog::Result result =
1420                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1421
1422                 if (result.first == FileDialog::Later)
1423                         return;
1424
1425                 filename = fromqstr(result.second);
1426
1427                 // check selected filename
1428                 if (filename.empty())
1429                         message(_("Canceled."));
1430         }
1431
1432         if (filename.empty())
1433                 return;
1434
1435         // get absolute path of file
1436         FileName const fullname(support::makeAbsPath(filename));
1437
1438         FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1439
1440         // Check if the document already is open
1441         Buffer * buf = theBufferList().getBuffer(lyxfile.absFilename());
1442         if (buf) {
1443                 setBuffer(buf);
1444                 if (!closeBuffer()) {
1445                         message(_("Canceled."));
1446                         return;
1447                 }
1448         }
1449
1450         docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1451
1452         // if the file exists already, and we didn't do
1453         // -i lyx thefile.lyx, warn
1454         if (lyxfile.exists() && fullname != lyxfile) {
1455
1456                 docstring text = bformat(_("The document %1$s already exists.\n\n"
1457                         "Do you want to overwrite that document?"), displaypath);
1458                 int const ret = Alert::prompt(_("Overwrite document?"),
1459                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
1460
1461                 if (ret == 1) {
1462                         message(_("Canceled."));
1463                         return;
1464                 }
1465         }
1466
1467         message(bformat(_("Importing %1$s..."), displaypath));
1468         ErrorList errorList;
1469         if (import(this, fullname, format, errorList))
1470                 message(_("imported."));
1471         else
1472                 message(_("file not imported!"));
1473
1474         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1475 }
1476
1477
1478 void GuiView::newDocument(string const & filename, bool from_template)
1479 {
1480         FileName initpath(lyxrc.document_path);
1481         Buffer * buf = buffer();
1482         if (buf) {
1483                 FileName const trypath(buf->filePath());
1484                 // If directory is writeable, use this as default.
1485                 if (trypath.isDirWritable())
1486                         initpath = trypath;
1487         }
1488
1489         string templatefile = from_template ?
1490                 selectTemplateFile().absFilename() : string();
1491         Buffer * b;
1492         if (filename.empty())
1493                 b = newUnnamedFile(templatefile, initpath);
1494         else
1495                 b = newFile(filename, templatefile, true);
1496
1497         if (b)
1498                 setBuffer(b);
1499         // Ensure the cursor is correctly positionned on screen.
1500         view()->showCursor();
1501 }
1502
1503
1504 void GuiView::insertLyXFile(docstring const & fname)
1505 {
1506         BufferView * bv = view();
1507         if (!bv)
1508                 return;
1509
1510         // FIXME UNICODE
1511         FileName filename(to_utf8(fname));
1512         
1513         if (!filename.empty()) {
1514                 bv->insertLyXFile(filename);
1515                 return;
1516         }
1517
1518         // Launch a file browser
1519         // FIXME UNICODE
1520         string initpath = lyxrc.document_path;
1521         string const trypath = bv->buffer().filePath();
1522         // If directory is writeable, use this as default.
1523         if (FileName(trypath).isDirWritable())
1524                 initpath = trypath;
1525
1526         // FIXME UNICODE
1527         FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1528         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1529         dlg.setButton2(qt_("Examples|#E#e"),
1530                 toqstr(addPath(package().system_support().absFilename(),
1531                 "examples")));
1532
1533         FileDialog::Result result = dlg.open(toqstr(initpath),
1534                              QStringList(qt_("LyX Documents (*.lyx)")));
1535
1536         if (result.first == FileDialog::Later)
1537                 return;
1538
1539         // FIXME UNICODE
1540         filename.set(fromqstr(result.second));
1541
1542         // check selected filename
1543         if (filename.empty()) {
1544                 // emit message signal.
1545                 message(_("Canceled."));
1546                 return;
1547         }
1548
1549         bv->insertLyXFile(filename);
1550 }
1551
1552
1553 void GuiView::insertPlaintextFile(docstring const & fname,
1554         bool asParagraph)
1555 {
1556         BufferView * bv = view();
1557         if (!bv)
1558                 return;
1559
1560         // FIXME UNICODE
1561         FileName filename(to_utf8(fname));
1562         
1563         if (!filename.empty()) {
1564                 bv->insertPlaintextFile(filename, asParagraph);
1565                 return;
1566         }
1567
1568         FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1569                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1570
1571         FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1572                 QStringList());
1573
1574         if (result.first == FileDialog::Later)
1575                 return;
1576
1577         // FIXME UNICODE
1578         filename.set(fromqstr(result.second));
1579
1580         // check selected filename
1581         if (filename.empty()) {
1582                 // emit message signal.
1583                 message(_("Canceled."));
1584                 return;
1585         }
1586
1587         bv->insertPlaintextFile(filename, asParagraph);
1588 }
1589
1590
1591 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1592 {
1593         FileName fname = b.fileName();
1594         FileName const oldname = fname;
1595
1596         if (!newname.empty()) {
1597                 // FIXME UNICODE
1598                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1599         } else {
1600                 // Switch to this Buffer.
1601                 setBuffer(&b);
1602
1603                 /// No argument? Ask user through dialog.
1604                 // FIXME UNICODE
1605                 FileDialog dlg(qt_("Choose a filename to save document as"),
1606                                    LFUN_BUFFER_WRITE_AS);
1607                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1608                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1609
1610                 if (!isLyXFilename(fname.absFilename()))
1611                         fname.changeExtension(".lyx");
1612
1613                 FileDialog::Result result =
1614                         dlg.save(toqstr(fname.onlyPath().absFilename()),
1615                                QStringList(qt_("LyX Documents (*.lyx)")),
1616                                      toqstr(fname.onlyFileName()));
1617
1618                 if (result.first == FileDialog::Later)
1619                         return false;
1620
1621                 fname.set(fromqstr(result.second));
1622
1623                 if (fname.empty())
1624                         return false;
1625
1626                 if (!isLyXFilename(fname.absFilename()))
1627                         fname.changeExtension(".lyx");
1628         }
1629
1630         if (FileName(fname).exists()) {
1631                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1632                 docstring text = bformat(_("The document %1$s already "
1633                                            "exists.\n\nDo you want to "
1634                                            "overwrite that document?"), 
1635                                          file);
1636                 int const ret = Alert::prompt(_("Overwrite document?"),
1637                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1638                 switch (ret) {
1639                 case 0: break;
1640                 case 1: return renameBuffer(b, docstring());
1641                 case 2: return false;
1642                 }
1643         }
1644
1645         // Ok, change the name of the buffer
1646         b.setFileName(fname.absFilename());
1647         b.markDirty();
1648         bool unnamed = b.isUnnamed();
1649         b.setUnnamed(false);
1650         b.saveCheckSum(fname);
1651
1652         if (!saveBuffer(b)) {
1653                 b.setFileName(oldname.absFilename());
1654                 b.setUnnamed(unnamed);
1655                 b.saveCheckSum(oldname);
1656                 return false;
1657         }
1658
1659         return true;
1660 }
1661
1662
1663 bool GuiView::saveBuffer(Buffer & b)
1664 {
1665         if (b.isUnnamed())
1666                 return renameBuffer(b, docstring());
1667
1668         if (b.save()) {
1669                 LyX::ref().session().lastFiles().add(b.fileName());
1670                 return true;
1671         }
1672
1673         // Switch to this Buffer.
1674         setBuffer(&b);
1675
1676         // FIXME: we don't tell the user *WHY* the save failed !!
1677         docstring const file = makeDisplayPath(b.absFileName(), 30);
1678         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1679                                    "Do you want to rename the document and "
1680                                    "try again?"), file);
1681         int const ret = Alert::prompt(_("Rename and save?"),
1682                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1683         switch (ret) {
1684         case 0:
1685                 if (!renameBuffer(b, docstring()))
1686                         return false;
1687                 break;
1688         case 1:
1689                 break;
1690         case 2:
1691                 return false;
1692         }
1693
1694         return saveBuffer(b);
1695 }
1696
1697
1698 bool GuiView::closeBuffer()
1699 {
1700         Buffer * buf = buffer();
1701         return buf && closeBuffer(*buf);
1702 }
1703
1704
1705 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1706 {
1707         // goto bookmark to update bookmark pit.
1708         //FIXME: we should update only the bookmarks related to this buffer!
1709         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1710                 theLyXFunc().gotoBookmark(i+1, false, false);
1711
1712         if (buf.isClean() || buf.paragraphs().empty()) {
1713                 if (buf.masterBuffer() == &buf && tolastopened)
1714                         LyX::ref().session().lastOpened().add(buf.fileName());
1715                 theBufferList().release(&buf);
1716                 return true;
1717         }
1718         // Switch to this Buffer.
1719         setBuffer(&buf);
1720
1721         docstring file;
1722         // FIXME: Unicode?
1723         if (buf.isUnnamed())
1724                 file = from_utf8(buf.fileName().onlyFileName());
1725         else
1726                 file = buf.fileName().displayName(30);
1727
1728         // Bring this window to top before asking questions.
1729         raise();
1730         activateWindow();
1731
1732         docstring const text = bformat(_("The document %1$s has unsaved changes."
1733                 "\n\nDo you want to save the document or discard the changes?"), file);
1734         int const ret = Alert::prompt(_("Save changed document?"),
1735                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1736
1737         switch (ret) {
1738         case 0:
1739                 if (!saveBuffer(buf))
1740                         return false;
1741                 break;
1742         case 1:
1743                 // if we crash after this we could
1744                 // have no autosave file but I guess
1745                 // this is really improbable (Jug)
1746                 removeAutosaveFile(buf.absFileName());
1747                 break;
1748         case 2:
1749                 return false;
1750         }
1751
1752         // save file names to .lyx/session
1753         // if master/slave are both open, do not save slave since it
1754         // will be automatically loaded when the master is loaded
1755         if (buf.masterBuffer() == &buf && tolastopened)
1756                 LyX::ref().session().lastOpened().add(buf.fileName());
1757
1758         if (buf.parent())
1759                 // Don't close child documents.
1760                 removeWorkArea(d.current_work_area_);
1761         else
1762                 theBufferList().release(&buf);
1763
1764         return true;
1765 }
1766
1767
1768 bool GuiView::dispatch(FuncRequest const & cmd)
1769 {
1770         BufferView * bv = view();
1771         // By default we won't need any update.
1772         if (bv)
1773                 bv->cursor().updateFlags(Update::None);
1774         bool dispatched = true;
1775
1776         switch(cmd.action) {
1777                 case LFUN_BUFFER_IMPORT:
1778                         importDocument(to_utf8(cmd.argument()));
1779                         break;
1780
1781                 case LFUN_BUFFER_SWITCH:
1782                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1783                         break;
1784
1785                 case LFUN_BUFFER_NEXT:
1786                         setBuffer(theBufferList().next(buffer()));
1787                         break;
1788
1789                 case LFUN_BUFFER_PREVIOUS:
1790                         setBuffer(theBufferList().previous(buffer()));
1791                         break;
1792
1793                 case LFUN_COMMAND_EXECUTE: {
1794                         bool const show_it = cmd.argument() != "off";
1795                         // FIXME: this is a hack, "minibuffer" should not be
1796                         // hardcoded.
1797                         if (GuiToolbar * t = toolbar("minibuffer")) {
1798                                 t->setVisible(show_it);
1799                                 if (show_it && t->commandBuffer())
1800                                         t->commandBuffer()->setFocus();
1801                         }
1802                         break;
1803                 }
1804                 case LFUN_DROP_LAYOUTS_CHOICE:
1805                         if (d.layout_)
1806                                 d.layout_->showPopup();
1807                         break;
1808
1809                 case LFUN_MENU_OPEN:
1810                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1811                                 menu->exec(QCursor::pos());
1812                         break;
1813
1814                 case LFUN_FILE_INSERT:
1815                         insertLyXFile(cmd.argument());
1816                         break;
1817                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1818                         insertPlaintextFile(cmd.argument(), true);
1819                         break;
1820
1821                 case LFUN_FILE_INSERT_PLAINTEXT:
1822                         insertPlaintextFile(cmd.argument(), false);
1823                         break;
1824
1825                 case LFUN_BUFFER_WRITE:
1826                         if (bv)
1827                                 saveBuffer(bv->buffer());
1828                         break;
1829
1830                 case LFUN_BUFFER_WRITE_AS:
1831                         if (bv)
1832                                 renameBuffer(bv->buffer(), cmd.argument());
1833                         break;
1834
1835                 case LFUN_BUFFER_WRITE_ALL: {
1836                         Buffer * first = theBufferList().first();
1837                         if (!first)
1838                                 break;
1839                         message(_("Saving all documents..."));
1840                         // We cannot use a for loop as the buffer list cycles.
1841                         Buffer * b = first;
1842                         do {
1843                                 if (!b->isClean()) {
1844                                         saveBuffer(*b);
1845                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1846                                 }
1847                                 b = theBufferList().next(b);
1848                         } while (b != first); 
1849                         message(_("All documents saved."));
1850                         break;
1851                 }
1852
1853                 case LFUN_TOOLBAR_TOGGLE: {
1854                         string const name = cmd.getArg(0);
1855                         if (GuiToolbar * t = toolbar(name))
1856                                 t->toggle();
1857                         break;
1858                 }
1859
1860                 case LFUN_DIALOG_UPDATE: {
1861                         string const name = to_utf8(cmd.argument());
1862                         // Can only update a dialog connected to an existing inset
1863                         Inset * inset = getOpenInset(name);
1864                         if (inset) {
1865                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1866                                 inset->dispatch(view()->cursor(), fr);
1867                         } else if (name == "paragraph") {
1868                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1869                         } else if (name == "prefs") {
1870                                 updateDialog(name, string());
1871                         }
1872                         break;
1873                 }
1874
1875                 case LFUN_DIALOG_TOGGLE: {
1876                         if (isDialogVisible(cmd.getArg(0)))
1877                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1878                         else
1879                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1880                         break;
1881                 }
1882
1883                 case LFUN_DIALOG_DISCONNECT_INSET:
1884                         disconnectDialog(to_utf8(cmd.argument()));
1885                         break;
1886
1887                 case LFUN_DIALOG_HIDE: {
1888                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1889                         break;
1890                 }
1891
1892                 case LFUN_DIALOG_SHOW: {
1893                         string const name = cmd.getArg(0);
1894                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1895
1896                         if (name == "character") {
1897                                 data = freefont2string();
1898                                 if (!data.empty())
1899                                         showDialog("character", data);
1900                         } else if (name == "latexlog") {
1901                                 Buffer::LogType type; 
1902                                 string const logfile = buffer()->logName(&type);
1903                                 switch (type) {
1904                                 case Buffer::latexlog:
1905                                         data = "latex ";
1906                                         break;
1907                                 case Buffer::buildlog:
1908                                         data = "literate ";
1909                                         break;
1910                                 }
1911                                 data += Lexer::quoteString(logfile);
1912                                 showDialog("log", data);
1913                         } else if (name == "vclog") {
1914                                 string const data = "vc " +
1915                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1916                                 showDialog("log", data);
1917                         } else if (name == "symbols") {
1918                                 data = bv->cursor().getEncoding()->name();
1919                                 if (!data.empty())
1920                                         showDialog("symbols", data);
1921                         } else
1922                                 showDialog(name, data);
1923                         break;
1924                 }
1925
1926                 case LFUN_INSET_APPLY: {
1927                         view()->cursor().recordUndoFullDocument();
1928                         string const name = cmd.getArg(0);
1929                         Inset * inset = getOpenInset(name);
1930                         if (inset) {
1931                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1932                                 inset->dispatch(view()->cursor(), fr);
1933                         } else {
1934                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1935                                 lyx::dispatch(fr);
1936                         }
1937                         break;
1938                 }
1939
1940                 case LFUN_UI_TOGGLE:
1941                         lfunUiToggle(cmd);
1942                         // Make sure the keyboard focus stays in the work area.
1943                         setFocus();
1944                         break;
1945
1946                 case LFUN_COMPLETION_INLINE:
1947                         if (d.current_work_area_)
1948                                 d.current_work_area_->completer().showInline();
1949                         break;
1950
1951                 case LFUN_SPLIT_VIEW:
1952                         if (Buffer * buf = buffer()) {
1953                                 string const orientation = cmd.getArg(0);
1954                                 d.splitter_->setOrientation(orientation == "vertical"
1955                                         ? Qt::Vertical : Qt::Horizontal);
1956                                 TabWorkArea * twa = addTabWorkArea();
1957                                 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1958                                 setCurrentWorkArea(wa);
1959                         }
1960                         break;
1961
1962                 case LFUN_CLOSE_TAB_GROUP:
1963                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
1964                                 delete twa;
1965                                 twa = d.currentTabWorkArea();
1966                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
1967                                 if (twa) {
1968                                         d.current_work_area_ = twa->currentWorkArea();
1969                                         // Make sure the work area is up to date.
1970                                         twa->setCurrentWorkArea(d.current_work_area_);
1971                                 } else {
1972                                         d.current_work_area_ = 0;
1973                                 }
1974                                 if (d.splitter_->count() == 0)
1975                                         // No more work area, switch to the background widget.
1976                                         d.setBackground();
1977                         }
1978                         break;
1979                         
1980                 case LFUN_COMPLETION_POPUP:
1981                         if (d.current_work_area_)
1982                                 d.current_work_area_->completer().showPopup();
1983                         break;
1984
1985
1986                 case LFUN_COMPLETION_COMPLETE:
1987                         if (d.current_work_area_)
1988                                 d.current_work_area_->completer().tab();
1989                         break;
1990
1991                 default:
1992                         dispatched = false;
1993                         break;
1994         }
1995
1996         if (isFullScreen()) {
1997                 if (menuBar()->isVisible())
1998                         menuBar()->hide();
1999                 if (statusBar()->isVisible())
2000                         statusBar()->hide();
2001         }
2002
2003         return dispatched;
2004 }
2005
2006
2007 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2008 {
2009         string const arg = cmd.getArg(0);
2010         if (arg == "scrollbar") {
2011                 // hide() is of no help
2012                 if (d.current_work_area_->verticalScrollBarPolicy() ==
2013                         Qt::ScrollBarAlwaysOff)
2014
2015                         d.current_work_area_->setVerticalScrollBarPolicy(
2016                                 Qt::ScrollBarAsNeeded);
2017                 else
2018                         d.current_work_area_->setVerticalScrollBarPolicy(
2019                                 Qt::ScrollBarAlwaysOff);
2020                 return;
2021         }
2022         if (arg == "statusbar") {
2023                 statusBar()->setVisible(!statusBar()->isVisible());
2024                 return;
2025         }
2026         if (arg == "menubar") {
2027                 menuBar()->setVisible(!menuBar()->isVisible());
2028                 return;
2029         }
2030 #if QT_VERSION >= 0x040300
2031         if (arg == "frame") {
2032                 int l, t, r, b;
2033                 getContentsMargins(&l, &t, &r, &b);
2034                 //are the frames in default state?
2035                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2036                 if (l == 0) {
2037                         setContentsMargins(-2, -2, -2, -2);
2038                 } else {
2039                         setContentsMargins(0, 0, 0, 0);
2040                 }
2041                 return;
2042         }
2043 #endif
2044         if (arg == "fullscreen") {
2045                 toggleFullScreen();
2046                 return;
2047         }
2048
2049         message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2050 }
2051
2052
2053 void GuiView::toggleFullScreen()
2054 {
2055         if (isFullScreen()) {
2056                 for (int i = 0; i != d.splitter_->count(); ++i)
2057                         d.tabWorkArea(i)->setFullScreen(false);
2058 #if QT_VERSION >= 0x040300
2059                 setContentsMargins(0, 0, 0, 0);
2060 #endif
2061                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2062                 restoreLayout();
2063                 menuBar()->show();
2064                 statusBar()->show();
2065         } else {
2066                 for (int i = 0; i != d.splitter_->count(); ++i)
2067                         d.tabWorkArea(i)->setFullScreen(true);
2068 #if QT_VERSION >= 0x040300
2069                 setContentsMargins(-2, -2, -2, -2);
2070 #endif
2071                 saveLayout();
2072                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2073                 statusBar()->hide();
2074                 menuBar()->hide();
2075                 if (lyxrc.full_screen_toolbars) {
2076                         ToolbarMap::iterator end = d.toolbars_.end();
2077                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2078                                 it->second->hide();
2079                 }
2080         }
2081 }
2082
2083
2084 Buffer const * GuiView::updateInset(Inset const * inset)
2085 {
2086         if (!d.current_work_area_)
2087                 return 0;
2088
2089         if (inset)
2090                 d.current_work_area_->scheduleRedraw();
2091
2092         return &d.current_work_area_->bufferView().buffer();
2093 }
2094
2095
2096 void GuiView::restartCursor()
2097 {
2098         /* When we move around, or type, it's nice to be able to see
2099          * the cursor immediately after the keypress.
2100          */
2101         if (d.current_work_area_)
2102                 d.current_work_area_->startBlinkingCursor();
2103
2104         // Take this occasion to update the other GUI elements.
2105         updateDialogs();
2106         updateStatusBar();
2107 }
2108
2109
2110 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2111 {
2112         if (d.current_work_area_)
2113                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2114 }
2115
2116 namespace {
2117
2118 // This list should be kept in sync with the list of insets in
2119 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
2120 // dialog should have the same name as the inset.
2121 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2122 // docs in LyXAction.cpp.
2123
2124 char const * const dialognames[] = {
2125 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2126 "citation", "document", "errorlist", "ert", "external", "file",
2127 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2128 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print", 
2129 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2130
2131 #ifdef HAVE_LIBAIKSAURUS
2132 "thesaurus",
2133 #endif
2134
2135 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2136
2137 char const * const * const end_dialognames =
2138         dialognames + (sizeof(dialognames) / sizeof(char *));
2139
2140 class cmpCStr {
2141 public:
2142         cmpCStr(char const * name) : name_(name) {}
2143         bool operator()(char const * other) {
2144                 return strcmp(other, name_) == 0;
2145         }
2146 private:
2147         char const * name_;
2148 };
2149
2150
2151 bool isValidName(string const & name)
2152 {
2153         return find_if(dialognames, end_dialognames,
2154                             cmpCStr(name.c_str())) != end_dialognames;
2155 }
2156
2157 } // namespace anon
2158
2159
2160 void GuiView::resetDialogs()
2161 {
2162         // Make sure that no LFUN uses any LyXView.
2163         theLyXFunc().setLyXView(0);
2164         saveLayout();
2165         menuBar()->clear();
2166         constructToolbars();
2167         guiApp->menus().fillMenuBar(menuBar(), this, true);
2168         if (d.layout_)
2169                 d.layout_->updateContents(true);
2170         // Now update controls with current buffer.
2171         theLyXFunc().setLyXView(this);
2172         restoreLayout();
2173         restartCursor();
2174 }
2175
2176
2177 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2178 {
2179         if (!isValidName(name))
2180                 return 0;
2181
2182         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2183
2184         if (it != d.dialogs_.end())
2185                 return it->second.get();
2186
2187         Dialog * dialog = build(name);
2188         d.dialogs_[name].reset(dialog);
2189         if (lyxrc.allow_geometry_session)
2190                 dialog->restoreSession();
2191         if (hide_it)
2192                 dialog->hideView();
2193         return dialog;
2194 }
2195
2196
2197 void GuiView::showDialog(string const & name, string const & data,
2198         Inset * inset)
2199 {
2200         if (d.in_show_)
2201                 return;
2202
2203         d.in_show_ = true;
2204         Dialog * dialog = findOrBuild(name, false);
2205         if (dialog) {
2206                 dialog->showData(data);
2207                 if (inset)
2208                         d.open_insets_[name] = inset;
2209         }
2210         d.in_show_ = false;
2211 }
2212
2213
2214 bool GuiView::isDialogVisible(string const & name) const
2215 {
2216         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2217         if (it == d.dialogs_.end())
2218                 return false;
2219         return it->second.get()->isVisibleView();
2220 }
2221
2222
2223 void GuiView::hideDialog(string const & name, Inset * inset)
2224 {
2225         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2226         if (it == d.dialogs_.end())
2227                 return;
2228
2229         if (inset && inset != getOpenInset(name))
2230                 return;
2231
2232         Dialog * const dialog = it->second.get();
2233         if (dialog->isVisibleView())
2234                 dialog->hideView();
2235         d.open_insets_[name] = 0;
2236 }
2237
2238
2239 void GuiView::disconnectDialog(string const & name)
2240 {
2241         if (!isValidName(name))
2242                 return;
2243
2244         if (d.open_insets_.find(name) != d.open_insets_.end())
2245                 d.open_insets_[name] = 0;
2246 }
2247
2248
2249 Inset * GuiView::getOpenInset(string const & name) const
2250 {
2251         if (!isValidName(name))
2252                 return 0;
2253
2254         map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2255         return it == d.open_insets_.end() ? 0 : it->second;
2256 }
2257
2258
2259 void GuiView::hideAll() const
2260 {
2261         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2262         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2263
2264         for(; it != end; ++it)
2265                 it->second->hideView();
2266 }
2267
2268
2269 void GuiView::updateDialogs()
2270 {
2271         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2272         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2273
2274         for(; it != end; ++it) {
2275                 Dialog * dialog = it->second.get();
2276                 if (dialog && dialog->isVisibleView())
2277                         dialog->checkStatus();
2278         }
2279         updateToolbars();
2280         updateLayoutList();
2281 }
2282
2283
2284 // will be replaced by a proper factory...
2285 Dialog * createGuiAbout(GuiView & lv);
2286 Dialog * createGuiBibitem(GuiView & lv);
2287 Dialog * createGuiBibtex(GuiView & lv);
2288 Dialog * createGuiBox(GuiView & lv);
2289 Dialog * createGuiBranch(GuiView & lv);
2290 Dialog * createGuiChanges(GuiView & lv);
2291 Dialog * createGuiCharacter(GuiView & lv);
2292 Dialog * createGuiCitation(GuiView & lv);
2293 Dialog * createGuiDelimiter(GuiView & lv);
2294 Dialog * createGuiDocument(GuiView & lv);
2295 Dialog * createGuiErrorList(GuiView & lv);
2296 Dialog * createGuiERT(GuiView & lv);
2297 Dialog * createGuiExternal(GuiView & lv);
2298 Dialog * createGuiFloat(GuiView & lv);
2299 Dialog * createGuiGraphics(GuiView & lv);
2300 Dialog * createGuiHSpace(GuiView & lv);
2301 Dialog * createGuiInclude(GuiView & lv);
2302 Dialog * createGuiInfo(GuiView & lv);
2303 Dialog * createGuiLabel(GuiView & lv);
2304 Dialog * createGuiListings(GuiView & lv);
2305 Dialog * createGuiLog(GuiView & lv);
2306 Dialog * createGuiMathMatrix(GuiView & lv);
2307 Dialog * createGuiNomenclature(GuiView & lv);
2308 Dialog * createGuiNote(GuiView & lv);
2309 Dialog * createGuiParagraph(GuiView & lv);
2310 Dialog * createGuiPreferences(GuiView & lv);
2311 Dialog * createGuiPrint(GuiView & lv);
2312 Dialog * createGuiRef(GuiView & lv);
2313 Dialog * createGuiSearch(GuiView & lv);
2314 Dialog * createGuiSendTo(GuiView & lv);
2315 Dialog * createGuiShowFile(GuiView & lv);
2316 Dialog * createGuiSpellchecker(GuiView & lv);
2317 Dialog * createGuiSymbols(GuiView & lv);
2318 Dialog * createGuiTabularCreate(GuiView & lv);
2319 Dialog * createGuiTabular(GuiView & lv);
2320 Dialog * createGuiTexInfo(GuiView & lv);
2321 Dialog * createGuiToc(GuiView & lv);
2322 Dialog * createGuiThesaurus(GuiView & lv);
2323 Dialog * createGuiHyperlink(GuiView & lv);
2324 Dialog * createGuiVSpace(GuiView & lv);
2325 Dialog * createGuiViewSource(GuiView & lv);
2326 Dialog * createGuiWrap(GuiView & lv);
2327
2328
2329 Dialog * GuiView::build(string const & name)
2330 {
2331         LASSERT(isValidName(name), /**/);
2332
2333         if (name == "aboutlyx")
2334                 return createGuiAbout(*this);
2335         if (name == "bibitem")
2336                 return createGuiBibitem(*this);
2337         if (name == "bibtex")
2338                 return createGuiBibtex(*this);
2339         if (name == "box")
2340                 return createGuiBox(*this);
2341         if (name == "branch")
2342                 return createGuiBranch(*this);
2343         if (name == "changes")
2344                 return createGuiChanges(*this);
2345         if (name == "character")
2346                 return createGuiCharacter(*this);
2347         if (name == "citation")
2348                 return createGuiCitation(*this);
2349         if (name == "document")
2350                 return createGuiDocument(*this);
2351         if (name == "errorlist")
2352                 return createGuiErrorList(*this);
2353         if (name == "ert")
2354                 return createGuiERT(*this);
2355         if (name == "external")
2356                 return createGuiExternal(*this);
2357         if (name == "file")
2358                 return createGuiShowFile(*this);
2359         if (name == "findreplace")
2360                 return createGuiSearch(*this);
2361         if (name == "float")
2362                 return createGuiFloat(*this);
2363         if (name == "graphics")
2364                 return createGuiGraphics(*this);
2365         if (name == "include")
2366                 return createGuiInclude(*this);
2367         if (name == "info")
2368                 return createGuiInfo(*this);
2369         if (name == "nomenclature")
2370                 return createGuiNomenclature(*this);
2371         if (name == "label")
2372                 return createGuiLabel(*this);
2373         if (name == "log")
2374                 return createGuiLog(*this);
2375         if (name == "view-source")
2376                 return createGuiViewSource(*this);
2377         if (name == "mathdelimiter")
2378                 return createGuiDelimiter(*this);
2379         if (name == "mathmatrix")
2380                 return createGuiMathMatrix(*this);
2381         if (name == "note")
2382                 return createGuiNote(*this);
2383         if (name == "paragraph")
2384                 return createGuiParagraph(*this);
2385         if (name == "prefs")
2386                 return createGuiPreferences(*this);
2387         if (name == "print")
2388                 return createGuiPrint(*this);
2389         if (name == "ref")
2390                 return createGuiRef(*this);
2391         if (name == "sendto")
2392                 return createGuiSendTo(*this);
2393         if (name == "space")
2394                 return createGuiHSpace(*this);
2395         if (name == "spellchecker")
2396                 return createGuiSpellchecker(*this);
2397         if (name == "symbols")
2398                 return createGuiSymbols(*this);
2399         if (name == "tabular")
2400                 return createGuiTabular(*this);
2401         if (name == "tabularcreate")
2402                 return createGuiTabularCreate(*this);
2403         if (name == "texinfo")
2404                 return createGuiTexInfo(*this);
2405 #ifdef HAVE_LIBAIKSAURUS
2406         if (name == "thesaurus")
2407                 return createGuiThesaurus(*this);
2408 #endif
2409         if (name == "toc")
2410                 return createGuiToc(*this);
2411         if (name == "href")
2412                 return createGuiHyperlink(*this);
2413         if (name == "vspace")
2414                 return createGuiVSpace(*this);
2415         if (name == "wrap")
2416                 return createGuiWrap(*this);
2417         if (name == "listings")
2418                 return createGuiListings(*this);
2419
2420         return 0;
2421 }
2422
2423
2424 } // namespace frontend
2425 } // namespace lyx
2426
2427 #include "GuiView_moc.cpp"