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