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