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);
336 // This enables to clear session data if any.
338 settings.remove("views");
348 void GuiView::saveLayout() const
351 settings.beginGroup("views");
352 settings.beginGroup(QString::number(id_));
354 settings.setValue("pos", pos());
355 settings.setValue("size", size());
357 settings.setValue("geometry", saveGeometry());
359 settings.setValue("layout", saveState(0));
360 settings.setValue("icon_size", iconSize());
364 bool GuiView::restoreLayout()
367 settings.beginGroup("views");
368 settings.beginGroup(QString::number(id_));
369 QString const icon_key = "icon_size";
370 if (!settings.contains(icon_key))
373 setIconSize(settings.value(icon_key).toSize());
375 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
376 QSize size = settings.value("size", QSize(690, 510)).toSize();
380 if (!restoreGeometry(settings.value("geometry").toByteArray()))
381 setGeometry(50, 50, 690, 510);
383 // Make sure layout is correctly oriented.
384 setLayoutDirection(qApp->layoutDirection());
386 // Allow the toc and view-source dock widget to be restored if needed.
387 findOrBuild("toc", true);
388 findOrBuild("view-source", true);
390 if (!restoreState(settings.value("layout").toByteArray(), 0))
397 GuiToolbar * GuiView::toolbar(string const & name)
399 ToolbarMap::iterator it = d.toolbars_.find(name);
400 if (it != d.toolbars_.end())
403 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
404 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
409 void GuiView::constructToolbars()
411 ToolbarMap::iterator it = d.toolbars_.begin();
412 for (; it != d.toolbars_.end(); ++it)
417 // extracts the toolbars from the backend
418 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
419 Toolbars::Infos::iterator end = guiApp->toolbars().end();
420 for (; cit != end; ++cit)
421 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
425 void GuiView::initToolbars()
427 // extracts the toolbars from the backend
428 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
429 Toolbars::Infos::iterator end = guiApp->toolbars().end();
430 for (; cit != end; ++cit) {
431 GuiToolbar * tb = toolbar(cit->name);
434 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
436 tb->setVisible(false);
437 tb->setVisibility(visibility);
439 if (visibility & Toolbars::TOP) {
441 addToolBarBreak(Qt::TopToolBarArea);
442 addToolBar(Qt::TopToolBarArea, tb);
445 if (visibility & Toolbars::BOTTOM) {
446 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
447 #if (QT_VERSION >= 0x040202)
448 addToolBarBreak(Qt::BottomToolBarArea);
450 addToolBar(Qt::BottomToolBarArea, tb);
453 if (visibility & Toolbars::LEFT) {
454 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456 addToolBarBreak(Qt::LeftToolBarArea);
458 addToolBar(Qt::LeftToolBarArea, tb);
461 if (visibility & Toolbars::RIGHT) {
462 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
463 #if (QT_VERSION >= 0x040202)
464 addToolBarBreak(Qt::RightToolBarArea);
466 addToolBar(Qt::RightToolBarArea, tb);
469 if (visibility & Toolbars::ON)
470 tb->setVisible(true);
475 TocModels & GuiView::tocModels()
477 return d.toc_models_;
481 void GuiView::setFocus()
483 // Make sure LyXFunc points to the correct view.
484 guiApp->setCurrentView(this);
485 theLyXFunc().setLyXView(this);
486 QMainWindow::setFocus();
487 if (d.current_work_area_)
488 d.current_work_area_->setFocus();
492 QMenu * GuiView::createPopupMenu()
494 return d.toolBarPopup(this);
498 void GuiView::showEvent(QShowEvent * e)
500 LYXERR(Debug::GUI, "Passed Geometry "
501 << size().height() << "x" << size().width()
502 << "+" << pos().x() << "+" << pos().y());
504 if (d.splitter_->count() == 0)
505 // No work area, switch to the background widget.
508 QMainWindow::showEvent(e);
512 void GuiView::closeEvent(QCloseEvent * close_event)
516 // it can happen that this event arrives without selecting the view,
517 // e.g. when clicking the close button on a background window.
520 while (Buffer * b = buffer()) {
522 // This is a child document, just close the tab after saving
523 // but keep the file loaded.
524 if (!saveBuffer(*b)) {
526 close_event->ignore();
529 removeWorkArea(d.current_work_area_);
533 QList<int> const ids = guiApp->viewIds();
534 for (int i = 0; i != ids.size(); ++i) {
537 if (guiApp->view(ids[i]).workArea(*b)) {
538 // FIXME 1: should we put an alert box here that the buffer
539 // is viewed elsewhere?
540 // FIXME 2: should we try to save this buffer in any case?
543 // This buffer is also opened in another view, so
544 // close the associated work area...
545 removeWorkArea(d.current_work_area_);
546 // ... but don't close the buffer.
551 if (b && !closeBuffer(*b, true)) {
553 close_event->ignore();
558 // Make sure that nothing will use this close to be closed View.
559 guiApp->unregisterView(this);
561 if (isFullScreen()) {
562 // Switch off fullscreen before closing.
567 // Make sure the timer time out will not trigger a statusbar update.
568 d.statusbar_timer_.stop();
570 // Saving fullscreen requires additional tweaks in the toolbar code.
571 // It wouldn't also work under linux natively.
572 if (lyxrc.allow_geometry_session) {
573 // Save this window geometry and layout.
575 // Then the toolbar private states.
576 ToolbarMap::iterator end = d.toolbars_.end();
577 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
578 it->second->saveSession();
579 // Now take care of all other dialogs:
580 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
581 for (; it!= d.dialogs_.end(); ++it)
582 it->second->saveSession();
585 close_event->accept();
589 void GuiView::dragEnterEvent(QDragEnterEvent * event)
591 if (event->mimeData()->hasUrls())
593 /// \todo Ask lyx-devel is this is enough:
594 /// if (event->mimeData()->hasFormat("text/plain"))
595 /// event->acceptProposedAction();
599 void GuiView::dropEvent(QDropEvent* event)
601 QList<QUrl> files = event->mimeData()->urls();
605 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
606 for (int i = 0; i != files.size(); ++i) {
607 string const file = os::internal_path(fromqstr(
608 files.at(i).toLocalFile()));
610 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
615 void GuiView::message(docstring const & str)
617 if (ForkedProcess::iAmAChild())
620 statusBar()->showMessage(toqstr(str));
621 d.statusbar_timer_.stop();
622 d.statusbar_timer_.start(3000);
626 void GuiView::smallSizedIcons()
628 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
632 void GuiView::normalSizedIcons()
634 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
638 void GuiView::bigSizedIcons()
640 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
644 void GuiView::clearMessage()
648 theLyXFunc().setLyXView(this);
649 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
650 d.statusbar_timer_.stop();
654 void GuiView::updateWindowTitle(GuiWorkArea * wa)
656 if (wa != d.current_work_area_)
658 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
659 setWindowIconText(wa->windowIconText());
663 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
666 disconnectBufferView();
667 connectBufferView(wa->bufferView());
668 connectBuffer(wa->bufferView().buffer());
669 d.current_work_area_ = wa;
670 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
671 this, SLOT(updateWindowTitle(GuiWorkArea *)));
672 updateWindowTitle(wa);
676 // The document settings needs to be reinitialised.
677 updateDialog("document", "");
679 // Buffer-dependent dialogs must be updated. This is done here because
680 // some dialogs require buffer()->text.
685 void GuiView::on_lastWorkAreaRemoved()
688 // We already are in a close event. Nothing more to do.
691 if (d.splitter_->count() > 1)
692 // We have a splitter so don't close anything.
695 // Reset and updates the dialogs.
696 d.toc_models_.reset(0);
697 updateDialog("document", "");
700 resetWindowTitleAndIconText();
702 if (lyxrc.open_buffers_in_tabs)
703 // Nothing more to do, the window should stay open.
706 if (guiApp->viewIds().size() > 1) {
712 // On Mac we also close the last window because the application stay
713 // resident in memory. On other platforms we don't close the last
714 // window because this would quit the application.
720 void GuiView::updateStatusBar()
722 // let the user see the explicit message
723 if (d.statusbar_timer_.isActive())
726 theLyXFunc().setLyXView(this);
727 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
731 bool GuiView::hasFocus() const
733 return qApp->activeWindow() == this;
737 bool GuiView::event(QEvent * e)
741 // Useful debug code:
742 //case QEvent::ActivationChange:
743 //case QEvent::WindowDeactivate:
744 //case QEvent::Paint:
745 //case QEvent::Enter:
746 //case QEvent::Leave:
747 //case QEvent::HoverEnter:
748 //case QEvent::HoverLeave:
749 //case QEvent::HoverMove:
750 //case QEvent::StatusTip:
751 //case QEvent::DragEnter:
752 //case QEvent::DragLeave:
756 case QEvent::WindowActivate: {
757 if (this == guiApp->currentView()) {
759 return QMainWindow::event(e);
761 guiApp->setCurrentView(this);
762 theLyXFunc().setLyXView(this);
763 if (d.current_work_area_) {
764 BufferView & bv = d.current_work_area_->bufferView();
765 connectBufferView(bv);
766 connectBuffer(bv.buffer());
767 // The document structure, name and dialogs might have
768 // changed in another view.
770 // The document settings needs to be reinitialised.
771 updateDialog("document", "");
774 resetWindowTitleAndIconText();
777 return QMainWindow::event(e);
780 case QEvent::ShortcutOverride: {
784 if (isFullScreen() && menuBar()->isHidden()) {
785 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
786 // FIXME: we should also try to detect special LyX shortcut such as
787 // Alt-P and Alt-M. Right now there is a hack in
788 // GuiWorkArea::processKeySym() that hides again the menubar for
790 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
792 return QMainWindow::event(e);
796 if (d.current_work_area_)
797 // Nothing special to do.
798 return QMainWindow::event(e);
800 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
801 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
803 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
804 || ke->key() == Qt::Key_Backtab)
805 return QMainWindow::event(e);
807 // Allow processing of shortcuts that are allowed even when no Buffer
809 theLyXFunc().setLyXView(this);
811 setKeySymbol(&sym, ke);
812 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
818 return QMainWindow::event(e);
822 void GuiView::resetWindowTitleAndIconText()
824 setWindowTitle(qt_("LyX"));
825 setWindowIconText(qt_("LyX"));
828 bool GuiView::focusNextPrevChild(bool /*next*/)
835 void GuiView::setBusy(bool busy)
837 if (d.current_work_area_) {
838 d.current_work_area_->setUpdatesEnabled(!busy);
840 d.current_work_area_->stopBlinkingCursor();
842 d.current_work_area_->startBlinkingCursor();
846 QApplication::setOverrideCursor(Qt::WaitCursor);
848 QApplication::restoreOverrideCursor();
852 GuiWorkArea * GuiView::workArea(Buffer & buffer)
854 if (TabWorkArea * twa = d.currentTabWorkArea())
855 return twa->workArea(buffer);
860 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
862 // Automatically create a TabWorkArea if there are none yet.
863 TabWorkArea * tab_widget = d.splitter_->count()
864 ? d.currentTabWorkArea() : addTabWorkArea();
865 return tab_widget->addWorkArea(buffer, *this);
869 TabWorkArea * GuiView::addTabWorkArea()
871 TabWorkArea * twa = new TabWorkArea;
872 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
873 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
874 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
875 this, SLOT(on_lastWorkAreaRemoved()));
877 d.splitter_->addWidget(twa);
878 d.stack_widget_->setCurrentWidget(d.splitter_);
883 GuiWorkArea const * GuiView::currentWorkArea() const
885 return d.current_work_area_;
889 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
892 d.current_work_area_ = wa;
893 for (int i = 0; i != d.splitter_->count(); ++i) {
894 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
900 void GuiView::removeWorkArea(GuiWorkArea * wa)
903 if (wa == d.current_work_area_) {
905 disconnectBufferView();
906 d.current_work_area_ = 0;
909 for (int i = 0; i != d.splitter_->count(); ++i) {
910 TabWorkArea * twa = d.tabWorkArea(i);
911 if (!twa->removeWorkArea(wa))
912 // Not found in this tab group.
915 // We found and removed the GuiWorkArea.
917 // No more WorkAreas in this tab group, so delete it.
922 if (d.current_work_area_)
923 // This means that we are not closing the current GuiWorkArea;
926 // Switch to the next GuiWorkArea in the found TabWorkArea.
927 d.current_work_area_ = twa->currentWorkArea();
931 if (d.splitter_->count() == 0)
932 // No more work area, switch to the background widget.
937 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
943 void GuiView::updateLayoutList()
946 d.layout_->updateContents(false);
950 void GuiView::updateToolbars()
952 ToolbarMap::iterator end = d.toolbars_.end();
953 if (d.current_work_area_) {
955 d.current_work_area_->bufferView().cursor().inMathed();
957 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
959 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
960 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
961 bool const mathmacrotemplate =
962 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
964 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
965 it->second->update(math, table, review, mathmacrotemplate);
967 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
968 it->second->update(false, false, false, false);
972 Buffer * GuiView::buffer()
974 if (d.current_work_area_)
975 return &d.current_work_area_->bufferView().buffer();
980 Buffer const * GuiView::buffer() const
982 if (d.current_work_area_)
983 return &d.current_work_area_->bufferView().buffer();
988 void GuiView::setBuffer(Buffer * newBuffer)
990 LASSERT(newBuffer, return);
993 GuiWorkArea * wa = workArea(*newBuffer);
995 updateLabels(*newBuffer->masterBuffer());
996 wa = addWorkArea(*newBuffer);
998 //Disconnect the old buffer...there's no new one.
1001 connectBuffer(*newBuffer);
1002 connectBufferView(wa->bufferView());
1003 setCurrentWorkArea(wa);
1009 void GuiView::connectBuffer(Buffer & buf)
1011 buf.setGuiDelegate(this);
1015 void GuiView::disconnectBuffer()
1017 if (d.current_work_area_)
1018 d.current_work_area_->bufferView().setGuiDelegate(0);
1022 void GuiView::connectBufferView(BufferView & bv)
1024 bv.setGuiDelegate(this);
1028 void GuiView::disconnectBufferView()
1030 if (d.current_work_area_)
1031 d.current_work_area_->bufferView().setGuiDelegate(0);
1035 void GuiView::errors(string const & error_type)
1037 ErrorList & el = buffer()->errorList(error_type);
1039 showDialog("errorlist", error_type);
1043 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1045 d.toc_models_.updateItem(toqstr(type), dit);
1049 void GuiView::structureChanged()
1051 d.toc_models_.reset(view());
1052 // Navigator needs more than a simple update in this case. It needs to be
1054 updateDialog("toc", "");
1058 void GuiView::updateDialog(string const & name, string const & data)
1060 if (!isDialogVisible(name))
1063 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1064 if (it == d.dialogs_.end())
1067 Dialog * const dialog = it->second.get();
1068 if (dialog->isVisibleView())
1069 dialog->initialiseParams(data);
1073 BufferView * GuiView::view()
1075 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1079 void GuiView::autoSave()
1081 LYXERR(Debug::INFO, "Running autoSave()");
1084 view()->buffer().autoSave();
1088 void GuiView::resetAutosaveTimers()
1091 d.autosave_timeout_.restart();
1095 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1098 Buffer * buf = buffer();
1100 /* In LyX/Mac, when a dialog is open, the menus of the
1101 application can still be accessed without giving focus to
1102 the main window. In this case, we want to disable the menu
1103 entries that are buffer-related.
1105 Note that this code is not perfect, as bug 1941 attests:
1106 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1108 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1111 switch(cmd.action) {
1112 case LFUN_BUFFER_WRITE:
1113 enable = buf && (buf->isUnnamed() || !buf->isClean());
1116 case LFUN_BUFFER_WRITE_AS:
1120 case LFUN_SPLIT_VIEW:
1121 if (cmd.getArg(0) == "vertical")
1122 enable = buf && (d.splitter_->count() == 1 ||
1123 d.splitter_->orientation() == Qt::Vertical);
1125 enable = buf && (d.splitter_->count() == 1 ||
1126 d.splitter_->orientation() == Qt::Horizontal);
1129 case LFUN_CLOSE_TAB_GROUP:
1130 enable = d.currentTabWorkArea();
1133 case LFUN_TOOLBAR_TOGGLE:
1134 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1135 flag.setOnOff(t->isVisible());
1138 case LFUN_UI_TOGGLE:
1139 flag.setOnOff(isFullScreen());
1142 case LFUN_DIALOG_TOGGLE:
1143 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1144 // fall through to set "enable"
1145 case LFUN_DIALOG_SHOW: {
1146 string const name = cmd.getArg(0);
1148 enable = name == "aboutlyx"
1149 || name == "file" //FIXME: should be removed.
1151 || name == "texinfo";
1152 else if (name == "print")
1153 enable = buf->isExportable("dvi")
1154 && lyxrc.print_command != "none";
1155 else if (name == "character") {
1159 InsetCode ic = view()->cursor().inset().lyxCode();
1160 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1163 else if (name == "symbols") {
1164 if (!view() || view()->cursor().inMathed())
1167 InsetCode ic = view()->cursor().inset().lyxCode();
1168 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1171 else if (name == "latexlog")
1172 enable = FileName(buf->logName()).isReadableFile();
1173 else if (name == "spellchecker")
1174 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1175 enable = !buf->isReadonly();
1179 else if (name == "vclog")
1180 enable = buf->lyxvc().inUse();
1184 case LFUN_DIALOG_UPDATE: {
1185 string const name = cmd.getArg(0);
1187 enable = name == "prefs";
1191 case LFUN_INSET_APPLY: {
1192 string const name = cmd.getArg(0);
1193 Inset * inset = getOpenInset(name);
1195 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1197 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1198 // Every inset is supposed to handle this
1199 LASSERT(false, break);
1203 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1204 flag |= lyx::getStatus(fr);
1206 enable = flag.enabled();
1210 case LFUN_COMPLETION_INLINE:
1211 if (!d.current_work_area_
1212 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1216 case LFUN_COMPLETION_POPUP:
1217 if (!d.current_work_area_
1218 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1222 case LFUN_COMPLETION_COMPLETE:
1223 if (!d.current_work_area_
1224 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1228 case LFUN_COMPLETION_ACCEPT:
1229 case LFUN_COMPLETION_CANCEL:
1230 if (!d.current_work_area_
1231 || (!d.current_work_area_->completer().popupVisible()
1232 && !d.current_work_area_->completer().inlineVisible()))
1241 flag.setEnabled(false);
1247 static FileName selectTemplateFile()
1249 FileDialog dlg(qt_("Select template file"));
1250 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1251 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1253 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1254 QStringList(qt_("LyX Documents (*.lyx)")));
1256 if (result.first == FileDialog::Later)
1258 if (result.second.isEmpty())
1260 return FileName(fromqstr(result.second));
1264 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1268 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1271 message(_("Document not loaded."));
1276 setBuffer(newBuffer);
1278 // scroll to the position when the file was last closed
1279 if (lyxrc.use_lastfilepos) {
1280 LastFilePosSection::FilePos filepos =
1281 theSession().lastFilePos().load(filename);
1282 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1286 theSession().lastFiles().add(filename);
1293 void GuiView::openDocument(string const & fname)
1295 string initpath = lyxrc.document_path;
1298 string const trypath = buffer()->filePath();
1299 // If directory is writeable, use this as default.
1300 if (FileName(trypath).isDirWritable())
1306 if (fname.empty()) {
1307 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1308 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1309 dlg.setButton2(qt_("Examples|#E#e"),
1310 toqstr(addPath(package().system_support().absFilename(), "examples")));
1312 QStringList filter(qt_("LyX Documents (*.lyx)"));
1313 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1314 << qt_("LyX-1.4.x Documents (*.lyx14)")
1315 << qt_("LyX-1.5.x Documents (*.lyx15)");
1316 FileDialog::Result result =
1317 dlg.open(toqstr(initpath), filter);
1319 if (result.first == FileDialog::Later)
1322 filename = fromqstr(result.second);
1324 // check selected filename
1325 if (filename.empty()) {
1326 message(_("Canceled."));
1332 // get absolute path of file and add ".lyx" to the filename if
1334 FileName const fullname =
1335 fileSearch(string(), filename, "lyx", support::may_not_exist);
1336 if (!fullname.empty())
1337 filename = fullname.absFilename();
1339 if (!fullname.onlyPath().isDirectory()) {
1340 Alert::warning(_("Invalid filename"),
1341 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1342 from_utf8(fullname.absFilename())));
1345 // if the file doesn't exist, let the user create one
1346 if (!fullname.exists()) {
1347 // the user specifically chose this name. Believe him.
1348 Buffer * const b = newFile(filename, string(), true);
1354 docstring const disp_fn = makeDisplayPath(filename);
1355 message(bformat(_("Opening document %1$s..."), disp_fn));
1358 Buffer * buf = loadDocument(fullname);
1363 buf->errors("Parse");
1364 str2 = bformat(_("Document %1$s opened."), disp_fn);
1365 if (buf->lyxvc().inUse())
1366 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1367 " " + _("Version control detected.");
1369 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1374 // FIXME: clean that
1375 static bool import(GuiView * lv, FileName const & filename,
1376 string const & format, ErrorList & errorList)
1378 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1380 string loader_format;
1381 vector<string> loaders = theConverters().loaders();
1382 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1383 for (vector<string>::const_iterator it = loaders.begin();
1384 it != loaders.end(); ++it) {
1385 if (!theConverters().isReachable(format, *it))
1388 string const tofile =
1389 support::changeExtension(filename.absFilename(),
1390 formats.extension(*it));
1391 if (!theConverters().convert(0, filename, FileName(tofile),
1392 filename, format, *it, errorList))
1394 loader_format = *it;
1397 if (loader_format.empty()) {
1398 frontend::Alert::error(_("Couldn't import file"),
1399 bformat(_("No information for importing the format %1$s."),
1400 formats.prettyName(format)));
1404 loader_format = format;
1406 if (loader_format == "lyx") {
1407 Buffer * buf = lv->loadDocument(lyxfile);
1412 buf->errors("Parse");
1414 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1418 bool as_paragraphs = loader_format == "textparagraph";
1419 string filename2 = (loader_format == format) ? filename.absFilename()
1420 : support::changeExtension(filename.absFilename(),
1421 formats.extension(loader_format));
1422 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1423 theLyXFunc().setLyXView(lv);
1424 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1431 void GuiView::importDocument(string const & argument)
1434 string filename = split(argument, format, ' ');
1436 LYXERR(Debug::INFO, format << " file: " << filename);
1438 // need user interaction
1439 if (filename.empty()) {
1440 string initpath = lyxrc.document_path;
1442 Buffer const * buf = buffer();
1444 string const trypath = buf->filePath();
1445 // If directory is writeable, use this as default.
1446 if (FileName(trypath).isDirWritable())
1450 docstring const text = bformat(_("Select %1$s file to import"),
1451 formats.prettyName(format));
1453 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1454 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1455 dlg.setButton2(qt_("Examples|#E#e"),
1456 toqstr(addPath(package().system_support().absFilename(), "examples")));
1458 docstring filter = formats.prettyName(format);
1461 filter += from_utf8(formats.extension(format));
1464 FileDialog::Result result =
1465 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1467 if (result.first == FileDialog::Later)
1470 filename = fromqstr(result.second);
1472 // check selected filename
1473 if (filename.empty())
1474 message(_("Canceled."));
1477 if (filename.empty())
1480 // get absolute path of file
1481 FileName const fullname(support::makeAbsPath(filename));
1483 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1485 // Check if the document already is open
1486 Buffer * buf = theBufferList().getBuffer(lyxfile);
1489 if (!closeBuffer()) {
1490 message(_("Canceled."));
1495 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1497 // if the file exists already, and we didn't do
1498 // -i lyx thefile.lyx, warn
1499 if (lyxfile.exists() && fullname != lyxfile) {
1501 docstring text = bformat(_("The document %1$s already exists.\n\n"
1502 "Do you want to overwrite that document?"), displaypath);
1503 int const ret = Alert::prompt(_("Overwrite document?"),
1504 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1507 message(_("Canceled."));
1512 message(bformat(_("Importing %1$s..."), displaypath));
1513 ErrorList errorList;
1514 if (import(this, fullname, format, errorList))
1515 message(_("imported."));
1517 message(_("file not imported!"));
1519 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1523 void GuiView::newDocument(string const & filename, bool from_template)
1525 FileName initpath(lyxrc.document_path);
1526 Buffer * buf = buffer();
1528 FileName const trypath(buf->filePath());
1529 // If directory is writeable, use this as default.
1530 if (trypath.isDirWritable())
1534 string templatefile = from_template ?
1535 selectTemplateFile().absFilename() : string();
1537 if (filename.empty())
1538 b = newUnnamedFile(templatefile, initpath);
1540 b = newFile(filename, templatefile, true);
1544 // Ensure the cursor is correctly positionned on screen.
1545 view()->showCursor();
1549 void GuiView::insertLyXFile(docstring const & fname)
1551 BufferView * bv = view();
1556 FileName filename(to_utf8(fname));
1558 if (!filename.empty()) {
1559 bv->insertLyXFile(filename);
1563 // Launch a file browser
1565 string initpath = lyxrc.document_path;
1566 string const trypath = bv->buffer().filePath();
1567 // If directory is writeable, use this as default.
1568 if (FileName(trypath).isDirWritable())
1572 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1573 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1574 dlg.setButton2(qt_("Examples|#E#e"),
1575 toqstr(addPath(package().system_support().absFilename(),
1578 FileDialog::Result result = dlg.open(toqstr(initpath),
1579 QStringList(qt_("LyX Documents (*.lyx)")));
1581 if (result.first == FileDialog::Later)
1585 filename.set(fromqstr(result.second));
1587 // check selected filename
1588 if (filename.empty()) {
1589 // emit message signal.
1590 message(_("Canceled."));
1594 bv->insertLyXFile(filename);
1598 void GuiView::insertPlaintextFile(docstring const & fname,
1601 BufferView * bv = view();
1606 FileName filename(to_utf8(fname));
1608 if (!filename.empty()) {
1609 bv->insertPlaintextFile(filename, asParagraph);
1613 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1614 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1616 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1619 if (result.first == FileDialog::Later)
1623 filename.set(fromqstr(result.second));
1625 // check selected filename
1626 if (filename.empty()) {
1627 // emit message signal.
1628 message(_("Canceled."));
1632 bv->insertPlaintextFile(filename, asParagraph);
1636 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1638 FileName fname = b.fileName();
1639 FileName const oldname = fname;
1641 if (!newname.empty()) {
1643 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1645 // Switch to this Buffer.
1648 /// No argument? Ask user through dialog.
1650 FileDialog dlg(qt_("Choose a filename to save document as"),
1651 LFUN_BUFFER_WRITE_AS);
1652 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1653 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1655 if (!isLyXFilename(fname.absFilename()))
1656 fname.changeExtension(".lyx");
1658 FileDialog::Result result =
1659 dlg.save(toqstr(fname.onlyPath().absFilename()),
1660 QStringList(qt_("LyX Documents (*.lyx)")),
1661 toqstr(fname.onlyFileName()));
1663 if (result.first == FileDialog::Later)
1666 fname.set(fromqstr(result.second));
1671 if (!isLyXFilename(fname.absFilename()))
1672 fname.changeExtension(".lyx");
1675 if (FileName(fname).exists()) {
1676 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1677 docstring text = bformat(_("The document %1$s already "
1678 "exists.\n\nDo you want to "
1679 "overwrite that document?"),
1681 int const ret = Alert::prompt(_("Overwrite document?"),
1682 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1685 case 1: return renameBuffer(b, docstring());
1686 case 2: return false;
1690 // Ok, change the name of the buffer
1691 b.setFileName(fname.absFilename());
1693 bool unnamed = b.isUnnamed();
1694 b.setUnnamed(false);
1695 b.saveCheckSum(fname);
1697 if (!saveBuffer(b)) {
1698 b.setFileName(oldname.absFilename());
1699 b.setUnnamed(unnamed);
1700 b.saveCheckSum(oldname);
1708 bool GuiView::saveBuffer(Buffer & b)
1711 return renameBuffer(b, docstring());
1714 theSession().lastFiles().add(b.fileName());
1718 // Switch to this Buffer.
1721 // FIXME: we don't tell the user *WHY* the save failed !!
1722 docstring const file = makeDisplayPath(b.absFileName(), 30);
1723 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1724 "Do you want to rename the document and "
1725 "try again?"), file);
1726 int const ret = Alert::prompt(_("Rename and save?"),
1727 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1730 if (!renameBuffer(b, docstring()))
1739 return saveBuffer(b);
1743 bool GuiView::closeBuffer()
1745 Buffer * buf = buffer();
1746 return buf && closeBuffer(*buf);
1750 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1752 // goto bookmark to update bookmark pit.
1753 //FIXME: we should update only the bookmarks related to this buffer!
1754 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1755 theLyXFunc().gotoBookmark(i+1, false, false);
1757 if (buf.isClean() || buf.paragraphs().empty()) {
1758 if (buf.masterBuffer() == &buf && tolastopened)
1759 theSession().lastOpened().add(buf.fileName());
1761 // Don't close child documents.
1762 removeWorkArea(d.current_work_area_);
1764 theBufferList().release(&buf);
1767 // Switch to this Buffer.
1772 if (buf.isUnnamed())
1773 file = from_utf8(buf.fileName().onlyFileName());
1775 file = buf.fileName().displayName(30);
1777 // Bring this window to top before asking questions.
1781 docstring const text = bformat(_("The document %1$s has unsaved changes."
1782 "\n\nDo you want to save the document or discard the changes?"), file);
1783 int const ret = Alert::prompt(_("Save changed document?"),
1784 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1788 if (!saveBuffer(buf))
1792 // if we crash after this we could
1793 // have no autosave file but I guess
1794 // this is really improbable (Jug)
1795 removeAutosaveFile(buf.absFileName());
1801 // save file names to .lyx/session
1802 // if master/slave are both open, do not save slave since it
1803 // will be automatically loaded when the master is loaded
1804 if (buf.masterBuffer() == &buf && tolastopened)
1805 theSession().lastOpened().add(buf.fileName());
1808 // Don't close child documents.
1809 removeWorkArea(d.current_work_area_);
1811 theBufferList().release(&buf);
1817 bool GuiView::dispatch(FuncRequest const & cmd)
1819 BufferView * bv = view();
1820 // By default we won't need any update.
1822 bv->cursor().updateFlags(Update::None);
1823 bool dispatched = true;
1825 switch(cmd.action) {
1826 case LFUN_BUFFER_IMPORT:
1827 importDocument(to_utf8(cmd.argument()));
1830 case LFUN_BUFFER_SWITCH:
1831 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1834 case LFUN_BUFFER_NEXT:
1835 setBuffer(theBufferList().next(buffer()));
1838 case LFUN_BUFFER_PREVIOUS:
1839 setBuffer(theBufferList().previous(buffer()));
1842 case LFUN_COMMAND_EXECUTE: {
1843 bool const show_it = cmd.argument() != "off";
1844 // FIXME: this is a hack, "minibuffer" should not be
1846 if (GuiToolbar * t = toolbar("minibuffer")) {
1847 t->setVisible(show_it);
1848 if (show_it && t->commandBuffer())
1849 t->commandBuffer()->setFocus();
1853 case LFUN_DROP_LAYOUTS_CHOICE:
1855 d.layout_->showPopup();
1858 case LFUN_MENU_OPEN:
1859 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1860 menu->exec(QCursor::pos());
1863 case LFUN_FILE_INSERT:
1864 insertLyXFile(cmd.argument());
1866 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1867 insertPlaintextFile(cmd.argument(), true);
1870 case LFUN_FILE_INSERT_PLAINTEXT:
1871 insertPlaintextFile(cmd.argument(), false);
1874 case LFUN_BUFFER_WRITE:
1876 saveBuffer(bv->buffer());
1879 case LFUN_BUFFER_WRITE_AS:
1881 renameBuffer(bv->buffer(), cmd.argument());
1884 case LFUN_BUFFER_WRITE_ALL: {
1885 Buffer * first = theBufferList().first();
1888 message(_("Saving all documents..."));
1889 // We cannot use a for loop as the buffer list cycles.
1892 if (!b->isClean()) {
1894 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1896 b = theBufferList().next(b);
1897 } while (b != first);
1898 message(_("All documents saved."));
1902 case LFUN_TOOLBAR_TOGGLE: {
1903 string const name = cmd.getArg(0);
1904 if (GuiToolbar * t = toolbar(name))
1909 case LFUN_DIALOG_UPDATE: {
1910 string const name = to_utf8(cmd.argument());
1911 // Can only update a dialog connected to an existing inset
1912 Inset * inset = getOpenInset(name);
1914 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1915 inset->dispatch(view()->cursor(), fr);
1916 } else if (name == "paragraph") {
1917 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1918 } else if (name == "prefs") {
1919 updateDialog(name, string());
1924 case LFUN_DIALOG_TOGGLE: {
1925 if (isDialogVisible(cmd.getArg(0)))
1926 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1928 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1932 case LFUN_DIALOG_DISCONNECT_INSET:
1933 disconnectDialog(to_utf8(cmd.argument()));
1936 case LFUN_DIALOG_HIDE: {
1937 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1941 case LFUN_DIALOG_SHOW: {
1942 string const name = cmd.getArg(0);
1943 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1945 if (name == "character") {
1946 data = freefont2string();
1948 showDialog("character", data);
1949 } else if (name == "latexlog") {
1950 Buffer::LogType type;
1951 string const logfile = buffer()->logName(&type);
1953 case Buffer::latexlog:
1956 case Buffer::buildlog:
1960 data += Lexer::quoteString(logfile);
1961 showDialog("log", data);
1962 } else if (name == "vclog") {
1963 string const data = "vc " +
1964 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1965 showDialog("log", data);
1966 } else if (name == "symbols") {
1967 data = bv->cursor().getEncoding()->name();
1969 showDialog("symbols", data);
1971 showDialog(name, data);
1975 case LFUN_INSET_APPLY: {
1976 string const name = cmd.getArg(0);
1977 Inset * inset = getOpenInset(name);
1979 // put cursor in front of inset.
1980 if (!view()->setCursorFromInset(inset))
1981 LASSERT(false, break);
1983 // useful if we are called from a dialog.
1984 view()->cursor().beginUndoGroup();
1985 view()->cursor().recordUndo();
1986 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1987 inset->dispatch(view()->cursor(), fr);
1988 view()->cursor().endUndoGroup();
1990 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1996 case LFUN_UI_TOGGLE:
1998 // Make sure the keyboard focus stays in the work area.
2002 case LFUN_SPLIT_VIEW:
2003 if (Buffer * buf = buffer()) {
2004 string const orientation = cmd.getArg(0);
2005 d.splitter_->setOrientation(orientation == "vertical"
2006 ? Qt::Vertical : Qt::Horizontal);
2007 TabWorkArea * twa = addTabWorkArea();
2008 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2009 setCurrentWorkArea(wa);
2013 case LFUN_CLOSE_TAB_GROUP:
2014 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2016 twa = d.currentTabWorkArea();
2017 // Switch to the next GuiWorkArea in the found TabWorkArea.
2019 d.current_work_area_ = twa->currentWorkArea();
2020 // Make sure the work area is up to date.
2021 twa->setCurrentWorkArea(d.current_work_area_);
2023 d.current_work_area_ = 0;
2025 if (d.splitter_->count() == 0)
2026 // No more work area, switch to the background widget.
2031 case LFUN_COMPLETION_INLINE:
2032 if (d.current_work_area_)
2033 d.current_work_area_->completer().showInline();
2036 case LFUN_COMPLETION_POPUP:
2037 if (d.current_work_area_)
2038 d.current_work_area_->completer().showPopup();
2042 case LFUN_COMPLETION_COMPLETE:
2043 if (d.current_work_area_)
2044 d.current_work_area_->completer().tab();
2047 case LFUN_COMPLETION_CANCEL:
2048 if (d.current_work_area_) {
2049 if (d.current_work_area_->completer().popupVisible())
2050 d.current_work_area_->completer().hidePopup();
2052 d.current_work_area_->completer().hideInline();
2056 case LFUN_COMPLETION_ACCEPT:
2057 if (d.current_work_area_)
2058 d.current_work_area_->completer().activate();
2067 // Part of automatic menu appearance feature.
2068 if (isFullScreen()) {
2069 if (menuBar()->isVisible())
2071 if (statusBar()->isVisible())
2072 statusBar()->hide();
2079 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2081 string const arg = cmd.getArg(0);
2082 if (arg == "scrollbar") {
2083 // hide() is of no help
2084 if (d.current_work_area_->verticalScrollBarPolicy() ==
2085 Qt::ScrollBarAlwaysOff)
2087 d.current_work_area_->setVerticalScrollBarPolicy(
2088 Qt::ScrollBarAsNeeded);
2090 d.current_work_area_->setVerticalScrollBarPolicy(
2091 Qt::ScrollBarAlwaysOff);
2094 if (arg == "statusbar") {
2095 statusBar()->setVisible(!statusBar()->isVisible());
2098 if (arg == "menubar") {
2099 menuBar()->setVisible(!menuBar()->isVisible());
2102 #if QT_VERSION >= 0x040300
2103 if (arg == "frame") {
2105 getContentsMargins(&l, &t, &r, &b);
2106 //are the frames in default state?
2107 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2109 setContentsMargins(-2, -2, -2, -2);
2111 setContentsMargins(0, 0, 0, 0);
2116 if (arg == "fullscreen") {
2121 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2125 void GuiView::toggleFullScreen()
2127 if (isFullScreen()) {
2128 for (int i = 0; i != d.splitter_->count(); ++i)
2129 d.tabWorkArea(i)->setFullScreen(false);
2130 #if QT_VERSION >= 0x040300
2131 setContentsMargins(0, 0, 0, 0);
2133 setWindowState(windowState() ^ Qt::WindowFullScreen);
2136 statusBar()->show();
2138 for (int i = 0; i != d.splitter_->count(); ++i)
2139 d.tabWorkArea(i)->setFullScreen(true);
2140 #if QT_VERSION >= 0x040300
2141 setContentsMargins(-2, -2, -2, -2);
2144 setWindowState(windowState() ^ Qt::WindowFullScreen);
2145 statusBar()->hide();
2147 if (lyxrc.full_screen_toolbars) {
2148 ToolbarMap::iterator end = d.toolbars_.end();
2149 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2154 // give dialogs like the TOC a chance to adapt
2159 Buffer const * GuiView::updateInset(Inset const * inset)
2161 if (!d.current_work_area_)
2165 d.current_work_area_->scheduleRedraw();
2167 return &d.current_work_area_->bufferView().buffer();
2171 void GuiView::restartCursor()
2173 /* When we move around, or type, it's nice to be able to see
2174 * the cursor immediately after the keypress.
2176 if (d.current_work_area_)
2177 d.current_work_area_->startBlinkingCursor();
2179 // Take this occasion to update the other GUI elements.
2185 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2187 if (d.current_work_area_)
2188 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2193 // This list should be kept in sync with the list of insets in
2194 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2195 // dialog should have the same name as the inset.
2196 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2197 // docs in LyXAction.cpp.
2199 char const * const dialognames[] = {
2200 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2201 "citation", "document", "errorlist", "ert", "external", "file",
2202 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2203 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2204 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2206 #ifdef HAVE_LIBAIKSAURUS
2210 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2212 char const * const * const end_dialognames =
2213 dialognames + (sizeof(dialognames) / sizeof(char *));
2217 cmpCStr(char const * name) : name_(name) {}
2218 bool operator()(char const * other) {
2219 return strcmp(other, name_) == 0;
2226 bool isValidName(string const & name)
2228 return find_if(dialognames, end_dialognames,
2229 cmpCStr(name.c_str())) != end_dialognames;
2235 void GuiView::resetDialogs()
2237 // Make sure that no LFUN uses any LyXView.
2238 theLyXFunc().setLyXView(0);
2241 constructToolbars();
2242 guiApp->menus().fillMenuBar(menuBar(), this, false);
2244 d.layout_->updateContents(true);
2245 // Now update controls with current buffer.
2246 theLyXFunc().setLyXView(this);
2252 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2254 if (!isValidName(name))
2257 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2259 if (it != d.dialogs_.end()) {
2261 it->second->hideView();
2262 return it->second.get();
2265 Dialog * dialog = build(name);
2266 d.dialogs_[name].reset(dialog);
2267 if (lyxrc.allow_geometry_session)
2268 dialog->restoreSession();
2275 void GuiView::showDialog(string const & name, string const & data,
2283 Dialog * dialog = findOrBuild(name, false);
2285 dialog->showData(data);
2287 d.open_insets_[name] = inset;
2290 catch (ExceptionMessage const & ex) {
2298 bool GuiView::isDialogVisible(string const & name) const
2300 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2301 if (it == d.dialogs_.end())
2303 return it->second.get()->isVisibleView();
2307 void GuiView::hideDialog(string const & name, Inset * inset)
2309 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2310 if (it == d.dialogs_.end())
2313 if (inset && inset != getOpenInset(name))
2316 Dialog * const dialog = it->second.get();
2317 if (dialog->isVisibleView())
2319 d.open_insets_[name] = 0;
2323 void GuiView::disconnectDialog(string const & name)
2325 if (!isValidName(name))
2328 if (d.open_insets_.find(name) != d.open_insets_.end())
2329 d.open_insets_[name] = 0;
2333 Inset * GuiView::getOpenInset(string const & name) const
2335 if (!isValidName(name))
2338 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2339 return it == d.open_insets_.end() ? 0 : it->second;
2343 void GuiView::hideAll() const
2345 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2346 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2348 for(; it != end; ++it)
2349 it->second->hideView();
2353 void GuiView::updateDialogs()
2355 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2356 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2358 for(; it != end; ++it) {
2359 Dialog * dialog = it->second.get();
2360 if (dialog && dialog->isVisibleView())
2361 dialog->checkStatus();
2368 // will be replaced by a proper factory...
2369 Dialog * createGuiAbout(GuiView & lv);
2370 Dialog * createGuiBibitem(GuiView & lv);
2371 Dialog * createGuiBibtex(GuiView & lv);
2372 Dialog * createGuiBox(GuiView & lv);
2373 Dialog * createGuiBranch(GuiView & lv);
2374 Dialog * createGuiChanges(GuiView & lv);
2375 Dialog * createGuiCharacter(GuiView & lv);
2376 Dialog * createGuiCitation(GuiView & lv);
2377 Dialog * createGuiDelimiter(GuiView & lv);
2378 Dialog * createGuiDocument(GuiView & lv);
2379 Dialog * createGuiErrorList(GuiView & lv);
2380 Dialog * createGuiERT(GuiView & lv);
2381 Dialog * createGuiExternal(GuiView & lv);
2382 Dialog * createGuiFloat(GuiView & lv);
2383 Dialog * createGuiGraphics(GuiView & lv);
2384 Dialog * createGuiHSpace(GuiView & lv);
2385 Dialog * createGuiInclude(GuiView & lv);
2386 Dialog * createGuiInfo(GuiView & lv);
2387 Dialog * createGuiLabel(GuiView & lv);
2388 Dialog * createGuiListings(GuiView & lv);
2389 Dialog * createGuiLog(GuiView & lv);
2390 Dialog * createGuiMathMatrix(GuiView & lv);
2391 Dialog * createGuiNomenclature(GuiView & lv);
2392 Dialog * createGuiNote(GuiView & lv);
2393 Dialog * createGuiParagraph(GuiView & lv);
2394 Dialog * createGuiPreferences(GuiView & lv);
2395 Dialog * createGuiPrint(GuiView & lv);
2396 Dialog * createGuiRef(GuiView & lv);
2397 Dialog * createGuiSearch(GuiView & lv);
2398 Dialog * createGuiSendTo(GuiView & lv);
2399 Dialog * createGuiShowFile(GuiView & lv);
2400 Dialog * createGuiSpellchecker(GuiView & lv);
2401 Dialog * createGuiSymbols(GuiView & lv);
2402 Dialog * createGuiTabularCreate(GuiView & lv);
2403 Dialog * createGuiTabular(GuiView & lv);
2404 Dialog * createGuiTexInfo(GuiView & lv);
2405 Dialog * createGuiToc(GuiView & lv);
2406 Dialog * createGuiThesaurus(GuiView & lv);
2407 Dialog * createGuiHyperlink(GuiView & lv);
2408 Dialog * createGuiVSpace(GuiView & lv);
2409 Dialog * createGuiViewSource(GuiView & lv);
2410 Dialog * createGuiWrap(GuiView & lv);
2413 Dialog * GuiView::build(string const & name)
2415 LASSERT(isValidName(name), return 0);
2417 if (name == "aboutlyx")
2418 return createGuiAbout(*this);
2419 if (name == "bibitem")
2420 return createGuiBibitem(*this);
2421 if (name == "bibtex")
2422 return createGuiBibtex(*this);
2424 return createGuiBox(*this);
2425 if (name == "branch")
2426 return createGuiBranch(*this);
2427 if (name == "changes")
2428 return createGuiChanges(*this);
2429 if (name == "character")
2430 return createGuiCharacter(*this);
2431 if (name == "citation")
2432 return createGuiCitation(*this);
2433 if (name == "document")
2434 return createGuiDocument(*this);
2435 if (name == "errorlist")
2436 return createGuiErrorList(*this);
2438 return createGuiERT(*this);
2439 if (name == "external")
2440 return createGuiExternal(*this);
2442 return createGuiShowFile(*this);
2443 if (name == "findreplace")
2444 return createGuiSearch(*this);
2445 if (name == "float")
2446 return createGuiFloat(*this);
2447 if (name == "graphics")
2448 return createGuiGraphics(*this);
2449 if (name == "include")
2450 return createGuiInclude(*this);
2452 return createGuiInfo(*this);
2453 if (name == "nomenclature")
2454 return createGuiNomenclature(*this);
2455 if (name == "label")
2456 return createGuiLabel(*this);
2458 return createGuiLog(*this);
2459 if (name == "view-source")
2460 return createGuiViewSource(*this);
2461 if (name == "mathdelimiter")
2462 return createGuiDelimiter(*this);
2463 if (name == "mathmatrix")
2464 return createGuiMathMatrix(*this);
2466 return createGuiNote(*this);
2467 if (name == "paragraph")
2468 return createGuiParagraph(*this);
2469 if (name == "prefs")
2470 return createGuiPreferences(*this);
2471 if (name == "print")
2472 return createGuiPrint(*this);
2474 return createGuiRef(*this);
2475 if (name == "sendto")
2476 return createGuiSendTo(*this);
2477 if (name == "space")
2478 return createGuiHSpace(*this);
2479 if (name == "spellchecker")
2480 return createGuiSpellchecker(*this);
2481 if (name == "symbols")
2482 return createGuiSymbols(*this);
2483 if (name == "tabular")
2484 return createGuiTabular(*this);
2485 if (name == "tabularcreate")
2486 return createGuiTabularCreate(*this);
2487 if (name == "texinfo")
2488 return createGuiTexInfo(*this);
2489 #ifdef HAVE_LIBAIKSAURUS
2490 if (name == "thesaurus")
2491 return createGuiThesaurus(*this);
2494 return createGuiToc(*this);
2496 return createGuiHyperlink(*this);
2497 if (name == "vspace")
2498 return createGuiVSpace(*this);
2500 return createGuiWrap(*this);
2501 if (name == "listings")
2502 return createGuiListings(*this);
2508 } // namespace frontend
2511 #include "GuiView_moc.cpp"