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