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