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