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