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