]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
even less framing
[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                 return;
941
942         theGuiApp()->setCurrentView(this);
943         d.current_work_area_ = wa;
944         for (int i = 0; i != d.splitter_->count(); ++i) {
945                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
946                         //if (d.current_main_work_area_)
947                         //      d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
948                         d.current_main_work_area_ = wa;
949                         //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
950                         //d.current_main_work_area_->setLineWidth(2);
951                         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
952                         return;
953                 }
954         }
955         LYXERR(Debug::DEBUG, "This is not a tabbed wa");
956         on_currentWorkAreaChanged(wa);
957         BufferView & bv = wa->bufferView();
958         bv.cursor().fixIfBroken();
959         bv.updateMetrics();
960         wa->setUpdatesEnabled(true);
961         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
962 }
963
964
965 void GuiView::removeWorkArea(GuiWorkArea * wa)
966 {
967         LASSERT(wa, return);
968         if (wa == d.current_work_area_) {
969                 disconnectBuffer();
970                 disconnectBufferView();
971                 d.current_work_area_ = 0;
972                 d.current_main_work_area_ = 0;
973         }
974
975         bool found_twa = false;
976         for (int i = 0; i != d.splitter_->count(); ++i) {
977                 TabWorkArea * twa = d.tabWorkArea(i);
978                 if (twa->removeWorkArea(wa)) {
979                         // Found in this tab group, and deleted the GuiWorkArea.
980                         found_twa = true;
981                         if (twa->count() != 0) {
982                                 if (d.current_work_area_ == 0)
983                                         // This means that we are closing the current GuiWorkArea, so
984                                         // switch to the next GuiWorkArea in the found TabWorkArea.
985                                         setCurrentWorkArea(twa->currentWorkArea());
986                         } else {
987                                 // No more WorkAreas in this tab group, so delete it.
988                                 delete twa;
989                         }
990                         break;
991                 }
992         }
993
994         // It is not a tabbed work area (i.e., the search work area), so it
995         // should be deleted by other means.
996         LASSERT(found_twa, /* */);
997
998         if (d.current_work_area_ == 0) {
999                 if (d.splitter_->count() != 0) {
1000                         TabWorkArea * twa = d.currentTabWorkArea();
1001                         setCurrentWorkArea(twa->currentWorkArea());
1002                 } else {
1003                         // No more work areas, switch to the background widget.
1004                         setCurrentWorkArea(0);
1005                 }
1006         }
1007 }
1008
1009
1010 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1011 {
1012         d.layout_ = layout;
1013 }
1014
1015
1016 void GuiView::updateLayoutList()
1017 {
1018         if (d.layout_)
1019                 d.layout_->updateContents(false);
1020 }
1021
1022
1023 void GuiView::updateToolbars()
1024 {
1025         ToolbarMap::iterator end = d.toolbars_.end();
1026         if (d.current_work_area_) {
1027                 bool const math =
1028                         d.current_work_area_->bufferView().cursor().inMathed();
1029                 bool const table =
1030                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1031                 bool const review =
1032                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1033                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1034                 bool const mathmacrotemplate =
1035                         lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1036
1037                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1038                         it->second->update(math, table, review, mathmacrotemplate);
1039         } else
1040                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1041                         it->second->update(false, false, false, false);
1042 }
1043
1044
1045 Buffer * GuiView::buffer()
1046 {
1047         if (d.current_work_area_)
1048                 return &d.current_work_area_->bufferView().buffer();
1049         return 0;
1050 }
1051
1052
1053 Buffer const * GuiView::buffer() const
1054 {
1055         if (d.current_work_area_)
1056                 return &d.current_work_area_->bufferView().buffer();
1057         return 0;
1058 }
1059
1060
1061 void GuiView::setBuffer(Buffer * newBuffer)
1062 {
1063         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1064         LASSERT(newBuffer, return);
1065         setBusy(true);
1066
1067         GuiWorkArea * wa = workArea(*newBuffer);
1068         if (wa == 0) {
1069                 updateLabels(*newBuffer->masterBuffer());
1070                 wa = addWorkArea(*newBuffer);
1071         } else {
1072                 //Disconnect the old buffer...there's no new one.
1073                 disconnectBuffer();
1074         }
1075         connectBuffer(*newBuffer);
1076         connectBufferView(wa->bufferView());
1077         setCurrentWorkArea(wa);
1078
1079         setBusy(false);
1080 }
1081
1082
1083 void GuiView::connectBuffer(Buffer & buf)
1084 {
1085         buf.setGuiDelegate(this);
1086 }
1087
1088
1089 void GuiView::disconnectBuffer()
1090 {
1091         if (d.current_work_area_)
1092                 d.current_work_area_->bufferView().setGuiDelegate(0);
1093 }
1094
1095
1096 void GuiView::connectBufferView(BufferView & bv)
1097 {
1098         bv.setGuiDelegate(this);
1099 }
1100
1101
1102 void GuiView::disconnectBufferView()
1103 {
1104         if (d.current_work_area_)
1105                 d.current_work_area_->bufferView().setGuiDelegate(0);
1106 }
1107
1108
1109 void GuiView::errors(string const & error_type)
1110 {
1111         ErrorList & el = buffer()->errorList(error_type);
1112         if (!el.empty())
1113                 showDialog("errorlist", error_type);
1114 }
1115
1116
1117 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1118 {
1119         d.toc_models_.updateItem(toqstr(type), dit);
1120 }
1121
1122
1123 void GuiView::structureChanged()
1124 {
1125         d.toc_models_.reset(view());
1126         // Navigator needs more than a simple update in this case. It needs to be
1127         // rebuilt.
1128         updateDialog("toc", "");
1129 }
1130
1131
1132 void GuiView::updateDialog(string const & name, string const & data)
1133 {
1134         if (!isDialogVisible(name))
1135                 return;
1136
1137         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1138         if (it == d.dialogs_.end())
1139                 return;
1140
1141         Dialog * const dialog = it->second.get();
1142         if (dialog->isVisibleView())
1143                 dialog->initialiseParams(data);
1144 }
1145
1146
1147 BufferView * GuiView::view()
1148 {
1149         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1150 }
1151
1152
1153 void GuiView::autoSave()
1154 {
1155         LYXERR(Debug::INFO, "Running autoSave()");
1156
1157         if (buffer())
1158                 view()->buffer().autoSave();
1159 }
1160
1161
1162 void GuiView::resetAutosaveTimers()
1163 {
1164         if (lyxrc.autosave)
1165                 d.autosave_timeout_.restart();
1166 }
1167
1168
1169 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1170 {
1171         bool enable = true;
1172         Buffer * buf = buffer();
1173
1174         /* In LyX/Mac, when a dialog is open, the menus of the
1175            application can still be accessed without giving focus to
1176            the main window. In this case, we want to disable the menu
1177            entries that are buffer-related.
1178
1179            Note that this code is not perfect, as bug 1941 attests:
1180            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1181         */
1182         if (cmd.origin == FuncRequest::MENU && !hasFocus())
1183                 buf = 0;
1184
1185         switch(cmd.action) {
1186         case LFUN_BUFFER_WRITE:
1187                 enable = buf && (buf->isUnnamed() || !buf->isClean());
1188                 break;
1189
1190         case LFUN_BUFFER_WRITE_AS:
1191                 enable = buf;
1192                 break;
1193
1194         case LFUN_SPLIT_VIEW:
1195                 if (cmd.getArg(0) == "vertical")
1196                         enable = buf && (d.splitter_->count() == 1 ||
1197                                          d.splitter_->orientation() == Qt::Vertical);
1198                 else
1199                         enable = buf && (d.splitter_->count() == 1 ||
1200                                          d.splitter_->orientation() == Qt::Horizontal);
1201                 break;
1202
1203         case LFUN_CLOSE_TAB_GROUP:
1204                 enable = d.currentTabWorkArea();
1205                 break;
1206
1207         case LFUN_TOOLBAR_TOGGLE:
1208                 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1209                         flag.setOnOff(t->isVisible());
1210                 break;
1211
1212         case LFUN_UI_TOGGLE:
1213                 flag.setOnOff(isFullScreen());
1214                 break;
1215
1216         case LFUN_DIALOG_TOGGLE:
1217                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1218                 // fall through to set "enable"
1219         case LFUN_DIALOG_SHOW: {
1220                 string const name = cmd.getArg(0);
1221                 if (!buf)
1222                         enable = name == "aboutlyx"
1223                                 || name == "file" //FIXME: should be removed.
1224                                 || name == "prefs"
1225                                 || name == "texinfo";
1226                 else if (name == "print")
1227                         enable = buf->isExportable("dvi")
1228                                 && lyxrc.print_command != "none";
1229                 else if (name == "character") {
1230                         if (!view())
1231                                 enable = false;
1232                         else {
1233                                 InsetCode ic = view()->cursor().inset().lyxCode();
1234                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1235                         }
1236                 }
1237                 else if (name == "symbols") {
1238                         if (!view() || view()->cursor().inMathed())
1239                                 enable = false;
1240                         else {
1241                                 InsetCode ic = view()->cursor().inset().lyxCode();
1242                                 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1243                         }
1244                 }
1245                 else if (name == "latexlog")
1246                         enable = FileName(buf->logName()).isReadableFile();
1247                 else if (name == "spellchecker")
1248 #if defined (USE_ASPELL)
1249                         enable = !buf->isReadonly();
1250 #else
1251                         enable = false;
1252 #endif
1253                 else if (name == "vclog")
1254                         enable = buf->lyxvc().inUse();
1255                 break;
1256         }
1257
1258         case LFUN_DIALOG_UPDATE: {
1259                 string const name = cmd.getArg(0);
1260                 if (!buf)
1261                         enable = name == "prefs";
1262                 break;
1263         }
1264
1265         case LFUN_INSET_APPLY: {
1266                 string const name = cmd.getArg(0);
1267                 Inset * inset = getOpenInset(name);
1268                 if (inset) {
1269                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1270                         FuncStatus fs;
1271                         if (!inset->getStatus(view()->cursor(), fr, fs)) {
1272                                 // Every inset is supposed to handle this
1273                                 LASSERT(false, break);
1274                         }
1275                         flag |= fs;
1276                 } else {
1277                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1278                         flag |= lyx::getStatus(fr);
1279                 }
1280                 enable = flag.enabled();
1281                 break;
1282         }
1283
1284         case LFUN_COMPLETION_INLINE:
1285                 if (!d.current_work_area_
1286                     || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1287                     enable = false;
1288                 break;
1289
1290         case LFUN_COMPLETION_POPUP:
1291                 if (!d.current_work_area_
1292                     || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1293                     enable = false;
1294                 break;
1295
1296         case LFUN_COMPLETION_COMPLETE:
1297                 if (!d.current_work_area_
1298                         || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1299                     enable = false;
1300                 break;
1301
1302         case LFUN_COMPLETION_ACCEPT:
1303         case LFUN_COMPLETION_CANCEL:
1304                 if (!d.current_work_area_
1305                     || (!d.current_work_area_->completer().popupVisible()
1306                         && !d.current_work_area_->completer().inlineVisible()))
1307                         enable = false;
1308                 break;
1309
1310         default:
1311                 return false;
1312         }
1313
1314         if (!enable)
1315                 flag.setEnabled(false);
1316
1317         return true;
1318 }
1319
1320
1321 static FileName selectTemplateFile()
1322 {
1323         FileDialog dlg(qt_("Select template file"));
1324         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1325         dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1326
1327         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1328                              QStringList(qt_("LyX Documents (*.lyx)")));
1329
1330         if (result.first == FileDialog::Later)
1331                 return FileName();
1332         if (result.second.isEmpty())
1333                 return FileName();
1334         return FileName(fromqstr(result.second));
1335 }
1336
1337
1338 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1339 {
1340         setBusy(true);
1341
1342         Buffer * newBuffer = checkAndLoadLyXFile(filename);
1343
1344         if (!newBuffer) {
1345                 message(_("Document not loaded."));
1346                 setBusy(false);
1347                 return 0;
1348         }
1349         
1350         setBuffer(newBuffer);
1351
1352         // scroll to the position when the file was last closed
1353         if (lyxrc.use_lastfilepos) {
1354                 LastFilePosSection::FilePos filepos =
1355                         theSession().lastFilePos().load(filename);
1356                 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1357         }
1358
1359         if (tolastfiles)
1360                 theSession().lastFiles().add(filename);
1361
1362         setBusy(false);
1363         return newBuffer;
1364 }
1365
1366
1367 void GuiView::openDocument(string const & fname)
1368 {
1369         string initpath = lyxrc.document_path;
1370
1371         if (buffer()) {
1372                 string const trypath = buffer()->filePath();
1373                 // If directory is writeable, use this as default.
1374                 if (FileName(trypath).isDirWritable())
1375                         initpath = trypath;
1376         }
1377
1378         string filename;
1379
1380         if (fname.empty()) {
1381                 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1382                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1383                 dlg.setButton2(qt_("Examples|#E#e"),
1384                                 toqstr(addPath(package().system_support().absFilename(), "examples")));
1385
1386                 QStringList filter(qt_("LyX Documents (*.lyx)"));
1387                 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1388                         << qt_("LyX-1.4.x Documents (*.lyx14)")
1389                         << qt_("LyX-1.5.x Documents (*.lyx15)");
1390                 FileDialog::Result result =
1391                         dlg.open(toqstr(initpath), filter);
1392
1393                 if (result.first == FileDialog::Later)
1394                         return;
1395
1396                 filename = fromqstr(result.second);
1397
1398                 // check selected filename
1399                 if (filename.empty()) {
1400                         message(_("Canceled."));
1401                         return;
1402                 }
1403         } else
1404                 filename = fname;
1405
1406         // get absolute path of file and add ".lyx" to the filename if
1407         // necessary. 
1408         FileName const fullname = 
1409                         fileSearch(string(), filename, "lyx", support::may_not_exist);
1410         if (!fullname.empty())
1411                 filename = fullname.absFilename();
1412
1413         if (!fullname.onlyPath().isDirectory()) {
1414                 Alert::warning(_("Invalid filename"),
1415                                 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1416                                 from_utf8(fullname.absFilename())));
1417                 return;
1418         }
1419         // if the file doesn't exist, let the user create one
1420         if (!fullname.exists()) {
1421                 // the user specifically chose this name. Believe him.
1422                 Buffer * const b = newFile(filename, string(), true);
1423                 if (b)
1424                         setBuffer(b);
1425                 return;
1426         }
1427
1428         docstring const disp_fn = makeDisplayPath(filename);
1429         message(bformat(_("Opening document %1$s..."), disp_fn));
1430
1431         docstring str2;
1432         Buffer * buf = loadDocument(fullname);
1433         if (buf) {
1434                 updateLabels(*buf);
1435                 
1436                 setBuffer(buf);
1437                 buf->errors("Parse");
1438                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1439                 if (buf->lyxvc().inUse())
1440                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1441                                 " " + _("Version control detected.");
1442         } else {
1443                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1444         }
1445         message(str2);
1446 }
1447
1448 // FIXME: clean that
1449 static bool import(GuiView * lv, FileName const & filename,
1450         string const & format, ErrorList & errorList)
1451 {
1452         FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1453
1454         string loader_format;
1455         vector<string> loaders = theConverters().loaders();
1456         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1457                 for (vector<string>::const_iterator it = loaders.begin();
1458                      it != loaders.end(); ++it) {
1459                         if (!theConverters().isReachable(format, *it))
1460                                 continue;
1461
1462                         string const tofile =
1463                                 support::changeExtension(filename.absFilename(),
1464                                 formats.extension(*it));
1465                         if (!theConverters().convert(0, filename, FileName(tofile),
1466                                 filename, format, *it, errorList))
1467                                 return false;
1468                         loader_format = *it;
1469                         break;
1470                 }
1471                 if (loader_format.empty()) {
1472                         frontend::Alert::error(_("Couldn't import file"),
1473                                      bformat(_("No information for importing the format %1$s."),
1474                                          formats.prettyName(format)));
1475                         return false;
1476                 }
1477         } else
1478                 loader_format = format;
1479
1480         if (loader_format == "lyx") {
1481                 Buffer * buf = lv->loadDocument(lyxfile);
1482                 if (!buf)
1483                         return false;
1484                 updateLabels(*buf);
1485                 lv->setBuffer(buf);
1486                 buf->errors("Parse");
1487         } else {
1488                 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1489                 if (!b)
1490                         return false;
1491                 lv->setBuffer(b);
1492                 bool as_paragraphs = loader_format == "textparagraph";
1493                 string filename2 = (loader_format == format) ? filename.absFilename()
1494                         : support::changeExtension(filename.absFilename(),
1495                                           formats.extension(loader_format));
1496                 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1497                 theLyXFunc().setLyXView(lv);
1498                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1499         }
1500
1501         return true;
1502 }
1503
1504
1505 void GuiView::importDocument(string const & argument)
1506 {
1507         string format;
1508         string filename = split(argument, format, ' ');
1509
1510         LYXERR(Debug::INFO, format << " file: " << filename);
1511
1512         // need user interaction
1513         if (filename.empty()) {
1514                 string initpath = lyxrc.document_path;
1515
1516                 Buffer const * buf = buffer();
1517                 if (buf) {
1518                         string const trypath = buf->filePath();
1519                         // If directory is writeable, use this as default.
1520                         if (FileName(trypath).isDirWritable())
1521                                 initpath = trypath;
1522                 }
1523
1524                 docstring const text = bformat(_("Select %1$s file to import"),
1525                         formats.prettyName(format));
1526
1527                 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1528                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1529                 dlg.setButton2(qt_("Examples|#E#e"),
1530                         toqstr(addPath(package().system_support().absFilename(), "examples")));
1531
1532                 docstring filter = formats.prettyName(format);
1533                 filter += " (*.";
1534                 // FIXME UNICODE
1535                 filter += from_utf8(formats.extension(format));
1536                 filter += ')';
1537
1538                 FileDialog::Result result =
1539                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1540
1541                 if (result.first == FileDialog::Later)
1542                         return;
1543
1544                 filename = fromqstr(result.second);
1545
1546                 // check selected filename
1547                 if (filename.empty())
1548                         message(_("Canceled."));
1549         }
1550
1551         if (filename.empty())
1552                 return;
1553
1554         // get absolute path of file
1555         FileName const fullname(support::makeAbsPath(filename));
1556
1557         FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1558
1559         // Check if the document already is open
1560         Buffer * buf = theBufferList().getBuffer(lyxfile);
1561         if (buf) {
1562                 setBuffer(buf);
1563                 if (!closeBuffer()) {
1564                         message(_("Canceled."));
1565                         return;
1566                 }
1567         }
1568
1569         docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1570
1571         // if the file exists already, and we didn't do
1572         // -i lyx thefile.lyx, warn
1573         if (lyxfile.exists() && fullname != lyxfile) {
1574
1575                 docstring text = bformat(_("The document %1$s already exists.\n\n"
1576                         "Do you want to overwrite that document?"), displaypath);
1577                 int const ret = Alert::prompt(_("Overwrite document?"),
1578                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
1579
1580                 if (ret == 1) {
1581                         message(_("Canceled."));
1582                         return;
1583                 }
1584         }
1585
1586         message(bformat(_("Importing %1$s..."), displaypath));
1587         ErrorList errorList;
1588         if (import(this, fullname, format, errorList))
1589                 message(_("imported."));
1590         else
1591                 message(_("file not imported!"));
1592
1593         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1594 }
1595
1596
1597 void GuiView::newDocument(string const & filename, bool from_template)
1598 {
1599         FileName initpath(lyxrc.document_path);
1600         Buffer * buf = buffer();
1601         if (buf) {
1602                 FileName const trypath(buf->filePath());
1603                 // If directory is writeable, use this as default.
1604                 if (trypath.isDirWritable())
1605                         initpath = trypath;
1606         }
1607
1608         string templatefile = from_template ?
1609                 selectTemplateFile().absFilename() : string();
1610         Buffer * b;
1611         if (filename.empty())
1612                 b = newUnnamedFile(templatefile, initpath);
1613         else
1614                 b = newFile(filename, templatefile, true);
1615
1616         if (b)
1617                 setBuffer(b);
1618         // Ensure the cursor is correctly positionned on screen.
1619         view()->showCursor();
1620 }
1621
1622
1623 void GuiView::insertLyXFile(docstring const & fname)
1624 {
1625         BufferView * bv = view();
1626         if (!bv)
1627                 return;
1628
1629         // FIXME UNICODE
1630         FileName filename(to_utf8(fname));
1631         
1632         if (!filename.empty()) {
1633                 bv->insertLyXFile(filename);
1634                 return;
1635         }
1636
1637         // Launch a file browser
1638         // FIXME UNICODE
1639         string initpath = lyxrc.document_path;
1640         string const trypath = bv->buffer().filePath();
1641         // If directory is writeable, use this as default.
1642         if (FileName(trypath).isDirWritable())
1643                 initpath = trypath;
1644
1645         // FIXME UNICODE
1646         FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1647         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1648         dlg.setButton2(qt_("Examples|#E#e"),
1649                 toqstr(addPath(package().system_support().absFilename(),
1650                 "examples")));
1651
1652         FileDialog::Result result = dlg.open(toqstr(initpath),
1653                              QStringList(qt_("LyX Documents (*.lyx)")));
1654
1655         if (result.first == FileDialog::Later)
1656                 return;
1657
1658         // FIXME UNICODE
1659         filename.set(fromqstr(result.second));
1660
1661         // check selected filename
1662         if (filename.empty()) {
1663                 // emit message signal.
1664                 message(_("Canceled."));
1665                 return;
1666         }
1667
1668         bv->insertLyXFile(filename);
1669 }
1670
1671
1672 void GuiView::insertPlaintextFile(docstring const & fname,
1673         bool asParagraph)
1674 {
1675         BufferView * bv = view();
1676         if (!bv)
1677                 return;
1678
1679         // FIXME UNICODE
1680         FileName filename(to_utf8(fname));
1681         
1682         if (!filename.empty()) {
1683                 bv->insertPlaintextFile(filename, asParagraph);
1684                 return;
1685         }
1686
1687         FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1688                 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1689
1690         FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1691                 QStringList());
1692
1693         if (result.first == FileDialog::Later)
1694                 return;
1695
1696         // FIXME UNICODE
1697         filename.set(fromqstr(result.second));
1698
1699         // check selected filename
1700         if (filename.empty()) {
1701                 // emit message signal.
1702                 message(_("Canceled."));
1703                 return;
1704         }
1705
1706         bv->insertPlaintextFile(filename, asParagraph);
1707 }
1708
1709
1710 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1711 {
1712         FileName fname = b.fileName();
1713         FileName const oldname = fname;
1714
1715         if (!newname.empty()) {
1716                 // FIXME UNICODE
1717                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1718         } else {
1719                 // Switch to this Buffer.
1720                 setBuffer(&b);
1721
1722                 /// No argument? Ask user through dialog.
1723                 // FIXME UNICODE
1724                 FileDialog dlg(qt_("Choose a filename to save document as"),
1725                                    LFUN_BUFFER_WRITE_AS);
1726                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1727                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1728
1729                 if (!isLyXFilename(fname.absFilename()))
1730                         fname.changeExtension(".lyx");
1731
1732                 FileDialog::Result result =
1733                         dlg.save(toqstr(fname.onlyPath().absFilename()),
1734                                QStringList(qt_("LyX Documents (*.lyx)")),
1735                                      toqstr(fname.onlyFileName()));
1736
1737                 if (result.first == FileDialog::Later)
1738                         return false;
1739
1740                 fname.set(fromqstr(result.second));
1741
1742                 if (fname.empty())
1743                         return false;
1744
1745                 if (!isLyXFilename(fname.absFilename()))
1746                         fname.changeExtension(".lyx");
1747         }
1748
1749         if (FileName(fname).exists()) {
1750                 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1751                 docstring text = bformat(_("The document %1$s already "
1752                                            "exists.\n\nDo you want to "
1753                                            "overwrite that document?"), 
1754                                          file);
1755                 int const ret = Alert::prompt(_("Overwrite document?"),
1756                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1757                 switch (ret) {
1758                 case 0: break;
1759                 case 1: return renameBuffer(b, docstring());
1760                 case 2: return false;
1761                 }
1762         }
1763
1764         // Ok, change the name of the buffer
1765         b.setFileName(fname.absFilename());
1766         b.markDirty();
1767         bool unnamed = b.isUnnamed();
1768         b.setUnnamed(false);
1769         b.saveCheckSum(fname);
1770
1771         if (!saveBuffer(b)) {
1772                 b.setFileName(oldname.absFilename());
1773                 b.setUnnamed(unnamed);
1774                 b.saveCheckSum(oldname);
1775                 return false;
1776         }
1777
1778         return true;
1779 }
1780
1781
1782 bool GuiView::saveBuffer(Buffer & b)
1783 {
1784         if (workArea(b) && workArea(b)->inDialogMode())
1785                 return true;
1786
1787         if (b.isUnnamed())
1788                 return renameBuffer(b, docstring());
1789
1790         if (b.save()) {
1791                 theSession().lastFiles().add(b.fileName());
1792                 return true;
1793         }
1794
1795         // Switch to this Buffer.
1796         setBuffer(&b);
1797
1798         // FIXME: we don't tell the user *WHY* the save failed !!
1799         docstring const file = makeDisplayPath(b.absFileName(), 30);
1800         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1801                                    "Do you want to rename the document and "
1802                                    "try again?"), file);
1803         int const ret = Alert::prompt(_("Rename and save?"),
1804                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1805         switch (ret) {
1806         case 0:
1807                 if (!renameBuffer(b, docstring()))
1808                         return false;
1809                 break;
1810         case 1:
1811                 break;
1812         case 2:
1813                 return false;
1814         }
1815
1816         return saveBuffer(b);
1817 }
1818
1819
1820 bool GuiView::closeBuffer()
1821 {
1822         Buffer * buf = buffer();
1823         return buf && closeBuffer(*buf);
1824 }
1825
1826
1827 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1828 {
1829         // goto bookmark to update bookmark pit.
1830         //FIXME: we should update only the bookmarks related to this buffer!
1831         LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1832         for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1833                 theLyXFunc().gotoBookmark(i+1, false, false);
1834
1835         if (buf.isClean() || buf.paragraphs().empty()) {
1836                 if (buf.masterBuffer() == &buf && tolastopened)
1837                         theSession().lastOpened().add(buf.fileName());
1838                 if (buf.parent())
1839                         // Don't close child documents.
1840                         removeWorkArea(currentMainWorkArea());
1841                 else
1842                         theBufferList().release(&buf);
1843                 return true;
1844         }
1845         // Switch to this Buffer.
1846         setBuffer(&buf);
1847
1848         docstring file;
1849         // FIXME: Unicode?
1850         if (buf.isUnnamed())
1851                 file = from_utf8(buf.fileName().onlyFileName());
1852         else
1853                 file = buf.fileName().displayName(30);
1854
1855         // Bring this window to top before asking questions.
1856         raise();
1857         activateWindow();
1858
1859         docstring const text = bformat(_("The document %1$s has unsaved changes."
1860                 "\n\nDo you want to save the document or discard the changes?"), file);
1861         int const ret = Alert::prompt(_("Save changed document?"),
1862                 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1863
1864         switch (ret) {
1865         case 0:
1866                 if (!saveBuffer(buf))
1867                         return false;
1868                 break;
1869         case 1:
1870                 // if we crash after this we could
1871                 // have no autosave file but I guess
1872                 // this is really improbable (Jug)
1873                 removeAutosaveFile(buf.absFileName());
1874                 break;
1875         case 2:
1876                 return false;
1877         }
1878
1879         // save file names to .lyx/session
1880         // if master/slave are both open, do not save slave since it
1881         // will be automatically loaded when the master is loaded
1882         if (buf.masterBuffer() == &buf && tolastopened)
1883                 theSession().lastOpened().add(buf.fileName());
1884
1885         if (buf.parent())
1886                 // Don't close child documents.
1887                 removeWorkArea(currentMainWorkArea());
1888         else
1889                 theBufferList().release(&buf);
1890
1891         return true;
1892 }
1893
1894
1895 bool GuiView::dispatch(FuncRequest const & cmd)
1896 {
1897         BufferView * bv = view();
1898         // By default we won't need any update.
1899         if (bv)
1900                 bv->cursor().updateFlags(Update::None);
1901         bool dispatched = true;
1902
1903         switch(cmd.action) {
1904                 case LFUN_BUFFER_IMPORT:
1905                         importDocument(to_utf8(cmd.argument()));
1906                         break;
1907
1908                 case LFUN_BUFFER_SWITCH:
1909                         setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1910                         break;
1911
1912                 case LFUN_BUFFER_NEXT:
1913                         setBuffer(theBufferList().next(buffer()));
1914                         break;
1915
1916                 case LFUN_BUFFER_PREVIOUS:
1917                         setBuffer(theBufferList().previous(buffer()));
1918                         break;
1919
1920                 case LFUN_COMMAND_EXECUTE: {
1921                         bool const show_it = cmd.argument() != "off";
1922                         // FIXME: this is a hack, "minibuffer" should not be
1923                         // hardcoded.
1924                         if (GuiToolbar * t = toolbar("minibuffer")) {
1925                                 t->setVisible(show_it);
1926                                 if (show_it && t->commandBuffer())
1927                                         t->commandBuffer()->setFocus();
1928                         }
1929                         break;
1930                 }
1931                 case LFUN_DROP_LAYOUTS_CHOICE:
1932                         if (d.layout_)
1933                                 d.layout_->showPopup();
1934                         break;
1935
1936                 case LFUN_MENU_OPEN:
1937                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1938                                 menu->exec(QCursor::pos());
1939                         break;
1940
1941                 case LFUN_FILE_INSERT:
1942                         insertLyXFile(cmd.argument());
1943                         break;
1944                 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1945                         insertPlaintextFile(cmd.argument(), true);
1946                         break;
1947
1948                 case LFUN_FILE_INSERT_PLAINTEXT:
1949                         insertPlaintextFile(cmd.argument(), false);
1950                         break;
1951
1952                 case LFUN_BUFFER_WRITE:
1953                         if (bv)
1954                                 saveBuffer(bv->buffer());
1955                         break;
1956
1957                 case LFUN_BUFFER_WRITE_AS:
1958                         if (bv)
1959                                 renameBuffer(bv->buffer(), cmd.argument());
1960                         break;
1961
1962                 case LFUN_BUFFER_WRITE_ALL: {
1963                         Buffer * first = theBufferList().first();
1964                         if (!first)
1965                                 break;
1966                         message(_("Saving all documents..."));
1967                         // We cannot use a for loop as the buffer list cycles.
1968                         Buffer * b = first;
1969                         do {
1970                                 if (!b->isClean()) {
1971                                         saveBuffer(*b);
1972                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1973                                 }
1974                                 b = theBufferList().next(b);
1975                         } while (b != first); 
1976                         message(_("All documents saved."));
1977                         break;
1978                 }
1979
1980                 case LFUN_TOOLBAR_TOGGLE: {
1981                         string const name = cmd.getArg(0);
1982                         if (GuiToolbar * t = toolbar(name))
1983                                 t->toggle();
1984                         break;
1985                 }
1986
1987                 case LFUN_DIALOG_UPDATE: {
1988                         string const name = to_utf8(cmd.argument());
1989                         // Can only update a dialog connected to an existing inset
1990                         Inset * inset = getOpenInset(name);
1991                         if (inset) {
1992                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1993                                 inset->dispatch(view()->cursor(), fr);
1994                         } else if (name == "paragraph") {
1995                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1996                         } else if (name == "prefs") {
1997                                 updateDialog(name, string());
1998                         }
1999                         break;
2000                 }
2001
2002                 case LFUN_DIALOG_TOGGLE: {
2003                         if (isDialogVisible(cmd.getArg(0)))
2004                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2005                         else
2006                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2007                         break;
2008                 }
2009
2010                 case LFUN_DIALOG_DISCONNECT_INSET:
2011                         disconnectDialog(to_utf8(cmd.argument()));
2012                         break;
2013
2014                 case LFUN_DIALOG_HIDE: {
2015                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2016                         break;
2017                 }
2018
2019                 case LFUN_DIALOG_SHOW: {
2020                         string const name = cmd.getArg(0);
2021                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2022
2023                         if (name == "character") {
2024                                 data = freefont2string();
2025                                 if (!data.empty())
2026                                         showDialog("character", data);
2027                         } else if (name == "latexlog") {
2028                                 Buffer::LogType type; 
2029                                 string const logfile = buffer()->logName(&type);
2030                                 switch (type) {
2031                                 case Buffer::latexlog:
2032                                         data = "latex ";
2033                                         break;
2034                                 case Buffer::buildlog:
2035                                         data = "literate ";
2036                                         break;
2037                                 }
2038                                 data += Lexer::quoteString(logfile);
2039                                 showDialog("log", data);
2040                         } else if (name == "vclog") {
2041                                 string const data = "vc " +
2042                                         Lexer::quoteString(buffer()->lyxvc().getLogFile());
2043                                 showDialog("log", data);
2044                         } else if (name == "symbols") {
2045                                 data = bv->cursor().getEncoding()->name();
2046                                 if (!data.empty())
2047                                         showDialog("symbols", data);
2048                         // bug 5274
2049                         } else if (name == "prefs" && isFullScreen()) {
2050                                 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2051                                 lfunUiToggle(fr);
2052                                 showDialog("prefs", data);
2053                         } else
2054                                 showDialog(name, data);
2055                         break;
2056                 }
2057
2058                 case LFUN_INSET_APPLY: {
2059                         string const name = cmd.getArg(0);
2060                         Inset * inset = getOpenInset(name);
2061                         if (inset) {
2062                                 // put cursor in front of inset.
2063                                 if (!view()->setCursorFromInset(inset))
2064                                         LASSERT(false, break);
2065                                 
2066                                 // useful if we are called from a dialog.
2067                                 view()->cursor().beginUndoGroup();
2068                                 view()->cursor().recordUndo();
2069                                 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2070                                 inset->dispatch(view()->cursor(), fr);
2071                                 view()->cursor().endUndoGroup();
2072                         } else {
2073                                 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2074                                 lyx::dispatch(fr);
2075                         }
2076                         break;
2077                 }
2078
2079                 case LFUN_UI_TOGGLE:
2080                         lfunUiToggle(cmd);
2081                         // Make sure the keyboard focus stays in the work area.
2082                         setFocus();
2083                         break;
2084
2085                 case LFUN_SPLIT_VIEW:
2086                         if (Buffer * buf = buffer()) {
2087                                 string const orientation = cmd.getArg(0);
2088                                 d.splitter_->setOrientation(orientation == "vertical"
2089                                         ? Qt::Vertical : Qt::Horizontal);
2090                                 TabWorkArea * twa = addTabWorkArea();
2091                                 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2092                                 setCurrentWorkArea(wa);
2093                         }
2094                         break;
2095
2096                 case LFUN_CLOSE_TAB_GROUP:
2097                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
2098                                 delete twa;
2099                                 twa = d.currentTabWorkArea();
2100                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
2101                                 if (twa) {
2102                                         // Make sure the work area is up to date.
2103                                         setCurrentWorkArea(twa->currentWorkArea());
2104                                 } else {
2105                                         setCurrentWorkArea(0);
2106                                 }
2107                         }
2108                         break;
2109                         
2110                 case LFUN_COMPLETION_INLINE:
2111                         if (d.current_work_area_)
2112                                 d.current_work_area_->completer().showInline();
2113                         break;
2114
2115                 case LFUN_COMPLETION_POPUP:
2116                         if (d.current_work_area_)
2117                                 d.current_work_area_->completer().showPopup();
2118                         break;
2119
2120
2121                 case LFUN_COMPLETION_COMPLETE:
2122                         if (d.current_work_area_)
2123                                 d.current_work_area_->completer().tab();
2124                         break;
2125
2126                 case LFUN_COMPLETION_CANCEL:
2127                         if (d.current_work_area_) {
2128                                 if (d.current_work_area_->completer().popupVisible())
2129                                         d.current_work_area_->completer().hidePopup();
2130                                 else
2131                                         d.current_work_area_->completer().hideInline();
2132                         }
2133                         break;
2134
2135                 case LFUN_COMPLETION_ACCEPT:
2136                         if (d.current_work_area_)
2137                                 d.current_work_area_->completer().activate();
2138                         break;
2139
2140
2141                 default:
2142                         dispatched = false;
2143                         break;
2144         }
2145
2146         // Part of automatic menu appearance feature.
2147         if (isFullScreen()) {
2148                 if (menuBar()->isVisible())
2149                         menuBar()->hide();
2150                 if (statusBar()->isVisible())
2151                         statusBar()->hide();
2152         }
2153
2154         return dispatched;
2155 }
2156
2157
2158 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2159 {
2160         string const arg = cmd.getArg(0);
2161         if (arg == "scrollbar") {
2162                 // hide() is of no help
2163                 if (d.current_work_area_->verticalScrollBarPolicy() ==
2164                         Qt::ScrollBarAlwaysOff)
2165
2166                         d.current_work_area_->setVerticalScrollBarPolicy(
2167                                 Qt::ScrollBarAsNeeded);
2168                 else
2169                         d.current_work_area_->setVerticalScrollBarPolicy(
2170                                 Qt::ScrollBarAlwaysOff);
2171                 return;
2172         }
2173         if (arg == "statusbar") {
2174                 statusBar()->setVisible(!statusBar()->isVisible());
2175                 return;
2176         }
2177         if (arg == "menubar") {
2178                 menuBar()->setVisible(!menuBar()->isVisible());
2179                 return;
2180         }
2181 #if QT_VERSION >= 0x040300
2182         if (arg == "frame") {
2183                 int l, t, r, b;
2184                 getContentsMargins(&l, &t, &r, &b);
2185                 //are the frames in default state?
2186                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2187                 if (l == 0) {
2188                         setContentsMargins(-2, -2, -2, -2);
2189                 } else {
2190                         setContentsMargins(0, 0, 0, 0);
2191                 }
2192                 return;
2193         }
2194 #endif
2195         if (arg == "fullscreen") {
2196                 toggleFullScreen();
2197                 return;
2198         }
2199
2200         message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2201 }
2202
2203
2204 void GuiView::toggleFullScreen()
2205 {
2206         if (isFullScreen()) {
2207                 for (int i = 0; i != d.splitter_->count(); ++i)
2208                         d.tabWorkArea(i)->setFullScreen(false);
2209 #if QT_VERSION >= 0x040300
2210                 setContentsMargins(0, 0, 0, 0);
2211 #endif
2212                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2213                 restoreLayout();
2214                 menuBar()->show();
2215                 statusBar()->show();
2216         } else {
2217                 // bug 5274
2218                 hideDialogs("prefs", 0);
2219                 for (int i = 0; i != d.splitter_->count(); ++i)
2220                         d.tabWorkArea(i)->setFullScreen(true);
2221 #if QT_VERSION >= 0x040300
2222                 setContentsMargins(-2, -2, -2, -2);
2223 #endif
2224                 saveLayout();
2225                 setWindowState(windowState() ^ Qt::WindowFullScreen);
2226                 statusBar()->hide();
2227                 menuBar()->hide();
2228                 if (lyxrc.full_screen_toolbars) {
2229                         ToolbarMap::iterator end = d.toolbars_.end();
2230                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2231                                 it->second->hide();
2232                 }
2233         }
2234
2235         // give dialogs like the TOC a chance to adapt
2236         updateDialogs();
2237 }
2238
2239
2240 Buffer const * GuiView::updateInset(Inset const * inset)
2241 {
2242         if (!d.current_work_area_)
2243                 return 0;
2244
2245         if (inset)
2246                 d.current_work_area_->scheduleRedraw();
2247
2248         return &d.current_work_area_->bufferView().buffer();
2249 }
2250
2251
2252 void GuiView::restartCursor()
2253 {
2254         /* When we move around, or type, it's nice to be able to see
2255          * the cursor immediately after the keypress.
2256          */
2257         if (d.current_work_area_)
2258                 d.current_work_area_->startBlinkingCursor();
2259
2260         // Take this occasion to update the other GUI elements.
2261         updateDialogs();
2262         updateStatusBar();
2263 }
2264
2265
2266 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2267 {
2268         if (d.current_work_area_)
2269                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2270 }
2271
2272 namespace {
2273
2274 // This list should be kept in sync with the list of insets in
2275 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
2276 // dialog should have the same name as the inset.
2277 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2278 // docs in LyXAction.cpp.
2279
2280 char const * const dialognames[] = {
2281 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2282 "citation", "document", "errorlist", "ert", "external", "file",
2283 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2284 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print", 
2285 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2286
2287 #ifdef HAVE_LIBAIKSAURUS
2288 "thesaurus",
2289 #endif
2290
2291 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2292
2293 char const * const * const end_dialognames =
2294         dialognames + (sizeof(dialognames) / sizeof(char *));
2295
2296 class cmpCStr {
2297 public:
2298         cmpCStr(char const * name) : name_(name) {}
2299         bool operator()(char const * other) {
2300                 return strcmp(other, name_) == 0;
2301         }
2302 private:
2303         char const * name_;
2304 };
2305
2306
2307 bool isValidName(string const & name)
2308 {
2309         return find_if(dialognames, end_dialognames,
2310                             cmpCStr(name.c_str())) != end_dialognames;
2311 }
2312
2313 } // namespace anon
2314
2315
2316 void GuiView::resetDialogs()
2317 {
2318         // Make sure that no LFUN uses any LyXView.
2319         theLyXFunc().setLyXView(0);
2320         saveLayout();
2321         menuBar()->clear();
2322         constructToolbars();
2323         guiApp->menus().fillMenuBar(menuBar(), this, false);
2324         if (d.layout_)
2325                 d.layout_->updateContents(true);
2326         // Now update controls with current buffer.
2327         theLyXFunc().setLyXView(this);
2328         restoreLayout();
2329         restartCursor();
2330 }
2331
2332
2333 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2334 {
2335         if (!isValidName(name))
2336                 return 0;
2337
2338         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2339
2340         if (it != d.dialogs_.end()) {
2341                 if (hide_it)
2342                         it->second->hideView();
2343                 return it->second.get();
2344         }
2345
2346         Dialog * dialog = build(name);
2347         d.dialogs_[name].reset(dialog);
2348         if (lyxrc.allow_geometry_session)
2349                 dialog->restoreSession();
2350         if (hide_it)
2351                 dialog->hideView();
2352         return dialog;
2353 }
2354
2355
2356 void GuiView::showDialog(string const & name, string const & data,
2357         Inset * inset)
2358 {
2359         if (d.in_show_)
2360                 return;
2361
2362         d.in_show_ = true;
2363         try {
2364                 Dialog * dialog = findOrBuild(name, false);
2365                 if (dialog) {
2366                         dialog->showData(data);
2367                         if (inset)
2368                                 d.open_insets_[name] = inset;
2369                 }
2370         }
2371         catch (ExceptionMessage const & ex) {
2372                 d.in_show_ = false;
2373                 throw ex;
2374         }
2375         d.in_show_ = false;
2376 }
2377
2378
2379 bool GuiView::isDialogVisible(string const & name) const
2380 {
2381         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2382         if (it == d.dialogs_.end())
2383                 return false;
2384         return it->second.get()->isVisibleView();
2385 }
2386
2387
2388 void GuiView::hideDialog(string const & name, Inset * inset)
2389 {
2390         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2391         if (it == d.dialogs_.end())
2392                 return;
2393
2394         if (inset && inset != getOpenInset(name))
2395                 return;
2396
2397         Dialog * const dialog = it->second.get();
2398         if (dialog->isVisibleView())
2399                 dialog->hideView();
2400         d.open_insets_[name] = 0;
2401 }
2402
2403
2404 void GuiView::disconnectDialog(string const & name)
2405 {
2406         if (!isValidName(name))
2407                 return;
2408
2409         if (d.open_insets_.find(name) != d.open_insets_.end())
2410                 d.open_insets_[name] = 0;
2411 }
2412
2413
2414 Inset * GuiView::getOpenInset(string const & name) const
2415 {
2416         if (!isValidName(name))
2417                 return 0;
2418
2419         map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2420         return it == d.open_insets_.end() ? 0 : it->second;
2421 }
2422
2423
2424 void GuiView::hideAll() const
2425 {
2426         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2427         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2428
2429         for(; it != end; ++it)
2430                 it->second->hideView();
2431 }
2432
2433
2434 void GuiView::updateDialogs()
2435 {
2436         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
2437         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2438
2439         for(; it != end; ++it) {
2440                 Dialog * dialog = it->second.get();
2441                 if (dialog && dialog->isVisibleView())
2442                         dialog->checkStatus();
2443         }
2444         updateToolbars();
2445         updateLayoutList();
2446 }
2447
2448
2449 // will be replaced by a proper factory...
2450 Dialog * createGuiAbout(GuiView & lv);
2451 Dialog * createGuiBibitem(GuiView & lv);
2452 Dialog * createGuiBibtex(GuiView & lv);
2453 Dialog * createGuiBox(GuiView & lv);
2454 Dialog * createGuiBranch(GuiView & lv);
2455 Dialog * createGuiChanges(GuiView & lv);
2456 Dialog * createGuiCharacter(GuiView & lv);
2457 Dialog * createGuiCitation(GuiView & lv);
2458 Dialog * createGuiDelimiter(GuiView & lv);
2459 Dialog * createGuiDocument(GuiView & lv);
2460 Dialog * createGuiErrorList(GuiView & lv);
2461 Dialog * createGuiERT(GuiView & lv);
2462 Dialog * createGuiExternal(GuiView & lv);
2463 Dialog * createGuiFloat(GuiView & lv);
2464 Dialog * createGuiGraphics(GuiView & lv);
2465 Dialog * createGuiHSpace(GuiView & lv);
2466 Dialog * createGuiInclude(GuiView & lv);
2467 Dialog * createGuiInfo(GuiView & lv);
2468 Dialog * createGuiLabel(GuiView & lv);
2469 Dialog * createGuiListings(GuiView & lv);
2470 Dialog * createGuiLog(GuiView & lv);
2471 Dialog * createGuiMathMatrix(GuiView & lv);
2472 Dialog * createGuiNomenclature(GuiView & lv);
2473 Dialog * createGuiNote(GuiView & lv);
2474 Dialog * createGuiParagraph(GuiView & lv);
2475 Dialog * createGuiPreferences(GuiView & lv);
2476 Dialog * createGuiPrint(GuiView & lv);
2477 Dialog * createGuiRef(GuiView & lv);
2478 Dialog * createGuiSearch(GuiView & lv);
2479 Dialog * createGuiSearchAdv(GuiView & lv);
2480 Dialog * createGuiSendTo(GuiView & lv);
2481 Dialog * createGuiShowFile(GuiView & lv);
2482 Dialog * createGuiSpellchecker(GuiView & lv);
2483 Dialog * createGuiSymbols(GuiView & lv);
2484 Dialog * createGuiTabularCreate(GuiView & lv);
2485 Dialog * createGuiTabular(GuiView & lv);
2486 Dialog * createGuiTexInfo(GuiView & lv);
2487 Dialog * createGuiToc(GuiView & lv);
2488 Dialog * createGuiThesaurus(GuiView & lv);
2489 Dialog * createGuiHyperlink(GuiView & lv);
2490 Dialog * createGuiVSpace(GuiView & lv);
2491 Dialog * createGuiViewSource(GuiView & lv);
2492 Dialog * createGuiWrap(GuiView & lv);
2493
2494
2495 Dialog * GuiView::build(string const & name)
2496 {
2497         LASSERT(isValidName(name), return 0);
2498
2499         if (name == "aboutlyx")
2500                 return createGuiAbout(*this);
2501         if (name == "bibitem")
2502                 return createGuiBibitem(*this);
2503         if (name == "bibtex")
2504                 return createGuiBibtex(*this);
2505         if (name == "box")
2506                 return createGuiBox(*this);
2507         if (name == "branch")
2508                 return createGuiBranch(*this);
2509         if (name == "changes")
2510                 return createGuiChanges(*this);
2511         if (name == "character")
2512                 return createGuiCharacter(*this);
2513         if (name == "citation")
2514                 return createGuiCitation(*this);
2515         if (name == "document")
2516                 return createGuiDocument(*this);
2517         if (name == "errorlist")
2518                 return createGuiErrorList(*this);
2519         if (name == "ert")
2520                 return createGuiERT(*this);
2521         if (name == "external")
2522                 return createGuiExternal(*this);
2523         if (name == "file")
2524                 return createGuiShowFile(*this);
2525         if (name == "findreplace")
2526                 return createGuiSearch(*this);
2527         if (name == "findreplaceadv")
2528                 return createGuiSearchAdv(*this);
2529         if (name == "float")
2530                 return createGuiFloat(*this);
2531         if (name == "graphics")
2532                 return createGuiGraphics(*this);
2533         if (name == "include")
2534                 return createGuiInclude(*this);
2535         if (name == "info")
2536                 return createGuiInfo(*this);
2537         if (name == "nomenclature")
2538                 return createGuiNomenclature(*this);
2539         if (name == "label")
2540                 return createGuiLabel(*this);
2541         if (name == "log")
2542                 return createGuiLog(*this);
2543         if (name == "view-source")
2544                 return createGuiViewSource(*this);
2545         if (name == "mathdelimiter")
2546                 return createGuiDelimiter(*this);
2547         if (name == "mathmatrix")
2548                 return createGuiMathMatrix(*this);
2549         if (name == "note")
2550                 return createGuiNote(*this);
2551         if (name == "paragraph")
2552                 return createGuiParagraph(*this);
2553         if (name == "prefs")
2554                 return createGuiPreferences(*this);
2555         if (name == "print")
2556                 return createGuiPrint(*this);
2557         if (name == "ref")
2558                 return createGuiRef(*this);
2559         if (name == "sendto")
2560                 return createGuiSendTo(*this);
2561         if (name == "space")
2562                 return createGuiHSpace(*this);
2563         if (name == "spellchecker")
2564                 return createGuiSpellchecker(*this);
2565         if (name == "symbols")
2566                 return createGuiSymbols(*this);
2567         if (name == "tabular")
2568                 return createGuiTabular(*this);
2569         if (name == "tabularcreate")
2570                 return createGuiTabularCreate(*this);
2571         if (name == "texinfo")
2572                 return createGuiTexInfo(*this);
2573 #ifdef HAVE_LIBAIKSAURUS
2574         if (name == "thesaurus")
2575                 return createGuiThesaurus(*this);
2576 #endif
2577         if (name == "toc")
2578                 return createGuiToc(*this);
2579         if (name == "href")
2580                 return createGuiHyperlink(*this);
2581         if (name == "vspace")
2582                 return createGuiVSpace(*this);
2583         if (name == "wrap")
2584                 return createGuiWrap(*this);
2585         if (name == "listings")
2586                 return createGuiListings(*this);
2587
2588         return 0;
2589 }
2590
2591
2592 } // namespace frontend
2593 } // namespace lyx
2594
2595 #include "moc_GuiView.cpp"