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