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