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