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