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