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