]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Patch from Vincent van Ravesteijn.
[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 "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 #ifndef Q_WS_X11
774                 // FIXME bug 4888
775                 if (isFullScreen() && menuBar()->isHidden()) {
776                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
777                         // FIXME: we should also try to detect special LyX shortcut such as
778                         // Alt-P and Alt-M. Right now there is a hack in
779                         // GuiWorkArea::processKeySym() that hides again the menubar for
780                         // those cases.
781                         if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
782                                 menuBar()->show();
783                         return QMainWindow::event(e);
784                 }
785 #endif
786
787                 if (d.current_work_area_)
788                         // Nothing special to do.
789                         return QMainWindow::event(e);
790
791                 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
792                 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
793                 // between controls.
794                 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab 
795                         || ke->key() == Qt::Key_Backtab)
796                         return QMainWindow::event(e);
797
798                 // Allow processing of shortcuts that are allowed even when no Buffer
799                 // is viewed.
800                 theLyXFunc().setLyXView(this);
801                 KeySymbol sym;
802                 setKeySymbol(&sym, ke);
803                 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
804                 e->accept();
805                 return true;
806         }
807
808         default:
809                 return QMainWindow::event(e);
810         }
811 }
812
813
814 bool GuiView::focusNextPrevChild(bool /*next*/)
815 {
816         setFocus();
817         return true;
818 }
819
820
821 void GuiView::setBusy(bool busy)
822 {
823         if (d.current_work_area_) {
824                 d.current_work_area_->setUpdatesEnabled(!busy);
825                 if (busy)
826                         d.current_work_area_->stopBlinkingCursor();
827                 else
828                         d.current_work_area_->startBlinkingCursor();
829         }
830
831         if (busy)
832                 QApplication::setOverrideCursor(Qt::WaitCursor);
833         else
834                 QApplication::restoreOverrideCursor();
835 }
836
837
838 GuiWorkArea * GuiView::workArea(Buffer & buffer)
839 {
840         if (TabWorkArea * twa = d.currentTabWorkArea())
841                 return twa->workArea(buffer);
842         return 0;
843 }
844
845
846 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
847 {
848         // Automatically create a TabWorkArea if there are none yet.
849         TabWorkArea * tab_widget = d.splitter_->count() 
850                 ? d.currentTabWorkArea() : addTabWorkArea();
851         return tab_widget->addWorkArea(buffer, *this);
852 }
853
854
855 TabWorkArea * GuiView::addTabWorkArea()
856 {
857         TabWorkArea * twa = new TabWorkArea;
858         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
859                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
860         QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
861                          this, SLOT(on_lastWorkAreaRemoved()));
862
863         d.splitter_->addWidget(twa);
864         d.stack_widget_->setCurrentWidget(d.splitter_);
865         return twa;
866 }
867
868
869 GuiWorkArea const * GuiView::currentWorkArea() const
870 {
871         return d.current_work_area_;
872 }
873
874
875 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
876 {
877         LASSERT(wa, /**/);
878         d.current_work_area_ = wa;
879         for (int i = 0; i != d.splitter_->count(); ++i) {
880                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
881                         return;
882         }
883 }
884
885
886 void GuiView::removeWorkArea(GuiWorkArea * wa)
887 {
888         LASSERT(wa, /**/);
889         if (wa == d.current_work_area_) {
890                 disconnectBuffer();
891                 disconnectBufferView();
892                 d.current_work_area_ = 0;
893         }
894
895         for (int i = 0; i != d.splitter_->count(); ++i) {
896                 TabWorkArea * twa = d.tabWorkArea(i);
897                 if (!twa->removeWorkArea(wa))
898                         // Not found in this tab group.
899                         continue;
900
901                 // We found and removed the GuiWorkArea.
902                 if (!twa->count()) {
903                         // No more WorkAreas in this tab group, so delete it.
904                         delete twa;
905                         break;
906                 }
907
908                 if (d.current_work_area_)
909                         // This means that we are not closing the current GuiWorkArea;
910                         break;
911
912                 // Switch to the next GuiWorkArea in the found TabWorkArea.
913                 d.current_work_area_ = twa->currentWorkArea();
914                 break;
915         }
916
917         if (d.splitter_->count() == 0)
918                 // No more work area, switch to the background widget.
919                 d.setBackground();
920 }
921
922
923 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
924 {
925         d.layout_ = layout;
926 }
927
928
929 void GuiView::updateLayoutList()
930 {
931         if (d.layout_)
932                 d.layout_->updateContents(false);
933 }
934
935
936 void GuiView::updateToolbars()
937 {
938         ToolbarMap::iterator end = d.toolbars_.end();
939         if (d.current_work_area_) {
940                 bool const math =
941                         d.current_work_area_->bufferView().cursor().inMathed();
942                 bool const table =
943                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
944                 bool const review =
945                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
946                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
947                 bool const mathmacrotemplate =
948                         lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
949
950                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
951                         it->second->update(math, table, review, mathmacrotemplate);
952         } else
953                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
954                         it->second->update(false, false, false, false);
955 }
956
957
958 Buffer * GuiView::buffer()
959 {
960         if (d.current_work_area_)
961                 return &d.current_work_area_->bufferView().buffer();
962         return 0;
963 }
964
965
966 Buffer const * GuiView::buffer() const
967 {
968         if (d.current_work_area_)
969                 return &d.current_work_area_->bufferView().buffer();
970         return 0;
971 }
972
973
974 void GuiView::setBuffer(Buffer * newBuffer)
975 {
976         LASSERT(newBuffer, /**/);
977         setBusy(true);
978
979         GuiWorkArea * wa = workArea(*newBuffer);
980         if (wa == 0) {
981                 updateLabels(*newBuffer->masterBuffer());
982                 wa = addWorkArea(*newBuffer);
983         } else {
984                 //Disconnect the old buffer...there's no new one.
985                 disconnectBuffer();
986         }
987         connectBuffer(*newBuffer);
988         connectBufferView(wa->bufferView());
989         setCurrentWorkArea(wa);
990
991         setBusy(false);
992 }
993
994
995 void GuiView::connectBuffer(Buffer & buf)
996 {
997         buf.setGuiDelegate(this);
998 }
999
1000
1001 void GuiView::disconnectBuffer()
1002 {
1003         if (d.current_work_area_)
1004                 d.current_work_area_->bufferView().setGuiDelegate(0);
1005 }
1006
1007
1008 void GuiView::connectBufferView(BufferView & bv)
1009 {
1010         bv.setGuiDelegate(this);
1011 }
1012
1013
1014 void GuiView::disconnectBufferView()
1015 {
1016         if (d.current_work_area_)
1017                 d.current_work_area_->bufferView().setGuiDelegate(0);
1018 }
1019
1020
1021 void GuiView::errors(string const & error_type)
1022 {
1023         ErrorList & el = buffer()->errorList(error_type);
1024         if (!el.empty())
1025                 showDialog("errorlist", error_type);
1026 }
1027
1028
1029 void GuiView::structureChanged()
1030 {
1031         d.toc_models_.reset(view());
1032         // Navigator needs more than a simple update in this case. It needs to be
1033         // rebuilt.
1034         updateDialog("toc", "");
1035 }
1036
1037
1038 void GuiView::updateDialog(string const & name, string const & data)
1039 {
1040         if (!isDialogVisible(name))
1041                 return;
1042
1043         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1044         if (it == d.dialogs_.end())
1045                 return;
1046
1047         Dialog * const dialog = it->second.get();
1048         if (dialog->isVisibleView())
1049                 dialog->initialiseParams(data);
1050 }
1051
1052
1053 BufferView * GuiView::view()
1054 {
1055         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1056 }
1057
1058
1059 void GuiView::autoSave()
1060 {
1061         LYXERR(Debug::INFO, "Running autoSave()");
1062
1063         if (buffer())
1064                 view()->buffer().autoSave();
1065 }
1066
1067
1068 void GuiView::resetAutosaveTimers()
1069 {
1070         if (lyxrc.autosave)
1071                 d.autosave_timeout_.restart();
1072 }
1073
1074
1075 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1076 {
1077         bool enable = true;
1078         Buffer * buf = buffer();
1079
1080         /* In LyX/Mac, when a dialog is open, the menus of the
1081            application can still be accessed without giving focus to
1082            the main window. In this case, we want to disable the menu
1083            entries that are buffer-related.
1084
1085            Note that this code is not perfect, as bug 1941 attests:
1086            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1087         */
1088         if (cmd.origin == FuncRequest::MENU && !hasFocus())
1089                 buf = 0;
1090
1091         switch(cmd.action) {
1092         case LFUN_BUFFER_WRITE:
1093                 enable = buf && (buf->isUnnamed() || !buf->isClean());
1094                 break;
1095
1096         case LFUN_BUFFER_WRITE_AS:
1097                 enable = buf;
1098                 break;
1099
1100         case LFUN_SPLIT_VIEW:
1101                 if (cmd.getArg(0) == "vertical")
1102                         enable = buf && (d.splitter_->count() == 1 ||
1103                                          d.splitter_->orientation() == Qt::Vertical);
1104                 else
1105                         enable = buf && (d.splitter_->count() == 1 ||
1106                                          d.splitter_->orientation() == Qt::Horizontal);
1107                 break;
1108
1109         case LFUN_CLOSE_TAB_GROUP:
1110                 enable = d.currentTabWorkArea();
1111                 break;
1112
1113         case LFUN_TOOLBAR_TOGGLE:
1114                 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1115                         flag.setOnOff(t->isVisible());
1116                 break;
1117
1118         case LFUN_UI_TOGGLE:
1119                 flag.setOnOff(isFullScreen());
1120                 break;
1121
1122         case LFUN_DIALOG_TOGGLE:
1123                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1124                 // fall through to set "enable"
1125         case LFUN_DIALOG_SHOW: {
1126                 string const name = cmd.getArg(0);
1127                 if (!buf)
1128                         enable = name == "aboutlyx"
1129                                 || name == "file" //FIXME: should be removed.
1130                                 || name == "prefs"
1131                                 || name == "texinfo";
1132                 else if (name == "print")
1133                         enable = buf->isExportable("dvi")
1134                                 && lyxrc.print_command != "none";
1135                 else if (name == "character") {
1136                         if (!view())
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 == "symbols") {
1144                         if (!view() || view()->cursor().inMathed())
1145                                 enable = false;
1146                         else {
1147                                 InsetCode ic = view()->cursor().inset().lyxCode();
1148                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1149                         }
1150                 }
1151                 else if (name == "latexlog")
1152                         enable = FileName(buf->logName()).isReadableFile();
1153                 else if (name == "spellchecker")
1154 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1155                         enable = !buf->isReadonly();
1156 #else
1157                         enable = false;
1158 #endif
1159                 else if (name == "vclog")
1160                         enable = buf->lyxvc().inUse();
1161                 break;
1162         }
1163
1164         case LFUN_DIALOG_UPDATE: {
1165                 string const name = cmd.getArg(0);
1166                 if (!buf)
1167                         enable = name == "prefs";
1168                 break;
1169         }
1170
1171         case LFUN_INSET_APPLY: {
1172                 string const name = cmd.getArg(0);
1173                 Inset * inset = getOpenInset(name);
1174                 if (inset) {
1175                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1176                         FuncStatus fs;
1177                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1178                                 // Every inset is supposed to handle this
1179                                 LASSERT(false, /**/);
1180                         }
1181                         flag |= fs;
1182                 } else {
1183                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1184                         flag |= lyx::getStatus(fr);
1185                 }
1186                 enable = flag.enabled();
1187                 break;
1188         }
1189
1190         case LFUN_COMPLETION_INLINE:
1191                 if (!d.current_work_area_
1192                     || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1193                     enable = false;
1194                 break;
1195
1196         case LFUN_COMPLETION_POPUP:
1197                 if (!d.current_work_area_
1198                     || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1199                     enable = false;
1200                 break;
1201
1202         case LFUN_COMPLETION_COMPLETE:
1203                 if (!d.current_work_area_
1204                         || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1205                     enable = false;
1206                 break;
1207
1208         default:
1209                 return false;
1210         }
1211
1212         if (!enable)
1213                 flag.setEnabled(false);
1214
1215         return true;
1216 }
1217
1218
1219 static FileName selectTemplateFile()
1220 {
1221         FileDialog dlg(qt_("Select template file"));
1222         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1223         dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1224
1225         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1226                              QStringList(qt_("LyX Documents (*.lyx)")));
1227
1228         if (result.first == FileDialog::Later)
1229                 return FileName();
1230         if (result.second.isEmpty())
1231                 return FileName();
1232         return FileName(fromqstr(result.second));
1233 }
1234
1235
1236 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1237 {
1238         setBusy(true);
1239
1240         Buffer * newBuffer = checkAndLoadLyXFile(filename);
1241
1242         if (!newBuffer) {
1243                 message(_("Document not loaded."));
1244                 setBusy(false);
1245                 return 0;
1246         }
1247         
1248         setBuffer(newBuffer);
1249
1250         // scroll to the position when the file was last closed
1251         if (lyxrc.use_lastfilepos) {
1252                 LastFilePosSection::FilePos filepos =
1253                         theSession().lastFilePos().load(filename);
1254                 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1255         }
1256
1257         if (tolastfiles)
1258                 theSession().lastFiles().add(filename);
1259
1260         setBusy(false);
1261         return newBuffer;
1262 }
1263
1264
1265 void GuiView::openDocument(string const & fname)
1266 {
1267         string initpath = lyxrc.document_path;
1268
1269         if (buffer()) {
1270                 string const trypath = buffer()->filePath();
1271                 // If directory is writeable, use this as default.
1272                 if (FileName(trypath).isDirWritable())
1273                         initpath = trypath;
1274         }
1275
1276         string filename;
1277
1278         if (fname.empty()) {
1279                 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1280                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1281                 dlg.setButton2(qt_("Examples|#E#e"),
1282                                 toqstr(addPath(package().system_support().absFilename(), "examples")));
1283
1284                 QStringList filter(qt_("LyX Documents (*.lyx)"));
1285                 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1286                         << qt_("LyX-1.4.x Documents (*.lyx14)")
1287                         << qt_("LyX-1.5.x Documents (*.lyx15)");
1288                 FileDialog::Result result =
1289                         dlg.open(toqstr(initpath), filter);
1290
1291                 if (result.first == FileDialog::Later)
1292                         return;
1293
1294                 filename = fromqstr(result.second);
1295
1296                 // check selected filename
1297                 if (filename.empty()) {
1298                         message(_("Canceled."));
1299                         return;
1300                 }
1301         } else
1302                 filename = fname;
1303
1304         // get absolute path of file and add ".lyx" to the filename if
1305         // necessary. 
1306         FileName const fullname = 
1307                         fileSearch(string(), filename, "lyx", support::may_not_exist);
1308         if (!fullname.empty())
1309                 filename = fullname.absFilename();
1310
1311         // if the file doesn't exist, let the user create one
1312         if (!fullname.exists()) {
1313                 // the user specifically chose this name. Believe him.
1314                 Buffer * const b = newFile(filename, string(), true);
1315                 if (b)
1316                         setBuffer(b);
1317                 return;
1318         }
1319
1320         docstring const disp_fn = makeDisplayPath(filename);
1321         message(bformat(_("Opening document %1$s..."), disp_fn));
1322
1323         docstring str2;
1324         Buffer * buf = loadDocument(fullname);
1325         if (buf) {
1326                 updateLabels(*buf);
1327                 
1328                 setBuffer(buf);
1329                 buf->errors("Parse");
1330                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1331                 if (buf->lyxvc().inUse())
1332                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1333                                 " " + _("Version control detected.");
1334         } else {
1335                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1336         }
1337         message(str2);
1338 }
1339
1340 // FIXME: clean that
1341 static bool import(GuiView * lv, FileName const & filename,
1342         string const & format, ErrorList & errorList)
1343 {
1344         FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1345
1346         string loader_format;
1347         vector<string> loaders = theConverters().loaders();
1348         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1349                 for (vector<string>::const_iterator it = loaders.begin();
1350                      it != loaders.end(); ++it) {
1351                         if (!theConverters().isReachable(format, *it))
1352                                 continue;
1353
1354                         string const tofile =
1355                                 support::changeExtension(filename.absFilename(),
1356                                 formats.extension(*it));
1357                         if (!theConverters().convert(0, filename, FileName(tofile),
1358                                 filename, format, *it, errorList))
1359                                 return false;
1360                         loader_format = *it;
1361                         break;
1362                 }
1363                 if (loader_format.empty()) {
1364                         frontend::Alert::error(_("Couldn't import file"),
1365                                      bformat(_("No information for importing the format %1$s."),
1366                                          formats.prettyName(format)));
1367                         return false;
1368                 }
1369         } else
1370                 loader_format = format;
1371
1372         if (loader_format == "lyx") {
1373                 Buffer * buf = lv->loadDocument(lyxfile);
1374                 if (!buf)
1375                         return false;
1376                 updateLabels(*buf);
1377                 lv->setBuffer(buf);
1378                 buf->errors("Parse");
1379         } else {
1380                 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1381                 if (!b)
1382                         return false;
1383                 lv->setBuffer(b);
1384                 bool as_paragraphs = loader_format == "textparagraph";
1385                 string filename2 = (loader_format == format) ? filename.absFilename()
1386                         : support::changeExtension(filename.absFilename(),
1387                                           formats.extension(loader_format));
1388                 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1389                 theLyXFunc().setLyXView(lv);
1390                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1391         }
1392
1393         return true;
1394 }
1395
1396
1397 void GuiView::importDocument(string const & argument)
1398 {
1399         string format;
1400         string filename = split(argument, format, ' ');
1401
1402         LYXERR(Debug::INFO, format << " file: " << filename);
1403
1404         // need user interaction
1405         if (filename.empty()) {
1406                 string initpath = lyxrc.document_path;
1407
1408                 Buffer const * buf = buffer();
1409                 if (buf) {
1410                         string const trypath = buf->filePath();
1411                         // If directory is writeable, use this as default.
1412                         if (FileName(trypath).isDirWritable())
1413                                 initpath = trypath;
1414                 }
1415
1416                 docstring const text = bformat(_("Select %1$s file to import"),
1417                         formats.prettyName(format));
1418
1419                 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1420                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1421                 dlg.setButton2(qt_("Examples|#E#e"),
1422                         toqstr(addPath(package().system_support().absFilename(), "examples")));
1423
1424                 docstring filter = formats.prettyName(format);
1425                 filter += " (*.";
1426                 // FIXME UNICODE
1427                 filter += from_utf8(formats.extension(format));
1428                 filter += ')';
1429
1430                 FileDialog::Result result =
1431                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1432
1433                 if (result.first == FileDialog::Later)
1434                         return;
1435
1436                 filename = fromqstr(result.second);
1437
1438                 // check selected filename
1439                 if (filename.empty())
1440                         message(_("Canceled."));
1441         }
1442
1443         if (filename.empty())
1444                 return;
1445
1446         // get absolute path of file
1447         FileName const fullname(support::makeAbsPath(filename));
1448
1449         FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1450
1451         // Check if the document already is open
1452         Buffer * buf = theBufferList().getBuffer(lyxfile);
1453         if (buf) {
1454                 setBuffer(buf);
1455                 if (!closeBuffer()) {
1456                         message(_("Canceled."));
1457                         return;
1458                 }
1459         }
1460
1461         docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1462
1463         // if the file exists already, and we didn't do
1464         // -i lyx thefile.lyx, warn
1465         if (lyxfile.exists() && fullname != lyxfile) {
1466
1467                 docstring text = bformat(_("The document %1$s already exists.\n\n"
1468                         "Do you want to overwrite that document?"), displaypath);
1469                 int const ret = Alert::prompt(_("Overwrite document?"),
1470                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
1471
1472                 if (ret == 1) {
1473                         message(_("Canceled."));
1474                         return;
1475                 }
1476         }
1477
1478         message(bformat(_("Importing %1$s..."), displaypath));
1479         ErrorList errorList;
1480         if (import(this, fullname, format, errorList))
1481                 message(_("imported."));
1482         else
1483                 message(_("file not imported!"));
1484
1485         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1486 }
1487
1488
1489 void GuiView::newDocument(string const & filename, bool from_template)
1490 {
1491         FileName initpath(lyxrc.document_path);
1492         Buffer * buf = buffer();
1493         if (buf) {
1494                 FileName const trypath(buf->filePath());
1495                 // If directory is writeable, use this as default.
1496                 if (trypath.isDirWritable())
1497                         initpath = trypath;
1498         }
1499
1500         string templatefile = from_template ?
1501                 selectTemplateFile().absFilename() : string();
1502         Buffer * b;
1503         if (filename.empty())
1504                 b = newUnnamedFile(templatefile, initpath);
1505         else
1506                 b = newFile(filename, templatefile, true);
1507
1508         if (b)
1509                 setBuffer(b);
1510         // Ensure the cursor is correctly positionned on screen.
1511         view()->showCursor();
1512 }
1513
1514
1515 void GuiView::insertLyXFile(docstring const & fname)
1516 {
1517         BufferView * bv = view();
1518         if (!bv)
1519                 return;
1520
1521         // FIXME UNICODE
1522         FileName filename(to_utf8(fname));
1523         
1524         if (!filename.empty()) {
1525                 bv->insertLyXFile(filename);
1526                 return;
1527         }
1528
1529         // Launch a file browser
1530         // FIXME UNICODE
1531         string initpath = lyxrc.document_path;
1532         string const trypath = bv->buffer().filePath();
1533         // If directory is writeable, use this as default.
1534         if (FileName(trypath).isDirWritable())
1535                 initpath = trypath;
1536
1537         // FIXME UNICODE
1538         FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1539         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1540         dlg.setButton2(qt_("Examples|#E#e"),
1541                 toqstr(addPath(package().system_support().absFilename(),
1542                 "examples")));
1543
1544         FileDialog::Result result = dlg.open(toqstr(initpath),
1545                              QStringList(qt_("LyX Documents (*.lyx)")));
1546
1547         if (result.first == FileDialog::Later)
1548                 return;
1549
1550         // FIXME UNICODE
1551         filename.set(fromqstr(result.second));
1552
1553         // check selected filename
1554         if (filename.empty()) {
1555                 // emit message signal.
1556                 message(_("Canceled."));
1557                 return;
1558         }
1559
1560         bv->insertLyXFile(filename);
1561 }
1562
1563
1564 void GuiView::insertPlaintextFile(docstring const & fname,
1565         bool asParagraph)
1566 {
1567         BufferView * bv = view();
1568         if (!bv)
1569                 return;
1570
1571         // FIXME UNICODE
1572         FileName filename(to_utf8(fname));
1573         
1574         if (!filename.empty()) {
1575                 bv->insertPlaintextFile(filename, asParagraph);
1576                 return;
1577         }
1578
1579         FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1580                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1581
1582         FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1583                 QStringList());
1584
1585         if (result.first == FileDialog::Later)
1586                 return;
1587
1588         // FIXME UNICODE
1589         filename.set(fromqstr(result.second));
1590
1591         // check selected filename
1592         if (filename.empty()) {
1593                 // emit message signal.
1594                 message(_("Canceled."));
1595                 return;
1596         }
1597
1598         bv->insertPlaintextFile(filename, asParagraph);
1599 }
1600
1601
1602 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1603 {
1604         FileName fname = b.fileName();
1605         FileName const oldname = fname;
1606
1607         if (!newname.empty()) {
1608                 // FIXME UNICODE
1609                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1610         } else {
1611                 // Switch to this Buffer.
1612                 setBuffer(&b);
1613
1614                 /// No argument? Ask user through dialog.
1615                 // FIXME UNICODE
1616                 FileDialog dlg(qt_("Choose a filename to save document as"),
1617                                    LFUN_BUFFER_WRITE_AS);
1618                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1619                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1620
1621                 if (!isLyXFilename(fname.absFilename()))
1622                         fname.changeExtension(".lyx");
1623
1624                 FileDialog::Result result =
1625                         dlg.save(toqstr(fname.onlyPath().absFilename()),
1626                                QStringList(qt_("LyX Documents (*.lyx)")),
1627                                      toqstr(fname.onlyFileName()));
1628
1629                 if (result.first == FileDialog::Later)
1630                         return false;
1631
1632                 fname.set(fromqstr(result.second));
1633
1634                 if (fname.empty())
1635                         return false;
1636
1637                 if (!isLyXFilename(fname.absFilename()))
1638                         fname.changeExtension(".lyx");
1639         }
1640
1641         if (FileName(fname).exists()) {
1642                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1643                 docstring text = bformat(_("The document %1$s already "
1644                                            "exists.\n\nDo you want to "
1645                                            "overwrite that document?"), 
1646                                          file);
1647                 int const ret = Alert::prompt(_("Overwrite document?"),
1648                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1649                 switch (ret) {
1650                 case 0: break;
1651                 case 1: return renameBuffer(b, docstring());
1652                 case 2: return false;
1653                 }
1654         }
1655
1656         // Ok, change the name of the buffer
1657         b.setFileName(fname.absFilename());
1658         b.markDirty();
1659         bool unnamed = b.isUnnamed();
1660         b.setUnnamed(false);
1661         b.saveCheckSum(fname);
1662
1663         if (!saveBuffer(b)) {
1664                 b.setFileName(oldname.absFilename());
1665                 b.setUnnamed(unnamed);
1666                 b.saveCheckSum(oldname);
1667                 return false;
1668         }
1669
1670         return true;
1671 }
1672
1673
1674 bool GuiView::saveBuffer(Buffer & b)
1675 {
1676         if (b.isUnnamed())
1677                 return renameBuffer(b, docstring());
1678
1679         if (b.save()) {
1680                 theSession().lastFiles().add(b.fileName());
1681                 return true;
1682         }
1683
1684         // Switch to this Buffer.
1685         setBuffer(&b);
1686
1687         // FIXME: we don't tell the user *WHY* the save failed !!
1688         docstring const file = makeDisplayPath(b.absFileName(), 30);
1689         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1690                                    "Do you want to rename the document and "
1691                                    "try again?"), file);
1692         int const ret = Alert::prompt(_("Rename and save?"),
1693                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1694         switch (ret) {
1695         case 0:
1696                 if (!renameBuffer(b, docstring()))
1697                         return false;
1698                 break;
1699         case 1:
1700                 break;
1701         case 2:
1702                 return false;
1703         }
1704
1705         return saveBuffer(b);
1706 }
1707
1708
1709 bool GuiView::closeBuffer()
1710 {
1711         Buffer * buf = buffer();
1712         return buf && closeBuffer(*buf);
1713 }
1714
1715
1716 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1717 {
1718         // goto bookmark to update bookmark pit.
1719         //FIXME: we should update only the bookmarks related to this buffer!
1720         for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1721                 theLyXFunc().gotoBookmark(i+1, false, false);
1722
1723         if (buf.isClean() || buf.paragraphs().empty()) {
1724                 if (buf.masterBuffer() == &buf && tolastopened)
1725                         theSession().lastOpened().add(buf.fileName());
1726                 theBufferList().release(&buf);
1727                 return true;
1728         }
1729         // Switch to this Buffer.
1730         setBuffer(&buf);
1731
1732         docstring file;
1733         // FIXME: Unicode?
1734         if (buf.isUnnamed())
1735                 file = from_utf8(buf.fileName().onlyFileName());
1736         else
1737                 file = buf.fileName().displayName(30);
1738
1739         // Bring this window to top before asking questions.
1740         raise();
1741         activateWindow();
1742
1743         docstring const text = bformat(_("The document %1$s has unsaved changes."
1744                 "\n\nDo you want to save the document or discard the changes?"), file);
1745         int const ret = Alert::prompt(_("Save changed document?"),
1746                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1747
1748         switch (ret) {
1749         case 0:
1750                 if (!saveBuffer(buf))
1751                         return false;
1752                 break;
1753         case 1:
1754                 // if we crash after this we could
1755                 // have no autosave file but I guess
1756                 // this is really improbable (Jug)
1757                 removeAutosaveFile(buf.absFileName());
1758                 break;
1759         case 2:
1760                 return false;
1761         }
1762
1763         // save file names to .lyx/session
1764         // if master/slave are both open, do not save slave since it
1765         // will be automatically loaded when the master is loaded
1766         if (buf.masterBuffer() == &buf && tolastopened)
1767                 theSession().lastOpened().add(buf.fileName());
1768
1769         if (buf.parent())
1770                 // Don't close child documents.
1771                 removeWorkArea(d.current_work_area_);
1772         else
1773                 theBufferList().release(&buf);
1774
1775         return true;
1776 }
1777
1778
1779 bool GuiView::dispatch(FuncRequest const & cmd)
1780 {
1781         BufferView * bv = view();
1782         // By default we won't need any update.
1783         if (bv)
1784                 bv->cursor().updateFlags(Update::None);
1785         bool dispatched = true;
1786
1787         switch(cmd.action) {
1788                 case LFUN_BUFFER_IMPORT:
1789                         importDocument(to_utf8(cmd.argument()));
1790                         break;
1791
1792                 case LFUN_BUFFER_SWITCH:
1793                         setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1794                         break;
1795
1796                 case LFUN_BUFFER_NEXT:
1797                         setBuffer(theBufferList().next(buffer()));
1798                         break;
1799
1800                 case LFUN_BUFFER_PREVIOUS:
1801                         setBuffer(theBufferList().previous(buffer()));
1802                         break;
1803
1804                 case LFUN_COMMAND_EXECUTE: {
1805                         bool const show_it = cmd.argument() != "off";
1806                         // FIXME: this is a hack, "minibuffer" should not be
1807                         // hardcoded.
1808                         if (GuiToolbar * t = toolbar("minibuffer")) {
1809                                 t->setVisible(show_it);
1810                                 if (show_it && t->commandBuffer())
1811                                         t->commandBuffer()->setFocus();
1812                         }
1813                         break;
1814                 }
1815                 case LFUN_DROP_LAYOUTS_CHOICE:
1816                         if (d.layout_)
1817                                 d.layout_->showPopup();
1818                         break;
1819
1820                 case LFUN_MENU_OPEN:
1821                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1822                                 menu->exec(QCursor::pos());
1823                         break;
1824
1825                 case LFUN_FILE_INSERT:
1826                         insertLyXFile(cmd.argument());
1827                         break;
1828                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1829                         insertPlaintextFile(cmd.argument(), true);
1830                         break;
1831
1832                 case LFUN_FILE_INSERT_PLAINTEXT:
1833                         insertPlaintextFile(cmd.argument(), false);
1834                         break;
1835
1836                 case LFUN_BUFFER_WRITE:
1837                         if (bv)
1838                                 saveBuffer(bv->buffer());
1839                         break;
1840
1841                 case LFUN_BUFFER_WRITE_AS:
1842                         if (bv)
1843                                 renameBuffer(bv->buffer(), cmd.argument());
1844                         break;
1845
1846                 case LFUN_BUFFER_WRITE_ALL: {
1847                         Buffer * first = theBufferList().first();
1848                         if (!first)
1849                                 break;
1850                         message(_("Saving all documents..."));
1851                         // We cannot use a for loop as the buffer list cycles.
1852                         Buffer * b = first;
1853                         do {
1854                                 if (!b->isClean()) {
1855                                         saveBuffer(*b);
1856                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1857                                 }
1858                                 b = theBufferList().next(b);
1859                         } while (b != first); 
1860                         message(_("All documents saved."));
1861                         break;
1862                 }
1863
1864                 case LFUN_TOOLBAR_TOGGLE: {
1865                         string const name = cmd.getArg(0);
1866                         if (GuiToolbar * t = toolbar(name))
1867                                 t->toggle();
1868                         break;
1869                 }
1870
1871                 case LFUN_DIALOG_UPDATE: {
1872                         string const name = to_utf8(cmd.argument());
1873                         // Can only update a dialog connected to an existing inset
1874                         Inset * inset = getOpenInset(name);
1875                         if (inset) {
1876                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1877                                 inset->dispatch(view()->cursor(), fr);
1878                         } else if (name == "paragraph") {
1879                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1880                         } else if (name == "prefs") {
1881                                 updateDialog(name, string());
1882                         }
1883                         break;
1884                 }
1885
1886                 case LFUN_DIALOG_TOGGLE: {
1887                         if (isDialogVisible(cmd.getArg(0)))
1888                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1889                         else
1890                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1891                         break;
1892                 }
1893
1894                 case LFUN_DIALOG_DISCONNECT_INSET:
1895                         disconnectDialog(to_utf8(cmd.argument()));
1896                         break;
1897
1898                 case LFUN_DIALOG_HIDE: {
1899                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1900                         break;
1901                 }
1902
1903                 case LFUN_DIALOG_SHOW: {
1904                         string const name = cmd.getArg(0);
1905                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1906
1907                         if (name == "character") {
1908                                 data = freefont2string();
1909                                 if (!data.empty())
1910                                         showDialog("character", data);
1911                         } else if (name == "latexlog") {
1912                                 Buffer::LogType type; 
1913                                 string const logfile = buffer()->logName(&type);
1914                                 switch (type) {
1915                                 case Buffer::latexlog:
1916                                         data = "latex ";
1917                                         break;
1918                                 case Buffer::buildlog:
1919                                         data = "literate ";
1920                                         break;
1921                                 }
1922                                 data += Lexer::quoteString(logfile);
1923                                 showDialog("log", data);
1924                         } else if (name == "vclog") {
1925                                 string const data = "vc " +
1926                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
1927                                 showDialog("log", data);
1928                         } else if (name == "symbols") {
1929                                 data = bv->cursor().getEncoding()->name();
1930                                 if (!data.empty())
1931                                         showDialog("symbols", data);
1932                         } else
1933                                 showDialog(name, data);
1934                         break;
1935                 }
1936
1937                 case LFUN_INSET_APPLY: {
1938                         view()->cursor().recordUndoFullDocument();
1939                         string const name = cmd.getArg(0);
1940                         Inset * inset = getOpenInset(name);
1941                         if (inset) {
1942                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1943                                 inset->dispatch(view()->cursor(), fr);
1944                         } else {
1945                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1946                                 lyx::dispatch(fr);
1947                         }
1948                         break;
1949                 }
1950
1951                 case LFUN_UI_TOGGLE:
1952                         lfunUiToggle(cmd);
1953                         // Make sure the keyboard focus stays in the work area.
1954                         setFocus();
1955                         break;
1956
1957                 case LFUN_COMPLETION_INLINE:
1958                         if (d.current_work_area_)
1959                                 d.current_work_area_->completer().showInline();
1960                         break;
1961
1962                 case LFUN_SPLIT_VIEW:
1963                         if (Buffer * buf = buffer()) {
1964                                 string const orientation = cmd.getArg(0);
1965                                 d.splitter_->setOrientation(orientation == "vertical"
1966                                         ? Qt::Vertical : Qt::Horizontal);
1967                                 TabWorkArea * twa = addTabWorkArea();
1968                                 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1969                                 setCurrentWorkArea(wa);
1970                         }
1971                         break;
1972
1973                 case LFUN_CLOSE_TAB_GROUP:
1974                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
1975                                 delete twa;
1976                                 twa = d.currentTabWorkArea();
1977                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
1978                                 if (twa) {
1979                                         d.current_work_area_ = twa->currentWorkArea();
1980                                         // Make sure the work area is up to date.
1981                                         twa->setCurrentWorkArea(d.current_work_area_);
1982                                 } else {
1983                                         d.current_work_area_ = 0;
1984                                 }
1985                                 if (d.splitter_->count() == 0)
1986                                         // No more work area, switch to the background widget.
1987                                         d.setBackground();
1988                         }
1989                         break;
1990                         
1991                 case LFUN_COMPLETION_POPUP:
1992                         if (d.current_work_area_)
1993                                 d.current_work_area_->completer().showPopup();
1994                         break;
1995
1996
1997                 case LFUN_COMPLETION_COMPLETE:
1998                         if (d.current_work_area_)
1999                                 d.current_work_area_->completer().tab();
2000                         break;
2001
2002                 default:
2003                         dispatched = false;
2004                         break;
2005         }
2006
2007         // Part of automatic menu appearance feature.
2008         if (isFullScreen()) {
2009                 if (menuBar()->isVisible())
2010                         menuBar()->hide();
2011                 if (statusBar()->isVisible())
2012                         statusBar()->hide();
2013         }
2014
2015         return dispatched;
2016 }
2017
2018
2019 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2020 {
2021         string const arg = cmd.getArg(0);
2022         if (arg == "scrollbar") {
2023                 // hide() is of no help
2024                 if (d.current_work_area_->verticalScrollBarPolicy() ==
2025                         Qt::ScrollBarAlwaysOff)
2026
2027                         d.current_work_area_->setVerticalScrollBarPolicy(
2028                                 Qt::ScrollBarAsNeeded);
2029                 else
2030                         d.current_work_area_->setVerticalScrollBarPolicy(
2031                                 Qt::ScrollBarAlwaysOff);
2032                 return;
2033         }
2034         if (arg == "statusbar") {
2035                 statusBar()->setVisible(!statusBar()->isVisible());
2036                 return;
2037         }
2038         if (arg == "menubar") {
2039                 menuBar()->setVisible(!menuBar()->isVisible());
2040                 return;
2041         }
2042 #if QT_VERSION >= 0x040300
2043         if (arg == "frame") {
2044                 int l, t, r, b;
2045                 getContentsMargins(&l, &t, &r, &b);
2046                 //are the frames in default state?
2047                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2048                 if (l == 0) {
2049                         setContentsMargins(-2, -2, -2, -2);
2050                 } else {
2051                         setContentsMargins(0, 0, 0, 0);
2052                 }
2053                 return;
2054         }
2055 #endif
2056         if (arg == "fullscreen") {
2057                 toggleFullScreen();
2058                 return;
2059         }
2060
2061         message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2062 }
2063
2064
2065 void GuiView::toggleFullScreen()
2066 {
2067         if (isFullScreen()) {
2068                 for (int i = 0; i != d.splitter_->count(); ++i)
2069                         d.tabWorkArea(i)->setFullScreen(false);
2070 #if QT_VERSION >= 0x040300
2071                 setContentsMargins(0, 0, 0, 0);
2072 #endif
2073                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2074                 restoreLayout();
2075                 menuBar()->show();
2076                 statusBar()->show();
2077         } else {
2078                 for (int i = 0; i != d.splitter_->count(); ++i)
2079                         d.tabWorkArea(i)->setFullScreen(true);
2080 #if QT_VERSION >= 0x040300
2081                 setContentsMargins(-2, -2, -2, -2);
2082 #endif
2083                 saveLayout();
2084                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2085                 statusBar()->hide();
2086                 menuBar()->hide();
2087                 if (lyxrc.full_screen_toolbars) {
2088                         ToolbarMap::iterator end = d.toolbars_.end();
2089                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2090                                 it->second->hide();
2091                 }
2092         }
2093 }
2094
2095
2096 Buffer const * GuiView::updateInset(Inset const * inset)
2097 {
2098         if (!d.current_work_area_)
2099                 return 0;
2100
2101         if (inset)
2102                 d.current_work_area_->scheduleRedraw();
2103
2104         return &d.current_work_area_->bufferView().buffer();
2105 }
2106
2107
2108 void GuiView::restartCursor()
2109 {
2110         /* When we move around, or type, it's nice to be able to see
2111          * the cursor immediately after the keypress.
2112          */
2113         if (d.current_work_area_)
2114                 d.current_work_area_->startBlinkingCursor();
2115
2116         // Take this occasion to update the other GUI elements.
2117         updateDialogs();
2118         updateStatusBar();
2119 }
2120
2121
2122 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2123 {
2124         if (d.current_work_area_)
2125                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2126 }
2127
2128 namespace {
2129
2130 // This list should be kept in sync with the list of insets in
2131 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
2132 // dialog should have the same name as the inset.
2133 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2134 // docs in LyXAction.cpp.
2135
2136 char const * const dialognames[] = {
2137 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2138 "citation", "document", "errorlist", "ert", "external", "file",
2139 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2140 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print", 
2141 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2142
2143 #ifdef HAVE_LIBAIKSAURUS
2144 "thesaurus",
2145 #endif
2146
2147 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2148
2149 char const * const * const end_dialognames =
2150         dialognames + (sizeof(dialognames) / sizeof(char *));
2151
2152 class cmpCStr {
2153 public:
2154         cmpCStr(char const * name) : name_(name) {}
2155         bool operator()(char const * other) {
2156                 return strcmp(other, name_) == 0;
2157         }
2158 private:
2159         char const * name_;
2160 };
2161
2162
2163 bool isValidName(string const & name)
2164 {
2165         return find_if(dialognames, end_dialognames,
2166                             cmpCStr(name.c_str())) != end_dialognames;
2167 }
2168
2169 } // namespace anon
2170
2171
2172 void GuiView::resetDialogs()
2173 {
2174         // Make sure that no LFUN uses any LyXView.
2175         theLyXFunc().setLyXView(0);
2176         saveLayout();
2177         menuBar()->clear();
2178         constructToolbars();
2179         guiApp->menus().fillMenuBar(menuBar(), this, true);
2180         if (d.layout_)
2181                 d.layout_->updateContents(true);
2182         // Now update controls with current buffer.
2183         theLyXFunc().setLyXView(this);
2184         restoreLayout();
2185         restartCursor();
2186 }
2187
2188
2189 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2190 {
2191         if (!isValidName(name))
2192                 return 0;
2193
2194         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2195
2196         if (it != d.dialogs_.end())
2197                 return it->second.get();
2198
2199         Dialog * dialog = build(name);
2200         d.dialogs_[name].reset(dialog);
2201         if (lyxrc.allow_geometry_session)
2202                 dialog->restoreSession();
2203         if (hide_it)
2204                 dialog->hideView();
2205         return dialog;
2206 }
2207
2208
2209 void GuiView::showDialog(string const & name, string const & data,
2210         Inset * inset)
2211 {
2212         if (d.in_show_)
2213                 return;
2214
2215         d.in_show_ = true;
2216         Dialog * dialog = findOrBuild(name, false);
2217         if (dialog) {
2218                 dialog->showData(data);
2219                 if (inset)
2220                         d.open_insets_[name] = inset;
2221         }
2222         d.in_show_ = false;
2223 }
2224
2225
2226 bool GuiView::isDialogVisible(string const & name) const
2227 {
2228         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2229         if (it == d.dialogs_.end())
2230                 return false;
2231         return it->second.get()->isVisibleView();
2232 }
2233
2234
2235 void GuiView::hideDialog(string const & name, Inset * inset)
2236 {
2237         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2238         if (it == d.dialogs_.end())
2239                 return;
2240
2241         if (inset && inset != getOpenInset(name))
2242                 return;
2243
2244         Dialog * const dialog = it->second.get();
2245         if (dialog->isVisibleView())
2246                 dialog->hideView();
2247         d.open_insets_[name] = 0;
2248 }
2249
2250
2251 void GuiView::disconnectDialog(string const & name)
2252 {
2253         if (!isValidName(name))
2254                 return;
2255
2256         if (d.open_insets_.find(name) != d.open_insets_.end())
2257                 d.open_insets_[name] = 0;
2258 }
2259
2260
2261 Inset * GuiView::getOpenInset(string const & name) const
2262 {
2263         if (!isValidName(name))
2264                 return 0;
2265
2266         map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2267         return it == d.open_insets_.end() ? 0 : it->second;
2268 }
2269
2270
2271 void GuiView::hideAll() const
2272 {
2273         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2274         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2275
2276         for(; it != end; ++it)
2277                 it->second->hideView();
2278 }
2279
2280
2281 void GuiView::updateDialogs()
2282 {
2283         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2284         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2285
2286         for(; it != end; ++it) {
2287                 Dialog * dialog = it->second.get();
2288                 if (dialog && dialog->isVisibleView())
2289                         dialog->checkStatus();
2290         }
2291         updateToolbars();
2292         updateLayoutList();
2293 }
2294
2295
2296 // will be replaced by a proper factory...
2297 Dialog * createGuiAbout(GuiView & lv);
2298 Dialog * createGuiBibitem(GuiView & lv);
2299 Dialog * createGuiBibtex(GuiView & lv);
2300 Dialog * createGuiBox(GuiView & lv);
2301 Dialog * createGuiBranch(GuiView & lv);
2302 Dialog * createGuiChanges(GuiView & lv);
2303 Dialog * createGuiCharacter(GuiView & lv);
2304 Dialog * createGuiCitation(GuiView & lv);
2305 Dialog * createGuiDelimiter(GuiView & lv);
2306 Dialog * createGuiDocument(GuiView & lv);
2307 Dialog * createGuiErrorList(GuiView & lv);
2308 Dialog * createGuiERT(GuiView & lv);
2309 Dialog * createGuiExternal(GuiView & lv);
2310 Dialog * createGuiFloat(GuiView & lv);
2311 Dialog * createGuiGraphics(GuiView & lv);
2312 Dialog * createGuiHSpace(GuiView & lv);
2313 Dialog * createGuiInclude(GuiView & lv);
2314 Dialog * createGuiInfo(GuiView & lv);
2315 Dialog * createGuiLabel(GuiView & lv);
2316 Dialog * createGuiListings(GuiView & lv);
2317 Dialog * createGuiLog(GuiView & lv);
2318 Dialog * createGuiMathMatrix(GuiView & lv);
2319 Dialog * createGuiNomenclature(GuiView & lv);
2320 Dialog * createGuiNote(GuiView & lv);
2321 Dialog * createGuiParagraph(GuiView & lv);
2322 Dialog * createGuiPreferences(GuiView & lv);
2323 Dialog * createGuiPrint(GuiView & lv);
2324 Dialog * createGuiRef(GuiView & lv);
2325 Dialog * createGuiSearch(GuiView & lv);
2326 Dialog * createGuiSendTo(GuiView & lv);
2327 Dialog * createGuiShowFile(GuiView & lv);
2328 Dialog * createGuiSpellchecker(GuiView & lv);
2329 Dialog * createGuiSymbols(GuiView & lv);
2330 Dialog * createGuiTabularCreate(GuiView & lv);
2331 Dialog * createGuiTabular(GuiView & lv);
2332 Dialog * createGuiTexInfo(GuiView & lv);
2333 Dialog * createGuiToc(GuiView & lv);
2334 Dialog * createGuiThesaurus(GuiView & lv);
2335 Dialog * createGuiHyperlink(GuiView & lv);
2336 Dialog * createGuiVSpace(GuiView & lv);
2337 Dialog * createGuiViewSource(GuiView & lv);
2338 Dialog * createGuiWrap(GuiView & lv);
2339
2340
2341 Dialog * GuiView::build(string const & name)
2342 {
2343         LASSERT(isValidName(name), /**/);
2344
2345         if (name == "aboutlyx")
2346                 return createGuiAbout(*this);
2347         if (name == "bibitem")
2348                 return createGuiBibitem(*this);
2349         if (name == "bibtex")
2350                 return createGuiBibtex(*this);
2351         if (name == "box")
2352                 return createGuiBox(*this);
2353         if (name == "branch")
2354                 return createGuiBranch(*this);
2355         if (name == "changes")
2356                 return createGuiChanges(*this);
2357         if (name == "character")
2358                 return createGuiCharacter(*this);
2359         if (name == "citation")
2360                 return createGuiCitation(*this);
2361         if (name == "document")
2362                 return createGuiDocument(*this);
2363         if (name == "errorlist")
2364                 return createGuiErrorList(*this);
2365         if (name == "ert")
2366                 return createGuiERT(*this);
2367         if (name == "external")
2368                 return createGuiExternal(*this);
2369         if (name == "file")
2370                 return createGuiShowFile(*this);
2371         if (name == "findreplace")
2372                 return createGuiSearch(*this);
2373         if (name == "float")
2374                 return createGuiFloat(*this);
2375         if (name == "graphics")
2376                 return createGuiGraphics(*this);
2377         if (name == "include")
2378                 return createGuiInclude(*this);
2379         if (name == "info")
2380                 return createGuiInfo(*this);
2381         if (name == "nomenclature")
2382                 return createGuiNomenclature(*this);
2383         if (name == "label")
2384                 return createGuiLabel(*this);
2385         if (name == "log")
2386                 return createGuiLog(*this);
2387         if (name == "view-source")
2388                 return createGuiViewSource(*this);
2389         if (name == "mathdelimiter")
2390                 return createGuiDelimiter(*this);
2391         if (name == "mathmatrix")
2392                 return createGuiMathMatrix(*this);
2393         if (name == "note")
2394                 return createGuiNote(*this);
2395         if (name == "paragraph")
2396                 return createGuiParagraph(*this);
2397         if (name == "prefs")
2398                 return createGuiPreferences(*this);
2399         if (name == "print")
2400                 return createGuiPrint(*this);
2401         if (name == "ref")
2402                 return createGuiRef(*this);
2403         if (name == "sendto")
2404                 return createGuiSendTo(*this);
2405         if (name == "space")
2406                 return createGuiHSpace(*this);
2407         if (name == "spellchecker")
2408                 return createGuiSpellchecker(*this);
2409         if (name == "symbols")
2410                 return createGuiSymbols(*this);
2411         if (name == "tabular")
2412                 return createGuiTabular(*this);
2413         if (name == "tabularcreate")
2414                 return createGuiTabularCreate(*this);
2415         if (name == "texinfo")
2416                 return createGuiTexInfo(*this);
2417 #ifdef HAVE_LIBAIKSAURUS
2418         if (name == "thesaurus")
2419                 return createGuiThesaurus(*this);
2420 #endif
2421         if (name == "toc")
2422                 return createGuiToc(*this);
2423         if (name == "href")
2424                 return createGuiHyperlink(*this);
2425         if (name == "vspace")
2426                 return createGuiVSpace(*this);
2427         if (name == "wrap")
2428                 return createGuiWrap(*this);
2429         if (name == "listings")
2430                 return createGuiListings(*this);
2431
2432         return 0;
2433 }
2434
2435
2436 } // namespace frontend
2437 } // namespace lyx
2438
2439 #include "GuiView_moc.cpp"