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