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