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