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