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