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