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
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
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"
29 #include "qt_helpers.h"
31 #include "frontends/alert.h"
33 #include "buffer_funcs.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
41 #include "ErrorList.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
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"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
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");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
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()));
130 pain.drawText(190, 225, text);
133 void paintEvent(QPaintEvent *)
135 int x = (width() - splash_.width()) / 2;
136 int y = (height() - splash_.height()) / 2;
138 pain.drawPixmap(x, y, splash_);
145 /// Toolbar store providing access to individual toolbars by name.
146 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
148 typedef boost::shared_ptr<Dialog> DialogPtr;
153 struct GuiView::GuiViewPrivate
156 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
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
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_);
176 delete stack_widget_;
179 QMenu * toolBarPopup(GuiView * parent)
181 // FIXME: translation
182 QMenu * menu = new QMenu(parent);
183 QActionGroup * iconSizeGroup = new QActionGroup(parent);
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);
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);
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);
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);
219 stack_widget_->setCurrentWidget(bg_widget_);
220 bg_widget_->setUpdatesEnabled(true);
223 TabWorkArea * tabWorkArea(int i)
225 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
228 TabWorkArea * currentTabWorkArea()
230 if (splitter_->count() == 1)
231 // The first TabWorkArea is always the first one, if any.
232 return tabWorkArea(0);
234 for (int i = 0; i != splitter_->count(); ++i) {
235 TabWorkArea * twa = tabWorkArea(i);
236 if (current_work_area_ == twa->currentWorkArea())
240 // None has the focus so we just take the first one.
241 return tabWorkArea(0);
245 GuiWorkArea * current_work_area_;
246 QSplitter * splitter_;
247 QStackedWidget * stack_widget_;
248 BackgroundWidget * bg_widget_;
250 ToolbarMap toolbars_;
251 /// The main layout box.
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.
257 * FIXME: replace that with a proper model so that we are not limited
258 * to only one dialog.
260 GuiLayoutBox * layout_;
263 map<string, Inset *> open_insets_;
266 map<string, DialogPtr> dialogs_;
268 unsigned int smallIconSize;
269 unsigned int normalIconSize;
270 unsigned int bigIconSize;
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
279 TocModels toc_models_;
283 GuiView::GuiView(int id)
284 : d(*new GuiViewPrivate), id_(id), closing_(false)
286 // GuiToolbars *must* be initialised before the menu bar.
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);
294 // Fill up the menu bar.
295 guiApp->menus().fillMenuBar(menuBar(), this, true);
297 setCentralWidget(d.stack_widget_);
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();
305 connect(&d.statusbar_timer_, SIGNAL(timeout()),
306 this, SLOT(clearMessage()));
308 // We don't want to keep the window in memory if it is closed.
309 setAttribute(Qt::WA_DeleteOnClose, true);
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"));
318 setAcceptDrops(true);
320 statusBar()->setSizeGripEnabled(true);
322 // Forbid too small unresizable window because it can happen
323 // with some window manager under X11.
324 setMinimumSize(300, 200);
326 if (lyxrc.allow_geometry_session) {
327 // Now take care of session management.
332 // No session handling, default to a sane size.
333 setGeometry(50, 50, 690, 510);
335 // This enables to clear session data if any.
337 settings.remove("views");
347 void GuiView::saveLayout() const
350 settings.beginGroup("views");
351 settings.beginGroup(QString::number(id_));
353 settings.setValue("pos", pos());
354 settings.setValue("size", size());
356 settings.setValue("geometry", saveGeometry());
358 settings.setValue("layout", saveState(0));
359 settings.setValue("icon_size", iconSize());
363 bool GuiView::restoreLayout()
366 settings.beginGroup("views");
367 settings.beginGroup(QString::number(id_));
368 QString const icon_key = "icon_size";
369 if (!settings.contains(icon_key))
372 setIconSize(settings.value(icon_key).toSize());
374 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
375 QSize size = settings.value("size", QSize(690, 510)).toSize();
379 if (!restoreGeometry(settings.value("geometry").toByteArray()))
380 setGeometry(50, 50, 690, 510);
382 // Make sure layout is correctly oriented.
383 setLayoutDirection(qApp->layoutDirection());
385 // Allow the toc and view-source dock widget to be restored if needed.
387 if ((tmp = findOrBuild("toc", true)))
389 if ((tmp = findOrBuild("view-source", true)))
392 if (!restoreState(settings.value("layout").toByteArray(), 0))
399 GuiToolbar * GuiView::toolbar(string const & name)
401 ToolbarMap::iterator it = d.toolbars_.find(name);
402 if (it != d.toolbars_.end())
405 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
406 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
411 void GuiView::constructToolbars()
413 ToolbarMap::iterator it = d.toolbars_.begin();
414 for (; it != d.toolbars_.end(); ++it)
419 // extracts the toolbars from the backend
420 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
421 Toolbars::Infos::iterator end = guiApp->toolbars().end();
422 for (; cit != end; ++cit)
423 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
427 void GuiView::initToolbars()
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 GuiToolbar * tb = toolbar(cit->name);
436 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
438 tb->setVisible(false);
439 tb->setVisibility(visibility);
441 if (visibility & Toolbars::TOP) {
443 addToolBarBreak(Qt::TopToolBarArea);
444 addToolBar(Qt::TopToolBarArea, tb);
447 if (visibility & Toolbars::BOTTOM) {
448 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
449 #if (QT_VERSION >= 0x040202)
450 addToolBarBreak(Qt::BottomToolBarArea);
452 addToolBar(Qt::BottomToolBarArea, tb);
455 if (visibility & Toolbars::LEFT) {
456 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
457 #if (QT_VERSION >= 0x040202)
458 addToolBarBreak(Qt::LeftToolBarArea);
460 addToolBar(Qt::LeftToolBarArea, tb);
463 if (visibility & Toolbars::RIGHT) {
464 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
465 #if (QT_VERSION >= 0x040202)
466 addToolBarBreak(Qt::RightToolBarArea);
468 addToolBar(Qt::RightToolBarArea, tb);
471 if (visibility & Toolbars::ON)
472 tb->setVisible(true);
477 TocModels & GuiView::tocModels()
479 return d.toc_models_;
483 void GuiView::setFocus()
485 // Make sure LyXFunc points to the correct view.
486 guiApp->setCurrentView(this);
487 theLyXFunc().setLyXView(this);
488 QMainWindow::setFocus();
489 if (d.current_work_area_)
490 d.current_work_area_->setFocus();
494 QMenu * GuiView::createPopupMenu()
496 return d.toolBarPopup(this);
500 void GuiView::showEvent(QShowEvent * e)
502 LYXERR(Debug::GUI, "Passed Geometry "
503 << size().height() << "x" << size().width()
504 << "+" << pos().x() << "+" << pos().y());
506 if (d.splitter_->count() == 0)
507 // No work area, switch to the background widget.
510 QMainWindow::showEvent(e);
514 void GuiView::closeEvent(QCloseEvent * close_event)
518 // it can happen that this event arrives without selecting the view,
519 // e.g. when clicking the close button on a background window.
522 while (Buffer * b = buffer()) {
524 // This is a child document, just close the tab after saving
525 // but keep the file loaded.
526 if (!saveBuffer(*b)) {
528 close_event->ignore();
531 removeWorkArea(d.current_work_area_);
535 QList<int> const ids = guiApp->viewIds();
536 for (int i = 0; i != ids.size(); ++i) {
539 if (guiApp->view(ids[i]).workArea(*b)) {
540 // FIXME 1: should we put an alert box here that the buffer
541 // is viewed elsewhere?
542 // FIXME 2: should we try to save this buffer in any case?
545 // This buffer is also opened in another view, so
546 // close the associated work area...
547 removeWorkArea(d.current_work_area_);
548 // ... but don't close the buffer.
553 if (b && !closeBuffer(*b, true)) {
555 close_event->ignore();
560 // Make sure that nothing will use this close to be closed View.
561 guiApp->unregisterView(this);
563 if (isFullScreen()) {
564 // Switch off fullscreen before closing.
569 // Make sure the timer time out will not trigger a statusbar update.
570 d.statusbar_timer_.stop();
572 // Saving fullscreen requires additional tweaks in the toolbar code.
573 // It wouldn't also work under linux natively.
574 if (lyxrc.allow_geometry_session) {
575 // Save this window geometry and layout.
577 // Then the toolbar private states.
578 ToolbarMap::iterator end = d.toolbars_.end();
579 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
580 it->second->saveSession();
581 // Now take care of all other dialogs:
582 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
583 for (; it!= d.dialogs_.end(); ++it)
584 it->second->saveSession();
587 close_event->accept();
591 void GuiView::dragEnterEvent(QDragEnterEvent * event)
593 if (event->mimeData()->hasUrls())
595 /// \todo Ask lyx-devel is this is enough:
596 /// if (event->mimeData()->hasFormat("text/plain"))
597 /// event->acceptProposedAction();
601 void GuiView::dropEvent(QDropEvent* event)
603 QList<QUrl> files = event->mimeData()->urls();
607 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
608 for (int i = 0; i != files.size(); ++i) {
609 string const file = os::internal_path(fromqstr(
610 files.at(i).toLocalFile()));
612 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
617 void GuiView::message(docstring const & str)
619 if (ForkedProcess::iAmAChild())
622 statusBar()->showMessage(toqstr(str));
623 d.statusbar_timer_.stop();
624 d.statusbar_timer_.start(3000);
628 void GuiView::smallSizedIcons()
630 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
634 void GuiView::normalSizedIcons()
636 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
640 void GuiView::bigSizedIcons()
642 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
646 void GuiView::clearMessage()
650 theLyXFunc().setLyXView(this);
651 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
652 d.statusbar_timer_.stop();
656 void GuiView::updateWindowTitle(GuiWorkArea * wa)
658 if (wa != d.current_work_area_)
660 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
661 setWindowIconText(wa->windowIconText());
665 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
668 disconnectBufferView();
669 connectBufferView(wa->bufferView());
670 connectBuffer(wa->bufferView().buffer());
671 d.current_work_area_ = wa;
672 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
673 this, SLOT(updateWindowTitle(GuiWorkArea *)));
674 updateWindowTitle(wa);
678 // The document settings needs to be reinitialised.
679 updateDialog("document", "");
681 // Buffer-dependent dialogs must be updated. This is done here because
682 // some dialogs require buffer()->text.
687 void GuiView::on_lastWorkAreaRemoved()
690 // We already are in a close event. Nothing more to do.
693 if (d.splitter_->count() > 1)
694 // We have a splitter so don't close anything.
697 // Reset and updates the dialogs.
698 d.toc_models_.reset(0);
699 updateDialog("document", "");
702 resetWindowTitleAndIconText();
704 if (lyxrc.open_buffers_in_tabs)
705 // Nothing more to do, the window should stay open.
708 if (guiApp->viewIds().size() > 1) {
714 // On Mac we also close the last window because the application stay
715 // resident in memory. On other platforms we don't close the last
716 // window because this would quit the application.
722 void GuiView::updateStatusBar()
724 // let the user see the explicit message
725 if (d.statusbar_timer_.isActive())
728 theLyXFunc().setLyXView(this);
729 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
733 bool GuiView::hasFocus() const
735 return qApp->activeWindow() == this;
739 bool GuiView::event(QEvent * e)
743 // Useful debug code:
744 //case QEvent::ActivationChange:
745 //case QEvent::WindowDeactivate:
746 //case QEvent::Paint:
747 //case QEvent::Enter:
748 //case QEvent::Leave:
749 //case QEvent::HoverEnter:
750 //case QEvent::HoverLeave:
751 //case QEvent::HoverMove:
752 //case QEvent::StatusTip:
753 //case QEvent::DragEnter:
754 //case QEvent::DragLeave:
758 case QEvent::WindowActivate: {
759 if (this == guiApp->currentView()) {
761 return QMainWindow::event(e);
763 guiApp->setCurrentView(this);
764 theLyXFunc().setLyXView(this);
765 if (d.current_work_area_) {
766 BufferView & bv = d.current_work_area_->bufferView();
767 connectBufferView(bv);
768 connectBuffer(bv.buffer());
769 // The document structure, name and dialogs might have
770 // changed in another view.
772 // The document settings needs to be reinitialised.
773 updateDialog("document", "");
776 resetWindowTitleAndIconText();
779 return QMainWindow::event(e);
782 case QEvent::ShortcutOverride: {
786 if (isFullScreen() && menuBar()->isHidden()) {
787 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
788 // FIXME: we should also try to detect special LyX shortcut such as
789 // Alt-P and Alt-M. Right now there is a hack in
790 // GuiWorkArea::processKeySym() that hides again the menubar for
792 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
794 return QMainWindow::event(e);
798 if (d.current_work_area_)
799 // Nothing special to do.
800 return QMainWindow::event(e);
802 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
803 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
805 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
806 || ke->key() == Qt::Key_Backtab)
807 return QMainWindow::event(e);
809 // Allow processing of shortcuts that are allowed even when no Buffer
811 theLyXFunc().setLyXView(this);
813 setKeySymbol(&sym, ke);
814 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
820 return QMainWindow::event(e);
824 void GuiView::resetWindowTitleAndIconText()
826 setWindowTitle(qt_("LyX"));
827 setWindowIconText(qt_("LyX"));
830 bool GuiView::focusNextPrevChild(bool /*next*/)
837 void GuiView::setBusy(bool busy)
839 if (d.current_work_area_) {
840 d.current_work_area_->setUpdatesEnabled(!busy);
842 d.current_work_area_->stopBlinkingCursor();
844 d.current_work_area_->startBlinkingCursor();
848 QApplication::setOverrideCursor(Qt::WaitCursor);
850 QApplication::restoreOverrideCursor();
854 GuiWorkArea * GuiView::workArea(Buffer & buffer)
856 if (TabWorkArea * twa = d.currentTabWorkArea())
857 return twa->workArea(buffer);
862 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
864 // Automatically create a TabWorkArea if there are none yet.
865 TabWorkArea * tab_widget = d.splitter_->count()
866 ? d.currentTabWorkArea() : addTabWorkArea();
867 return tab_widget->addWorkArea(buffer, *this);
871 TabWorkArea * GuiView::addTabWorkArea()
873 TabWorkArea * twa = new TabWorkArea;
874 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
875 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
876 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
877 this, SLOT(on_lastWorkAreaRemoved()));
879 d.splitter_->addWidget(twa);
880 d.stack_widget_->setCurrentWidget(d.splitter_);
885 GuiWorkArea const * GuiView::currentWorkArea() const
887 return d.current_work_area_;
891 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
894 d.current_work_area_ = wa;
895 for (int i = 0; i != d.splitter_->count(); ++i) {
896 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
902 void GuiView::removeWorkArea(GuiWorkArea * wa)
905 if (wa == d.current_work_area_) {
907 disconnectBufferView();
908 d.current_work_area_ = 0;
911 for (int i = 0; i != d.splitter_->count(); ++i) {
912 TabWorkArea * twa = d.tabWorkArea(i);
913 if (!twa->removeWorkArea(wa))
914 // Not found in this tab group.
917 // We found and removed the GuiWorkArea.
919 // No more WorkAreas in this tab group, so delete it.
924 if (d.current_work_area_)
925 // This means that we are not closing the current GuiWorkArea;
928 // Switch to the next GuiWorkArea in the found TabWorkArea.
929 d.current_work_area_ = twa->currentWorkArea();
933 if (d.splitter_->count() == 0)
934 // No more work area, switch to the background widget.
939 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
945 void GuiView::updateLayoutList()
948 d.layout_->updateContents(false);
952 void GuiView::updateToolbars()
954 ToolbarMap::iterator end = d.toolbars_.end();
955 if (d.current_work_area_) {
957 d.current_work_area_->bufferView().cursor().inMathed();
959 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
961 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
962 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
963 bool const mathmacrotemplate =
964 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
966 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
967 it->second->update(math, table, review, mathmacrotemplate);
969 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
970 it->second->update(false, false, false, false);
974 Buffer * GuiView::buffer()
976 if (d.current_work_area_)
977 return &d.current_work_area_->bufferView().buffer();
982 Buffer const * GuiView::buffer() const
984 if (d.current_work_area_)
985 return &d.current_work_area_->bufferView().buffer();
990 void GuiView::setBuffer(Buffer * newBuffer)
992 LASSERT(newBuffer, return);
995 GuiWorkArea * wa = workArea(*newBuffer);
997 updateLabels(*newBuffer->masterBuffer());
998 wa = addWorkArea(*newBuffer);
1000 //Disconnect the old buffer...there's no new one.
1003 connectBuffer(*newBuffer);
1004 connectBufferView(wa->bufferView());
1005 setCurrentWorkArea(wa);
1011 void GuiView::connectBuffer(Buffer & buf)
1013 buf.setGuiDelegate(this);
1017 void GuiView::disconnectBuffer()
1019 if (d.current_work_area_)
1020 d.current_work_area_->bufferView().setGuiDelegate(0);
1024 void GuiView::connectBufferView(BufferView & bv)
1026 bv.setGuiDelegate(this);
1030 void GuiView::disconnectBufferView()
1032 if (d.current_work_area_)
1033 d.current_work_area_->bufferView().setGuiDelegate(0);
1037 void GuiView::errors(string const & error_type)
1039 ErrorList & el = buffer()->errorList(error_type);
1041 showDialog("errorlist", error_type);
1045 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1047 d.toc_models_.updateItem(toqstr(type), dit);
1051 void GuiView::structureChanged()
1053 d.toc_models_.reset(view());
1054 // Navigator needs more than a simple update in this case. It needs to be
1056 updateDialog("toc", "");
1060 void GuiView::updateDialog(string const & name, string const & data)
1062 if (!isDialogVisible(name))
1065 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1066 if (it == d.dialogs_.end())
1069 Dialog * const dialog = it->second.get();
1070 if (dialog->isVisibleView())
1071 dialog->initialiseParams(data);
1075 BufferView * GuiView::view()
1077 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1081 void GuiView::autoSave()
1083 LYXERR(Debug::INFO, "Running autoSave()");
1086 view()->buffer().autoSave();
1090 void GuiView::resetAutosaveTimers()
1093 d.autosave_timeout_.restart();
1097 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1100 Buffer * buf = buffer();
1102 /* In LyX/Mac, when a dialog is open, the menus of the
1103 application can still be accessed without giving focus to
1104 the main window. In this case, we want to disable the menu
1105 entries that are buffer-related.
1107 Note that this code is not perfect, as bug 1941 attests:
1108 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1110 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1113 switch(cmd.action) {
1114 case LFUN_BUFFER_WRITE:
1115 enable = buf && (buf->isUnnamed() || !buf->isClean());
1118 case LFUN_BUFFER_WRITE_AS:
1122 case LFUN_SPLIT_VIEW:
1123 if (cmd.getArg(0) == "vertical")
1124 enable = buf && (d.splitter_->count() == 1 ||
1125 d.splitter_->orientation() == Qt::Vertical);
1127 enable = buf && (d.splitter_->count() == 1 ||
1128 d.splitter_->orientation() == Qt::Horizontal);
1131 case LFUN_CLOSE_TAB_GROUP:
1132 enable = d.currentTabWorkArea();
1135 case LFUN_TOOLBAR_TOGGLE:
1136 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1137 flag.setOnOff(t->isVisible());
1140 case LFUN_UI_TOGGLE:
1141 flag.setOnOff(isFullScreen());
1144 case LFUN_DIALOG_TOGGLE:
1145 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1146 // fall through to set "enable"
1147 case LFUN_DIALOG_SHOW: {
1148 string const name = cmd.getArg(0);
1150 enable = name == "aboutlyx"
1151 || name == "file" //FIXME: should be removed.
1153 || name == "texinfo";
1154 else if (name == "print")
1155 enable = buf->isExportable("dvi")
1156 && lyxrc.print_command != "none";
1157 else if (name == "character") {
1161 InsetCode ic = view()->cursor().inset().lyxCode();
1162 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1165 else if (name == "symbols") {
1166 if (!view() || view()->cursor().inMathed())
1169 InsetCode ic = view()->cursor().inset().lyxCode();
1170 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1173 else if (name == "latexlog")
1174 enable = FileName(buf->logName()).isReadableFile();
1175 else if (name == "spellchecker")
1176 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1177 enable = !buf->isReadonly();
1181 else if (name == "vclog")
1182 enable = buf->lyxvc().inUse();
1186 case LFUN_DIALOG_UPDATE: {
1187 string const name = cmd.getArg(0);
1189 enable = name == "prefs";
1193 case LFUN_INSET_APPLY: {
1194 string const name = cmd.getArg(0);
1195 Inset * inset = getOpenInset(name);
1197 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1199 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1200 // Every inset is supposed to handle this
1201 LASSERT(false, break);
1205 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1206 flag |= lyx::getStatus(fr);
1208 enable = flag.enabled();
1212 case LFUN_COMPLETION_INLINE:
1213 if (!d.current_work_area_
1214 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1218 case LFUN_COMPLETION_POPUP:
1219 if (!d.current_work_area_
1220 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1224 case LFUN_COMPLETION_COMPLETE:
1225 if (!d.current_work_area_
1226 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1230 case LFUN_COMPLETION_ACCEPT:
1231 case LFUN_COMPLETION_CANCEL:
1232 if (!d.current_work_area_
1233 || (!d.current_work_area_->completer().popupVisible()
1234 && !d.current_work_area_->completer().inlineVisible()))
1243 flag.setEnabled(false);
1249 static FileName selectTemplateFile()
1251 FileDialog dlg(qt_("Select template file"));
1252 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1253 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1255 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1256 QStringList(qt_("LyX Documents (*.lyx)")));
1258 if (result.first == FileDialog::Later)
1260 if (result.second.isEmpty())
1262 return FileName(fromqstr(result.second));
1266 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1270 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1273 message(_("Document not loaded."));
1278 setBuffer(newBuffer);
1280 // scroll to the position when the file was last closed
1281 if (lyxrc.use_lastfilepos) {
1282 LastFilePosSection::FilePos filepos =
1283 theSession().lastFilePos().load(filename);
1284 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1288 theSession().lastFiles().add(filename);
1295 void GuiView::openDocument(string const & fname)
1297 string initpath = lyxrc.document_path;
1300 string const trypath = buffer()->filePath();
1301 // If directory is writeable, use this as default.
1302 if (FileName(trypath).isDirWritable())
1308 if (fname.empty()) {
1309 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1310 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1311 dlg.setButton2(qt_("Examples|#E#e"),
1312 toqstr(addPath(package().system_support().absFilename(), "examples")));
1314 QStringList filter(qt_("LyX Documents (*.lyx)"));
1315 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1316 << qt_("LyX-1.4.x Documents (*.lyx14)")
1317 << qt_("LyX-1.5.x Documents (*.lyx15)");
1318 FileDialog::Result result =
1319 dlg.open(toqstr(initpath), filter);
1321 if (result.first == FileDialog::Later)
1324 filename = fromqstr(result.second);
1326 // check selected filename
1327 if (filename.empty()) {
1328 message(_("Canceled."));
1334 // get absolute path of file and add ".lyx" to the filename if
1336 FileName const fullname =
1337 fileSearch(string(), filename, "lyx", support::may_not_exist);
1338 if (!fullname.empty())
1339 filename = fullname.absFilename();
1341 if (!fullname.onlyPath().isDirectory()) {
1342 Alert::warning(_("Invalid filename"),
1343 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1344 from_utf8(fullname.absFilename())));
1347 // if the file doesn't exist, let the user create one
1348 if (!fullname.exists()) {
1349 // the user specifically chose this name. Believe him.
1350 Buffer * const b = newFile(filename, string(), true);
1356 docstring const disp_fn = makeDisplayPath(filename);
1357 message(bformat(_("Opening document %1$s..."), disp_fn));
1360 Buffer * buf = loadDocument(fullname);
1365 buf->errors("Parse");
1366 str2 = bformat(_("Document %1$s opened."), disp_fn);
1367 if (buf->lyxvc().inUse())
1368 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1369 " " + _("Version control detected.");
1371 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1376 // FIXME: clean that
1377 static bool import(GuiView * lv, FileName const & filename,
1378 string const & format, ErrorList & errorList)
1380 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1382 string loader_format;
1383 vector<string> loaders = theConverters().loaders();
1384 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1385 for (vector<string>::const_iterator it = loaders.begin();
1386 it != loaders.end(); ++it) {
1387 if (!theConverters().isReachable(format, *it))
1390 string const tofile =
1391 support::changeExtension(filename.absFilename(),
1392 formats.extension(*it));
1393 if (!theConverters().convert(0, filename, FileName(tofile),
1394 filename, format, *it, errorList))
1396 loader_format = *it;
1399 if (loader_format.empty()) {
1400 frontend::Alert::error(_("Couldn't import file"),
1401 bformat(_("No information for importing the format %1$s."),
1402 formats.prettyName(format)));
1406 loader_format = format;
1408 if (loader_format == "lyx") {
1409 Buffer * buf = lv->loadDocument(lyxfile);
1414 buf->errors("Parse");
1416 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1420 bool as_paragraphs = loader_format == "textparagraph";
1421 string filename2 = (loader_format == format) ? filename.absFilename()
1422 : support::changeExtension(filename.absFilename(),
1423 formats.extension(loader_format));
1424 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1425 theLyXFunc().setLyXView(lv);
1426 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1433 void GuiView::importDocument(string const & argument)
1436 string filename = split(argument, format, ' ');
1438 LYXERR(Debug::INFO, format << " file: " << filename);
1440 // need user interaction
1441 if (filename.empty()) {
1442 string initpath = lyxrc.document_path;
1444 Buffer const * buf = buffer();
1446 string const trypath = buf->filePath();
1447 // If directory is writeable, use this as default.
1448 if (FileName(trypath).isDirWritable())
1452 docstring const text = bformat(_("Select %1$s file to import"),
1453 formats.prettyName(format));
1455 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1456 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1457 dlg.setButton2(qt_("Examples|#E#e"),
1458 toqstr(addPath(package().system_support().absFilename(), "examples")));
1460 docstring filter = formats.prettyName(format);
1463 filter += from_utf8(formats.extension(format));
1466 FileDialog::Result result =
1467 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1469 if (result.first == FileDialog::Later)
1472 filename = fromqstr(result.second);
1474 // check selected filename
1475 if (filename.empty())
1476 message(_("Canceled."));
1479 if (filename.empty())
1482 // get absolute path of file
1483 FileName const fullname(support::makeAbsPath(filename));
1485 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1487 // Check if the document already is open
1488 Buffer * buf = theBufferList().getBuffer(lyxfile);
1491 if (!closeBuffer()) {
1492 message(_("Canceled."));
1497 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1499 // if the file exists already, and we didn't do
1500 // -i lyx thefile.lyx, warn
1501 if (lyxfile.exists() && fullname != lyxfile) {
1503 docstring text = bformat(_("The document %1$s already exists.\n\n"
1504 "Do you want to overwrite that document?"), displaypath);
1505 int const ret = Alert::prompt(_("Overwrite document?"),
1506 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1509 message(_("Canceled."));
1514 message(bformat(_("Importing %1$s..."), displaypath));
1515 ErrorList errorList;
1516 if (import(this, fullname, format, errorList))
1517 message(_("imported."));
1519 message(_("file not imported!"));
1521 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1525 void GuiView::newDocument(string const & filename, bool from_template)
1527 FileName initpath(lyxrc.document_path);
1528 Buffer * buf = buffer();
1530 FileName const trypath(buf->filePath());
1531 // If directory is writeable, use this as default.
1532 if (trypath.isDirWritable())
1536 string templatefile = from_template ?
1537 selectTemplateFile().absFilename() : string();
1539 if (filename.empty())
1540 b = newUnnamedFile(templatefile, initpath);
1542 b = newFile(filename, templatefile, true);
1546 // Ensure the cursor is correctly positionned on screen.
1547 view()->showCursor();
1551 void GuiView::insertLyXFile(docstring const & fname)
1553 BufferView * bv = view();
1558 FileName filename(to_utf8(fname));
1560 if (!filename.empty()) {
1561 bv->insertLyXFile(filename);
1565 // Launch a file browser
1567 string initpath = lyxrc.document_path;
1568 string const trypath = bv->buffer().filePath();
1569 // If directory is writeable, use this as default.
1570 if (FileName(trypath).isDirWritable())
1574 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1575 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1576 dlg.setButton2(qt_("Examples|#E#e"),
1577 toqstr(addPath(package().system_support().absFilename(),
1580 FileDialog::Result result = dlg.open(toqstr(initpath),
1581 QStringList(qt_("LyX Documents (*.lyx)")));
1583 if (result.first == FileDialog::Later)
1587 filename.set(fromqstr(result.second));
1589 // check selected filename
1590 if (filename.empty()) {
1591 // emit message signal.
1592 message(_("Canceled."));
1596 bv->insertLyXFile(filename);
1600 void GuiView::insertPlaintextFile(docstring const & fname,
1603 BufferView * bv = view();
1608 FileName filename(to_utf8(fname));
1610 if (!filename.empty()) {
1611 bv->insertPlaintextFile(filename, asParagraph);
1615 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1616 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1618 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1621 if (result.first == FileDialog::Later)
1625 filename.set(fromqstr(result.second));
1627 // check selected filename
1628 if (filename.empty()) {
1629 // emit message signal.
1630 message(_("Canceled."));
1634 bv->insertPlaintextFile(filename, asParagraph);
1638 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1640 FileName fname = b.fileName();
1641 FileName const oldname = fname;
1643 if (!newname.empty()) {
1645 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1647 // Switch to this Buffer.
1650 /// No argument? Ask user through dialog.
1652 FileDialog dlg(qt_("Choose a filename to save document as"),
1653 LFUN_BUFFER_WRITE_AS);
1654 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1655 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1657 if (!isLyXFilename(fname.absFilename()))
1658 fname.changeExtension(".lyx");
1660 FileDialog::Result result =
1661 dlg.save(toqstr(fname.onlyPath().absFilename()),
1662 QStringList(qt_("LyX Documents (*.lyx)")),
1663 toqstr(fname.onlyFileName()));
1665 if (result.first == FileDialog::Later)
1668 fname.set(fromqstr(result.second));
1673 if (!isLyXFilename(fname.absFilename()))
1674 fname.changeExtension(".lyx");
1677 if (FileName(fname).exists()) {
1678 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1679 docstring text = bformat(_("The document %1$s already "
1680 "exists.\n\nDo you want to "
1681 "overwrite that document?"),
1683 int const ret = Alert::prompt(_("Overwrite document?"),
1684 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1687 case 1: return renameBuffer(b, docstring());
1688 case 2: return false;
1692 // Ok, change the name of the buffer
1693 b.setFileName(fname.absFilename());
1695 bool unnamed = b.isUnnamed();
1696 b.setUnnamed(false);
1697 b.saveCheckSum(fname);
1699 if (!saveBuffer(b)) {
1700 b.setFileName(oldname.absFilename());
1701 b.setUnnamed(unnamed);
1702 b.saveCheckSum(oldname);
1710 bool GuiView::saveBuffer(Buffer & b)
1713 return renameBuffer(b, docstring());
1716 theSession().lastFiles().add(b.fileName());
1720 // Switch to this Buffer.
1723 // FIXME: we don't tell the user *WHY* the save failed !!
1724 docstring const file = makeDisplayPath(b.absFileName(), 30);
1725 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1726 "Do you want to rename the document and "
1727 "try again?"), file);
1728 int const ret = Alert::prompt(_("Rename and save?"),
1729 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1732 if (!renameBuffer(b, docstring()))
1741 return saveBuffer(b);
1745 bool GuiView::closeBuffer()
1747 Buffer * buf = buffer();
1748 return buf && closeBuffer(*buf);
1752 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1754 // goto bookmark to update bookmark pit.
1755 //FIXME: we should update only the bookmarks related to this buffer!
1756 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1757 theLyXFunc().gotoBookmark(i+1, false, false);
1759 if (buf.isClean() || buf.paragraphs().empty()) {
1760 if (buf.masterBuffer() == &buf && tolastopened)
1761 theSession().lastOpened().add(buf.fileName());
1762 theBufferList().release(&buf);
1765 // Switch to this Buffer.
1770 if (buf.isUnnamed())
1771 file = from_utf8(buf.fileName().onlyFileName());
1773 file = buf.fileName().displayName(30);
1775 // Bring this window to top before asking questions.
1779 docstring const text = bformat(_("The document %1$s has unsaved changes."
1780 "\n\nDo you want to save the document or discard the changes?"), file);
1781 int const ret = Alert::prompt(_("Save changed document?"),
1782 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1786 if (!saveBuffer(buf))
1790 // if we crash after this we could
1791 // have no autosave file but I guess
1792 // this is really improbable (Jug)
1793 removeAutosaveFile(buf.absFileName());
1799 // save file names to .lyx/session
1800 // if master/slave are both open, do not save slave since it
1801 // will be automatically loaded when the master is loaded
1802 if (buf.masterBuffer() == &buf && tolastopened)
1803 theSession().lastOpened().add(buf.fileName());
1806 // Don't close child documents.
1807 removeWorkArea(d.current_work_area_);
1809 theBufferList().release(&buf);
1815 bool GuiView::dispatch(FuncRequest const & cmd)
1817 BufferView * bv = view();
1818 // By default we won't need any update.
1820 bv->cursor().updateFlags(Update::None);
1821 bool dispatched = true;
1823 switch(cmd.action) {
1824 case LFUN_BUFFER_IMPORT:
1825 importDocument(to_utf8(cmd.argument()));
1828 case LFUN_BUFFER_SWITCH:
1829 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1832 case LFUN_BUFFER_NEXT:
1833 setBuffer(theBufferList().next(buffer()));
1836 case LFUN_BUFFER_PREVIOUS:
1837 setBuffer(theBufferList().previous(buffer()));
1840 case LFUN_COMMAND_EXECUTE: {
1841 bool const show_it = cmd.argument() != "off";
1842 // FIXME: this is a hack, "minibuffer" should not be
1844 if (GuiToolbar * t = toolbar("minibuffer")) {
1845 t->setVisible(show_it);
1846 if (show_it && t->commandBuffer())
1847 t->commandBuffer()->setFocus();
1851 case LFUN_DROP_LAYOUTS_CHOICE:
1853 d.layout_->showPopup();
1856 case LFUN_MENU_OPEN:
1857 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1858 menu->exec(QCursor::pos());
1861 case LFUN_FILE_INSERT:
1862 insertLyXFile(cmd.argument());
1864 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1865 insertPlaintextFile(cmd.argument(), true);
1868 case LFUN_FILE_INSERT_PLAINTEXT:
1869 insertPlaintextFile(cmd.argument(), false);
1872 case LFUN_BUFFER_WRITE:
1874 saveBuffer(bv->buffer());
1877 case LFUN_BUFFER_WRITE_AS:
1879 renameBuffer(bv->buffer(), cmd.argument());
1882 case LFUN_BUFFER_WRITE_ALL: {
1883 Buffer * first = theBufferList().first();
1886 message(_("Saving all documents..."));
1887 // We cannot use a for loop as the buffer list cycles.
1890 if (!b->isClean()) {
1892 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1894 b = theBufferList().next(b);
1895 } while (b != first);
1896 message(_("All documents saved."));
1900 case LFUN_TOOLBAR_TOGGLE: {
1901 string const name = cmd.getArg(0);
1902 if (GuiToolbar * t = toolbar(name))
1907 case LFUN_DIALOG_UPDATE: {
1908 string const name = to_utf8(cmd.argument());
1909 // Can only update a dialog connected to an existing inset
1910 Inset * inset = getOpenInset(name);
1912 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1913 inset->dispatch(view()->cursor(), fr);
1914 } else if (name == "paragraph") {
1915 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1916 } else if (name == "prefs") {
1917 updateDialog(name, string());
1922 case LFUN_DIALOG_TOGGLE: {
1923 if (isDialogVisible(cmd.getArg(0)))
1924 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1926 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1930 case LFUN_DIALOG_DISCONNECT_INSET:
1931 disconnectDialog(to_utf8(cmd.argument()));
1934 case LFUN_DIALOG_HIDE: {
1935 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1939 case LFUN_DIALOG_SHOW: {
1940 string const name = cmd.getArg(0);
1941 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1943 if (name == "character") {
1944 data = freefont2string();
1946 showDialog("character", data);
1947 } else if (name == "latexlog") {
1948 Buffer::LogType type;
1949 string const logfile = buffer()->logName(&type);
1951 case Buffer::latexlog:
1954 case Buffer::buildlog:
1958 data += Lexer::quoteString(logfile);
1959 showDialog("log", data);
1960 } else if (name == "vclog") {
1961 string const data = "vc " +
1962 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1963 showDialog("log", data);
1964 } else if (name == "symbols") {
1965 data = bv->cursor().getEncoding()->name();
1967 showDialog("symbols", data);
1969 showDialog(name, data);
1973 case LFUN_INSET_APPLY: {
1974 string const name = cmd.getArg(0);
1975 Inset * inset = getOpenInset(name);
1977 // put cursor in front of inset.
1978 if (!view()->setCursorFromInset(inset))
1979 LASSERT(false, break);
1981 // useful if we are called from a dialog.
1982 view()->cursor().beginUndoGroup();
1983 view()->cursor().recordUndo();
1984 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1985 inset->dispatch(view()->cursor(), fr);
1986 view()->cursor().endUndoGroup();
1988 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1994 case LFUN_UI_TOGGLE:
1996 // Make sure the keyboard focus stays in the work area.
2000 case LFUN_SPLIT_VIEW:
2001 if (Buffer * buf = buffer()) {
2002 string const orientation = cmd.getArg(0);
2003 d.splitter_->setOrientation(orientation == "vertical"
2004 ? Qt::Vertical : Qt::Horizontal);
2005 TabWorkArea * twa = addTabWorkArea();
2006 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2007 setCurrentWorkArea(wa);
2011 case LFUN_CLOSE_TAB_GROUP:
2012 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2014 twa = d.currentTabWorkArea();
2015 // Switch to the next GuiWorkArea in the found TabWorkArea.
2017 d.current_work_area_ = twa->currentWorkArea();
2018 // Make sure the work area is up to date.
2019 twa->setCurrentWorkArea(d.current_work_area_);
2021 d.current_work_area_ = 0;
2023 if (d.splitter_->count() == 0)
2024 // No more work area, switch to the background widget.
2029 case LFUN_COMPLETION_INLINE:
2030 if (d.current_work_area_)
2031 d.current_work_area_->completer().showInline();
2034 case LFUN_COMPLETION_POPUP:
2035 if (d.current_work_area_)
2036 d.current_work_area_->completer().showPopup();
2040 case LFUN_COMPLETION_COMPLETE:
2041 if (d.current_work_area_)
2042 d.current_work_area_->completer().tab();
2045 case LFUN_COMPLETION_CANCEL:
2046 if (d.current_work_area_) {
2047 if (d.current_work_area_->completer().popupVisible())
2048 d.current_work_area_->completer().hidePopup();
2050 d.current_work_area_->completer().hideInline();
2054 case LFUN_COMPLETION_ACCEPT:
2055 if (d.current_work_area_)
2056 d.current_work_area_->completer().activate();
2065 // Part of automatic menu appearance feature.
2066 if (isFullScreen()) {
2067 if (menuBar()->isVisible())
2069 if (statusBar()->isVisible())
2070 statusBar()->hide();
2077 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2079 string const arg = cmd.getArg(0);
2080 if (arg == "scrollbar") {
2081 // hide() is of no help
2082 if (d.current_work_area_->verticalScrollBarPolicy() ==
2083 Qt::ScrollBarAlwaysOff)
2085 d.current_work_area_->setVerticalScrollBarPolicy(
2086 Qt::ScrollBarAsNeeded);
2088 d.current_work_area_->setVerticalScrollBarPolicy(
2089 Qt::ScrollBarAlwaysOff);
2092 if (arg == "statusbar") {
2093 statusBar()->setVisible(!statusBar()->isVisible());
2096 if (arg == "menubar") {
2097 menuBar()->setVisible(!menuBar()->isVisible());
2100 #if QT_VERSION >= 0x040300
2101 if (arg == "frame") {
2103 getContentsMargins(&l, &t, &r, &b);
2104 //are the frames in default state?
2105 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2107 setContentsMargins(-2, -2, -2, -2);
2109 setContentsMargins(0, 0, 0, 0);
2114 if (arg == "fullscreen") {
2119 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2123 void GuiView::toggleFullScreen()
2125 if (isFullScreen()) {
2126 for (int i = 0; i != d.splitter_->count(); ++i)
2127 d.tabWorkArea(i)->setFullScreen(false);
2128 #if QT_VERSION >= 0x040300
2129 setContentsMargins(0, 0, 0, 0);
2131 setWindowState(windowState() ^ Qt::WindowFullScreen);
2134 statusBar()->show();
2136 for (int i = 0; i != d.splitter_->count(); ++i)
2137 d.tabWorkArea(i)->setFullScreen(true);
2138 #if QT_VERSION >= 0x040300
2139 setContentsMargins(-2, -2, -2, -2);
2142 setWindowState(windowState() ^ Qt::WindowFullScreen);
2143 statusBar()->hide();
2145 if (lyxrc.full_screen_toolbars) {
2146 ToolbarMap::iterator end = d.toolbars_.end();
2147 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2154 Buffer const * GuiView::updateInset(Inset const * inset)
2156 if (!d.current_work_area_)
2160 d.current_work_area_->scheduleRedraw();
2162 return &d.current_work_area_->bufferView().buffer();
2166 void GuiView::restartCursor()
2168 /* When we move around, or type, it's nice to be able to see
2169 * the cursor immediately after the keypress.
2171 if (d.current_work_area_)
2172 d.current_work_area_->startBlinkingCursor();
2174 // Take this occasion to update the other GUI elements.
2180 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2182 if (d.current_work_area_)
2183 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2188 // This list should be kept in sync with the list of insets in
2189 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2190 // dialog should have the same name as the inset.
2191 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2192 // docs in LyXAction.cpp.
2194 char const * const dialognames[] = {
2195 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2196 "citation", "document", "errorlist", "ert", "external", "file",
2197 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2198 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2199 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2201 #ifdef HAVE_LIBAIKSAURUS
2205 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2207 char const * const * const end_dialognames =
2208 dialognames + (sizeof(dialognames) / sizeof(char *));
2212 cmpCStr(char const * name) : name_(name) {}
2213 bool operator()(char const * other) {
2214 return strcmp(other, name_) == 0;
2221 bool isValidName(string const & name)
2223 return find_if(dialognames, end_dialognames,
2224 cmpCStr(name.c_str())) != end_dialognames;
2230 void GuiView::resetDialogs()
2232 // Make sure that no LFUN uses any LyXView.
2233 theLyXFunc().setLyXView(0);
2236 constructToolbars();
2237 guiApp->menus().fillMenuBar(menuBar(), this, true);
2239 d.layout_->updateContents(true);
2240 // Now update controls with current buffer.
2241 theLyXFunc().setLyXView(this);
2247 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2249 if (!isValidName(name))
2252 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2254 if (it != d.dialogs_.end())
2255 return it->second.get();
2257 Dialog * dialog = build(name);
2258 d.dialogs_[name].reset(dialog);
2259 if (lyxrc.allow_geometry_session)
2260 dialog->restoreSession();
2267 void GuiView::showDialog(string const & name, string const & data,
2275 Dialog * dialog = findOrBuild(name, false);
2277 dialog->showData(data);
2279 d.open_insets_[name] = inset;
2282 catch (ExceptionMessage const & ex) {
2290 bool GuiView::isDialogVisible(string const & name) const
2292 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2293 if (it == d.dialogs_.end())
2295 return it->second.get()->isVisibleView();
2299 void GuiView::hideDialog(string const & name, Inset * inset)
2301 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2302 if (it == d.dialogs_.end())
2305 if (inset && inset != getOpenInset(name))
2308 Dialog * const dialog = it->second.get();
2309 if (dialog->isVisibleView())
2311 d.open_insets_[name] = 0;
2315 void GuiView::disconnectDialog(string const & name)
2317 if (!isValidName(name))
2320 if (d.open_insets_.find(name) != d.open_insets_.end())
2321 d.open_insets_[name] = 0;
2325 Inset * GuiView::getOpenInset(string const & name) const
2327 if (!isValidName(name))
2330 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2331 return it == d.open_insets_.end() ? 0 : it->second;
2335 void GuiView::hideAll() const
2337 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2338 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2340 for(; it != end; ++it)
2341 it->second->hideView();
2345 void GuiView::updateDialogs()
2347 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2348 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2350 for(; it != end; ++it) {
2351 Dialog * dialog = it->second.get();
2352 if (dialog && dialog->isVisibleView())
2353 dialog->checkStatus();
2360 // will be replaced by a proper factory...
2361 Dialog * createGuiAbout(GuiView & lv);
2362 Dialog * createGuiBibitem(GuiView & lv);
2363 Dialog * createGuiBibtex(GuiView & lv);
2364 Dialog * createGuiBox(GuiView & lv);
2365 Dialog * createGuiBranch(GuiView & lv);
2366 Dialog * createGuiChanges(GuiView & lv);
2367 Dialog * createGuiCharacter(GuiView & lv);
2368 Dialog * createGuiCitation(GuiView & lv);
2369 Dialog * createGuiDelimiter(GuiView & lv);
2370 Dialog * createGuiDocument(GuiView & lv);
2371 Dialog * createGuiErrorList(GuiView & lv);
2372 Dialog * createGuiERT(GuiView & lv);
2373 Dialog * createGuiExternal(GuiView & lv);
2374 Dialog * createGuiFloat(GuiView & lv);
2375 Dialog * createGuiGraphics(GuiView & lv);
2376 Dialog * createGuiHSpace(GuiView & lv);
2377 Dialog * createGuiInclude(GuiView & lv);
2378 Dialog * createGuiInfo(GuiView & lv);
2379 Dialog * createGuiLabel(GuiView & lv);
2380 Dialog * createGuiListings(GuiView & lv);
2381 Dialog * createGuiLog(GuiView & lv);
2382 Dialog * createGuiMathMatrix(GuiView & lv);
2383 Dialog * createGuiNomenclature(GuiView & lv);
2384 Dialog * createGuiNote(GuiView & lv);
2385 Dialog * createGuiParagraph(GuiView & lv);
2386 Dialog * createGuiPreferences(GuiView & lv);
2387 Dialog * createGuiPrint(GuiView & lv);
2388 Dialog * createGuiRef(GuiView & lv);
2389 Dialog * createGuiSearch(GuiView & lv);
2390 Dialog * createGuiSendTo(GuiView & lv);
2391 Dialog * createGuiShowFile(GuiView & lv);
2392 Dialog * createGuiSpellchecker(GuiView & lv);
2393 Dialog * createGuiSymbols(GuiView & lv);
2394 Dialog * createGuiTabularCreate(GuiView & lv);
2395 Dialog * createGuiTabular(GuiView & lv);
2396 Dialog * createGuiTexInfo(GuiView & lv);
2397 Dialog * createGuiToc(GuiView & lv);
2398 Dialog * createGuiThesaurus(GuiView & lv);
2399 Dialog * createGuiHyperlink(GuiView & lv);
2400 Dialog * createGuiVSpace(GuiView & lv);
2401 Dialog * createGuiViewSource(GuiView & lv);
2402 Dialog * createGuiWrap(GuiView & lv);
2405 Dialog * GuiView::build(string const & name)
2407 LASSERT(isValidName(name), return 0);
2409 if (name == "aboutlyx")
2410 return createGuiAbout(*this);
2411 if (name == "bibitem")
2412 return createGuiBibitem(*this);
2413 if (name == "bibtex")
2414 return createGuiBibtex(*this);
2416 return createGuiBox(*this);
2417 if (name == "branch")
2418 return createGuiBranch(*this);
2419 if (name == "changes")
2420 return createGuiChanges(*this);
2421 if (name == "character")
2422 return createGuiCharacter(*this);
2423 if (name == "citation")
2424 return createGuiCitation(*this);
2425 if (name == "document")
2426 return createGuiDocument(*this);
2427 if (name == "errorlist")
2428 return createGuiErrorList(*this);
2430 return createGuiERT(*this);
2431 if (name == "external")
2432 return createGuiExternal(*this);
2434 return createGuiShowFile(*this);
2435 if (name == "findreplace")
2436 return createGuiSearch(*this);
2437 if (name == "float")
2438 return createGuiFloat(*this);
2439 if (name == "graphics")
2440 return createGuiGraphics(*this);
2441 if (name == "include")
2442 return createGuiInclude(*this);
2444 return createGuiInfo(*this);
2445 if (name == "nomenclature")
2446 return createGuiNomenclature(*this);
2447 if (name == "label")
2448 return createGuiLabel(*this);
2450 return createGuiLog(*this);
2451 if (name == "view-source")
2452 return createGuiViewSource(*this);
2453 if (name == "mathdelimiter")
2454 return createGuiDelimiter(*this);
2455 if (name == "mathmatrix")
2456 return createGuiMathMatrix(*this);
2458 return createGuiNote(*this);
2459 if (name == "paragraph")
2460 return createGuiParagraph(*this);
2461 if (name == "prefs")
2462 return createGuiPreferences(*this);
2463 if (name == "print")
2464 return createGuiPrint(*this);
2466 return createGuiRef(*this);
2467 if (name == "sendto")
2468 return createGuiSendTo(*this);
2469 if (name == "space")
2470 return createGuiHSpace(*this);
2471 if (name == "spellchecker")
2472 return createGuiSpellchecker(*this);
2473 if (name == "symbols")
2474 return createGuiSymbols(*this);
2475 if (name == "tabular")
2476 return createGuiTabular(*this);
2477 if (name == "tabularcreate")
2478 return createGuiTabularCreate(*this);
2479 if (name == "texinfo")
2480 return createGuiTexInfo(*this);
2481 #ifdef HAVE_LIBAIKSAURUS
2482 if (name == "thesaurus")
2483 return createGuiThesaurus(*this);
2486 return createGuiToc(*this);
2488 return createGuiHyperlink(*this);
2489 if (name == "vspace")
2490 return createGuiVSpace(*this);
2492 return createGuiWrap(*this);
2493 if (name == "listings")
2494 return createGuiListings(*this);
2500 } // namespace frontend
2503 #include "GuiView_moc.cpp"