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