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