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