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()))
1215 case LFUN_COMPLETION_CANCEL:
1216 if (!d.current_work_area_
1217 || (!d.current_work_area_->completer().popupVisible()
1218 && !d.current_work_area_->completer().inlineVisible()))
1227 flag.setEnabled(false);
1233 static FileName selectTemplateFile()
1235 FileDialog dlg(qt_("Select template file"));
1236 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1237 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1239 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1240 QStringList(qt_("LyX Documents (*.lyx)")));
1242 if (result.first == FileDialog::Later)
1244 if (result.second.isEmpty())
1246 return FileName(fromqstr(result.second));
1250 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1254 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1257 message(_("Document not loaded."));
1262 setBuffer(newBuffer);
1264 // scroll to the position when the file was last closed
1265 if (lyxrc.use_lastfilepos) {
1266 LastFilePosSection::FilePos filepos =
1267 theSession().lastFilePos().load(filename);
1268 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1272 theSession().lastFiles().add(filename);
1279 void GuiView::openDocument(string const & fname)
1281 string initpath = lyxrc.document_path;
1284 string const trypath = buffer()->filePath();
1285 // If directory is writeable, use this as default.
1286 if (FileName(trypath).isDirWritable())
1292 if (fname.empty()) {
1293 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1294 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1295 dlg.setButton2(qt_("Examples|#E#e"),
1296 toqstr(addPath(package().system_support().absFilename(), "examples")));
1298 QStringList filter(qt_("LyX Documents (*.lyx)"));
1299 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1300 << qt_("LyX-1.4.x Documents (*.lyx14)")
1301 << qt_("LyX-1.5.x Documents (*.lyx15)");
1302 FileDialog::Result result =
1303 dlg.open(toqstr(initpath), filter);
1305 if (result.first == FileDialog::Later)
1308 filename = fromqstr(result.second);
1310 // check selected filename
1311 if (filename.empty()) {
1312 message(_("Canceled."));
1318 // get absolute path of file and add ".lyx" to the filename if
1320 FileName const fullname =
1321 fileSearch(string(), filename, "lyx", support::may_not_exist);
1322 if (!fullname.empty())
1323 filename = fullname.absFilename();
1325 // if the file doesn't exist, let the user create one
1326 if (!fullname.exists()) {
1327 // the user specifically chose this name. Believe him.
1328 Buffer * const b = newFile(filename, string(), true);
1334 docstring const disp_fn = makeDisplayPath(filename);
1335 message(bformat(_("Opening document %1$s..."), disp_fn));
1338 Buffer * buf = loadDocument(fullname);
1343 buf->errors("Parse");
1344 str2 = bformat(_("Document %1$s opened."), disp_fn);
1345 if (buf->lyxvc().inUse())
1346 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1347 " " + _("Version control detected.");
1349 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1354 // FIXME: clean that
1355 static bool import(GuiView * lv, FileName const & filename,
1356 string const & format, ErrorList & errorList)
1358 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1360 string loader_format;
1361 vector<string> loaders = theConverters().loaders();
1362 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1363 for (vector<string>::const_iterator it = loaders.begin();
1364 it != loaders.end(); ++it) {
1365 if (!theConverters().isReachable(format, *it))
1368 string const tofile =
1369 support::changeExtension(filename.absFilename(),
1370 formats.extension(*it));
1371 if (!theConverters().convert(0, filename, FileName(tofile),
1372 filename, format, *it, errorList))
1374 loader_format = *it;
1377 if (loader_format.empty()) {
1378 frontend::Alert::error(_("Couldn't import file"),
1379 bformat(_("No information for importing the format %1$s."),
1380 formats.prettyName(format)));
1384 loader_format = format;
1386 if (loader_format == "lyx") {
1387 Buffer * buf = lv->loadDocument(lyxfile);
1392 buf->errors("Parse");
1394 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1398 bool as_paragraphs = loader_format == "textparagraph";
1399 string filename2 = (loader_format == format) ? filename.absFilename()
1400 : support::changeExtension(filename.absFilename(),
1401 formats.extension(loader_format));
1402 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1403 theLyXFunc().setLyXView(lv);
1404 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1411 void GuiView::importDocument(string const & argument)
1414 string filename = split(argument, format, ' ');
1416 LYXERR(Debug::INFO, format << " file: " << filename);
1418 // need user interaction
1419 if (filename.empty()) {
1420 string initpath = lyxrc.document_path;
1422 Buffer const * buf = buffer();
1424 string const trypath = buf->filePath();
1425 // If directory is writeable, use this as default.
1426 if (FileName(trypath).isDirWritable())
1430 docstring const text = bformat(_("Select %1$s file to import"),
1431 formats.prettyName(format));
1433 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1434 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1435 dlg.setButton2(qt_("Examples|#E#e"),
1436 toqstr(addPath(package().system_support().absFilename(), "examples")));
1438 docstring filter = formats.prettyName(format);
1441 filter += from_utf8(formats.extension(format));
1444 FileDialog::Result result =
1445 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1447 if (result.first == FileDialog::Later)
1450 filename = fromqstr(result.second);
1452 // check selected filename
1453 if (filename.empty())
1454 message(_("Canceled."));
1457 if (filename.empty())
1460 // get absolute path of file
1461 FileName const fullname(support::makeAbsPath(filename));
1463 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1465 // Check if the document already is open
1466 Buffer * buf = theBufferList().getBuffer(lyxfile);
1469 if (!closeBuffer()) {
1470 message(_("Canceled."));
1475 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1477 // if the file exists already, and we didn't do
1478 // -i lyx thefile.lyx, warn
1479 if (lyxfile.exists() && fullname != lyxfile) {
1481 docstring text = bformat(_("The document %1$s already exists.\n\n"
1482 "Do you want to overwrite that document?"), displaypath);
1483 int const ret = Alert::prompt(_("Overwrite document?"),
1484 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1487 message(_("Canceled."));
1492 message(bformat(_("Importing %1$s..."), displaypath));
1493 ErrorList errorList;
1494 if (import(this, fullname, format, errorList))
1495 message(_("imported."));
1497 message(_("file not imported!"));
1499 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1503 void GuiView::newDocument(string const & filename, bool from_template)
1505 FileName initpath(lyxrc.document_path);
1506 Buffer * buf = buffer();
1508 FileName const trypath(buf->filePath());
1509 // If directory is writeable, use this as default.
1510 if (trypath.isDirWritable())
1514 string templatefile = from_template ?
1515 selectTemplateFile().absFilename() : string();
1517 if (filename.empty())
1518 b = newUnnamedFile(templatefile, initpath);
1520 b = newFile(filename, templatefile, true);
1524 // Ensure the cursor is correctly positionned on screen.
1525 view()->showCursor();
1529 void GuiView::insertLyXFile(docstring const & fname)
1531 BufferView * bv = view();
1536 FileName filename(to_utf8(fname));
1538 if (!filename.empty()) {
1539 bv->insertLyXFile(filename);
1543 // Launch a file browser
1545 string initpath = lyxrc.document_path;
1546 string const trypath = bv->buffer().filePath();
1547 // If directory is writeable, use this as default.
1548 if (FileName(trypath).isDirWritable())
1552 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1553 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1554 dlg.setButton2(qt_("Examples|#E#e"),
1555 toqstr(addPath(package().system_support().absFilename(),
1558 FileDialog::Result result = dlg.open(toqstr(initpath),
1559 QStringList(qt_("LyX Documents (*.lyx)")));
1561 if (result.first == FileDialog::Later)
1565 filename.set(fromqstr(result.second));
1567 // check selected filename
1568 if (filename.empty()) {
1569 // emit message signal.
1570 message(_("Canceled."));
1574 bv->insertLyXFile(filename);
1578 void GuiView::insertPlaintextFile(docstring const & fname,
1581 BufferView * bv = view();
1586 FileName filename(to_utf8(fname));
1588 if (!filename.empty()) {
1589 bv->insertPlaintextFile(filename, asParagraph);
1593 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1594 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1596 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1599 if (result.first == FileDialog::Later)
1603 filename.set(fromqstr(result.second));
1605 // check selected filename
1606 if (filename.empty()) {
1607 // emit message signal.
1608 message(_("Canceled."));
1612 bv->insertPlaintextFile(filename, asParagraph);
1616 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1618 FileName fname = b.fileName();
1619 FileName const oldname = fname;
1621 if (!newname.empty()) {
1623 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1625 // Switch to this Buffer.
1628 /// No argument? Ask user through dialog.
1630 FileDialog dlg(qt_("Choose a filename to save document as"),
1631 LFUN_BUFFER_WRITE_AS);
1632 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1633 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1635 if (!isLyXFilename(fname.absFilename()))
1636 fname.changeExtension(".lyx");
1638 FileDialog::Result result =
1639 dlg.save(toqstr(fname.onlyPath().absFilename()),
1640 QStringList(qt_("LyX Documents (*.lyx)")),
1641 toqstr(fname.onlyFileName()));
1643 if (result.first == FileDialog::Later)
1646 fname.set(fromqstr(result.second));
1651 if (!isLyXFilename(fname.absFilename()))
1652 fname.changeExtension(".lyx");
1655 if (FileName(fname).exists()) {
1656 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1657 docstring text = bformat(_("The document %1$s already "
1658 "exists.\n\nDo you want to "
1659 "overwrite that document?"),
1661 int const ret = Alert::prompt(_("Overwrite document?"),
1662 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1665 case 1: return renameBuffer(b, docstring());
1666 case 2: return false;
1670 // Ok, change the name of the buffer
1671 b.setFileName(fname.absFilename());
1673 bool unnamed = b.isUnnamed();
1674 b.setUnnamed(false);
1675 b.saveCheckSum(fname);
1677 if (!saveBuffer(b)) {
1678 b.setFileName(oldname.absFilename());
1679 b.setUnnamed(unnamed);
1680 b.saveCheckSum(oldname);
1688 bool GuiView::saveBuffer(Buffer & b)
1691 return renameBuffer(b, docstring());
1694 theSession().lastFiles().add(b.fileName());
1698 // Switch to this Buffer.
1701 // FIXME: we don't tell the user *WHY* the save failed !!
1702 docstring const file = makeDisplayPath(b.absFileName(), 30);
1703 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1704 "Do you want to rename the document and "
1705 "try again?"), file);
1706 int const ret = Alert::prompt(_("Rename and save?"),
1707 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1710 if (!renameBuffer(b, docstring()))
1719 return saveBuffer(b);
1723 bool GuiView::closeBuffer()
1725 Buffer * buf = buffer();
1726 return buf && closeBuffer(*buf);
1730 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1732 // goto bookmark to update bookmark pit.
1733 //FIXME: we should update only the bookmarks related to this buffer!
1734 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1735 theLyXFunc().gotoBookmark(i+1, false, false);
1737 if (buf.isClean() || buf.paragraphs().empty()) {
1738 if (buf.masterBuffer() == &buf && tolastopened)
1739 theSession().lastOpened().add(buf.fileName());
1740 theBufferList().release(&buf);
1743 // Switch to this Buffer.
1748 if (buf.isUnnamed())
1749 file = from_utf8(buf.fileName().onlyFileName());
1751 file = buf.fileName().displayName(30);
1753 // Bring this window to top before asking questions.
1757 docstring const text = bformat(_("The document %1$s has unsaved changes."
1758 "\n\nDo you want to save the document or discard the changes?"), file);
1759 int const ret = Alert::prompt(_("Save changed document?"),
1760 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1764 if (!saveBuffer(buf))
1768 // if we crash after this we could
1769 // have no autosave file but I guess
1770 // this is really improbable (Jug)
1771 removeAutosaveFile(buf.absFileName());
1777 // save file names to .lyx/session
1778 // if master/slave are both open, do not save slave since it
1779 // will be automatically loaded when the master is loaded
1780 if (buf.masterBuffer() == &buf && tolastopened)
1781 theSession().lastOpened().add(buf.fileName());
1784 // Don't close child documents.
1785 removeWorkArea(d.current_work_area_);
1787 theBufferList().release(&buf);
1793 bool GuiView::dispatch(FuncRequest const & cmd)
1795 BufferView * bv = view();
1796 // By default we won't need any update.
1798 bv->cursor().updateFlags(Update::None);
1799 bool dispatched = true;
1801 switch(cmd.action) {
1802 case LFUN_BUFFER_IMPORT:
1803 importDocument(to_utf8(cmd.argument()));
1806 case LFUN_BUFFER_SWITCH:
1807 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1810 case LFUN_BUFFER_NEXT:
1811 setBuffer(theBufferList().next(buffer()));
1814 case LFUN_BUFFER_PREVIOUS:
1815 setBuffer(theBufferList().previous(buffer()));
1818 case LFUN_COMMAND_EXECUTE: {
1819 bool const show_it = cmd.argument() != "off";
1820 // FIXME: this is a hack, "minibuffer" should not be
1822 if (GuiToolbar * t = toolbar("minibuffer")) {
1823 t->setVisible(show_it);
1824 if (show_it && t->commandBuffer())
1825 t->commandBuffer()->setFocus();
1829 case LFUN_DROP_LAYOUTS_CHOICE:
1831 d.layout_->showPopup();
1834 case LFUN_MENU_OPEN:
1835 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1836 menu->exec(QCursor::pos());
1839 case LFUN_FILE_INSERT:
1840 insertLyXFile(cmd.argument());
1842 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1843 insertPlaintextFile(cmd.argument(), true);
1846 case LFUN_FILE_INSERT_PLAINTEXT:
1847 insertPlaintextFile(cmd.argument(), false);
1850 case LFUN_BUFFER_WRITE:
1852 saveBuffer(bv->buffer());
1855 case LFUN_BUFFER_WRITE_AS:
1857 renameBuffer(bv->buffer(), cmd.argument());
1860 case LFUN_BUFFER_WRITE_ALL: {
1861 Buffer * first = theBufferList().first();
1864 message(_("Saving all documents..."));
1865 // We cannot use a for loop as the buffer list cycles.
1868 if (!b->isClean()) {
1870 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1872 b = theBufferList().next(b);
1873 } while (b != first);
1874 message(_("All documents saved."));
1878 case LFUN_TOOLBAR_TOGGLE: {
1879 string const name = cmd.getArg(0);
1880 if (GuiToolbar * t = toolbar(name))
1885 case LFUN_DIALOG_UPDATE: {
1886 string const name = to_utf8(cmd.argument());
1887 // Can only update a dialog connected to an existing inset
1888 Inset * inset = getOpenInset(name);
1890 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1891 inset->dispatch(view()->cursor(), fr);
1892 } else if (name == "paragraph") {
1893 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1894 } else if (name == "prefs") {
1895 updateDialog(name, string());
1900 case LFUN_DIALOG_TOGGLE: {
1901 if (isDialogVisible(cmd.getArg(0)))
1902 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1904 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1908 case LFUN_DIALOG_DISCONNECT_INSET:
1909 disconnectDialog(to_utf8(cmd.argument()));
1912 case LFUN_DIALOG_HIDE: {
1913 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1917 case LFUN_DIALOG_SHOW: {
1918 string const name = cmd.getArg(0);
1919 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1921 if (name == "character") {
1922 data = freefont2string();
1924 showDialog("character", data);
1925 } else if (name == "latexlog") {
1926 Buffer::LogType type;
1927 string const logfile = buffer()->logName(&type);
1929 case Buffer::latexlog:
1932 case Buffer::buildlog:
1936 data += Lexer::quoteString(logfile);
1937 showDialog("log", data);
1938 } else if (name == "vclog") {
1939 string const data = "vc " +
1940 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1941 showDialog("log", data);
1942 } else if (name == "symbols") {
1943 data = bv->cursor().getEncoding()->name();
1945 showDialog("symbols", data);
1947 showDialog(name, data);
1951 case LFUN_INSET_APPLY: {
1952 view()->cursor().recordUndoFullDocument();
1953 string const name = cmd.getArg(0);
1954 Inset * inset = getOpenInset(name);
1956 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1957 inset->dispatch(view()->cursor(), fr);
1959 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1965 case LFUN_UI_TOGGLE:
1967 // Make sure the keyboard focus stays in the work area.
1971 case LFUN_COMPLETION_INLINE:
1972 if (d.current_work_area_)
1973 d.current_work_area_->completer().showInline();
1976 case LFUN_SPLIT_VIEW:
1977 if (Buffer * buf = buffer()) {
1978 string const orientation = cmd.getArg(0);
1979 d.splitter_->setOrientation(orientation == "vertical"
1980 ? Qt::Vertical : Qt::Horizontal);
1981 TabWorkArea * twa = addTabWorkArea();
1982 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1983 setCurrentWorkArea(wa);
1987 case LFUN_CLOSE_TAB_GROUP:
1988 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1990 twa = d.currentTabWorkArea();
1991 // Switch to the next GuiWorkArea in the found TabWorkArea.
1993 d.current_work_area_ = twa->currentWorkArea();
1994 // Make sure the work area is up to date.
1995 twa->setCurrentWorkArea(d.current_work_area_);
1997 d.current_work_area_ = 0;
1999 if (d.splitter_->count() == 0)
2000 // No more work area, switch to the background widget.
2005 case LFUN_COMPLETION_POPUP:
2006 if (d.current_work_area_)
2007 d.current_work_area_->completer().showPopup();
2011 case LFUN_COMPLETION_COMPLETE:
2012 if (d.current_work_area_)
2013 d.current_work_area_->completer().tab();
2016 case LFUN_COMPLETION_CANCEL:
2017 if (d.current_work_area_) {
2018 if (d.current_work_area_->completer().popupVisible())
2019 d.current_work_area_->completer().hidePopup();
2021 d.current_work_area_->completer().hideInline();
2030 // Part of automatic menu appearance feature.
2031 if (isFullScreen()) {
2032 if (menuBar()->isVisible())
2034 if (statusBar()->isVisible())
2035 statusBar()->hide();
2042 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2044 string const arg = cmd.getArg(0);
2045 if (arg == "scrollbar") {
2046 // hide() is of no help
2047 if (d.current_work_area_->verticalScrollBarPolicy() ==
2048 Qt::ScrollBarAlwaysOff)
2050 d.current_work_area_->setVerticalScrollBarPolicy(
2051 Qt::ScrollBarAsNeeded);
2053 d.current_work_area_->setVerticalScrollBarPolicy(
2054 Qt::ScrollBarAlwaysOff);
2057 if (arg == "statusbar") {
2058 statusBar()->setVisible(!statusBar()->isVisible());
2061 if (arg == "menubar") {
2062 menuBar()->setVisible(!menuBar()->isVisible());
2065 #if QT_VERSION >= 0x040300
2066 if (arg == "frame") {
2068 getContentsMargins(&l, &t, &r, &b);
2069 //are the frames in default state?
2070 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2072 setContentsMargins(-2, -2, -2, -2);
2074 setContentsMargins(0, 0, 0, 0);
2079 if (arg == "fullscreen") {
2084 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2088 void GuiView::toggleFullScreen()
2090 if (isFullScreen()) {
2091 for (int i = 0; i != d.splitter_->count(); ++i)
2092 d.tabWorkArea(i)->setFullScreen(false);
2093 #if QT_VERSION >= 0x040300
2094 setContentsMargins(0, 0, 0, 0);
2096 setWindowState(windowState() ^ Qt::WindowFullScreen);
2099 statusBar()->show();
2101 for (int i = 0; i != d.splitter_->count(); ++i)
2102 d.tabWorkArea(i)->setFullScreen(true);
2103 #if QT_VERSION >= 0x040300
2104 setContentsMargins(-2, -2, -2, -2);
2107 setWindowState(windowState() ^ Qt::WindowFullScreen);
2108 statusBar()->hide();
2110 if (lyxrc.full_screen_toolbars) {
2111 ToolbarMap::iterator end = d.toolbars_.end();
2112 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2119 Buffer const * GuiView::updateInset(Inset const * inset)
2121 if (!d.current_work_area_)
2125 d.current_work_area_->scheduleRedraw();
2127 return &d.current_work_area_->bufferView().buffer();
2131 void GuiView::restartCursor()
2133 /* When we move around, or type, it's nice to be able to see
2134 * the cursor immediately after the keypress.
2136 if (d.current_work_area_)
2137 d.current_work_area_->startBlinkingCursor();
2139 // Take this occasion to update the other GUI elements.
2145 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2147 if (d.current_work_area_)
2148 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2153 // This list should be kept in sync with the list of insets in
2154 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2155 // dialog should have the same name as the inset.
2156 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2157 // docs in LyXAction.cpp.
2159 char const * const dialognames[] = {
2160 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2161 "citation", "document", "errorlist", "ert", "external", "file",
2162 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2163 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2164 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2166 #ifdef HAVE_LIBAIKSAURUS
2170 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2172 char const * const * const end_dialognames =
2173 dialognames + (sizeof(dialognames) / sizeof(char *));
2177 cmpCStr(char const * name) : name_(name) {}
2178 bool operator()(char const * other) {
2179 return strcmp(other, name_) == 0;
2186 bool isValidName(string const & name)
2188 return find_if(dialognames, end_dialognames,
2189 cmpCStr(name.c_str())) != end_dialognames;
2195 void GuiView::resetDialogs()
2197 // Make sure that no LFUN uses any LyXView.
2198 theLyXFunc().setLyXView(0);
2201 constructToolbars();
2202 guiApp->menus().fillMenuBar(menuBar(), this, true);
2204 d.layout_->updateContents(true);
2205 // Now update controls with current buffer.
2206 theLyXFunc().setLyXView(this);
2212 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2214 if (!isValidName(name))
2217 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2219 if (it != d.dialogs_.end())
2220 return it->second.get();
2222 Dialog * dialog = build(name);
2223 d.dialogs_[name].reset(dialog);
2224 if (lyxrc.allow_geometry_session)
2225 dialog->restoreSession();
2232 void GuiView::showDialog(string const & name, string const & data,
2240 Dialog * dialog = findOrBuild(name, false);
2242 dialog->showData(data);
2244 d.open_insets_[name] = inset;
2247 catch (ExceptionMessage const & ex) {
2255 bool GuiView::isDialogVisible(string const & name) const
2257 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2258 if (it == d.dialogs_.end())
2260 return it->second.get()->isVisibleView();
2264 void GuiView::hideDialog(string const & name, Inset * inset)
2266 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2267 if (it == d.dialogs_.end())
2270 if (inset && inset != getOpenInset(name))
2273 Dialog * const dialog = it->second.get();
2274 if (dialog->isVisibleView())
2276 d.open_insets_[name] = 0;
2280 void GuiView::disconnectDialog(string const & name)
2282 if (!isValidName(name))
2285 if (d.open_insets_.find(name) != d.open_insets_.end())
2286 d.open_insets_[name] = 0;
2290 Inset * GuiView::getOpenInset(string const & name) const
2292 if (!isValidName(name))
2295 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2296 return it == d.open_insets_.end() ? 0 : it->second;
2300 void GuiView::hideAll() const
2302 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2303 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2305 for(; it != end; ++it)
2306 it->second->hideView();
2310 void GuiView::updateDialogs()
2312 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2313 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2315 for(; it != end; ++it) {
2316 Dialog * dialog = it->second.get();
2317 if (dialog && dialog->isVisibleView())
2318 dialog->checkStatus();
2325 // will be replaced by a proper factory...
2326 Dialog * createGuiAbout(GuiView & lv);
2327 Dialog * createGuiBibitem(GuiView & lv);
2328 Dialog * createGuiBibtex(GuiView & lv);
2329 Dialog * createGuiBox(GuiView & lv);
2330 Dialog * createGuiBranch(GuiView & lv);
2331 Dialog * createGuiChanges(GuiView & lv);
2332 Dialog * createGuiCharacter(GuiView & lv);
2333 Dialog * createGuiCitation(GuiView & lv);
2334 Dialog * createGuiDelimiter(GuiView & lv);
2335 Dialog * createGuiDocument(GuiView & lv);
2336 Dialog * createGuiErrorList(GuiView & lv);
2337 Dialog * createGuiERT(GuiView & lv);
2338 Dialog * createGuiExternal(GuiView & lv);
2339 Dialog * createGuiFloat(GuiView & lv);
2340 Dialog * createGuiGraphics(GuiView & lv);
2341 Dialog * createGuiHSpace(GuiView & lv);
2342 Dialog * createGuiInclude(GuiView & lv);
2343 Dialog * createGuiInfo(GuiView & lv);
2344 Dialog * createGuiLabel(GuiView & lv);
2345 Dialog * createGuiListings(GuiView & lv);
2346 Dialog * createGuiLog(GuiView & lv);
2347 Dialog * createGuiMathMatrix(GuiView & lv);
2348 Dialog * createGuiNomenclature(GuiView & lv);
2349 Dialog * createGuiNote(GuiView & lv);
2350 Dialog * createGuiParagraph(GuiView & lv);
2351 Dialog * createGuiPreferences(GuiView & lv);
2352 Dialog * createGuiPrint(GuiView & lv);
2353 Dialog * createGuiRef(GuiView & lv);
2354 Dialog * createGuiSearch(GuiView & lv);
2355 Dialog * createGuiSendTo(GuiView & lv);
2356 Dialog * createGuiShowFile(GuiView & lv);
2357 Dialog * createGuiSpellchecker(GuiView & lv);
2358 Dialog * createGuiSymbols(GuiView & lv);
2359 Dialog * createGuiTabularCreate(GuiView & lv);
2360 Dialog * createGuiTabular(GuiView & lv);
2361 Dialog * createGuiTexInfo(GuiView & lv);
2362 Dialog * createGuiToc(GuiView & lv);
2363 Dialog * createGuiThesaurus(GuiView & lv);
2364 Dialog * createGuiHyperlink(GuiView & lv);
2365 Dialog * createGuiVSpace(GuiView & lv);
2366 Dialog * createGuiViewSource(GuiView & lv);
2367 Dialog * createGuiWrap(GuiView & lv);
2370 Dialog * GuiView::build(string const & name)
2372 LASSERT(isValidName(name), /**/);
2374 if (name == "aboutlyx")
2375 return createGuiAbout(*this);
2376 if (name == "bibitem")
2377 return createGuiBibitem(*this);
2378 if (name == "bibtex")
2379 return createGuiBibtex(*this);
2381 return createGuiBox(*this);
2382 if (name == "branch")
2383 return createGuiBranch(*this);
2384 if (name == "changes")
2385 return createGuiChanges(*this);
2386 if (name == "character")
2387 return createGuiCharacter(*this);
2388 if (name == "citation")
2389 return createGuiCitation(*this);
2390 if (name == "document")
2391 return createGuiDocument(*this);
2392 if (name == "errorlist")
2393 return createGuiErrorList(*this);
2395 return createGuiERT(*this);
2396 if (name == "external")
2397 return createGuiExternal(*this);
2399 return createGuiShowFile(*this);
2400 if (name == "findreplace")
2401 return createGuiSearch(*this);
2402 if (name == "float")
2403 return createGuiFloat(*this);
2404 if (name == "graphics")
2405 return createGuiGraphics(*this);
2406 if (name == "include")
2407 return createGuiInclude(*this);
2409 return createGuiInfo(*this);
2410 if (name == "nomenclature")
2411 return createGuiNomenclature(*this);
2412 if (name == "label")
2413 return createGuiLabel(*this);
2415 return createGuiLog(*this);
2416 if (name == "view-source")
2417 return createGuiViewSource(*this);
2418 if (name == "mathdelimiter")
2419 return createGuiDelimiter(*this);
2420 if (name == "mathmatrix")
2421 return createGuiMathMatrix(*this);
2423 return createGuiNote(*this);
2424 if (name == "paragraph")
2425 return createGuiParagraph(*this);
2426 if (name == "prefs")
2427 return createGuiPreferences(*this);
2428 if (name == "print")
2429 return createGuiPrint(*this);
2431 return createGuiRef(*this);
2432 if (name == "sendto")
2433 return createGuiSendTo(*this);
2434 if (name == "space")
2435 return createGuiHSpace(*this);
2436 if (name == "spellchecker")
2437 return createGuiSpellchecker(*this);
2438 if (name == "symbols")
2439 return createGuiSymbols(*this);
2440 if (name == "tabular")
2441 return createGuiTabular(*this);
2442 if (name == "tabularcreate")
2443 return createGuiTabularCreate(*this);
2444 if (name == "texinfo")
2445 return createGuiTexInfo(*this);
2446 #ifdef HAVE_LIBAIKSAURUS
2447 if (name == "thesaurus")
2448 return createGuiThesaurus(*this);
2451 return createGuiToc(*this);
2453 return createGuiHyperlink(*this);
2454 if (name == "vspace")
2455 return createGuiVSpace(*this);
2457 return createGuiWrap(*this);
2458 if (name == "listings")
2459 return createGuiListings(*this);
2465 } // namespace frontend
2468 #include "GuiView_moc.cpp"