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