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