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.
287 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
290 // set ourself as the current view. This is needed for the menu bar
291 // filling, at least for the static special menu item on Mac. Otherwise
292 // they are greyed out.
293 theLyXFunc().setLyXView(this);
295 // Fill up the menu bar.
296 guiApp->menus().fillMenuBar(menuBar(), this, true);
298 setCentralWidget(d.stack_widget_);
300 // Start autosave timer
301 if (lyxrc.autosave) {
302 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
303 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
304 d.autosave_timeout_.start();
306 connect(&d.statusbar_timer_, SIGNAL(timeout()),
307 this, SLOT(clearMessage()));
309 // We don't want to keep the window in memory if it is closed.
310 setAttribute(Qt::WA_DeleteOnClose, true);
312 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
313 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
314 // since the icon is provided in the application bundle.
315 setWindowIcon(QPixmap(":/images/lyx.png"));
319 setAcceptDrops(true);
321 statusBar()->setSizeGripEnabled(true);
323 // Forbid too small unresizable window because it can happen
324 // with some window manager under X11.
325 setMinimumSize(300, 200);
327 if (lyxrc.allow_geometry_session) {
328 // Now take care of session management.
333 // no session handling, default to a sane size.
334 setGeometry(50, 50, 690, 510);
337 // clear session data if any.
339 settings.remove("views");
349 void GuiView::saveLayout() const
352 settings.beginGroup("views");
353 settings.beginGroup(QString::number(id_));
355 settings.setValue("pos", pos());
356 settings.setValue("size", size());
358 settings.setValue("geometry", saveGeometry());
360 settings.setValue("layout", saveState(0));
361 settings.setValue("icon_size", iconSize());
365 bool GuiView::restoreLayout()
368 settings.beginGroup("views");
369 settings.beginGroup(QString::number(id_));
370 QString const icon_key = "icon_size";
371 if (!settings.contains(icon_key))
374 setIconSize(settings.value(icon_key).toSize());
376 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
377 QSize size = settings.value("size", QSize(690, 510)).toSize();
381 if (!restoreGeometry(settings.value("geometry").toByteArray()))
382 setGeometry(50, 50, 690, 510);
384 // Make sure layout is correctly oriented.
385 setLayoutDirection(qApp->layoutDirection());
387 // Allow the toc and view-source dock widget to be restored if needed.
388 findOrBuild("toc", true);
389 findOrBuild("view-source", true);
391 if (!restoreState(settings.value("layout").toByteArray(), 0))
398 GuiToolbar * GuiView::toolbar(string const & name)
400 ToolbarMap::iterator it = d.toolbars_.find(name);
401 if (it != d.toolbars_.end())
404 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
405 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
410 void GuiView::constructToolbars()
412 ToolbarMap::iterator it = d.toolbars_.begin();
413 for (; it != d.toolbars_.end(); ++it)
418 // extracts the toolbars from the backend
419 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
420 Toolbars::Infos::iterator end = guiApp->toolbars().end();
421 for (; cit != end; ++cit)
422 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
426 void GuiView::initToolbars()
428 // extracts the toolbars from the backend
429 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
430 Toolbars::Infos::iterator end = guiApp->toolbars().end();
431 for (; cit != end; ++cit) {
432 GuiToolbar * tb = toolbar(cit->name);
435 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
437 tb->setVisible(false);
438 tb->setVisibility(visibility);
440 if (visibility & Toolbars::TOP) {
442 addToolBarBreak(Qt::TopToolBarArea);
443 addToolBar(Qt::TopToolBarArea, tb);
446 if (visibility & Toolbars::BOTTOM) {
447 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
448 #if (QT_VERSION >= 0x040202)
449 addToolBarBreak(Qt::BottomToolBarArea);
451 addToolBar(Qt::BottomToolBarArea, tb);
454 if (visibility & Toolbars::LEFT) {
455 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
456 #if (QT_VERSION >= 0x040202)
457 addToolBarBreak(Qt::LeftToolBarArea);
459 addToolBar(Qt::LeftToolBarArea, tb);
462 if (visibility & Toolbars::RIGHT) {
463 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
464 #if (QT_VERSION >= 0x040202)
465 addToolBarBreak(Qt::RightToolBarArea);
467 addToolBar(Qt::RightToolBarArea, tb);
470 if (visibility & Toolbars::ON)
471 tb->setVisible(true);
476 TocModels & GuiView::tocModels()
478 return d.toc_models_;
482 void GuiView::setFocus()
484 // Make sure LyXFunc points to the correct view.
485 guiApp->setCurrentView(this);
486 theLyXFunc().setLyXView(this);
487 QMainWindow::setFocus();
488 if (d.current_work_area_)
489 d.current_work_area_->setFocus();
493 QMenu * GuiView::createPopupMenu()
495 return d.toolBarPopup(this);
499 void GuiView::showEvent(QShowEvent * e)
501 LYXERR(Debug::GUI, "Passed Geometry "
502 << size().height() << "x" << size().width()
503 << "+" << pos().x() << "+" << pos().y());
505 if (d.splitter_->count() == 0)
506 // No work area, switch to the background widget.
509 QMainWindow::showEvent(e);
513 void GuiView::closeEvent(QCloseEvent * close_event)
517 // it can happen that this event arrives without selecting the view,
518 // e.g. when clicking the close button on a background window.
521 while (Buffer * b = buffer()) {
523 // This is a child document, just close the tab after saving
524 // but keep the file loaded.
525 if (!saveBuffer(*b)) {
527 close_event->ignore();
530 removeWorkArea(d.current_work_area_);
534 QList<int> const ids = guiApp->viewIds();
535 for (int i = 0; i != ids.size(); ++i) {
538 if (guiApp->view(ids[i]).workArea(*b)) {
539 // FIXME 1: should we put an alert box here that the buffer
540 // is viewed elsewhere?
541 // FIXME 2: should we try to save this buffer in any case?
544 // This buffer is also opened in another view, so
545 // close the associated work area...
546 removeWorkArea(d.current_work_area_);
547 // ... but don't close the buffer.
552 if (b && !closeBuffer(*b, true)) {
554 close_event->ignore();
559 // Make sure that nothing will use this close to be closed View.
560 guiApp->unregisterView(this);
562 if (isFullScreen()) {
563 // Switch off fullscreen before closing.
568 // Make sure the timer time out will not trigger a statusbar update.
569 d.statusbar_timer_.stop();
571 // Saving fullscreen requires additional tweaks in the toolbar code.
572 // It wouldn't also work under linux natively.
573 if (lyxrc.allow_geometry_session) {
574 // Save this window geometry and layout.
576 // Then the toolbar private states.
577 ToolbarMap::iterator end = d.toolbars_.end();
578 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
579 it->second->saveSession();
580 // Now take care of all other dialogs:
581 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
582 for (; it!= d.dialogs_.end(); ++it)
583 it->second->saveSession();
586 close_event->accept();
590 void GuiView::dragEnterEvent(QDragEnterEvent * event)
592 if (event->mimeData()->hasUrls())
594 /// \todo Ask lyx-devel is this is enough:
595 /// if (event->mimeData()->hasFormat("text/plain"))
596 /// event->acceptProposedAction();
600 void GuiView::dropEvent(QDropEvent* event)
602 QList<QUrl> files = event->mimeData()->urls();
606 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
607 for (int i = 0; i != files.size(); ++i) {
608 string const file = os::internal_path(fromqstr(
609 files.at(i).toLocalFile()));
611 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
616 void GuiView::message(docstring const & str)
618 if (ForkedProcess::iAmAChild())
621 statusBar()->showMessage(toqstr(str));
622 d.statusbar_timer_.stop();
623 d.statusbar_timer_.start(3000);
627 void GuiView::smallSizedIcons()
629 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
633 void GuiView::normalSizedIcons()
635 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
639 void GuiView::bigSizedIcons()
641 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
645 void GuiView::clearMessage()
649 theLyXFunc().setLyXView(this);
650 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
651 d.statusbar_timer_.stop();
655 void GuiView::updateWindowTitle(GuiWorkArea * wa)
657 if (wa != d.current_work_area_)
659 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
660 setWindowIconText(wa->windowIconText());
664 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
667 disconnectBufferView();
668 connectBufferView(wa->bufferView());
669 connectBuffer(wa->bufferView().buffer());
670 d.current_work_area_ = wa;
671 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
672 this, SLOT(updateWindowTitle(GuiWorkArea *)));
673 updateWindowTitle(wa);
677 // The document settings needs to be reinitialised.
678 updateDialog("document", "");
680 // Buffer-dependent dialogs must be updated. This is done here because
681 // some dialogs require buffer()->text.
686 void GuiView::on_lastWorkAreaRemoved()
689 // We already are in a close event. Nothing more to do.
692 if (d.splitter_->count() > 1)
693 // We have a splitter so don't close anything.
696 // Reset and updates the dialogs.
697 d.toc_models_.reset(0);
698 updateDialog("document", "");
701 resetWindowTitleAndIconText();
703 if (lyxrc.open_buffers_in_tabs)
704 // Nothing more to do, the window should stay open.
707 if (guiApp->viewIds().size() > 1) {
713 // On Mac we also close the last window because the application stay
714 // resident in memory. On other platforms we don't close the last
715 // window because this would quit the application.
721 void GuiView::updateStatusBar()
723 // let the user see the explicit message
724 if (d.statusbar_timer_.isActive())
727 theLyXFunc().setLyXView(this);
728 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
732 bool GuiView::hasFocus() const
734 return qApp->activeWindow() == this;
738 bool GuiView::event(QEvent * e)
742 // Useful debug code:
743 //case QEvent::ActivationChange:
744 //case QEvent::WindowDeactivate:
745 //case QEvent::Paint:
746 //case QEvent::Enter:
747 //case QEvent::Leave:
748 //case QEvent::HoverEnter:
749 //case QEvent::HoverLeave:
750 //case QEvent::HoverMove:
751 //case QEvent::StatusTip:
752 //case QEvent::DragEnter:
753 //case QEvent::DragLeave:
757 case QEvent::WindowActivate: {
758 if (this == guiApp->currentView()) {
760 return QMainWindow::event(e);
762 guiApp->setCurrentView(this);
763 theLyXFunc().setLyXView(this);
764 if (d.current_work_area_) {
765 BufferView & bv = d.current_work_area_->bufferView();
766 connectBufferView(bv);
767 connectBuffer(bv.buffer());
768 // The document structure, name and dialogs might have
769 // changed in another view.
771 // The document settings needs to be reinitialised.
772 updateDialog("document", "");
775 resetWindowTitleAndIconText();
778 return QMainWindow::event(e);
781 case QEvent::ShortcutOverride: {
785 if (isFullScreen() && menuBar()->isHidden()) {
786 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
787 // FIXME: we should also try to detect special LyX shortcut such as
788 // Alt-P and Alt-M. Right now there is a hack in
789 // GuiWorkArea::processKeySym() that hides again the menubar for
791 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
793 return QMainWindow::event(e);
797 if (d.current_work_area_)
798 // Nothing special to do.
799 return QMainWindow::event(e);
801 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
802 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
804 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
805 || ke->key() == Qt::Key_Backtab)
806 return QMainWindow::event(e);
808 // Allow processing of shortcuts that are allowed even when no Buffer
810 theLyXFunc().setLyXView(this);
812 setKeySymbol(&sym, ke);
813 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
819 return QMainWindow::event(e);
823 void GuiView::resetWindowTitleAndIconText()
825 setWindowTitle(qt_("LyX"));
826 setWindowIconText(qt_("LyX"));
829 bool GuiView::focusNextPrevChild(bool /*next*/)
836 void GuiView::setBusy(bool busy)
838 if (d.current_work_area_) {
839 d.current_work_area_->setUpdatesEnabled(!busy);
841 d.current_work_area_->stopBlinkingCursor();
843 d.current_work_area_->startBlinkingCursor();
847 QApplication::setOverrideCursor(Qt::WaitCursor);
849 QApplication::restoreOverrideCursor();
853 GuiWorkArea * GuiView::workArea(Buffer & buffer)
855 if (TabWorkArea * twa = d.currentTabWorkArea())
856 return twa->workArea(buffer);
861 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
863 // Automatically create a TabWorkArea if there are none yet.
864 TabWorkArea * tab_widget = d.splitter_->count()
865 ? d.currentTabWorkArea() : addTabWorkArea();
866 return tab_widget->addWorkArea(buffer, *this);
870 TabWorkArea * GuiView::addTabWorkArea()
872 TabWorkArea * twa = new TabWorkArea;
873 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
874 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
875 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
876 this, SLOT(on_lastWorkAreaRemoved()));
878 d.splitter_->addWidget(twa);
879 d.stack_widget_->setCurrentWidget(d.splitter_);
884 GuiWorkArea const * GuiView::currentWorkArea() const
886 return d.current_work_area_;
890 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
893 d.current_work_area_ = wa;
894 for (int i = 0; i != d.splitter_->count(); ++i) {
895 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
901 void GuiView::removeWorkArea(GuiWorkArea * wa)
904 if (wa == d.current_work_area_) {
906 disconnectBufferView();
907 d.current_work_area_ = 0;
910 for (int i = 0; i != d.splitter_->count(); ++i) {
911 TabWorkArea * twa = d.tabWorkArea(i);
912 if (!twa->removeWorkArea(wa))
913 // Not found in this tab group.
916 // We found and removed the GuiWorkArea.
918 // No more WorkAreas in this tab group, so delete it.
923 if (d.current_work_area_)
924 // This means that we are not closing the current GuiWorkArea;
927 // Switch to the next GuiWorkArea in the found TabWorkArea.
928 d.current_work_area_ = twa->currentWorkArea();
932 if (d.splitter_->count() == 0)
933 // No more work area, switch to the background widget.
938 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
944 void GuiView::updateLayoutList()
947 d.layout_->updateContents(false);
951 void GuiView::updateToolbars()
953 ToolbarMap::iterator end = d.toolbars_.end();
954 if (d.current_work_area_) {
956 d.current_work_area_->bufferView().cursor().inMathed();
958 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
960 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
961 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
962 bool const mathmacrotemplate =
963 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
965 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
966 it->second->update(math, table, review, mathmacrotemplate);
968 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
969 it->second->update(false, false, false, false);
973 Buffer * GuiView::buffer()
975 if (d.current_work_area_)
976 return &d.current_work_area_->bufferView().buffer();
981 Buffer const * GuiView::buffer() const
983 if (d.current_work_area_)
984 return &d.current_work_area_->bufferView().buffer();
989 void GuiView::setBuffer(Buffer * newBuffer)
991 LASSERT(newBuffer, return);
994 GuiWorkArea * wa = workArea(*newBuffer);
996 updateLabels(*newBuffer->masterBuffer());
997 wa = addWorkArea(*newBuffer);
999 //Disconnect the old buffer...there's no new one.
1002 connectBuffer(*newBuffer);
1003 connectBufferView(wa->bufferView());
1004 setCurrentWorkArea(wa);
1010 void GuiView::connectBuffer(Buffer & buf)
1012 buf.setGuiDelegate(this);
1016 void GuiView::disconnectBuffer()
1018 if (d.current_work_area_)
1019 d.current_work_area_->bufferView().setGuiDelegate(0);
1023 void GuiView::connectBufferView(BufferView & bv)
1025 bv.setGuiDelegate(this);
1029 void GuiView::disconnectBufferView()
1031 if (d.current_work_area_)
1032 d.current_work_area_->bufferView().setGuiDelegate(0);
1036 void GuiView::errors(string const & error_type)
1038 ErrorList & el = buffer()->errorList(error_type);
1040 showDialog("errorlist", error_type);
1044 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1046 d.toc_models_.updateItem(toqstr(type), dit);
1050 void GuiView::structureChanged()
1052 d.toc_models_.reset(view());
1053 // Navigator needs more than a simple update in this case. It needs to be
1055 updateDialog("toc", "");
1059 void GuiView::updateDialog(string const & name, string const & data)
1061 if (!isDialogVisible(name))
1064 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1065 if (it == d.dialogs_.end())
1068 Dialog * const dialog = it->second.get();
1069 if (dialog->isVisibleView())
1070 dialog->initialiseParams(data);
1074 BufferView * GuiView::view()
1076 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1080 void GuiView::autoSave()
1082 LYXERR(Debug::INFO, "Running autoSave()");
1085 view()->buffer().autoSave();
1089 void GuiView::resetAutosaveTimers()
1092 d.autosave_timeout_.restart();
1096 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1099 Buffer * buf = buffer();
1101 /* In LyX/Mac, when a dialog is open, the menus of the
1102 application can still be accessed without giving focus to
1103 the main window. In this case, we want to disable the menu
1104 entries that are buffer-related.
1106 Note that this code is not perfect, as bug 1941 attests:
1107 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1109 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1112 switch(cmd.action) {
1113 case LFUN_BUFFER_WRITE:
1114 enable = buf && (buf->isUnnamed() || !buf->isClean());
1117 case LFUN_BUFFER_WRITE_AS:
1121 case LFUN_SPLIT_VIEW:
1122 if (cmd.getArg(0) == "vertical")
1123 enable = buf && (d.splitter_->count() == 1 ||
1124 d.splitter_->orientation() == Qt::Vertical);
1126 enable = buf && (d.splitter_->count() == 1 ||
1127 d.splitter_->orientation() == Qt::Horizontal);
1130 case LFUN_CLOSE_TAB_GROUP:
1131 enable = d.currentTabWorkArea();
1134 case LFUN_TOOLBAR_TOGGLE:
1135 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1136 flag.setOnOff(t->isVisible());
1139 case LFUN_UI_TOGGLE:
1140 flag.setOnOff(isFullScreen());
1143 case LFUN_DIALOG_TOGGLE:
1144 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1145 // fall through to set "enable"
1146 case LFUN_DIALOG_SHOW: {
1147 string const name = cmd.getArg(0);
1149 enable = name == "aboutlyx"
1150 || name == "file" //FIXME: should be removed.
1152 || name == "texinfo";
1153 else if (name == "print")
1154 enable = buf->isExportable("dvi")
1155 && lyxrc.print_command != "none";
1156 else if (name == "character") {
1160 InsetCode ic = view()->cursor().inset().lyxCode();
1161 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1164 else if (name == "symbols") {
1165 if (!view() || view()->cursor().inMathed())
1168 InsetCode ic = view()->cursor().inset().lyxCode();
1169 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1172 else if (name == "latexlog")
1173 enable = FileName(buf->logName()).isReadableFile();
1174 else if (name == "spellchecker")
1175 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1176 enable = !buf->isReadonly();
1180 else if (name == "vclog")
1181 enable = buf->lyxvc().inUse();
1185 case LFUN_DIALOG_UPDATE: {
1186 string const name = cmd.getArg(0);
1188 enable = name == "prefs";
1192 case LFUN_INSET_APPLY: {
1193 string const name = cmd.getArg(0);
1194 Inset * inset = getOpenInset(name);
1196 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1198 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1199 // Every inset is supposed to handle this
1200 LASSERT(false, break);
1204 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1205 flag |= lyx::getStatus(fr);
1207 enable = flag.enabled();
1211 case LFUN_COMPLETION_INLINE:
1212 if (!d.current_work_area_
1213 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1217 case LFUN_COMPLETION_POPUP:
1218 if (!d.current_work_area_
1219 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1223 case LFUN_COMPLETION_COMPLETE:
1224 if (!d.current_work_area_
1225 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1229 case LFUN_COMPLETION_ACCEPT:
1230 case LFUN_COMPLETION_CANCEL:
1231 if (!d.current_work_area_
1232 || (!d.current_work_area_->completer().popupVisible()
1233 && !d.current_work_area_->completer().inlineVisible()))
1242 flag.setEnabled(false);
1248 static FileName selectTemplateFile()
1250 FileDialog dlg(qt_("Select template file"));
1251 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1252 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1254 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1255 QStringList(qt_("LyX Documents (*.lyx)")));
1257 if (result.first == FileDialog::Later)
1259 if (result.second.isEmpty())
1261 return FileName(fromqstr(result.second));
1265 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1269 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1272 message(_("Document not loaded."));
1277 setBuffer(newBuffer);
1279 // scroll to the position when the file was last closed
1280 if (lyxrc.use_lastfilepos) {
1281 LastFilePosSection::FilePos filepos =
1282 theSession().lastFilePos().load(filename);
1283 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1287 theSession().lastFiles().add(filename);
1294 void GuiView::openDocument(string const & fname)
1296 string initpath = lyxrc.document_path;
1299 string const trypath = buffer()->filePath();
1300 // If directory is writeable, use this as default.
1301 if (FileName(trypath).isDirWritable())
1307 if (fname.empty()) {
1308 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1309 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1310 dlg.setButton2(qt_("Examples|#E#e"),
1311 toqstr(addPath(package().system_support().absFilename(), "examples")));
1313 QStringList filter(qt_("LyX Documents (*.lyx)"));
1314 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1315 << qt_("LyX-1.4.x Documents (*.lyx14)")
1316 << qt_("LyX-1.5.x Documents (*.lyx15)");
1317 FileDialog::Result result =
1318 dlg.open(toqstr(initpath), filter);
1320 if (result.first == FileDialog::Later)
1323 filename = fromqstr(result.second);
1325 // check selected filename
1326 if (filename.empty()) {
1327 message(_("Canceled."));
1333 // get absolute path of file and add ".lyx" to the filename if
1335 FileName const fullname =
1336 fileSearch(string(), filename, "lyx", support::may_not_exist);
1337 if (!fullname.empty())
1338 filename = fullname.absFilename();
1340 if (!fullname.onlyPath().isDirectory()) {
1341 Alert::warning(_("Invalid filename"),
1342 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1343 from_utf8(fullname.absFilename())));
1346 // if the file doesn't exist, let the user create one
1347 if (!fullname.exists()) {
1348 // the user specifically chose this name. Believe him.
1349 Buffer * const b = newFile(filename, string(), true);
1355 docstring const disp_fn = makeDisplayPath(filename);
1356 message(bformat(_("Opening document %1$s..."), disp_fn));
1359 Buffer * buf = loadDocument(fullname);
1364 buf->errors("Parse");
1365 str2 = bformat(_("Document %1$s opened."), disp_fn);
1366 if (buf->lyxvc().inUse())
1367 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1368 " " + _("Version control detected.");
1370 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1375 // FIXME: clean that
1376 static bool import(GuiView * lv, FileName const & filename,
1377 string const & format, ErrorList & errorList)
1379 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1381 string loader_format;
1382 vector<string> loaders = theConverters().loaders();
1383 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1384 for (vector<string>::const_iterator it = loaders.begin();
1385 it != loaders.end(); ++it) {
1386 if (!theConverters().isReachable(format, *it))
1389 string const tofile =
1390 support::changeExtension(filename.absFilename(),
1391 formats.extension(*it));
1392 if (!theConverters().convert(0, filename, FileName(tofile),
1393 filename, format, *it, errorList))
1395 loader_format = *it;
1398 if (loader_format.empty()) {
1399 frontend::Alert::error(_("Couldn't import file"),
1400 bformat(_("No information for importing the format %1$s."),
1401 formats.prettyName(format)));
1405 loader_format = format;
1407 if (loader_format == "lyx") {
1408 Buffer * buf = lv->loadDocument(lyxfile);
1413 buf->errors("Parse");
1415 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1419 bool as_paragraphs = loader_format == "textparagraph";
1420 string filename2 = (loader_format == format) ? filename.absFilename()
1421 : support::changeExtension(filename.absFilename(),
1422 formats.extension(loader_format));
1423 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1424 theLyXFunc().setLyXView(lv);
1425 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1432 void GuiView::importDocument(string const & argument)
1435 string filename = split(argument, format, ' ');
1437 LYXERR(Debug::INFO, format << " file: " << filename);
1439 // need user interaction
1440 if (filename.empty()) {
1441 string initpath = lyxrc.document_path;
1443 Buffer const * buf = buffer();
1445 string const trypath = buf->filePath();
1446 // If directory is writeable, use this as default.
1447 if (FileName(trypath).isDirWritable())
1451 docstring const text = bformat(_("Select %1$s file to import"),
1452 formats.prettyName(format));
1454 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1455 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1456 dlg.setButton2(qt_("Examples|#E#e"),
1457 toqstr(addPath(package().system_support().absFilename(), "examples")));
1459 docstring filter = formats.prettyName(format);
1462 filter += from_utf8(formats.extension(format));
1465 FileDialog::Result result =
1466 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1468 if (result.first == FileDialog::Later)
1471 filename = fromqstr(result.second);
1473 // check selected filename
1474 if (filename.empty())
1475 message(_("Canceled."));
1478 if (filename.empty())
1481 // get absolute path of file
1482 FileName const fullname(support::makeAbsPath(filename));
1484 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1486 // Check if the document already is open
1487 Buffer * buf = theBufferList().getBuffer(lyxfile);
1490 if (!closeBuffer()) {
1491 message(_("Canceled."));
1496 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1498 // if the file exists already, and we didn't do
1499 // -i lyx thefile.lyx, warn
1500 if (lyxfile.exists() && fullname != lyxfile) {
1502 docstring text = bformat(_("The document %1$s already exists.\n\n"
1503 "Do you want to overwrite that document?"), displaypath);
1504 int const ret = Alert::prompt(_("Overwrite document?"),
1505 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1508 message(_("Canceled."));
1513 message(bformat(_("Importing %1$s..."), displaypath));
1514 ErrorList errorList;
1515 if (import(this, fullname, format, errorList))
1516 message(_("imported."));
1518 message(_("file not imported!"));
1520 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1524 void GuiView::newDocument(string const & filename, bool from_template)
1526 FileName initpath(lyxrc.document_path);
1527 Buffer * buf = buffer();
1529 FileName const trypath(buf->filePath());
1530 // If directory is writeable, use this as default.
1531 if (trypath.isDirWritable())
1535 string templatefile = from_template ?
1536 selectTemplateFile().absFilename() : string();
1538 if (filename.empty())
1539 b = newUnnamedFile(templatefile, initpath);
1541 b = newFile(filename, templatefile, true);
1545 // Ensure the cursor is correctly positionned on screen.
1546 view()->showCursor();
1550 void GuiView::insertLyXFile(docstring const & fname)
1552 BufferView * bv = view();
1557 FileName filename(to_utf8(fname));
1559 if (!filename.empty()) {
1560 bv->insertLyXFile(filename);
1564 // Launch a file browser
1566 string initpath = lyxrc.document_path;
1567 string const trypath = bv->buffer().filePath();
1568 // If directory is writeable, use this as default.
1569 if (FileName(trypath).isDirWritable())
1573 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1574 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1575 dlg.setButton2(qt_("Examples|#E#e"),
1576 toqstr(addPath(package().system_support().absFilename(),
1579 FileDialog::Result result = dlg.open(toqstr(initpath),
1580 QStringList(qt_("LyX Documents (*.lyx)")));
1582 if (result.first == FileDialog::Later)
1586 filename.set(fromqstr(result.second));
1588 // check selected filename
1589 if (filename.empty()) {
1590 // emit message signal.
1591 message(_("Canceled."));
1595 bv->insertLyXFile(filename);
1599 void GuiView::insertPlaintextFile(docstring const & fname,
1602 BufferView * bv = view();
1607 FileName filename(to_utf8(fname));
1609 if (!filename.empty()) {
1610 bv->insertPlaintextFile(filename, asParagraph);
1614 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1615 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1617 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1620 if (result.first == FileDialog::Later)
1624 filename.set(fromqstr(result.second));
1626 // check selected filename
1627 if (filename.empty()) {
1628 // emit message signal.
1629 message(_("Canceled."));
1633 bv->insertPlaintextFile(filename, asParagraph);
1637 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1639 FileName fname = b.fileName();
1640 FileName const oldname = fname;
1642 if (!newname.empty()) {
1644 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1646 // Switch to this Buffer.
1649 /// No argument? Ask user through dialog.
1651 FileDialog dlg(qt_("Choose a filename to save document as"),
1652 LFUN_BUFFER_WRITE_AS);
1653 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1654 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1656 if (!isLyXFilename(fname.absFilename()))
1657 fname.changeExtension(".lyx");
1659 FileDialog::Result result =
1660 dlg.save(toqstr(fname.onlyPath().absFilename()),
1661 QStringList(qt_("LyX Documents (*.lyx)")),
1662 toqstr(fname.onlyFileName()));
1664 if (result.first == FileDialog::Later)
1667 fname.set(fromqstr(result.second));
1672 if (!isLyXFilename(fname.absFilename()))
1673 fname.changeExtension(".lyx");
1676 if (FileName(fname).exists()) {
1677 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1678 docstring text = bformat(_("The document %1$s already "
1679 "exists.\n\nDo you want to "
1680 "overwrite that document?"),
1682 int const ret = Alert::prompt(_("Overwrite document?"),
1683 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1686 case 1: return renameBuffer(b, docstring());
1687 case 2: return false;
1691 // Ok, change the name of the buffer
1692 b.setFileName(fname.absFilename());
1694 bool unnamed = b.isUnnamed();
1695 b.setUnnamed(false);
1696 b.saveCheckSum(fname);
1698 if (!saveBuffer(b)) {
1699 b.setFileName(oldname.absFilename());
1700 b.setUnnamed(unnamed);
1701 b.saveCheckSum(oldname);
1709 bool GuiView::saveBuffer(Buffer & b)
1712 return renameBuffer(b, docstring());
1715 theSession().lastFiles().add(b.fileName());
1719 // Switch to this Buffer.
1722 // FIXME: we don't tell the user *WHY* the save failed !!
1723 docstring const file = makeDisplayPath(b.absFileName(), 30);
1724 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1725 "Do you want to rename the document and "
1726 "try again?"), file);
1727 int const ret = Alert::prompt(_("Rename and save?"),
1728 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1731 if (!renameBuffer(b, docstring()))
1740 return saveBuffer(b);
1744 bool GuiView::closeBuffer()
1746 Buffer * buf = buffer();
1747 return buf && closeBuffer(*buf);
1751 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1753 // goto bookmark to update bookmark pit.
1754 //FIXME: we should update only the bookmarks related to this buffer!
1755 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1756 theLyXFunc().gotoBookmark(i+1, false, false);
1758 if (buf.isClean() || buf.paragraphs().empty()) {
1759 if (buf.masterBuffer() == &buf && tolastopened)
1760 theSession().lastOpened().add(buf.fileName());
1762 // Don't close child documents.
1763 removeWorkArea(d.current_work_area_);
1765 theBufferList().release(&buf);
1768 // Switch to this Buffer.
1773 if (buf.isUnnamed())
1774 file = from_utf8(buf.fileName().onlyFileName());
1776 file = buf.fileName().displayName(30);
1778 // Bring this window to top before asking questions.
1782 docstring const text = bformat(_("The document %1$s has unsaved changes."
1783 "\n\nDo you want to save the document or discard the changes?"), file);
1784 int const ret = Alert::prompt(_("Save changed document?"),
1785 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1789 if (!saveBuffer(buf))
1793 // if we crash after this we could
1794 // have no autosave file but I guess
1795 // this is really improbable (Jug)
1796 removeAutosaveFile(buf.absFileName());
1802 // save file names to .lyx/session
1803 // if master/slave are both open, do not save slave since it
1804 // will be automatically loaded when the master is loaded
1805 if (buf.masterBuffer() == &buf && tolastopened)
1806 theSession().lastOpened().add(buf.fileName());
1809 // Don't close child documents.
1810 removeWorkArea(d.current_work_area_);
1812 theBufferList().release(&buf);
1818 bool GuiView::dispatch(FuncRequest const & cmd)
1820 BufferView * bv = view();
1821 // By default we won't need any update.
1823 bv->cursor().updateFlags(Update::None);
1824 bool dispatched = true;
1826 switch(cmd.action) {
1827 case LFUN_BUFFER_IMPORT:
1828 importDocument(to_utf8(cmd.argument()));
1831 case LFUN_BUFFER_SWITCH:
1832 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1835 case LFUN_BUFFER_NEXT:
1836 setBuffer(theBufferList().next(buffer()));
1839 case LFUN_BUFFER_PREVIOUS:
1840 setBuffer(theBufferList().previous(buffer()));
1843 case LFUN_COMMAND_EXECUTE: {
1844 bool const show_it = cmd.argument() != "off";
1845 // FIXME: this is a hack, "minibuffer" should not be
1847 if (GuiToolbar * t = toolbar("minibuffer")) {
1848 t->setVisible(show_it);
1849 if (show_it && t->commandBuffer())
1850 t->commandBuffer()->setFocus();
1854 case LFUN_DROP_LAYOUTS_CHOICE:
1856 d.layout_->showPopup();
1859 case LFUN_MENU_OPEN:
1860 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1861 menu->exec(QCursor::pos());
1864 case LFUN_FILE_INSERT:
1865 insertLyXFile(cmd.argument());
1867 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1868 insertPlaintextFile(cmd.argument(), true);
1871 case LFUN_FILE_INSERT_PLAINTEXT:
1872 insertPlaintextFile(cmd.argument(), false);
1875 case LFUN_BUFFER_WRITE:
1877 saveBuffer(bv->buffer());
1880 case LFUN_BUFFER_WRITE_AS:
1882 renameBuffer(bv->buffer(), cmd.argument());
1885 case LFUN_BUFFER_WRITE_ALL: {
1886 Buffer * first = theBufferList().first();
1889 message(_("Saving all documents..."));
1890 // We cannot use a for loop as the buffer list cycles.
1893 if (!b->isClean()) {
1895 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1897 b = theBufferList().next(b);
1898 } while (b != first);
1899 message(_("All documents saved."));
1903 case LFUN_TOOLBAR_TOGGLE: {
1904 string const name = cmd.getArg(0);
1905 if (GuiToolbar * t = toolbar(name))
1910 case LFUN_DIALOG_UPDATE: {
1911 string const name = to_utf8(cmd.argument());
1912 // Can only update a dialog connected to an existing inset
1913 Inset * inset = getOpenInset(name);
1915 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1916 inset->dispatch(view()->cursor(), fr);
1917 } else if (name == "paragraph") {
1918 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1919 } else if (name == "prefs") {
1920 updateDialog(name, string());
1925 case LFUN_DIALOG_TOGGLE: {
1926 if (isDialogVisible(cmd.getArg(0)))
1927 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1929 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1933 case LFUN_DIALOG_DISCONNECT_INSET:
1934 disconnectDialog(to_utf8(cmd.argument()));
1937 case LFUN_DIALOG_HIDE: {
1938 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1942 case LFUN_DIALOG_SHOW: {
1943 string const name = cmd.getArg(0);
1944 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1946 if (name == "character") {
1947 data = freefont2string();
1949 showDialog("character", data);
1950 } else if (name == "latexlog") {
1951 Buffer::LogType type;
1952 string const logfile = buffer()->logName(&type);
1954 case Buffer::latexlog:
1957 case Buffer::buildlog:
1961 data += Lexer::quoteString(logfile);
1962 showDialog("log", data);
1963 } else if (name == "vclog") {
1964 string const data = "vc " +
1965 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1966 showDialog("log", data);
1967 } else if (name == "symbols") {
1968 data = bv->cursor().getEncoding()->name();
1970 showDialog("symbols", data);
1972 showDialog(name, data);
1976 case LFUN_INSET_APPLY: {
1977 string const name = cmd.getArg(0);
1978 Inset * inset = getOpenInset(name);
1980 // put cursor in front of inset.
1981 if (!view()->setCursorFromInset(inset))
1982 LASSERT(false, break);
1984 // useful if we are called from a dialog.
1985 view()->cursor().beginUndoGroup();
1986 view()->cursor().recordUndo();
1987 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1988 inset->dispatch(view()->cursor(), fr);
1989 view()->cursor().endUndoGroup();
1991 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1997 case LFUN_UI_TOGGLE:
1999 // Make sure the keyboard focus stays in the work area.
2003 case LFUN_SPLIT_VIEW:
2004 if (Buffer * buf = buffer()) {
2005 string const orientation = cmd.getArg(0);
2006 d.splitter_->setOrientation(orientation == "vertical"
2007 ? Qt::Vertical : Qt::Horizontal);
2008 TabWorkArea * twa = addTabWorkArea();
2009 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2010 setCurrentWorkArea(wa);
2014 case LFUN_CLOSE_TAB_GROUP:
2015 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2017 twa = d.currentTabWorkArea();
2018 // Switch to the next GuiWorkArea in the found TabWorkArea.
2020 d.current_work_area_ = twa->currentWorkArea();
2021 // Make sure the work area is up to date.
2022 twa->setCurrentWorkArea(d.current_work_area_);
2024 d.current_work_area_ = 0;
2026 if (d.splitter_->count() == 0)
2027 // No more work area, switch to the background widget.
2032 case LFUN_COMPLETION_INLINE:
2033 if (d.current_work_area_)
2034 d.current_work_area_->completer().showInline();
2037 case LFUN_COMPLETION_POPUP:
2038 if (d.current_work_area_)
2039 d.current_work_area_->completer().showPopup();
2043 case LFUN_COMPLETION_COMPLETE:
2044 if (d.current_work_area_)
2045 d.current_work_area_->completer().tab();
2048 case LFUN_COMPLETION_CANCEL:
2049 if (d.current_work_area_) {
2050 if (d.current_work_area_->completer().popupVisible())
2051 d.current_work_area_->completer().hidePopup();
2053 d.current_work_area_->completer().hideInline();
2057 case LFUN_COMPLETION_ACCEPT:
2058 if (d.current_work_area_)
2059 d.current_work_area_->completer().activate();
2068 // Part of automatic menu appearance feature.
2069 if (isFullScreen()) {
2070 if (menuBar()->isVisible())
2072 if (statusBar()->isVisible())
2073 statusBar()->hide();
2080 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2082 string const arg = cmd.getArg(0);
2083 if (arg == "scrollbar") {
2084 // hide() is of no help
2085 if (d.current_work_area_->verticalScrollBarPolicy() ==
2086 Qt::ScrollBarAlwaysOff)
2088 d.current_work_area_->setVerticalScrollBarPolicy(
2089 Qt::ScrollBarAsNeeded);
2091 d.current_work_area_->setVerticalScrollBarPolicy(
2092 Qt::ScrollBarAlwaysOff);
2095 if (arg == "statusbar") {
2096 statusBar()->setVisible(!statusBar()->isVisible());
2099 if (arg == "menubar") {
2100 menuBar()->setVisible(!menuBar()->isVisible());
2103 #if QT_VERSION >= 0x040300
2104 if (arg == "frame") {
2106 getContentsMargins(&l, &t, &r, &b);
2107 //are the frames in default state?
2108 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2110 setContentsMargins(-2, -2, -2, -2);
2112 setContentsMargins(0, 0, 0, 0);
2117 if (arg == "fullscreen") {
2122 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2126 void GuiView::toggleFullScreen()
2128 if (isFullScreen()) {
2129 for (int i = 0; i != d.splitter_->count(); ++i)
2130 d.tabWorkArea(i)->setFullScreen(false);
2131 #if QT_VERSION >= 0x040300
2132 setContentsMargins(0, 0, 0, 0);
2134 setWindowState(windowState() ^ Qt::WindowFullScreen);
2137 statusBar()->show();
2139 for (int i = 0; i != d.splitter_->count(); ++i)
2140 d.tabWorkArea(i)->setFullScreen(true);
2141 #if QT_VERSION >= 0x040300
2142 setContentsMargins(-2, -2, -2, -2);
2145 setWindowState(windowState() ^ Qt::WindowFullScreen);
2146 statusBar()->hide();
2148 if (lyxrc.full_screen_toolbars) {
2149 ToolbarMap::iterator end = d.toolbars_.end();
2150 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2155 // give dialogs like the TOC a chance to adapt
2160 Buffer const * GuiView::updateInset(Inset const * inset)
2162 if (!d.current_work_area_)
2166 d.current_work_area_->scheduleRedraw();
2168 return &d.current_work_area_->bufferView().buffer();
2172 void GuiView::restartCursor()
2174 /* When we move around, or type, it's nice to be able to see
2175 * the cursor immediately after the keypress.
2177 if (d.current_work_area_)
2178 d.current_work_area_->startBlinkingCursor();
2180 // Take this occasion to update the other GUI elements.
2186 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2188 if (d.current_work_area_)
2189 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2194 // This list should be kept in sync with the list of insets in
2195 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2196 // dialog should have the same name as the inset.
2197 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2198 // docs in LyXAction.cpp.
2200 char const * const dialognames[] = {
2201 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2202 "citation", "document", "errorlist", "ert", "external", "file",
2203 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2204 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2205 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2207 #ifdef HAVE_LIBAIKSAURUS
2211 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2213 char const * const * const end_dialognames =
2214 dialognames + (sizeof(dialognames) / sizeof(char *));
2218 cmpCStr(char const * name) : name_(name) {}
2219 bool operator()(char const * other) {
2220 return strcmp(other, name_) == 0;
2227 bool isValidName(string const & name)
2229 return find_if(dialognames, end_dialognames,
2230 cmpCStr(name.c_str())) != end_dialognames;
2236 void GuiView::resetDialogs()
2238 // Make sure that no LFUN uses any LyXView.
2239 theLyXFunc().setLyXView(0);
2242 constructToolbars();
2243 guiApp->menus().fillMenuBar(menuBar(), this, false);
2245 d.layout_->updateContents(true);
2246 // Now update controls with current buffer.
2247 theLyXFunc().setLyXView(this);
2253 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2255 if (!isValidName(name))
2258 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2260 if (it != d.dialogs_.end()) {
2262 it->second->hideView();
2263 return it->second.get();
2266 Dialog * dialog = build(name);
2267 d.dialogs_[name].reset(dialog);
2268 if (lyxrc.allow_geometry_session)
2269 dialog->restoreSession();
2276 void GuiView::showDialog(string const & name, string const & data,
2284 Dialog * dialog = findOrBuild(name, false);
2286 dialog->showData(data);
2288 d.open_insets_[name] = inset;
2291 catch (ExceptionMessage const & ex) {
2299 bool GuiView::isDialogVisible(string const & name) const
2301 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2302 if (it == d.dialogs_.end())
2304 return it->second.get()->isVisibleView();
2308 void GuiView::hideDialog(string const & name, Inset * inset)
2310 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2311 if (it == d.dialogs_.end())
2314 if (inset && inset != getOpenInset(name))
2317 Dialog * const dialog = it->second.get();
2318 if (dialog->isVisibleView())
2320 d.open_insets_[name] = 0;
2324 void GuiView::disconnectDialog(string const & name)
2326 if (!isValidName(name))
2329 if (d.open_insets_.find(name) != d.open_insets_.end())
2330 d.open_insets_[name] = 0;
2334 Inset * GuiView::getOpenInset(string const & name) const
2336 if (!isValidName(name))
2339 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2340 return it == d.open_insets_.end() ? 0 : it->second;
2344 void GuiView::hideAll() const
2346 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2347 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2349 for(; it != end; ++it)
2350 it->second->hideView();
2354 void GuiView::updateDialogs()
2356 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2357 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2359 for(; it != end; ++it) {
2360 Dialog * dialog = it->second.get();
2361 if (dialog && dialog->isVisibleView())
2362 dialog->checkStatus();
2369 // will be replaced by a proper factory...
2370 Dialog * createGuiAbout(GuiView & lv);
2371 Dialog * createGuiBibitem(GuiView & lv);
2372 Dialog * createGuiBibtex(GuiView & lv);
2373 Dialog * createGuiBox(GuiView & lv);
2374 Dialog * createGuiBranch(GuiView & lv);
2375 Dialog * createGuiChanges(GuiView & lv);
2376 Dialog * createGuiCharacter(GuiView & lv);
2377 Dialog * createGuiCitation(GuiView & lv);
2378 Dialog * createGuiDelimiter(GuiView & lv);
2379 Dialog * createGuiDocument(GuiView & lv);
2380 Dialog * createGuiErrorList(GuiView & lv);
2381 Dialog * createGuiERT(GuiView & lv);
2382 Dialog * createGuiExternal(GuiView & lv);
2383 Dialog * createGuiFloat(GuiView & lv);
2384 Dialog * createGuiGraphics(GuiView & lv);
2385 Dialog * createGuiHSpace(GuiView & lv);
2386 Dialog * createGuiInclude(GuiView & lv);
2387 Dialog * createGuiInfo(GuiView & lv);
2388 Dialog * createGuiLabel(GuiView & lv);
2389 Dialog * createGuiListings(GuiView & lv);
2390 Dialog * createGuiLog(GuiView & lv);
2391 Dialog * createGuiMathMatrix(GuiView & lv);
2392 Dialog * createGuiNomenclature(GuiView & lv);
2393 Dialog * createGuiNote(GuiView & lv);
2394 Dialog * createGuiParagraph(GuiView & lv);
2395 Dialog * createGuiPreferences(GuiView & lv);
2396 Dialog * createGuiPrint(GuiView & lv);
2397 Dialog * createGuiRef(GuiView & lv);
2398 Dialog * createGuiSearch(GuiView & lv);
2399 Dialog * createGuiSendTo(GuiView & lv);
2400 Dialog * createGuiShowFile(GuiView & lv);
2401 Dialog * createGuiSpellchecker(GuiView & lv);
2402 Dialog * createGuiSymbols(GuiView & lv);
2403 Dialog * createGuiTabularCreate(GuiView & lv);
2404 Dialog * createGuiTabular(GuiView & lv);
2405 Dialog * createGuiTexInfo(GuiView & lv);
2406 Dialog * createGuiToc(GuiView & lv);
2407 Dialog * createGuiThesaurus(GuiView & lv);
2408 Dialog * createGuiHyperlink(GuiView & lv);
2409 Dialog * createGuiVSpace(GuiView & lv);
2410 Dialog * createGuiViewSource(GuiView & lv);
2411 Dialog * createGuiWrap(GuiView & lv);
2414 Dialog * GuiView::build(string const & name)
2416 LASSERT(isValidName(name), return 0);
2418 if (name == "aboutlyx")
2419 return createGuiAbout(*this);
2420 if (name == "bibitem")
2421 return createGuiBibitem(*this);
2422 if (name == "bibtex")
2423 return createGuiBibtex(*this);
2425 return createGuiBox(*this);
2426 if (name == "branch")
2427 return createGuiBranch(*this);
2428 if (name == "changes")
2429 return createGuiChanges(*this);
2430 if (name == "character")
2431 return createGuiCharacter(*this);
2432 if (name == "citation")
2433 return createGuiCitation(*this);
2434 if (name == "document")
2435 return createGuiDocument(*this);
2436 if (name == "errorlist")
2437 return createGuiErrorList(*this);
2439 return createGuiERT(*this);
2440 if (name == "external")
2441 return createGuiExternal(*this);
2443 return createGuiShowFile(*this);
2444 if (name == "findreplace")
2445 return createGuiSearch(*this);
2446 if (name == "float")
2447 return createGuiFloat(*this);
2448 if (name == "graphics")
2449 return createGuiGraphics(*this);
2450 if (name == "include")
2451 return createGuiInclude(*this);
2453 return createGuiInfo(*this);
2454 if (name == "nomenclature")
2455 return createGuiNomenclature(*this);
2456 if (name == "label")
2457 return createGuiLabel(*this);
2459 return createGuiLog(*this);
2460 if (name == "view-source")
2461 return createGuiViewSource(*this);
2462 if (name == "mathdelimiter")
2463 return createGuiDelimiter(*this);
2464 if (name == "mathmatrix")
2465 return createGuiMathMatrix(*this);
2467 return createGuiNote(*this);
2468 if (name == "paragraph")
2469 return createGuiParagraph(*this);
2470 if (name == "prefs")
2471 return createGuiPreferences(*this);
2472 if (name == "print")
2473 return createGuiPrint(*this);
2475 return createGuiRef(*this);
2476 if (name == "sendto")
2477 return createGuiSendTo(*this);
2478 if (name == "space")
2479 return createGuiHSpace(*this);
2480 if (name == "spellchecker")
2481 return createGuiSpellchecker(*this);
2482 if (name == "symbols")
2483 return createGuiSymbols(*this);
2484 if (name == "tabular")
2485 return createGuiTabular(*this);
2486 if (name == "tabularcreate")
2487 return createGuiTabularCreate(*this);
2488 if (name == "texinfo")
2489 return createGuiTexInfo(*this);
2490 #ifdef HAVE_LIBAIKSAURUS
2491 if (name == "thesaurus")
2492 return createGuiThesaurus(*this);
2495 return createGuiToc(*this);
2497 return createGuiHyperlink(*this);
2498 if (name == "vspace")
2499 return createGuiVSpace(*this);
2501 return createGuiWrap(*this);
2502 if (name == "listings")
2503 return createGuiListings(*this);
2509 } // namespace frontend
2512 #include "GuiView_moc.cpp"