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