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