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