3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiCommandBuffer.h"
22 #include "GuiCompleter.h"
23 #include "GuiWorkArea.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToolbar.h"
29 #include "qt_helpers.h"
31 #include "frontends/alert.h"
33 #include "buffer_funcs.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
41 #include "ErrorList.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
58 #include "support/lassert.h"
59 #include "support/debug.h"
60 #include "support/ExceptionMessage.h"
61 #include "support/FileName.h"
62 #include "support/filetools.h"
63 #include "support/gettext.h"
64 #include "support/ForkedCalls.h"
65 #include "support/lstrings.h"
66 #include "support/os.h"
67 #include "support/Package.h"
68 #include "support/Timeout.h"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
116 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
117 /// The text to be written on top of the pixmap
118 QString const text = lyx_version ?
119 qt_("version ") + lyx_version : qt_("unknown version");
120 splash_ = QPixmap(":/images/banner.png");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
125 // The font used to display the version info
126 font.setStyleHint(QFont::SansSerif);
127 font.setWeight(QFont::Bold);
128 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
130 pain.drawText(190, 225, text);
133 void paintEvent(QPaintEvent *)
135 int x = (width() - splash_.width()) / 2;
136 int y = (height() - splash_.height()) / 2;
138 pain.drawPixmap(x, y, splash_);
145 /// Toolbar store providing access to individual toolbars by name.
146 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
148 typedef boost::shared_ptr<Dialog> DialogPtr;
153 struct GuiView::GuiViewPrivate
156 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
159 // hardcode here the platform specific icon size
160 smallIconSize = 14; // scaling problems
161 normalIconSize = 20; // ok, default
162 bigIconSize = 26; // better for some math icons
164 splitter_ = new QSplitter;
165 bg_widget_ = new BackgroundWidget;
166 stack_widget_ = new QStackedWidget;
167 stack_widget_->addWidget(bg_widget_);
168 stack_widget_->addWidget(splitter_);
176 delete stack_widget_;
179 QMenu * toolBarPopup(GuiView * parent)
181 // FIXME: translation
182 QMenu * menu = new QMenu(parent);
183 QActionGroup * iconSizeGroup = new QActionGroup(parent);
185 QAction * smallIcons = new QAction(iconSizeGroup);
186 smallIcons->setText(qt_("Small-sized icons"));
187 smallIcons->setCheckable(true);
188 QObject::connect(smallIcons, SIGNAL(triggered()),
189 parent, SLOT(smallSizedIcons()));
190 menu->addAction(smallIcons);
192 QAction * normalIcons = new QAction(iconSizeGroup);
193 normalIcons->setText(qt_("Normal-sized icons"));
194 normalIcons->setCheckable(true);
195 QObject::connect(normalIcons, SIGNAL(triggered()),
196 parent, SLOT(normalSizedIcons()));
197 menu->addAction(normalIcons);
199 QAction * bigIcons = new QAction(iconSizeGroup);
200 bigIcons->setText(qt_("Big-sized icons"));
201 bigIcons->setCheckable(true);
202 QObject::connect(bigIcons, SIGNAL(triggered()),
203 parent, SLOT(bigSizedIcons()));
204 menu->addAction(bigIcons);
206 unsigned int cur = parent->iconSize().width();
207 if ( cur == parent->d.smallIconSize)
208 smallIcons->setChecked(true);
209 else if (cur == parent->d.normalIconSize)
210 normalIcons->setChecked(true);
211 else if (cur == parent->d.bigIconSize)
212 bigIcons->setChecked(true);
219 stack_widget_->setCurrentWidget(bg_widget_);
220 bg_widget_->setUpdatesEnabled(true);
223 TabWorkArea * tabWorkArea(int i)
225 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
228 TabWorkArea * currentTabWorkArea()
230 if (splitter_->count() == 1)
231 // The first TabWorkArea is always the first one, if any.
232 return tabWorkArea(0);
234 for (int i = 0; i != splitter_->count(); ++i) {
235 TabWorkArea * twa = tabWorkArea(i);
236 if (current_work_area_ == twa->currentWorkArea())
240 // None has the focus so we just take the first one.
241 return tabWorkArea(0);
245 GuiWorkArea * current_work_area_;
246 QSplitter * splitter_;
247 QStackedWidget * stack_widget_;
248 BackgroundWidget * bg_widget_;
250 ToolbarMap toolbars_;
251 /// The main layout box.
253 * \warning Don't Delete! The layout box is actually owned by
254 * whichever toolbar contains it. All the GuiView class needs is a
255 * means of accessing it.
257 * FIXME: replace that with a proper model so that we are not limited
258 * to only one dialog.
260 GuiLayoutBox * layout_;
263 map<string, Inset *> open_insets_;
266 map<string, DialogPtr> dialogs_;
268 unsigned int smallIconSize;
269 unsigned int normalIconSize;
270 unsigned int bigIconSize;
272 QTimer statusbar_timer_;
273 /// auto-saving of buffers
274 Timeout autosave_timeout_;
275 /// flag against a race condition due to multiclicks, see bug #1119
279 TocModels toc_models_;
283 GuiView::GuiView(int id)
284 : d(*new GuiViewPrivate), id_(id), closing_(false)
286 // GuiToolbars *must* be initialised before the menu bar.
289 // set ourself as the current view. This is needed for the menu bar
290 // filling, at least for the static special menu item on Mac. Otherwise
291 // they are greyed out.
292 theLyXFunc().setLyXView(this);
294 // Fill up the menu bar.
295 guiApp->menus().fillMenuBar(menuBar(), this, true);
297 setCentralWidget(d.stack_widget_);
299 // Start autosave timer
300 if (lyxrc.autosave) {
301 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
302 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
303 d.autosave_timeout_.start();
305 connect(&d.statusbar_timer_, SIGNAL(timeout()),
306 this, SLOT(clearMessage()));
308 // We don't want to keep the window in memory if it is closed.
309 setAttribute(Qt::WA_DeleteOnClose, true);
311 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
312 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
313 // since the icon is provided in the application bundle.
314 setWindowIcon(QPixmap(":/images/lyx.png"));
318 setAcceptDrops(true);
320 statusBar()->setSizeGripEnabled(true);
322 // Forbid too small unresizable window because it can happen
323 // with some window manager under X11.
324 setMinimumSize(300, 200);
326 if (lyxrc.allow_geometry_session) {
327 // Now take care of session management.
332 // No session handling, default to a sane size.
333 setGeometry(50, 50, 690, 510);
335 // This enables to clear session data if any.
347 void GuiView::saveLayout() const
350 QString const key = "view-" + QString::number(id_);
352 settings.setValue(key + "/pos", pos());
353 settings.setValue(key + "/size", size());
355 settings.setValue(key + "/geometry", saveGeometry());
357 settings.setValue(key + "/layout", saveState(0));
358 settings.setValue(key + "/icon_size", iconSize());
362 bool GuiView::restoreLayout()
365 QString const key = "view-" + QString::number(id_);
366 QString const icon_key = key + "/icon_size";
367 if (!settings.contains(icon_key))
370 setIconSize(settings.value(icon_key).toSize());
372 QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
373 QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
377 if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
378 setGeometry(50, 50, 690, 510);
380 // Allow the toc and view-source dock widget to be restored if needed.
381 findOrBuild("toc", true);
382 findOrBuild("view-source", true);
383 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
390 GuiToolbar * GuiView::toolbar(string const & name)
392 ToolbarMap::iterator it = d.toolbars_.find(name);
393 if (it != d.toolbars_.end())
396 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
397 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
402 void GuiView::constructToolbars()
404 ToolbarMap::iterator it = d.toolbars_.begin();
405 for (; it != d.toolbars_.end(); ++it)
410 // extracts the toolbars from the backend
411 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
412 Toolbars::Infos::iterator end = guiApp->toolbars().end();
413 for (; cit != end; ++cit)
414 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
418 void GuiView::initToolbars()
420 // extracts the toolbars from the backend
421 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
422 Toolbars::Infos::iterator end = guiApp->toolbars().end();
423 for (; cit != end; ++cit) {
424 GuiToolbar * tb = toolbar(cit->name);
427 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
429 tb->setVisible(false);
430 tb->setVisibility(visibility);
432 if (visibility & Toolbars::TOP) {
434 addToolBarBreak(Qt::TopToolBarArea);
435 addToolBar(Qt::TopToolBarArea, tb);
438 if (visibility & Toolbars::BOTTOM) {
439 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
440 #if (QT_VERSION >= 0x040202)
441 addToolBarBreak(Qt::BottomToolBarArea);
443 addToolBar(Qt::BottomToolBarArea, tb);
446 if (visibility & Toolbars::LEFT) {
447 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
448 #if (QT_VERSION >= 0x040202)
449 addToolBarBreak(Qt::LeftToolBarArea);
451 addToolBar(Qt::LeftToolBarArea, tb);
454 if (visibility & Toolbars::RIGHT) {
455 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
456 #if (QT_VERSION >= 0x040202)
457 addToolBarBreak(Qt::RightToolBarArea);
459 addToolBar(Qt::RightToolBarArea, tb);
462 if (visibility & Toolbars::ON)
463 tb->setVisible(true);
468 TocModels & GuiView::tocModels()
470 return d.toc_models_;
474 void GuiView::setFocus()
476 // Make sure LyXFunc points to the correct view.
477 theLyXFunc().setLyXView(this);
478 if (d.current_work_area_)
479 d.current_work_area_->setFocus();
485 QMenu * GuiView::createPopupMenu()
487 return d.toolBarPopup(this);
491 void GuiView::showEvent(QShowEvent * e)
493 LYXERR(Debug::GUI, "Passed Geometry "
494 << size().height() << "x" << size().width()
495 << "+" << pos().x() << "+" << pos().y());
497 if (d.splitter_->count() == 0)
498 // No work area, switch to the background widget.
501 QMainWindow::showEvent(e);
505 void GuiView::closeEvent(QCloseEvent * close_event)
509 // it can happen that this event arrives without selecting the view,
510 // e.g. when clicking the close button on a background window.
511 theLyXFunc().setLyXView(this);
513 while (Buffer * b = buffer()) {
515 // This is a child document, just close the tab after saving
516 // but keep the file loaded.
517 if (!saveBuffer(*b)) {
519 close_event->ignore();
522 removeWorkArea(d.current_work_area_);
526 QList<int> const ids = guiApp->viewIds();
527 for (int i = 0; i != ids.size(); ++i) {
530 if (guiApp->view(ids[i]).workArea(*b)) {
531 // FIXME 1: should we put an alert box here that the buffer
532 // is viewed elsewhere?
533 // FIXME 2: should we try to save this buffer in any case?
536 // This buffer is also opened in another view, so
537 // close the associated work area...
538 removeWorkArea(d.current_work_area_);
539 // ... but don't close the buffer.
544 if (b && !closeBuffer(*b, true)) {
546 close_event->ignore();
551 // Make sure that nothing will use this close to be closed View.
552 guiApp->unregisterView(this);
554 if (isFullScreen()) {
555 // Switch off fullscreen before closing.
560 // Make sure the timer time out will not trigger a statusbar update.
561 d.statusbar_timer_.stop();
563 // Saving fullscreen requires additional tweaks in the toolbar code.
564 // It wouldn't also work under linux natively.
565 if (lyxrc.allow_geometry_session) {
566 // Save this window geometry and layout.
568 // Then the toolbar private states.
569 ToolbarMap::iterator end = d.toolbars_.end();
570 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
571 it->second->saveSession();
572 // Now take care of all other dialogs:
573 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
574 for (; it!= d.dialogs_.end(); ++it)
575 it->second->saveSession();
578 close_event->accept();
582 void GuiView::dragEnterEvent(QDragEnterEvent * event)
584 if (event->mimeData()->hasUrls())
586 /// \todo Ask lyx-devel is this is enough:
587 /// if (event->mimeData()->hasFormat("text/plain"))
588 /// event->acceptProposedAction();
592 void GuiView::dropEvent(QDropEvent* event)
594 QList<QUrl> files = event->mimeData()->urls();
598 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
599 for (int i = 0; i != files.size(); ++i) {
600 string const file = os::internal_path(fromqstr(
601 files.at(i).toLocalFile()));
603 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
608 void GuiView::message(docstring const & str)
610 if (ForkedProcess::iAmAChild())
613 statusBar()->showMessage(toqstr(str));
614 d.statusbar_timer_.stop();
615 d.statusbar_timer_.start(3000);
619 void GuiView::smallSizedIcons()
621 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
625 void GuiView::normalSizedIcons()
627 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
631 void GuiView::bigSizedIcons()
633 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
637 void GuiView::clearMessage()
641 theLyXFunc().setLyXView(this);
642 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
643 d.statusbar_timer_.stop();
647 void GuiView::updateWindowTitle(GuiWorkArea * wa)
649 if (wa != d.current_work_area_)
651 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
652 setWindowIconText(wa->windowIconText());
656 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
659 disconnectBufferView();
660 connectBufferView(wa->bufferView());
661 connectBuffer(wa->bufferView().buffer());
662 d.current_work_area_ = wa;
663 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
664 this, SLOT(updateWindowTitle(GuiWorkArea *)));
665 updateWindowTitle(wa);
669 // The document settings needs to be reinitialised.
670 updateDialog("document", "");
672 // Buffer-dependent dialogs must be updated. This is done here because
673 // some dialogs require buffer()->text.
678 void GuiView::on_lastWorkAreaRemoved()
681 // We already are in a close event. Nothing more to do.
684 if (d.splitter_->count() > 1)
685 // We have a splitter so don't close anything.
688 // Reset and updates the dialogs.
689 d.toc_models_.reset(0);
690 updateDialog("document", "");
693 resetWindowTitleAndIconText();
695 if (lyxrc.open_buffers_in_tabs)
696 // Nothing more to do, the window should stay open.
699 if (guiApp->viewIds().size() > 1) {
705 // On Mac we also close the last window because the application stay
706 // resident in memory. On other platforms we don't close the last
707 // window because this would quit the application.
713 void GuiView::updateStatusBar()
715 // let the user see the explicit message
716 if (d.statusbar_timer_.isActive())
719 theLyXFunc().setLyXView(this);
720 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
724 bool GuiView::hasFocus() const
726 return qApp->activeWindow() == this;
730 bool GuiView::event(QEvent * e)
734 // Useful debug code:
735 //case QEvent::ActivationChange:
736 //case QEvent::WindowDeactivate:
737 //case QEvent::Paint:
738 //case QEvent::Enter:
739 //case QEvent::Leave:
740 //case QEvent::HoverEnter:
741 //case QEvent::HoverLeave:
742 //case QEvent::HoverMove:
743 //case QEvent::StatusTip:
744 //case QEvent::DragEnter:
745 //case QEvent::DragLeave:
749 case QEvent::WindowActivate: {
750 if (this == guiApp->currentView()) {
752 return QMainWindow::event(e);
754 guiApp->setCurrentView(this);
755 theLyXFunc().setLyXView(this);
756 if (d.current_work_area_) {
757 BufferView & bv = d.current_work_area_->bufferView();
758 connectBufferView(bv);
759 connectBuffer(bv.buffer());
760 // The document structure, name and dialogs might have
761 // changed in another view.
763 // The document settings needs to be reinitialised.
764 updateDialog("document", "");
767 resetWindowTitleAndIconText();
770 return QMainWindow::event(e);
773 case QEvent::ShortcutOverride: {
777 if (isFullScreen() && menuBar()->isHidden()) {
778 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
779 // FIXME: we should also try to detect special LyX shortcut such as
780 // Alt-P and Alt-M. Right now there is a hack in
781 // GuiWorkArea::processKeySym() that hides again the menubar for
783 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
785 return QMainWindow::event(e);
789 if (d.current_work_area_)
790 // Nothing special to do.
791 return QMainWindow::event(e);
793 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
794 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
796 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
797 || ke->key() == Qt::Key_Backtab)
798 return QMainWindow::event(e);
800 // Allow processing of shortcuts that are allowed even when no Buffer
802 theLyXFunc().setLyXView(this);
804 setKeySymbol(&sym, ke);
805 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
811 return QMainWindow::event(e);
815 void GuiView::resetWindowTitleAndIconText()
817 setWindowTitle(qt_("LyX"));
818 setWindowIconText(qt_("LyX"));
821 bool GuiView::focusNextPrevChild(bool /*next*/)
828 void GuiView::setBusy(bool busy)
830 if (d.current_work_area_) {
831 d.current_work_area_->setUpdatesEnabled(!busy);
833 d.current_work_area_->stopBlinkingCursor();
835 d.current_work_area_->startBlinkingCursor();
839 QApplication::setOverrideCursor(Qt::WaitCursor);
841 QApplication::restoreOverrideCursor();
845 GuiWorkArea * GuiView::workArea(Buffer & buffer)
847 if (TabWorkArea * twa = d.currentTabWorkArea())
848 return twa->workArea(buffer);
853 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
855 // Automatically create a TabWorkArea if there are none yet.
856 TabWorkArea * tab_widget = d.splitter_->count()
857 ? d.currentTabWorkArea() : addTabWorkArea();
858 return tab_widget->addWorkArea(buffer, *this);
862 TabWorkArea * GuiView::addTabWorkArea()
864 TabWorkArea * twa = new TabWorkArea;
865 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
866 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
867 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
868 this, SLOT(on_lastWorkAreaRemoved()));
870 d.splitter_->addWidget(twa);
871 d.stack_widget_->setCurrentWidget(d.splitter_);
876 GuiWorkArea const * GuiView::currentWorkArea() const
878 return d.current_work_area_;
882 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
885 d.current_work_area_ = wa;
886 for (int i = 0; i != d.splitter_->count(); ++i) {
887 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
893 void GuiView::removeWorkArea(GuiWorkArea * wa)
896 if (wa == d.current_work_area_) {
898 disconnectBufferView();
899 d.current_work_area_ = 0;
902 for (int i = 0; i != d.splitter_->count(); ++i) {
903 TabWorkArea * twa = d.tabWorkArea(i);
904 if (!twa->removeWorkArea(wa))
905 // Not found in this tab group.
908 // We found and removed the GuiWorkArea.
910 // No more WorkAreas in this tab group, so delete it.
915 if (d.current_work_area_)
916 // This means that we are not closing the current GuiWorkArea;
919 // Switch to the next GuiWorkArea in the found TabWorkArea.
920 d.current_work_area_ = twa->currentWorkArea();
924 if (d.splitter_->count() == 0)
925 // No more work area, switch to the background widget.
930 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
936 void GuiView::updateLayoutList()
939 d.layout_->updateContents(false);
943 void GuiView::updateToolbars()
945 ToolbarMap::iterator end = d.toolbars_.end();
946 if (d.current_work_area_) {
948 d.current_work_area_->bufferView().cursor().inMathed();
950 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
952 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
953 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
954 bool const mathmacrotemplate =
955 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
957 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
958 it->second->update(math, table, review, mathmacrotemplate);
960 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
961 it->second->update(false, false, false, false);
965 Buffer * GuiView::buffer()
967 if (d.current_work_area_)
968 return &d.current_work_area_->bufferView().buffer();
973 Buffer const * GuiView::buffer() const
975 if (d.current_work_area_)
976 return &d.current_work_area_->bufferView().buffer();
981 void GuiView::setBuffer(Buffer * newBuffer)
983 LASSERT(newBuffer, /**/);
986 GuiWorkArea * wa = workArea(*newBuffer);
988 updateLabels(*newBuffer->masterBuffer());
989 wa = addWorkArea(*newBuffer);
991 //Disconnect the old buffer...there's no new one.
994 connectBuffer(*newBuffer);
995 connectBufferView(wa->bufferView());
996 setCurrentWorkArea(wa);
1002 void GuiView::connectBuffer(Buffer & buf)
1004 buf.setGuiDelegate(this);
1008 void GuiView::disconnectBuffer()
1010 if (d.current_work_area_)
1011 d.current_work_area_->bufferView().setGuiDelegate(0);
1015 void GuiView::connectBufferView(BufferView & bv)
1017 bv.setGuiDelegate(this);
1021 void GuiView::disconnectBufferView()
1023 if (d.current_work_area_)
1024 d.current_work_area_->bufferView().setGuiDelegate(0);
1028 void GuiView::errors(string const & error_type)
1030 ErrorList & el = buffer()->errorList(error_type);
1032 showDialog("errorlist", error_type);
1036 void GuiView::structureChanged()
1038 d.toc_models_.reset(view());
1039 // Navigator needs more than a simple update in this case. It needs to be
1041 updateDialog("toc", "");
1045 void GuiView::updateDialog(string const & name, string const & data)
1047 if (!isDialogVisible(name))
1050 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1051 if (it == d.dialogs_.end())
1054 Dialog * const dialog = it->second.get();
1055 if (dialog->isVisibleView())
1056 dialog->initialiseParams(data);
1060 BufferView * GuiView::view()
1062 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1066 void GuiView::autoSave()
1068 LYXERR(Debug::INFO, "Running autoSave()");
1071 view()->buffer().autoSave();
1075 void GuiView::resetAutosaveTimers()
1078 d.autosave_timeout_.restart();
1082 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1085 Buffer * buf = buffer();
1087 /* In LyX/Mac, when a dialog is open, the menus of the
1088 application can still be accessed without giving focus to
1089 the main window. In this case, we want to disable the menu
1090 entries that are buffer-related.
1092 Note that this code is not perfect, as bug 1941 attests:
1093 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1095 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1098 switch(cmd.action) {
1099 case LFUN_BUFFER_WRITE:
1100 enable = buf && (buf->isUnnamed() || !buf->isClean());
1103 case LFUN_BUFFER_WRITE_AS:
1107 case LFUN_SPLIT_VIEW:
1108 if (cmd.getArg(0) == "vertical")
1109 enable = buf && (d.splitter_->count() == 1 ||
1110 d.splitter_->orientation() == Qt::Vertical);
1112 enable = buf && (d.splitter_->count() == 1 ||
1113 d.splitter_->orientation() == Qt::Horizontal);
1116 case LFUN_CLOSE_TAB_GROUP:
1117 enable = d.currentTabWorkArea();
1120 case LFUN_TOOLBAR_TOGGLE:
1121 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1122 flag.setOnOff(t->isVisible());
1125 case LFUN_UI_TOGGLE:
1126 flag.setOnOff(isFullScreen());
1129 case LFUN_DIALOG_TOGGLE:
1130 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1131 // fall through to set "enable"
1132 case LFUN_DIALOG_SHOW: {
1133 string const name = cmd.getArg(0);
1135 enable = name == "aboutlyx"
1136 || name == "file" //FIXME: should be removed.
1138 || name == "texinfo";
1139 else if (name == "print")
1140 enable = buf->isExportable("dvi")
1141 && lyxrc.print_command != "none";
1142 else if (name == "character") {
1146 InsetCode ic = view()->cursor().inset().lyxCode();
1147 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1150 else if (name == "symbols") {
1151 if (!view() || view()->cursor().inMathed())
1154 InsetCode ic = view()->cursor().inset().lyxCode();
1155 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1158 else if (name == "latexlog")
1159 enable = FileName(buf->logName()).isReadableFile();
1160 else if (name == "spellchecker")
1161 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1162 enable = !buf->isReadonly();
1166 else if (name == "vclog")
1167 enable = buf->lyxvc().inUse();
1171 case LFUN_DIALOG_UPDATE: {
1172 string const name = cmd.getArg(0);
1174 enable = name == "prefs";
1178 case LFUN_INSET_APPLY: {
1179 string const name = cmd.getArg(0);
1180 Inset * inset = getOpenInset(name);
1182 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1184 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1185 // Every inset is supposed to handle this
1186 LASSERT(false, /**/);
1190 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1191 flag |= lyx::getStatus(fr);
1193 enable = flag.enabled();
1197 case LFUN_COMPLETION_INLINE:
1198 if (!d.current_work_area_
1199 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1203 case LFUN_COMPLETION_POPUP:
1204 if (!d.current_work_area_
1205 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1209 case LFUN_COMPLETION_COMPLETE:
1210 if (!d.current_work_area_
1211 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1220 flag.setEnabled(false);
1226 static FileName selectTemplateFile()
1228 FileDialog dlg(qt_("Select template file"));
1229 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1230 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1232 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1233 QStringList(qt_("LyX Documents (*.lyx)")));
1235 if (result.first == FileDialog::Later)
1237 if (result.second.isEmpty())
1239 return FileName(fromqstr(result.second));
1243 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1247 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1250 message(_("Document not loaded."));
1255 setBuffer(newBuffer);
1257 // scroll to the position when the file was last closed
1258 if (lyxrc.use_lastfilepos) {
1259 LastFilePosSection::FilePos filepos =
1260 theSession().lastFilePos().load(filename);
1261 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1265 theSession().lastFiles().add(filename);
1272 void GuiView::openDocument(string const & fname)
1274 string initpath = lyxrc.document_path;
1277 string const trypath = buffer()->filePath();
1278 // If directory is writeable, use this as default.
1279 if (FileName(trypath).isDirWritable())
1285 if (fname.empty()) {
1286 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1287 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1288 dlg.setButton2(qt_("Examples|#E#e"),
1289 toqstr(addPath(package().system_support().absFilename(), "examples")));
1291 QStringList filter(qt_("LyX Documents (*.lyx)"));
1292 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1293 << qt_("LyX-1.4.x Documents (*.lyx14)")
1294 << qt_("LyX-1.5.x Documents (*.lyx15)");
1295 FileDialog::Result result =
1296 dlg.open(toqstr(initpath), filter);
1298 if (result.first == FileDialog::Later)
1301 filename = fromqstr(result.second);
1303 // check selected filename
1304 if (filename.empty()) {
1305 message(_("Canceled."));
1311 // get absolute path of file and add ".lyx" to the filename if
1313 FileName const fullname =
1314 fileSearch(string(), filename, "lyx", support::may_not_exist);
1315 if (!fullname.empty())
1316 filename = fullname.absFilename();
1318 // if the file doesn't exist, let the user create one
1319 if (!fullname.exists()) {
1320 // the user specifically chose this name. Believe him.
1321 Buffer * const b = newFile(filename, string(), true);
1327 docstring const disp_fn = makeDisplayPath(filename);
1328 message(bformat(_("Opening document %1$s..."), disp_fn));
1331 Buffer * buf = loadDocument(fullname);
1336 buf->errors("Parse");
1337 str2 = bformat(_("Document %1$s opened."), disp_fn);
1338 if (buf->lyxvc().inUse())
1339 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1340 " " + _("Version control detected.");
1342 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1347 // FIXME: clean that
1348 static bool import(GuiView * lv, FileName const & filename,
1349 string const & format, ErrorList & errorList)
1351 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1353 string loader_format;
1354 vector<string> loaders = theConverters().loaders();
1355 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1356 for (vector<string>::const_iterator it = loaders.begin();
1357 it != loaders.end(); ++it) {
1358 if (!theConverters().isReachable(format, *it))
1361 string const tofile =
1362 support::changeExtension(filename.absFilename(),
1363 formats.extension(*it));
1364 if (!theConverters().convert(0, filename, FileName(tofile),
1365 filename, format, *it, errorList))
1367 loader_format = *it;
1370 if (loader_format.empty()) {
1371 frontend::Alert::error(_("Couldn't import file"),
1372 bformat(_("No information for importing the format %1$s."),
1373 formats.prettyName(format)));
1377 loader_format = format;
1379 if (loader_format == "lyx") {
1380 Buffer * buf = lv->loadDocument(lyxfile);
1385 buf->errors("Parse");
1387 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1391 bool as_paragraphs = loader_format == "textparagraph";
1392 string filename2 = (loader_format == format) ? filename.absFilename()
1393 : support::changeExtension(filename.absFilename(),
1394 formats.extension(loader_format));
1395 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1396 theLyXFunc().setLyXView(lv);
1397 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1404 void GuiView::importDocument(string const & argument)
1407 string filename = split(argument, format, ' ');
1409 LYXERR(Debug::INFO, format << " file: " << filename);
1411 // need user interaction
1412 if (filename.empty()) {
1413 string initpath = lyxrc.document_path;
1415 Buffer const * buf = buffer();
1417 string const trypath = buf->filePath();
1418 // If directory is writeable, use this as default.
1419 if (FileName(trypath).isDirWritable())
1423 docstring const text = bformat(_("Select %1$s file to import"),
1424 formats.prettyName(format));
1426 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1427 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1428 dlg.setButton2(qt_("Examples|#E#e"),
1429 toqstr(addPath(package().system_support().absFilename(), "examples")));
1431 docstring filter = formats.prettyName(format);
1434 filter += from_utf8(formats.extension(format));
1437 FileDialog::Result result =
1438 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1440 if (result.first == FileDialog::Later)
1443 filename = fromqstr(result.second);
1445 // check selected filename
1446 if (filename.empty())
1447 message(_("Canceled."));
1450 if (filename.empty())
1453 // get absolute path of file
1454 FileName const fullname(support::makeAbsPath(filename));
1456 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1458 // Check if the document already is open
1459 Buffer * buf = theBufferList().getBuffer(lyxfile);
1462 if (!closeBuffer()) {
1463 message(_("Canceled."));
1468 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1470 // if the file exists already, and we didn't do
1471 // -i lyx thefile.lyx, warn
1472 if (lyxfile.exists() && fullname != lyxfile) {
1474 docstring text = bformat(_("The document %1$s already exists.\n\n"
1475 "Do you want to overwrite that document?"), displaypath);
1476 int const ret = Alert::prompt(_("Overwrite document?"),
1477 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1480 message(_("Canceled."));
1485 message(bformat(_("Importing %1$s..."), displaypath));
1486 ErrorList errorList;
1487 if (import(this, fullname, format, errorList))
1488 message(_("imported."));
1490 message(_("file not imported!"));
1492 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1496 void GuiView::newDocument(string const & filename, bool from_template)
1498 FileName initpath(lyxrc.document_path);
1499 Buffer * buf = buffer();
1501 FileName const trypath(buf->filePath());
1502 // If directory is writeable, use this as default.
1503 if (trypath.isDirWritable())
1507 string templatefile = from_template ?
1508 selectTemplateFile().absFilename() : string();
1510 if (filename.empty())
1511 b = newUnnamedFile(templatefile, initpath);
1513 b = newFile(filename, templatefile, true);
1517 // Ensure the cursor is correctly positionned on screen.
1518 view()->showCursor();
1522 void GuiView::insertLyXFile(docstring const & fname)
1524 BufferView * bv = view();
1529 FileName filename(to_utf8(fname));
1531 if (!filename.empty()) {
1532 bv->insertLyXFile(filename);
1536 // Launch a file browser
1538 string initpath = lyxrc.document_path;
1539 string const trypath = bv->buffer().filePath();
1540 // If directory is writeable, use this as default.
1541 if (FileName(trypath).isDirWritable())
1545 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1546 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1547 dlg.setButton2(qt_("Examples|#E#e"),
1548 toqstr(addPath(package().system_support().absFilename(),
1551 FileDialog::Result result = dlg.open(toqstr(initpath),
1552 QStringList(qt_("LyX Documents (*.lyx)")));
1554 if (result.first == FileDialog::Later)
1558 filename.set(fromqstr(result.second));
1560 // check selected filename
1561 if (filename.empty()) {
1562 // emit message signal.
1563 message(_("Canceled."));
1567 bv->insertLyXFile(filename);
1571 void GuiView::insertPlaintextFile(docstring const & fname,
1574 BufferView * bv = view();
1579 FileName filename(to_utf8(fname));
1581 if (!filename.empty()) {
1582 bv->insertPlaintextFile(filename, asParagraph);
1586 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1587 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1589 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1592 if (result.first == FileDialog::Later)
1596 filename.set(fromqstr(result.second));
1598 // check selected filename
1599 if (filename.empty()) {
1600 // emit message signal.
1601 message(_("Canceled."));
1605 bv->insertPlaintextFile(filename, asParagraph);
1609 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1611 FileName fname = b.fileName();
1612 FileName const oldname = fname;
1614 if (!newname.empty()) {
1616 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1618 // Switch to this Buffer.
1621 /// No argument? Ask user through dialog.
1623 FileDialog dlg(qt_("Choose a filename to save document as"),
1624 LFUN_BUFFER_WRITE_AS);
1625 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1626 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1628 if (!isLyXFilename(fname.absFilename()))
1629 fname.changeExtension(".lyx");
1631 FileDialog::Result result =
1632 dlg.save(toqstr(fname.onlyPath().absFilename()),
1633 QStringList(qt_("LyX Documents (*.lyx)")),
1634 toqstr(fname.onlyFileName()));
1636 if (result.first == FileDialog::Later)
1639 fname.set(fromqstr(result.second));
1644 if (!isLyXFilename(fname.absFilename()))
1645 fname.changeExtension(".lyx");
1648 if (FileName(fname).exists()) {
1649 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1650 docstring text = bformat(_("The document %1$s already "
1651 "exists.\n\nDo you want to "
1652 "overwrite that document?"),
1654 int const ret = Alert::prompt(_("Overwrite document?"),
1655 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1658 case 1: return renameBuffer(b, docstring());
1659 case 2: return false;
1663 // Ok, change the name of the buffer
1664 b.setFileName(fname.absFilename());
1666 bool unnamed = b.isUnnamed();
1667 b.setUnnamed(false);
1668 b.saveCheckSum(fname);
1670 if (!saveBuffer(b)) {
1671 b.setFileName(oldname.absFilename());
1672 b.setUnnamed(unnamed);
1673 b.saveCheckSum(oldname);
1681 bool GuiView::saveBuffer(Buffer & b)
1684 return renameBuffer(b, docstring());
1687 theSession().lastFiles().add(b.fileName());
1691 // Switch to this Buffer.
1694 // FIXME: we don't tell the user *WHY* the save failed !!
1695 docstring const file = makeDisplayPath(b.absFileName(), 30);
1696 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1697 "Do you want to rename the document and "
1698 "try again?"), file);
1699 int const ret = Alert::prompt(_("Rename and save?"),
1700 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1703 if (!renameBuffer(b, docstring()))
1712 return saveBuffer(b);
1716 bool GuiView::closeBuffer()
1718 Buffer * buf = buffer();
1719 return buf && closeBuffer(*buf);
1723 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1725 // goto bookmark to update bookmark pit.
1726 //FIXME: we should update only the bookmarks related to this buffer!
1727 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1728 theLyXFunc().gotoBookmark(i+1, false, false);
1730 if (buf.isClean() || buf.paragraphs().empty()) {
1731 if (buf.masterBuffer() == &buf && tolastopened)
1732 theSession().lastOpened().add(buf.fileName());
1733 theBufferList().release(&buf);
1736 // Switch to this Buffer.
1741 if (buf.isUnnamed())
1742 file = from_utf8(buf.fileName().onlyFileName());
1744 file = buf.fileName().displayName(30);
1746 // Bring this window to top before asking questions.
1750 docstring const text = bformat(_("The document %1$s has unsaved changes."
1751 "\n\nDo you want to save the document or discard the changes?"), file);
1752 int const ret = Alert::prompt(_("Save changed document?"),
1753 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1757 if (!saveBuffer(buf))
1761 // if we crash after this we could
1762 // have no autosave file but I guess
1763 // this is really improbable (Jug)
1764 removeAutosaveFile(buf.absFileName());
1770 // save file names to .lyx/session
1771 // if master/slave are both open, do not save slave since it
1772 // will be automatically loaded when the master is loaded
1773 if (buf.masterBuffer() == &buf && tolastopened)
1774 theSession().lastOpened().add(buf.fileName());
1777 // Don't close child documents.
1778 removeWorkArea(d.current_work_area_);
1780 theBufferList().release(&buf);
1786 bool GuiView::dispatch(FuncRequest const & cmd)
1788 BufferView * bv = view();
1789 // By default we won't need any update.
1791 bv->cursor().updateFlags(Update::None);
1792 bool dispatched = true;
1794 switch(cmd.action) {
1795 case LFUN_BUFFER_IMPORT:
1796 importDocument(to_utf8(cmd.argument()));
1799 case LFUN_BUFFER_SWITCH:
1800 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1803 case LFUN_BUFFER_NEXT:
1804 setBuffer(theBufferList().next(buffer()));
1807 case LFUN_BUFFER_PREVIOUS:
1808 setBuffer(theBufferList().previous(buffer()));
1811 case LFUN_COMMAND_EXECUTE: {
1812 bool const show_it = cmd.argument() != "off";
1813 // FIXME: this is a hack, "minibuffer" should not be
1815 if (GuiToolbar * t = toolbar("minibuffer")) {
1816 t->setVisible(show_it);
1817 if (show_it && t->commandBuffer())
1818 t->commandBuffer()->setFocus();
1822 case LFUN_DROP_LAYOUTS_CHOICE:
1824 d.layout_->showPopup();
1827 case LFUN_MENU_OPEN:
1828 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1829 menu->exec(QCursor::pos());
1832 case LFUN_FILE_INSERT:
1833 insertLyXFile(cmd.argument());
1835 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1836 insertPlaintextFile(cmd.argument(), true);
1839 case LFUN_FILE_INSERT_PLAINTEXT:
1840 insertPlaintextFile(cmd.argument(), false);
1843 case LFUN_BUFFER_WRITE:
1845 saveBuffer(bv->buffer());
1848 case LFUN_BUFFER_WRITE_AS:
1850 renameBuffer(bv->buffer(), cmd.argument());
1853 case LFUN_BUFFER_WRITE_ALL: {
1854 Buffer * first = theBufferList().first();
1857 message(_("Saving all documents..."));
1858 // We cannot use a for loop as the buffer list cycles.
1861 if (!b->isClean()) {
1863 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1865 b = theBufferList().next(b);
1866 } while (b != first);
1867 message(_("All documents saved."));
1871 case LFUN_TOOLBAR_TOGGLE: {
1872 string const name = cmd.getArg(0);
1873 if (GuiToolbar * t = toolbar(name))
1878 case LFUN_DIALOG_UPDATE: {
1879 string const name = to_utf8(cmd.argument());
1880 // Can only update a dialog connected to an existing inset
1881 Inset * inset = getOpenInset(name);
1883 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1884 inset->dispatch(view()->cursor(), fr);
1885 } else if (name == "paragraph") {
1886 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1887 } else if (name == "prefs") {
1888 updateDialog(name, string());
1893 case LFUN_DIALOG_TOGGLE: {
1894 if (isDialogVisible(cmd.getArg(0)))
1895 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1897 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1901 case LFUN_DIALOG_DISCONNECT_INSET:
1902 disconnectDialog(to_utf8(cmd.argument()));
1905 case LFUN_DIALOG_HIDE: {
1906 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1910 case LFUN_DIALOG_SHOW: {
1911 string const name = cmd.getArg(0);
1912 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1914 if (name == "character") {
1915 data = freefont2string();
1917 showDialog("character", data);
1918 } else if (name == "latexlog") {
1919 Buffer::LogType type;
1920 string const logfile = buffer()->logName(&type);
1922 case Buffer::latexlog:
1925 case Buffer::buildlog:
1929 data += Lexer::quoteString(logfile);
1930 showDialog("log", data);
1931 } else if (name == "vclog") {
1932 string const data = "vc " +
1933 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1934 showDialog("log", data);
1935 } else if (name == "symbols") {
1936 data = bv->cursor().getEncoding()->name();
1938 showDialog("symbols", data);
1940 showDialog(name, data);
1944 case LFUN_INSET_APPLY: {
1945 view()->cursor().recordUndoFullDocument();
1946 string const name = cmd.getArg(0);
1947 Inset * inset = getOpenInset(name);
1949 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1950 inset->dispatch(view()->cursor(), fr);
1952 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1958 case LFUN_UI_TOGGLE:
1960 // Make sure the keyboard focus stays in the work area.
1964 case LFUN_COMPLETION_INLINE:
1965 if (d.current_work_area_)
1966 d.current_work_area_->completer().showInline();
1969 case LFUN_SPLIT_VIEW:
1970 if (Buffer * buf = buffer()) {
1971 string const orientation = cmd.getArg(0);
1972 d.splitter_->setOrientation(orientation == "vertical"
1973 ? Qt::Vertical : Qt::Horizontal);
1974 TabWorkArea * twa = addTabWorkArea();
1975 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1976 setCurrentWorkArea(wa);
1980 case LFUN_CLOSE_TAB_GROUP:
1981 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1983 twa = d.currentTabWorkArea();
1984 // Switch to the next GuiWorkArea in the found TabWorkArea.
1986 d.current_work_area_ = twa->currentWorkArea();
1987 // Make sure the work area is up to date.
1988 twa->setCurrentWorkArea(d.current_work_area_);
1990 d.current_work_area_ = 0;
1992 if (d.splitter_->count() == 0)
1993 // No more work area, switch to the background widget.
1998 case LFUN_COMPLETION_POPUP:
1999 if (d.current_work_area_)
2000 d.current_work_area_->completer().showPopup();
2004 case LFUN_COMPLETION_COMPLETE:
2005 if (d.current_work_area_)
2006 d.current_work_area_->completer().tab();
2014 // Part of automatic menu appearance feature.
2015 if (isFullScreen()) {
2016 if (menuBar()->isVisible())
2018 if (statusBar()->isVisible())
2019 statusBar()->hide();
2026 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2028 string const arg = cmd.getArg(0);
2029 if (arg == "scrollbar") {
2030 // hide() is of no help
2031 if (d.current_work_area_->verticalScrollBarPolicy() ==
2032 Qt::ScrollBarAlwaysOff)
2034 d.current_work_area_->setVerticalScrollBarPolicy(
2035 Qt::ScrollBarAsNeeded);
2037 d.current_work_area_->setVerticalScrollBarPolicy(
2038 Qt::ScrollBarAlwaysOff);
2041 if (arg == "statusbar") {
2042 statusBar()->setVisible(!statusBar()->isVisible());
2045 if (arg == "menubar") {
2046 menuBar()->setVisible(!menuBar()->isVisible());
2049 #if QT_VERSION >= 0x040300
2050 if (arg == "frame") {
2052 getContentsMargins(&l, &t, &r, &b);
2053 //are the frames in default state?
2054 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2056 setContentsMargins(-2, -2, -2, -2);
2058 setContentsMargins(0, 0, 0, 0);
2063 if (arg == "fullscreen") {
2068 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2072 void GuiView::toggleFullScreen()
2074 if (isFullScreen()) {
2075 for (int i = 0; i != d.splitter_->count(); ++i)
2076 d.tabWorkArea(i)->setFullScreen(false);
2077 #if QT_VERSION >= 0x040300
2078 setContentsMargins(0, 0, 0, 0);
2080 setWindowState(windowState() ^ Qt::WindowFullScreen);
2083 statusBar()->show();
2085 for (int i = 0; i != d.splitter_->count(); ++i)
2086 d.tabWorkArea(i)->setFullScreen(true);
2087 #if QT_VERSION >= 0x040300
2088 setContentsMargins(-2, -2, -2, -2);
2091 setWindowState(windowState() ^ Qt::WindowFullScreen);
2092 statusBar()->hide();
2094 if (lyxrc.full_screen_toolbars) {
2095 ToolbarMap::iterator end = d.toolbars_.end();
2096 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2103 Buffer const * GuiView::updateInset(Inset const * inset)
2105 if (!d.current_work_area_)
2109 d.current_work_area_->scheduleRedraw();
2111 return &d.current_work_area_->bufferView().buffer();
2115 void GuiView::restartCursor()
2117 /* When we move around, or type, it's nice to be able to see
2118 * the cursor immediately after the keypress.
2120 if (d.current_work_area_)
2121 d.current_work_area_->startBlinkingCursor();
2123 // Take this occasion to update the other GUI elements.
2129 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2131 if (d.current_work_area_)
2132 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2137 // This list should be kept in sync with the list of insets in
2138 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2139 // dialog should have the same name as the inset.
2140 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2141 // docs in LyXAction.cpp.
2143 char const * const dialognames[] = {
2144 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2145 "citation", "document", "errorlist", "ert", "external", "file",
2146 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2147 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2148 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2150 #ifdef HAVE_LIBAIKSAURUS
2154 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2156 char const * const * const end_dialognames =
2157 dialognames + (sizeof(dialognames) / sizeof(char *));
2161 cmpCStr(char const * name) : name_(name) {}
2162 bool operator()(char const * other) {
2163 return strcmp(other, name_) == 0;
2170 bool isValidName(string const & name)
2172 return find_if(dialognames, end_dialognames,
2173 cmpCStr(name.c_str())) != end_dialognames;
2179 void GuiView::resetDialogs()
2181 // Make sure that no LFUN uses any LyXView.
2182 theLyXFunc().setLyXView(0);
2185 constructToolbars();
2186 guiApp->menus().fillMenuBar(menuBar(), this, true);
2188 d.layout_->updateContents(true);
2189 // Now update controls with current buffer.
2190 theLyXFunc().setLyXView(this);
2196 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2198 if (!isValidName(name))
2201 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2203 if (it != d.dialogs_.end())
2204 return it->second.get();
2206 Dialog * dialog = build(name);
2207 d.dialogs_[name].reset(dialog);
2208 if (lyxrc.allow_geometry_session)
2209 dialog->restoreSession();
2216 void GuiView::showDialog(string const & name, string const & data,
2224 Dialog * dialog = findOrBuild(name, false);
2226 dialog->showData(data);
2228 d.open_insets_[name] = inset;
2231 catch (ExceptionMessage const & ex) {
2239 bool GuiView::isDialogVisible(string const & name) const
2241 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2242 if (it == d.dialogs_.end())
2244 return it->second.get()->isVisibleView();
2248 void GuiView::hideDialog(string const & name, Inset * inset)
2250 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2251 if (it == d.dialogs_.end())
2254 if (inset && inset != getOpenInset(name))
2257 Dialog * const dialog = it->second.get();
2258 if (dialog->isVisibleView())
2260 d.open_insets_[name] = 0;
2264 void GuiView::disconnectDialog(string const & name)
2266 if (!isValidName(name))
2269 if (d.open_insets_.find(name) != d.open_insets_.end())
2270 d.open_insets_[name] = 0;
2274 Inset * GuiView::getOpenInset(string const & name) const
2276 if (!isValidName(name))
2279 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2280 return it == d.open_insets_.end() ? 0 : it->second;
2284 void GuiView::hideAll() const
2286 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2287 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2289 for(; it != end; ++it)
2290 it->second->hideView();
2294 void GuiView::updateDialogs()
2296 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2297 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2299 for(; it != end; ++it) {
2300 Dialog * dialog = it->second.get();
2301 if (dialog && dialog->isVisibleView())
2302 dialog->checkStatus();
2309 // will be replaced by a proper factory...
2310 Dialog * createGuiAbout(GuiView & lv);
2311 Dialog * createGuiBibitem(GuiView & lv);
2312 Dialog * createGuiBibtex(GuiView & lv);
2313 Dialog * createGuiBox(GuiView & lv);
2314 Dialog * createGuiBranch(GuiView & lv);
2315 Dialog * createGuiChanges(GuiView & lv);
2316 Dialog * createGuiCharacter(GuiView & lv);
2317 Dialog * createGuiCitation(GuiView & lv);
2318 Dialog * createGuiDelimiter(GuiView & lv);
2319 Dialog * createGuiDocument(GuiView & lv);
2320 Dialog * createGuiErrorList(GuiView & lv);
2321 Dialog * createGuiERT(GuiView & lv);
2322 Dialog * createGuiExternal(GuiView & lv);
2323 Dialog * createGuiFloat(GuiView & lv);
2324 Dialog * createGuiGraphics(GuiView & lv);
2325 Dialog * createGuiHSpace(GuiView & lv);
2326 Dialog * createGuiInclude(GuiView & lv);
2327 Dialog * createGuiInfo(GuiView & lv);
2328 Dialog * createGuiLabel(GuiView & lv);
2329 Dialog * createGuiListings(GuiView & lv);
2330 Dialog * createGuiLog(GuiView & lv);
2331 Dialog * createGuiMathMatrix(GuiView & lv);
2332 Dialog * createGuiNomenclature(GuiView & lv);
2333 Dialog * createGuiNote(GuiView & lv);
2334 Dialog * createGuiParagraph(GuiView & lv);
2335 Dialog * createGuiPreferences(GuiView & lv);
2336 Dialog * createGuiPrint(GuiView & lv);
2337 Dialog * createGuiRef(GuiView & lv);
2338 Dialog * createGuiSearch(GuiView & lv);
2339 Dialog * createGuiSendTo(GuiView & lv);
2340 Dialog * createGuiShowFile(GuiView & lv);
2341 Dialog * createGuiSpellchecker(GuiView & lv);
2342 Dialog * createGuiSymbols(GuiView & lv);
2343 Dialog * createGuiTabularCreate(GuiView & lv);
2344 Dialog * createGuiTabular(GuiView & lv);
2345 Dialog * createGuiTexInfo(GuiView & lv);
2346 Dialog * createGuiToc(GuiView & lv);
2347 Dialog * createGuiThesaurus(GuiView & lv);
2348 Dialog * createGuiHyperlink(GuiView & lv);
2349 Dialog * createGuiVSpace(GuiView & lv);
2350 Dialog * createGuiViewSource(GuiView & lv);
2351 Dialog * createGuiWrap(GuiView & lv);
2354 Dialog * GuiView::build(string const & name)
2356 LASSERT(isValidName(name), /**/);
2358 if (name == "aboutlyx")
2359 return createGuiAbout(*this);
2360 if (name == "bibitem")
2361 return createGuiBibitem(*this);
2362 if (name == "bibtex")
2363 return createGuiBibtex(*this);
2365 return createGuiBox(*this);
2366 if (name == "branch")
2367 return createGuiBranch(*this);
2368 if (name == "changes")
2369 return createGuiChanges(*this);
2370 if (name == "character")
2371 return createGuiCharacter(*this);
2372 if (name == "citation")
2373 return createGuiCitation(*this);
2374 if (name == "document")
2375 return createGuiDocument(*this);
2376 if (name == "errorlist")
2377 return createGuiErrorList(*this);
2379 return createGuiERT(*this);
2380 if (name == "external")
2381 return createGuiExternal(*this);
2383 return createGuiShowFile(*this);
2384 if (name == "findreplace")
2385 return createGuiSearch(*this);
2386 if (name == "float")
2387 return createGuiFloat(*this);
2388 if (name == "graphics")
2389 return createGuiGraphics(*this);
2390 if (name == "include")
2391 return createGuiInclude(*this);
2393 return createGuiInfo(*this);
2394 if (name == "nomenclature")
2395 return createGuiNomenclature(*this);
2396 if (name == "label")
2397 return createGuiLabel(*this);
2399 return createGuiLog(*this);
2400 if (name == "view-source")
2401 return createGuiViewSource(*this);
2402 if (name == "mathdelimiter")
2403 return createGuiDelimiter(*this);
2404 if (name == "mathmatrix")
2405 return createGuiMathMatrix(*this);
2407 return createGuiNote(*this);
2408 if (name == "paragraph")
2409 return createGuiParagraph(*this);
2410 if (name == "prefs")
2411 return createGuiPreferences(*this);
2412 if (name == "print")
2413 return createGuiPrint(*this);
2415 return createGuiRef(*this);
2416 if (name == "sendto")
2417 return createGuiSendTo(*this);
2418 if (name == "space")
2419 return createGuiHSpace(*this);
2420 if (name == "spellchecker")
2421 return createGuiSpellchecker(*this);
2422 if (name == "symbols")
2423 return createGuiSymbols(*this);
2424 if (name == "tabular")
2425 return createGuiTabular(*this);
2426 if (name == "tabularcreate")
2427 return createGuiTabularCreate(*this);
2428 if (name == "texinfo")
2429 return createGuiTexInfo(*this);
2430 #ifdef HAVE_LIBAIKSAURUS
2431 if (name == "thesaurus")
2432 return createGuiThesaurus(*this);
2435 return createGuiToc(*this);
2437 return createGuiHyperlink(*this);
2438 if (name == "vspace")
2439 return createGuiVSpace(*this);
2441 return createGuiWrap(*this);
2442 if (name == "listings")
2443 return createGuiListings(*this);
2449 } // namespace frontend
2452 #include "GuiView_moc.cpp"