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