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