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/FileName.h"
61 #include "support/filetools.h"
62 #include "support/gettext.h"
63 #include "support/ForkedCalls.h"
64 #include "support/lstrings.h"
65 #include "support/os.h"
66 #include "support/Package.h"
67 #include "support/Timeout.h"
70 #include <QApplication>
71 #include <QCloseEvent>
73 #include <QDesktopWidget>
74 #include <QDragEnterEvent>
82 #include <QPushButton>
86 #include <QStackedWidget>
93 #include <boost/bind.hpp>
95 #ifdef HAVE_SYS_TIME_H
96 # include <sys/time.h>
103 using namespace lyx::support;
110 class BackgroundWidget : public QWidget
115 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
116 /// The text to be written on top of the pixmap
117 QString const text = lyx_version ?
118 qt_("version ") + lyx_version : qt_("unknown version");
119 splash_ = QPixmap(":/images/banner.png");
121 QPainter pain(&splash_);
122 pain.setPen(QColor(0, 0, 0));
124 // The font used to display the version info
125 font.setStyleHint(QFont::SansSerif);
126 font.setWeight(QFont::Bold);
127 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
129 pain.drawText(190, 225, text);
132 void paintEvent(QPaintEvent *)
134 int x = (width() - splash_.width()) / 2;
135 int y = (height() - splash_.height()) / 2;
137 pain.drawPixmap(x, y, splash_);
144 /// Toolbar store providing access to individual toolbars by name.
145 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
147 typedef boost::shared_ptr<Dialog> DialogPtr;
152 struct GuiView::GuiViewPrivate
155 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
158 // hardcode here the platform specific icon size
159 smallIconSize = 14; // scaling problems
160 normalIconSize = 20; // ok, default
161 bigIconSize = 26; // better for some math icons
163 splitter_ = new QSplitter;
164 bg_widget_ = new BackgroundWidget;
165 stack_widget_ = new QStackedWidget;
166 stack_widget_->addWidget(bg_widget_);
167 stack_widget_->addWidget(splitter_);
175 delete stack_widget_;
178 QMenu * toolBarPopup(GuiView * parent)
180 // FIXME: translation
181 QMenu * menu = new QMenu(parent);
182 QActionGroup * iconSizeGroup = new QActionGroup(parent);
184 QAction * smallIcons = new QAction(iconSizeGroup);
185 smallIcons->setText(qt_("Small-sized icons"));
186 smallIcons->setCheckable(true);
187 QObject::connect(smallIcons, SIGNAL(triggered()),
188 parent, SLOT(smallSizedIcons()));
189 menu->addAction(smallIcons);
191 QAction * normalIcons = new QAction(iconSizeGroup);
192 normalIcons->setText(qt_("Normal-sized icons"));
193 normalIcons->setCheckable(true);
194 QObject::connect(normalIcons, SIGNAL(triggered()),
195 parent, SLOT(normalSizedIcons()));
196 menu->addAction(normalIcons);
198 QAction * bigIcons = new QAction(iconSizeGroup);
199 bigIcons->setText(qt_("Big-sized icons"));
200 bigIcons->setCheckable(true);
201 QObject::connect(bigIcons, SIGNAL(triggered()),
202 parent, SLOT(bigSizedIcons()));
203 menu->addAction(bigIcons);
205 unsigned int cur = parent->iconSize().width();
206 if ( cur == parent->d.smallIconSize)
207 smallIcons->setChecked(true);
208 else if (cur == parent->d.normalIconSize)
209 normalIcons->setChecked(true);
210 else if (cur == parent->d.bigIconSize)
211 bigIcons->setChecked(true);
218 stack_widget_->setCurrentWidget(bg_widget_);
219 bg_widget_->setUpdatesEnabled(true);
222 TabWorkArea * tabWorkArea(int i)
224 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
227 TabWorkArea * currentTabWorkArea()
229 if (splitter_->count() == 1)
230 // The first TabWorkArea is always the first one, if any.
231 return tabWorkArea(0);
233 for (int i = 0; i != splitter_->count(); ++i) {
234 TabWorkArea * twa = tabWorkArea(i);
235 if (current_work_area_ == twa->currentWorkArea())
239 // None has the focus so we just take the first one.
240 return tabWorkArea(0);
244 GuiWorkArea * current_work_area_;
245 QSplitter * splitter_;
246 QStackedWidget * stack_widget_;
247 BackgroundWidget * bg_widget_;
249 ToolbarMap toolbars_;
250 /// The main layout box.
252 * \warning Don't Delete! The layout box is actually owned by
253 * whichever toolbar contains it. All the GuiView class needs is a
254 * means of accessing it.
256 * FIXME: replace that with a proper model so that we are not limited
257 * to only one dialog.
259 GuiLayoutBox * layout_;
262 map<string, Inset *> open_insets_;
265 map<string, DialogPtr> dialogs_;
267 unsigned int smallIconSize;
268 unsigned int normalIconSize;
269 unsigned int bigIconSize;
271 QTimer statusbar_timer_;
272 /// auto-saving of buffers
273 Timeout autosave_timeout_;
274 /// flag against a race condition due to multiclicks, see bug #1119
278 TocModels toc_models_;
282 GuiView::GuiView(int id)
283 : d(*new GuiViewPrivate), id_(id), closing_(false)
285 // GuiToolbars *must* be initialised before the menu bar.
288 // set ourself as the current view. This is needed for the menu bar
289 // filling, at least for the static special menu item on Mac. Otherwise
290 // they are greyed out.
291 theLyXFunc().setLyXView(this);
293 // Fill up the menu bar.
294 guiApp->menus().fillMenuBar(menuBar(), this, true);
296 setCentralWidget(d.stack_widget_);
298 // Start autosave timer
299 if (lyxrc.autosave) {
300 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
301 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
302 d.autosave_timeout_.start();
304 connect(&d.statusbar_timer_, SIGNAL(timeout()),
305 this, SLOT(clearMessage()));
307 // We don't want to keep the window in memory if it is closed.
308 setAttribute(Qt::WA_DeleteOnClose, true);
310 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
311 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
312 // since the icon is provided in the application bundle.
313 setWindowIcon(QPixmap(":/images/lyx.png"));
317 setAcceptDrops(true);
319 statusBar()->setSizeGripEnabled(true);
321 // Forbid too small unresizable window because it can happen
322 // with some window manager under X11.
323 setMinimumSize(300, 200);
325 if (lyxrc.allow_geometry_session) {
326 // Now take care of session management.
331 // No session handling, default to a sane size.
332 setGeometry(50, 50, 690, 510);
334 // This enables to clear session data if any.
346 void GuiView::saveLayout() const
349 QString const key = "view-" + QString::number(id_);
351 settings.setValue(key + "/pos", pos());
352 settings.setValue(key + "/size", size());
354 settings.setValue(key + "/geometry", saveGeometry());
356 settings.setValue(key + "/layout", saveState(0));
357 settings.setValue(key + "/icon_size", iconSize());
361 bool GuiView::restoreLayout()
364 QString const key = "view-" + QString::number(id_);
365 QString const icon_key = key + "/icon_size";
366 if (!settings.contains(icon_key))
369 setIconSize(settings.value(icon_key).toSize());
371 QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
372 QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
376 if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
377 setGeometry(50, 50, 690, 510);
379 // Allow the toc and view-source dock widget to be restored if needed.
380 findOrBuild("toc", true);
381 findOrBuild("view-source", true);
382 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
389 GuiToolbar * GuiView::toolbar(string const & name)
391 ToolbarMap::iterator it = d.toolbars_.find(name);
392 if (it != d.toolbars_.end())
395 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
396 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
401 void GuiView::constructToolbars()
403 ToolbarMap::iterator it = d.toolbars_.begin();
404 for (; it != d.toolbars_.end(); ++it)
409 // extracts the toolbars from the backend
410 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
411 Toolbars::Infos::iterator end = guiApp->toolbars().end();
412 for (; cit != end; ++cit)
413 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
417 void GuiView::initToolbars()
419 // extracts the toolbars from the backend
420 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
421 Toolbars::Infos::iterator end = guiApp->toolbars().end();
422 for (; cit != end; ++cit) {
423 GuiToolbar * tb = toolbar(cit->name);
426 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
428 tb->setVisible(false);
429 tb->setVisibility(visibility);
431 if (visibility & Toolbars::TOP) {
433 addToolBarBreak(Qt::TopToolBarArea);
434 addToolBar(Qt::TopToolBarArea, tb);
437 if (visibility & Toolbars::BOTTOM) {
438 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
439 #if (QT_VERSION >= 0x040202)
440 addToolBarBreak(Qt::BottomToolBarArea);
442 addToolBar(Qt::BottomToolBarArea, tb);
445 if (visibility & Toolbars::LEFT) {
446 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
447 #if (QT_VERSION >= 0x040202)
448 addToolBarBreak(Qt::LeftToolBarArea);
450 addToolBar(Qt::LeftToolBarArea, tb);
453 if (visibility & Toolbars::RIGHT) {
454 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456 addToolBarBreak(Qt::RightToolBarArea);
458 addToolBar(Qt::RightToolBarArea, tb);
461 if (visibility & Toolbars::ON)
462 tb->setVisible(true);
467 TocModels & GuiView::tocModels()
469 return d.toc_models_;
473 void GuiView::setFocus()
475 // Make sure LyXFunc points to the correct view.
476 theLyXFunc().setLyXView(this);
477 if (d.current_work_area_)
478 d.current_work_area_->setFocus();
484 QMenu * GuiView::createPopupMenu()
486 return d.toolBarPopup(this);
490 void GuiView::showEvent(QShowEvent * e)
492 LYXERR(Debug::GUI, "Passed Geometry "
493 << size().height() << "x" << size().width()
494 << "+" << pos().x() << "+" << pos().y());
496 if (d.splitter_->count() == 0)
497 // No work area, switch to the background widget.
500 QMainWindow::showEvent(e);
504 void GuiView::closeEvent(QCloseEvent * close_event)
508 // it can happen that this event arrives without selecting the view,
509 // e.g. when clicking the close button on a background window.
510 theLyXFunc().setLyXView(this);
512 while (Buffer * b = buffer()) {
514 // This is a child document, just close the tab after saving
515 // but keep the file loaded.
516 if (!saveBuffer(*b)) {
518 close_event->ignore();
521 removeWorkArea(d.current_work_area_);
525 QList<int> const ids = guiApp->viewIds();
526 for (int i = 0; i != ids.size(); ++i) {
529 if (guiApp->view(ids[i]).workArea(*b)) {
530 // FIXME 1: should we put an alert box here that the buffer
531 // is viewed elsewhere?
532 // FIXME 2: should we try to save this buffer in any case?
535 // This buffer is also opened in another view, so
536 // close the associated work area...
537 removeWorkArea(d.current_work_area_);
538 // ... but don't close the buffer.
543 if (b && !closeBuffer(*b, true)) {
545 close_event->ignore();
550 // Make sure that nothing will use this close to be closed View.
551 guiApp->unregisterView(this);
553 if (isFullScreen()) {
554 // Switch off fullscreen before closing.
559 // Make sure the timer time out will not trigger a statusbar update.
560 d.statusbar_timer_.stop();
562 // Saving fullscreen requires additional tweaks in the toolbar code.
563 // It wouldn't also work under linux natively.
564 if (lyxrc.allow_geometry_session) {
565 // Save this window geometry and layout.
567 // Then the toolbar private states.
568 ToolbarMap::iterator end = d.toolbars_.end();
569 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
570 it->second->saveSession();
571 // Now take care of all other dialogs:
572 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
573 for (; it!= d.dialogs_.end(); ++it)
574 it->second->saveSession();
577 close_event->accept();
581 void GuiView::dragEnterEvent(QDragEnterEvent * event)
583 if (event->mimeData()->hasUrls())
585 /// \todo Ask lyx-devel is this is enough:
586 /// if (event->mimeData()->hasFormat("text/plain"))
587 /// event->acceptProposedAction();
591 void GuiView::dropEvent(QDropEvent* event)
593 QList<QUrl> files = event->mimeData()->urls();
597 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
598 for (int i = 0; i != files.size(); ++i) {
599 string const file = os::internal_path(fromqstr(
600 files.at(i).toLocalFile()));
602 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
607 void GuiView::message(docstring const & str)
609 if (ForkedProcess::iAmAChild())
612 statusBar()->showMessage(toqstr(str));
613 d.statusbar_timer_.stop();
614 d.statusbar_timer_.start(3000);
618 void GuiView::smallSizedIcons()
620 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
624 void GuiView::normalSizedIcons()
626 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
630 void GuiView::bigSizedIcons()
632 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
636 void GuiView::clearMessage()
640 theLyXFunc().setLyXView(this);
641 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
642 d.statusbar_timer_.stop();
646 void GuiView::updateWindowTitle(GuiWorkArea * wa)
648 if (wa != d.current_work_area_)
650 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
651 setWindowIconText(wa->windowIconText());
655 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
658 disconnectBufferView();
659 connectBufferView(wa->bufferView());
660 connectBuffer(wa->bufferView().buffer());
661 d.current_work_area_ = wa;
662 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
663 this, SLOT(updateWindowTitle(GuiWorkArea *)));
664 updateWindowTitle(wa);
668 // The document settings needs to be reinitialised.
669 updateDialog("document", "");
671 // Buffer-dependent dialogs must be updated. This is done here because
672 // some dialogs require buffer()->text.
677 void GuiView::on_lastWorkAreaRemoved()
680 // We already are in a close event. Nothing more to do.
683 if (d.splitter_->count() > 1)
684 // We have a splitter so don't close anything.
687 // Reset and updates the dialogs.
688 d.toc_models_.reset(0);
689 updateDialog("document", "");
692 if (lyxrc.open_buffers_in_tabs)
693 // Nothing more to do, the window should stay open.
696 if (guiApp->viewIds().size() > 1) {
702 // On Mac we also close the last window because the application stay
703 // resident in memory. On other platforms we don't close the last
704 // window because this would quit the application.
710 void GuiView::updateStatusBar()
712 // let the user see the explicit message
713 if (d.statusbar_timer_.isActive())
716 theLyXFunc().setLyXView(this);
717 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
721 bool GuiView::hasFocus() const
723 return qApp->activeWindow() == this;
727 bool GuiView::event(QEvent * e)
731 // Useful debug code:
732 //case QEvent::ActivationChange:
733 //case QEvent::WindowDeactivate:
734 //case QEvent::Paint:
735 //case QEvent::Enter:
736 //case QEvent::Leave:
737 //case QEvent::HoverEnter:
738 //case QEvent::HoverLeave:
739 //case QEvent::HoverMove:
740 //case QEvent::StatusTip:
741 //case QEvent::DragEnter:
742 //case QEvent::DragLeave:
746 case QEvent::WindowActivate: {
747 if (this == guiApp->currentView()) {
749 return QMainWindow::event(e);
751 guiApp->setCurrentView(this);
752 theLyXFunc().setLyXView(this);
753 if (d.current_work_area_) {
754 BufferView & bv = d.current_work_area_->bufferView();
755 connectBufferView(bv);
756 connectBuffer(bv.buffer());
757 // The document structure, name and dialogs might have
758 // changed in another view.
760 // The document settings needs to be reinitialised.
761 updateDialog("document", "");
764 setWindowTitle(qt_("LyX"));
765 setWindowIconText(qt_("LyX"));
768 return QMainWindow::event(e);
771 case QEvent::ShortcutOverride: {
773 if (isFullScreen() && menuBar()->isHidden()) {
774 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
775 // FIXME: we should also try to detect special LyX shortcut such as
776 // Alt-P and Alt-M. Right now there is a hack in
777 // GuiWorkArea::processKeySym() that hides again the menubar for
779 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
781 return QMainWindow::event(e);
784 if (d.current_work_area_)
785 // Nothing special to do.
786 return QMainWindow::event(e);
788 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
789 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
791 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
792 || ke->key() == Qt::Key_Backtab)
793 return QMainWindow::event(e);
795 // Allow processing of shortcuts that are allowed even when no Buffer
797 theLyXFunc().setLyXView(this);
799 setKeySymbol(&sym, ke);
800 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
806 return QMainWindow::event(e);
811 bool GuiView::focusNextPrevChild(bool /*next*/)
818 void GuiView::setBusy(bool busy)
820 if (d.current_work_area_) {
821 d.current_work_area_->setUpdatesEnabled(!busy);
823 d.current_work_area_->stopBlinkingCursor();
825 d.current_work_area_->startBlinkingCursor();
829 QApplication::setOverrideCursor(Qt::WaitCursor);
831 QApplication::restoreOverrideCursor();
835 GuiWorkArea * GuiView::workArea(Buffer & buffer)
837 if (TabWorkArea * twa = d.currentTabWorkArea())
838 return twa->workArea(buffer);
843 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
845 // Automatically create a TabWorkArea if there are none yet.
846 TabWorkArea * tab_widget = d.splitter_->count()
847 ? d.currentTabWorkArea() : addTabWorkArea();
848 return tab_widget->addWorkArea(buffer, *this);
852 TabWorkArea * GuiView::addTabWorkArea()
854 TabWorkArea * twa = new TabWorkArea;
855 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
856 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
857 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
858 this, SLOT(on_lastWorkAreaRemoved()));
860 d.splitter_->addWidget(twa);
861 d.stack_widget_->setCurrentWidget(d.splitter_);
866 GuiWorkArea const * GuiView::currentWorkArea() const
868 return d.current_work_area_;
872 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
875 d.current_work_area_ = wa;
876 for (int i = 0; i != d.splitter_->count(); ++i) {
877 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
883 void GuiView::removeWorkArea(GuiWorkArea * wa)
886 if (wa == d.current_work_area_) {
888 disconnectBufferView();
889 d.current_work_area_ = 0;
892 for (int i = 0; i != d.splitter_->count(); ++i) {
893 TabWorkArea * twa = d.tabWorkArea(i);
894 if (!twa->removeWorkArea(wa))
895 // Not found in this tab group.
898 // We found and removed the GuiWorkArea.
900 // No more WorkAreas in this tab group, so delete it.
905 if (d.current_work_area_)
906 // This means that we are not closing the current GuiWorkArea;
909 // Switch to the next GuiWorkArea in the found TabWorkArea.
910 d.current_work_area_ = twa->currentWorkArea();
914 if (d.splitter_->count() == 0)
915 // No more work area, switch to the background widget.
920 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
926 void GuiView::updateLayoutList()
929 d.layout_->updateContents(false);
933 void GuiView::updateToolbars()
935 ToolbarMap::iterator end = d.toolbars_.end();
936 if (d.current_work_area_) {
938 d.current_work_area_->bufferView().cursor().inMathed();
940 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
942 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
943 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
944 bool const mathmacrotemplate =
945 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
947 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
948 it->second->update(math, table, review, mathmacrotemplate);
950 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
951 it->second->update(false, false, false, false);
955 Buffer * GuiView::buffer()
957 if (d.current_work_area_)
958 return &d.current_work_area_->bufferView().buffer();
963 Buffer const * GuiView::buffer() const
965 if (d.current_work_area_)
966 return &d.current_work_area_->bufferView().buffer();
971 void GuiView::setBuffer(Buffer * newBuffer)
973 LASSERT(newBuffer, /**/);
976 GuiWorkArea * wa = workArea(*newBuffer);
978 updateLabels(*newBuffer->masterBuffer());
979 wa = addWorkArea(*newBuffer);
981 //Disconnect the old buffer...there's no new one.
984 connectBuffer(*newBuffer);
985 connectBufferView(wa->bufferView());
986 setCurrentWorkArea(wa);
992 void GuiView::connectBuffer(Buffer & buf)
994 buf.setGuiDelegate(this);
998 void GuiView::disconnectBuffer()
1000 if (d.current_work_area_)
1001 d.current_work_area_->bufferView().setGuiDelegate(0);
1005 void GuiView::connectBufferView(BufferView & bv)
1007 bv.setGuiDelegate(this);
1011 void GuiView::disconnectBufferView()
1013 if (d.current_work_area_)
1014 d.current_work_area_->bufferView().setGuiDelegate(0);
1018 void GuiView::errors(string const & error_type)
1020 ErrorList & el = buffer()->errorList(error_type);
1022 showDialog("errorlist", error_type);
1026 void GuiView::structureChanged()
1028 d.toc_models_.reset(view());
1029 // Navigator needs more than a simple update in this case. It needs to be
1031 updateDialog("toc", "");
1035 void GuiView::updateDialog(string const & name, string const & data)
1037 if (!isDialogVisible(name))
1040 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1041 if (it == d.dialogs_.end())
1044 Dialog * const dialog = it->second.get();
1045 if (dialog->isVisibleView())
1046 dialog->initialiseParams(data);
1050 BufferView * GuiView::view()
1052 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1056 void GuiView::autoSave()
1058 LYXERR(Debug::INFO, "Running autoSave()");
1061 view()->buffer().autoSave();
1065 void GuiView::resetAutosaveTimers()
1068 d.autosave_timeout_.restart();
1072 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1075 Buffer * buf = buffer();
1077 /* In LyX/Mac, when a dialog is open, the menus of the
1078 application can still be accessed without giving focus to
1079 the main window. In this case, we want to disable the menu
1080 entries that are buffer-related.
1082 Note that this code is not perfect, as bug 1941 attests:
1083 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1085 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1088 switch(cmd.action) {
1089 case LFUN_BUFFER_WRITE:
1090 enable = buf && (buf->isUnnamed() || !buf->isClean());
1093 case LFUN_BUFFER_WRITE_AS:
1097 case LFUN_SPLIT_VIEW:
1101 case LFUN_CLOSE_TAB_GROUP:
1102 enable = d.currentTabWorkArea();
1105 case LFUN_TOOLBAR_TOGGLE:
1106 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1107 flag.setOnOff(t->isVisible());
1110 case LFUN_UI_TOGGLE:
1111 flag.setOnOff(isFullScreen());
1114 case LFUN_DIALOG_TOGGLE:
1115 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1116 // fall through to set "enable"
1117 case LFUN_DIALOG_SHOW: {
1118 string const name = cmd.getArg(0);
1120 enable = name == "aboutlyx"
1121 || name == "file" //FIXME: should be removed.
1123 || name == "texinfo";
1124 else if (name == "print")
1125 enable = buf->isExportable("dvi")
1126 && lyxrc.print_command != "none";
1127 else if (name == "character") {
1131 InsetCode ic = view()->cursor().inset().lyxCode();
1132 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1135 else if (name == "symbols") {
1136 if (!view() || view()->cursor().inMathed())
1139 InsetCode ic = view()->cursor().inset().lyxCode();
1140 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1143 else if (name == "latexlog")
1144 enable = FileName(buf->logName()).isReadableFile();
1145 else if (name == "spellchecker")
1146 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1147 enable = !buf->isReadonly();
1151 else if (name == "vclog")
1152 enable = buf->lyxvc().inUse();
1156 case LFUN_DIALOG_UPDATE: {
1157 string const name = cmd.getArg(0);
1159 enable = name == "prefs";
1163 case LFUN_INSET_APPLY: {
1164 string const name = cmd.getArg(0);
1165 Inset * inset = getOpenInset(name);
1167 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1169 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1170 // Every inset is supposed to handle this
1171 LASSERT(false, /**/);
1175 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1176 flag |= lyx::getStatus(fr);
1178 enable = flag.enabled();
1182 case LFUN_COMPLETION_INLINE:
1183 if (!d.current_work_area_
1184 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1188 case LFUN_COMPLETION_POPUP:
1189 if (!d.current_work_area_
1190 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1194 case LFUN_COMPLETION_COMPLETE:
1195 if (!d.current_work_area_
1196 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1205 flag.setEnabled(false);
1211 static FileName selectTemplateFile()
1213 FileDialog dlg(qt_("Select template file"));
1214 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1215 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1217 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1218 QStringList(qt_("LyX Documents (*.lyx)")));
1220 if (result.first == FileDialog::Later)
1222 if (result.second.isEmpty())
1224 return FileName(fromqstr(result.second));
1228 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1232 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1235 message(_("Document not loaded."));
1240 setBuffer(newBuffer);
1242 // scroll to the position when the file was last closed
1243 if (lyxrc.use_lastfilepos) {
1244 LastFilePosSection::FilePos filepos =
1245 LyX::ref().session().lastFilePos().load(filename);
1246 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1250 LyX::ref().session().lastFiles().add(filename);
1257 void GuiView::openDocument(string const & fname)
1259 string initpath = lyxrc.document_path;
1262 string const trypath = buffer()->filePath();
1263 // If directory is writeable, use this as default.
1264 if (FileName(trypath).isDirWritable())
1270 if (fname.empty()) {
1271 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1272 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1273 dlg.setButton2(qt_("Examples|#E#e"),
1274 toqstr(addPath(package().system_support().absFilename(), "examples")));
1276 FileDialog::Result result =
1277 dlg.open(toqstr(initpath), QStringList(qt_("LyX Documents (*.lyx)")));
1279 if (result.first == FileDialog::Later)
1282 filename = fromqstr(result.second);
1284 // check selected filename
1285 if (filename.empty()) {
1286 message(_("Canceled."));
1292 // get absolute path of file and add ".lyx" to the filename if
1294 FileName const fullname =
1295 fileSearch(string(), filename, "lyx", support::may_not_exist);
1296 if (!fullname.empty())
1297 filename = fullname.absFilename();
1299 // if the file doesn't exist, let the user create one
1300 if (!fullname.exists()) {
1301 // the user specifically chose this name. Believe him.
1302 Buffer * const b = newFile(filename, string(), true);
1308 docstring const disp_fn = makeDisplayPath(filename);
1309 message(bformat(_("Opening document %1$s..."), disp_fn));
1312 Buffer * buf = loadDocument(fullname);
1317 buf->errors("Parse");
1318 str2 = bformat(_("Document %1$s opened."), disp_fn);
1320 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1325 // FIXME: clean that
1326 static bool import(GuiView * lv, FileName const & filename,
1327 string const & format, ErrorList & errorList)
1329 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1331 string loader_format;
1332 vector<string> loaders = theConverters().loaders();
1333 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1334 for (vector<string>::const_iterator it = loaders.begin();
1335 it != loaders.end(); ++it) {
1336 if (!theConverters().isReachable(format, *it))
1339 string const tofile =
1340 support::changeExtension(filename.absFilename(),
1341 formats.extension(*it));
1342 if (!theConverters().convert(0, filename, FileName(tofile),
1343 filename, format, *it, errorList))
1345 loader_format = *it;
1348 if (loader_format.empty()) {
1349 frontend::Alert::error(_("Couldn't import file"),
1350 bformat(_("No information for importing the format %1$s."),
1351 formats.prettyName(format)));
1355 loader_format = format;
1357 if (loader_format == "lyx") {
1358 Buffer * buf = lv->loadDocument(lyxfile);
1363 buf->errors("Parse");
1365 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1369 bool as_paragraphs = loader_format == "textparagraph";
1370 string filename2 = (loader_format == format) ? filename.absFilename()
1371 : support::changeExtension(filename.absFilename(),
1372 formats.extension(loader_format));
1373 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1374 theLyXFunc().setLyXView(lv);
1375 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1382 void GuiView::importDocument(string const & argument)
1385 string filename = split(argument, format, ' ');
1387 LYXERR(Debug::INFO, format << " file: " << filename);
1389 // need user interaction
1390 if (filename.empty()) {
1391 string initpath = lyxrc.document_path;
1393 Buffer const * buf = buffer();
1395 string const trypath = buf->filePath();
1396 // If directory is writeable, use this as default.
1397 if (FileName(trypath).isDirWritable())
1401 docstring const text = bformat(_("Select %1$s file to import"),
1402 formats.prettyName(format));
1404 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1405 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1406 dlg.setButton2(qt_("Examples|#E#e"),
1407 toqstr(addPath(package().system_support().absFilename(), "examples")));
1409 docstring filter = formats.prettyName(format);
1412 filter += from_utf8(formats.extension(format));
1415 FileDialog::Result result =
1416 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1418 if (result.first == FileDialog::Later)
1421 filename = fromqstr(result.second);
1423 // check selected filename
1424 if (filename.empty())
1425 message(_("Canceled."));
1428 if (filename.empty())
1431 // get absolute path of file
1432 FileName const fullname(support::makeAbsPath(filename));
1434 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1436 // Check if the document already is open
1437 Buffer * buf = theBufferList().getBuffer(lyxfile.absFilename());
1440 if (!closeBuffer()) {
1441 message(_("Canceled."));
1446 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1448 // if the file exists already, and we didn't do
1449 // -i lyx thefile.lyx, warn
1450 if (lyxfile.exists() && fullname != lyxfile) {
1452 docstring text = bformat(_("The document %1$s already exists.\n\n"
1453 "Do you want to overwrite that document?"), displaypath);
1454 int const ret = Alert::prompt(_("Overwrite document?"),
1455 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1458 message(_("Canceled."));
1463 message(bformat(_("Importing %1$s..."), displaypath));
1464 ErrorList errorList;
1465 if (import(this, fullname, format, errorList))
1466 message(_("imported."));
1468 message(_("file not imported!"));
1470 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1474 void GuiView::newDocument(string const & filename, bool from_template)
1476 FileName initpath(lyxrc.document_path);
1477 Buffer * buf = buffer();
1479 FileName const trypath(buf->filePath());
1480 // If directory is writeable, use this as default.
1481 if (trypath.isDirWritable())
1485 string templatefile = from_template ?
1486 selectTemplateFile().absFilename() : string();
1488 if (filename.empty())
1489 b = newUnnamedFile(templatefile, initpath);
1491 b = newFile(filename, templatefile, true);
1495 // Ensure the cursor is correctly positionned on screen.
1496 view()->showCursor();
1500 void GuiView::insertLyXFile(docstring const & fname)
1502 BufferView * bv = view();
1507 FileName filename(to_utf8(fname));
1509 if (!filename.empty()) {
1510 bv->insertLyXFile(filename);
1514 // Launch a file browser
1516 string initpath = lyxrc.document_path;
1517 string const trypath = bv->buffer().filePath();
1518 // If directory is writeable, use this as default.
1519 if (FileName(trypath).isDirWritable())
1523 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1524 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1525 dlg.setButton2(qt_("Examples|#E#e"),
1526 toqstr(addPath(package().system_support().absFilename(),
1529 FileDialog::Result result = dlg.open(toqstr(initpath),
1530 QStringList(qt_("LyX Documents (*.lyx)")));
1532 if (result.first == FileDialog::Later)
1536 filename.set(fromqstr(result.second));
1538 // check selected filename
1539 if (filename.empty()) {
1540 // emit message signal.
1541 message(_("Canceled."));
1545 bv->insertLyXFile(filename);
1549 void GuiView::insertPlaintextFile(docstring const & fname,
1552 BufferView * bv = view();
1557 FileName filename(to_utf8(fname));
1559 if (!filename.empty()) {
1560 bv->insertPlaintextFile(filename, asParagraph);
1564 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1565 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1567 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1570 if (result.first == FileDialog::Later)
1574 filename.set(fromqstr(result.second));
1576 // check selected filename
1577 if (filename.empty()) {
1578 // emit message signal.
1579 message(_("Canceled."));
1583 bv->insertPlaintextFile(filename, asParagraph);
1587 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1589 FileName fname = b.fileName();
1590 FileName const oldname = fname;
1592 if (!newname.empty()) {
1594 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1596 // Switch to this Buffer.
1599 /// No argument? Ask user through dialog.
1601 FileDialog dlg(qt_("Choose a filename to save document as"),
1602 LFUN_BUFFER_WRITE_AS);
1603 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1604 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1606 if (!isLyXFilename(fname.absFilename()))
1607 fname.changeExtension(".lyx");
1609 FileDialog::Result result =
1610 dlg.save(toqstr(fname.onlyPath().absFilename()),
1611 QStringList(qt_("LyX Documents (*.lyx)")),
1612 toqstr(fname.onlyFileName()));
1614 if (result.first == FileDialog::Later)
1617 fname.set(fromqstr(result.second));
1622 if (!isLyXFilename(fname.absFilename()))
1623 fname.changeExtension(".lyx");
1626 if (FileName(fname).exists()) {
1627 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1628 docstring text = bformat(_("The document %1$s already "
1629 "exists.\n\nDo you want to "
1630 "overwrite that document?"),
1632 int const ret = Alert::prompt(_("Overwrite document?"),
1633 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1636 case 1: return renameBuffer(b, docstring());
1637 case 2: return false;
1641 // Ok, change the name of the buffer
1642 b.setFileName(fname.absFilename());
1644 bool unnamed = b.isUnnamed();
1645 b.setUnnamed(false);
1646 b.saveCheckSum(fname);
1648 if (!saveBuffer(b)) {
1649 b.setFileName(oldname.absFilename());
1650 b.setUnnamed(unnamed);
1651 b.saveCheckSum(oldname);
1659 bool GuiView::saveBuffer(Buffer & b)
1662 return renameBuffer(b, docstring());
1665 LyX::ref().session().lastFiles().add(b.fileName());
1669 // Switch to this Buffer.
1672 // FIXME: we don't tell the user *WHY* the save failed !!
1673 docstring const file = makeDisplayPath(b.absFileName(), 30);
1674 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1675 "Do you want to rename the document and "
1676 "try again?"), file);
1677 int const ret = Alert::prompt(_("Rename and save?"),
1678 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1681 if (!renameBuffer(b, docstring()))
1690 return saveBuffer(b);
1694 bool GuiView::closeBuffer()
1696 Buffer * buf = buffer();
1697 return buf && closeBuffer(*buf);
1701 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1703 // goto bookmark to update bookmark pit.
1704 //FIXME: we should update only the bookmarks related to this buffer!
1705 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1706 theLyXFunc().gotoBookmark(i+1, false, false);
1708 if (buf.isClean() || buf.paragraphs().empty()) {
1709 if (buf.masterBuffer() == &buf && tolastopened)
1710 LyX::ref().session().lastOpened().add(buf.fileName());
1711 theBufferList().release(&buf);
1714 // Switch to this Buffer.
1719 if (buf.isUnnamed())
1720 file = from_utf8(buf.fileName().onlyFileName());
1722 file = buf.fileName().displayName(30);
1724 // Bring this window to top before asking questions.
1728 docstring const text = bformat(_("The document %1$s has unsaved changes."
1729 "\n\nDo you want to save the document or discard the changes?"), file);
1730 int const ret = Alert::prompt(_("Save changed document?"),
1731 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1735 if (!saveBuffer(buf))
1739 // if we crash after this we could
1740 // have no autosave file but I guess
1741 // this is really improbable (Jug)
1742 removeAutosaveFile(buf.absFileName());
1748 // save file names to .lyx/session
1749 // if master/slave are both open, do not save slave since it
1750 // will be automatically loaded when the master is loaded
1751 if (buf.masterBuffer() == &buf && tolastopened)
1752 LyX::ref().session().lastOpened().add(buf.fileName());
1755 // Don't close child documents.
1756 removeWorkArea(d.current_work_area_);
1758 theBufferList().release(&buf);
1764 bool GuiView::dispatch(FuncRequest const & cmd)
1766 BufferView * bv = view();
1767 // By default we won't need any update.
1769 bv->cursor().updateFlags(Update::None);
1770 bool dispatched = true;
1772 switch(cmd.action) {
1773 case LFUN_BUFFER_IMPORT:
1774 importDocument(to_utf8(cmd.argument()));
1777 case LFUN_BUFFER_SWITCH:
1778 setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1781 case LFUN_BUFFER_NEXT:
1782 setBuffer(theBufferList().next(buffer()));
1785 case LFUN_BUFFER_PREVIOUS:
1786 setBuffer(theBufferList().previous(buffer()));
1789 case LFUN_COMMAND_EXECUTE: {
1790 bool const show_it = cmd.argument() != "off";
1791 // FIXME: this is a hack, "minibuffer" should not be
1793 if (GuiToolbar * t = toolbar("minibuffer")) {
1794 t->setVisible(show_it);
1795 if (show_it && t->commandBuffer())
1796 t->commandBuffer()->setFocus();
1800 case LFUN_DROP_LAYOUTS_CHOICE:
1802 d.layout_->showPopup();
1805 case LFUN_MENU_OPEN:
1806 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1807 menu->exec(QCursor::pos());
1810 case LFUN_FILE_INSERT:
1811 insertLyXFile(cmd.argument());
1813 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1814 insertPlaintextFile(cmd.argument(), true);
1817 case LFUN_FILE_INSERT_PLAINTEXT:
1818 insertPlaintextFile(cmd.argument(), false);
1821 case LFUN_BUFFER_WRITE:
1823 saveBuffer(bv->buffer());
1826 case LFUN_BUFFER_WRITE_AS:
1828 renameBuffer(bv->buffer(), cmd.argument());
1831 case LFUN_BUFFER_WRITE_ALL: {
1832 Buffer * first = theBufferList().first();
1835 message(_("Saving all documents..."));
1836 // We cannot use a for loop as the buffer list cycles.
1839 if (!b->isClean()) {
1841 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1843 b = theBufferList().next(b);
1844 } while (b != first);
1845 message(_("All documents saved."));
1849 case LFUN_TOOLBAR_TOGGLE: {
1850 string const name = cmd.getArg(0);
1851 if (GuiToolbar * t = toolbar(name))
1856 case LFUN_DIALOG_UPDATE: {
1857 string const name = to_utf8(cmd.argument());
1858 // Can only update a dialog connected to an existing inset
1859 Inset * inset = getOpenInset(name);
1861 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1862 inset->dispatch(view()->cursor(), fr);
1863 } else if (name == "paragraph") {
1864 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1865 } else if (name == "prefs") {
1866 updateDialog(name, string());
1871 case LFUN_DIALOG_TOGGLE: {
1872 if (isDialogVisible(cmd.getArg(0)))
1873 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1875 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1879 case LFUN_DIALOG_DISCONNECT_INSET:
1880 disconnectDialog(to_utf8(cmd.argument()));
1883 case LFUN_DIALOG_HIDE: {
1884 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1888 case LFUN_DIALOG_SHOW: {
1889 string const name = cmd.getArg(0);
1890 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1892 if (name == "character") {
1893 data = freefont2string();
1895 showDialog("character", data);
1896 } else if (name == "latexlog") {
1897 Buffer::LogType type;
1898 string const logfile = buffer()->logName(&type);
1900 case Buffer::latexlog:
1903 case Buffer::buildlog:
1907 data += Lexer::quoteString(logfile);
1908 showDialog("log", data);
1909 } else if (name == "vclog") {
1910 string const data = "vc " +
1911 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1912 showDialog("log", data);
1913 } else if (name == "symbols") {
1914 data = bv->cursor().getEncoding()->name();
1916 showDialog("symbols", data);
1918 showDialog(name, data);
1922 case LFUN_INSET_APPLY: {
1923 view()->cursor().recordUndoFullDocument();
1924 string const name = cmd.getArg(0);
1925 Inset * inset = getOpenInset(name);
1927 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1928 inset->dispatch(view()->cursor(), fr);
1930 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1936 case LFUN_UI_TOGGLE:
1938 // Make sure the keyboard focus stays in the work area.
1942 case LFUN_COMPLETION_INLINE:
1943 if (d.current_work_area_)
1944 d.current_work_area_->completer().showInline();
1947 case LFUN_SPLIT_VIEW:
1948 if (Buffer * buf = buffer()) {
1949 string const orientation = cmd.getArg(0);
1950 d.splitter_->setOrientation(orientation == "vertical"
1951 ? Qt::Vertical : Qt::Horizontal);
1952 TabWorkArea * twa = addTabWorkArea();
1953 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1954 setCurrentWorkArea(wa);
1958 case LFUN_CLOSE_TAB_GROUP:
1959 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1961 twa = d.currentTabWorkArea();
1962 // Switch to the next GuiWorkArea in the found TabWorkArea.
1963 d.current_work_area_ = twa? twa->currentWorkArea() : 0;
1964 if (d.splitter_->count() == 0)
1965 // No more work area, switch to the background widget.
1970 case LFUN_COMPLETION_POPUP:
1971 if (d.current_work_area_)
1972 d.current_work_area_->completer().showPopup();
1976 case LFUN_COMPLETION_COMPLETE:
1977 if (d.current_work_area_)
1978 d.current_work_area_->completer().tab();
1986 if (isFullScreen()) {
1987 if (menuBar()->isVisible())
1989 if (statusBar()->isVisible())
1990 statusBar()->hide();
1997 void GuiView::lfunUiToggle(FuncRequest const & cmd)
1999 string const arg = cmd.getArg(0);
2000 if (arg == "scrollbar") {
2001 // hide() is of no help
2002 if (d.current_work_area_->verticalScrollBarPolicy() ==
2003 Qt::ScrollBarAlwaysOff)
2005 d.current_work_area_->setVerticalScrollBarPolicy(
2006 Qt::ScrollBarAsNeeded);
2008 d.current_work_area_->setVerticalScrollBarPolicy(
2009 Qt::ScrollBarAlwaysOff);
2012 if (arg == "statusbar") {
2013 statusBar()->setVisible(!statusBar()->isVisible());
2016 if (arg == "menubar") {
2017 menuBar()->setVisible(!menuBar()->isVisible());
2020 #if QT_VERSION >= 0x040300
2021 if (arg == "frame") {
2023 getContentsMargins(&l, &t, &r, &b);
2024 //are the frames in default state?
2025 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2027 setContentsMargins(-2, -2, -2, -2);
2029 setContentsMargins(0, 0, 0, 0);
2034 if (arg == "fullscreen") {
2039 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2043 void GuiView::toggleFullScreen()
2045 if (isFullScreen()) {
2046 for (int i = 0; i != d.splitter_->count(); ++i)
2047 d.tabWorkArea(i)->setFullScreen(false);
2048 #if QT_VERSION >= 0x040300
2049 setContentsMargins(0, 0, 0, 0);
2051 setWindowState(windowState() ^ Qt::WindowFullScreen);
2054 statusBar()->show();
2056 for (int i = 0; i != d.splitter_->count(); ++i)
2057 d.tabWorkArea(i)->setFullScreen(true);
2058 #if QT_VERSION >= 0x040300
2059 setContentsMargins(-2, -2, -2, -2);
2062 setWindowState(windowState() ^ Qt::WindowFullScreen);
2063 statusBar()->hide();
2065 if (lyxrc.full_screen_toolbars) {
2066 ToolbarMap::iterator end = d.toolbars_.end();
2067 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2074 Buffer const * GuiView::updateInset(Inset const * inset)
2076 if (!d.current_work_area_)
2080 d.current_work_area_->scheduleRedraw();
2082 return &d.current_work_area_->bufferView().buffer();
2086 void GuiView::restartCursor()
2088 /* When we move around, or type, it's nice to be able to see
2089 * the cursor immediately after the keypress.
2091 if (d.current_work_area_)
2092 d.current_work_area_->startBlinkingCursor();
2094 // Take this occasion to update the other GUI elements.
2100 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2102 if (d.current_work_area_)
2103 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2108 // This list should be kept in sync with the list of insets in
2109 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2110 // dialog should have the same name as the inset.
2111 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2112 // docs in LyXAction.cpp.
2114 char const * const dialognames[] = {
2115 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2116 "citation", "document", "errorlist", "ert", "external", "file",
2117 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2118 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2119 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2121 #ifdef HAVE_LIBAIKSAURUS
2125 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2127 char const * const * const end_dialognames =
2128 dialognames + (sizeof(dialognames) / sizeof(char *));
2132 cmpCStr(char const * name) : name_(name) {}
2133 bool operator()(char const * other) {
2134 return strcmp(other, name_) == 0;
2141 bool isValidName(string const & name)
2143 return find_if(dialognames, end_dialognames,
2144 cmpCStr(name.c_str())) != end_dialognames;
2150 void GuiView::resetDialogs()
2152 // Make sure that no LFUN uses any LyXView.
2153 theLyXFunc().setLyXView(0);
2156 constructToolbars();
2157 guiApp->menus().fillMenuBar(menuBar(), this, true);
2159 d.layout_->updateContents(true);
2160 // Now update controls with current buffer.
2161 theLyXFunc().setLyXView(this);
2167 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2169 if (!isValidName(name))
2172 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2174 if (it != d.dialogs_.end())
2175 return it->second.get();
2177 Dialog * dialog = build(name);
2178 d.dialogs_[name].reset(dialog);
2179 if (lyxrc.allow_geometry_session)
2180 dialog->restoreSession();
2187 void GuiView::showDialog(string const & name, string const & data,
2194 Dialog * dialog = findOrBuild(name, false);
2196 dialog->showData(data);
2198 d.open_insets_[name] = inset;
2204 bool GuiView::isDialogVisible(string const & name) const
2206 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2207 if (it == d.dialogs_.end())
2209 return it->second.get()->isVisibleView();
2213 void GuiView::hideDialog(string const & name, Inset * inset)
2215 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2216 if (it == d.dialogs_.end())
2219 if (inset && inset != getOpenInset(name))
2222 Dialog * const dialog = it->second.get();
2223 if (dialog->isVisibleView())
2225 d.open_insets_[name] = 0;
2229 void GuiView::disconnectDialog(string const & name)
2231 if (!isValidName(name))
2234 if (d.open_insets_.find(name) != d.open_insets_.end())
2235 d.open_insets_[name] = 0;
2239 Inset * GuiView::getOpenInset(string const & name) const
2241 if (!isValidName(name))
2244 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2245 return it == d.open_insets_.end() ? 0 : it->second;
2249 void GuiView::hideAll() const
2251 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2252 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2254 for(; it != end; ++it)
2255 it->second->hideView();
2259 void GuiView::updateDialogs()
2261 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2262 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2264 for(; it != end; ++it) {
2265 Dialog * dialog = it->second.get();
2266 if (dialog && dialog->isVisibleView())
2267 dialog->checkStatus();
2274 // will be replaced by a proper factory...
2275 Dialog * createGuiAbout(GuiView & lv);
2276 Dialog * createGuiBibitem(GuiView & lv);
2277 Dialog * createGuiBibtex(GuiView & lv);
2278 Dialog * createGuiBox(GuiView & lv);
2279 Dialog * createGuiBranch(GuiView & lv);
2280 Dialog * createGuiChanges(GuiView & lv);
2281 Dialog * createGuiCharacter(GuiView & lv);
2282 Dialog * createGuiCitation(GuiView & lv);
2283 Dialog * createGuiDelimiter(GuiView & lv);
2284 Dialog * createGuiDocument(GuiView & lv);
2285 Dialog * createGuiErrorList(GuiView & lv);
2286 Dialog * createGuiERT(GuiView & lv);
2287 Dialog * createGuiExternal(GuiView & lv);
2288 Dialog * createGuiFloat(GuiView & lv);
2289 Dialog * createGuiGraphics(GuiView & lv);
2290 Dialog * createGuiHSpace(GuiView & lv);
2291 Dialog * createGuiInclude(GuiView & lv);
2292 Dialog * createGuiInfo(GuiView & lv);
2293 Dialog * createGuiLabel(GuiView & lv);
2294 Dialog * createGuiListings(GuiView & lv);
2295 Dialog * createGuiLog(GuiView & lv);
2296 Dialog * createGuiMathMatrix(GuiView & lv);
2297 Dialog * createGuiNomenclature(GuiView & lv);
2298 Dialog * createGuiNote(GuiView & lv);
2299 Dialog * createGuiParagraph(GuiView & lv);
2300 Dialog * createGuiPreferences(GuiView & lv);
2301 Dialog * createGuiPrint(GuiView & lv);
2302 Dialog * createGuiRef(GuiView & lv);
2303 Dialog * createGuiSearch(GuiView & lv);
2304 Dialog * createGuiSendTo(GuiView & lv);
2305 Dialog * createGuiShowFile(GuiView & lv);
2306 Dialog * createGuiSpellchecker(GuiView & lv);
2307 Dialog * createGuiSymbols(GuiView & lv);
2308 Dialog * createGuiTabularCreate(GuiView & lv);
2309 Dialog * createGuiTabular(GuiView & lv);
2310 Dialog * createGuiTexInfo(GuiView & lv);
2311 Dialog * createGuiToc(GuiView & lv);
2312 Dialog * createGuiThesaurus(GuiView & lv);
2313 Dialog * createGuiHyperlink(GuiView & lv);
2314 Dialog * createGuiVSpace(GuiView & lv);
2315 Dialog * createGuiViewSource(GuiView & lv);
2316 Dialog * createGuiWrap(GuiView & lv);
2319 Dialog * GuiView::build(string const & name)
2321 LASSERT(isValidName(name), /**/);
2323 if (name == "aboutlyx")
2324 return createGuiAbout(*this);
2325 if (name == "bibitem")
2326 return createGuiBibitem(*this);
2327 if (name == "bibtex")
2328 return createGuiBibtex(*this);
2330 return createGuiBox(*this);
2331 if (name == "branch")
2332 return createGuiBranch(*this);
2333 if (name == "changes")
2334 return createGuiChanges(*this);
2335 if (name == "character")
2336 return createGuiCharacter(*this);
2337 if (name == "citation")
2338 return createGuiCitation(*this);
2339 if (name == "document")
2340 return createGuiDocument(*this);
2341 if (name == "errorlist")
2342 return createGuiErrorList(*this);
2344 return createGuiERT(*this);
2345 if (name == "external")
2346 return createGuiExternal(*this);
2348 return createGuiShowFile(*this);
2349 if (name == "findreplace")
2350 return createGuiSearch(*this);
2351 if (name == "float")
2352 return createGuiFloat(*this);
2353 if (name == "graphics")
2354 return createGuiGraphics(*this);
2355 if (name == "include")
2356 return createGuiInclude(*this);
2358 return createGuiInfo(*this);
2359 if (name == "nomenclature")
2360 return createGuiNomenclature(*this);
2361 if (name == "label")
2362 return createGuiLabel(*this);
2364 return createGuiLog(*this);
2365 if (name == "view-source")
2366 return createGuiViewSource(*this);
2367 if (name == "mathdelimiter")
2368 return createGuiDelimiter(*this);
2369 if (name == "mathmatrix")
2370 return createGuiMathMatrix(*this);
2372 return createGuiNote(*this);
2373 if (name == "paragraph")
2374 return createGuiParagraph(*this);
2375 if (name == "prefs")
2376 return createGuiPreferences(*this);
2377 if (name == "print")
2378 return createGuiPrint(*this);
2380 return createGuiRef(*this);
2381 if (name == "sendto")
2382 return createGuiSendTo(*this);
2383 if (name == "space")
2384 return createGuiHSpace(*this);
2385 if (name == "spellchecker")
2386 return createGuiSpellchecker(*this);
2387 if (name == "symbols")
2388 return createGuiSymbols(*this);
2389 if (name == "tabular")
2390 return createGuiTabular(*this);
2391 if (name == "tabularcreate")
2392 return createGuiTabularCreate(*this);
2393 if (name == "texinfo")
2394 return createGuiTexInfo(*this);
2395 #ifdef HAVE_LIBAIKSAURUS
2396 if (name == "thesaurus")
2397 return createGuiThesaurus(*this);
2400 return createGuiToc(*this);
2402 return createGuiHyperlink(*this);
2403 if (name == "vspace")
2404 return createGuiVSpace(*this);
2406 return createGuiWrap(*this);
2407 if (name == "listings")
2408 return createGuiListings(*this);
2414 } // namespace frontend
2417 #include "GuiView_moc.cpp"