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:
1098 if (cmd.getArg(0) == "vertical")
1099 enable = buf && (d.splitter_->count() == 1 ||
1100 d.splitter_->orientation() == Qt::Vertical);
1102 enable = buf && (d.splitter_->count() == 1 ||
1103 d.splitter_->orientation() == Qt::Horizontal);
1106 case LFUN_CLOSE_TAB_GROUP:
1107 enable = d.currentTabWorkArea();
1110 case LFUN_TOOLBAR_TOGGLE:
1111 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1112 flag.setOnOff(t->isVisible());
1115 case LFUN_UI_TOGGLE:
1116 flag.setOnOff(isFullScreen());
1119 case LFUN_DIALOG_TOGGLE:
1120 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1121 // fall through to set "enable"
1122 case LFUN_DIALOG_SHOW: {
1123 string const name = cmd.getArg(0);
1125 enable = name == "aboutlyx"
1126 || name == "file" //FIXME: should be removed.
1128 || name == "texinfo";
1129 else if (name == "print")
1130 enable = buf->isExportable("dvi")
1131 && lyxrc.print_command != "none";
1132 else if (name == "character") {
1136 InsetCode ic = view()->cursor().inset().lyxCode();
1137 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1140 else if (name == "symbols") {
1141 if (!view() || view()->cursor().inMathed())
1144 InsetCode ic = view()->cursor().inset().lyxCode();
1145 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1148 else if (name == "latexlog")
1149 enable = FileName(buf->logName()).isReadableFile();
1150 else if (name == "spellchecker")
1151 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1152 enable = !buf->isReadonly();
1156 else if (name == "vclog")
1157 enable = buf->lyxvc().inUse();
1161 case LFUN_DIALOG_UPDATE: {
1162 string const name = cmd.getArg(0);
1164 enable = name == "prefs";
1168 case LFUN_INSET_APPLY: {
1169 string const name = cmd.getArg(0);
1170 Inset * inset = getOpenInset(name);
1172 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1174 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1175 // Every inset is supposed to handle this
1176 LASSERT(false, /**/);
1180 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1181 flag |= lyx::getStatus(fr);
1183 enable = flag.enabled();
1187 case LFUN_COMPLETION_INLINE:
1188 if (!d.current_work_area_
1189 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1193 case LFUN_COMPLETION_POPUP:
1194 if (!d.current_work_area_
1195 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1199 case LFUN_COMPLETION_COMPLETE:
1200 if (!d.current_work_area_
1201 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1210 flag.setEnabled(false);
1216 static FileName selectTemplateFile()
1218 FileDialog dlg(qt_("Select template file"));
1219 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1220 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1222 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1223 QStringList(qt_("LyX Documents (*.lyx)")));
1225 if (result.first == FileDialog::Later)
1227 if (result.second.isEmpty())
1229 return FileName(fromqstr(result.second));
1233 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1237 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1240 message(_("Document not loaded."));
1245 setBuffer(newBuffer);
1247 // scroll to the position when the file was last closed
1248 if (lyxrc.use_lastfilepos) {
1249 LastFilePosSection::FilePos filepos =
1250 theSession().lastFilePos().load(filename);
1251 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1255 theSession().lastFiles().add(filename);
1262 void GuiView::openDocument(string const & fname)
1264 string initpath = lyxrc.document_path;
1267 string const trypath = buffer()->filePath();
1268 // If directory is writeable, use this as default.
1269 if (FileName(trypath).isDirWritable())
1275 if (fname.empty()) {
1276 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1277 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1278 dlg.setButton2(qt_("Examples|#E#e"),
1279 toqstr(addPath(package().system_support().absFilename(), "examples")));
1281 QStringList filter(qt_("LyX Documents (*.lyx)"));
1282 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1283 << qt_("LyX-1.4.x Documents (*.lyx14)")
1284 << qt_("LyX-1.5.x Documents (*.lyx15)");
1285 FileDialog::Result result =
1286 dlg.open(toqstr(initpath), filter);
1288 if (result.first == FileDialog::Later)
1291 filename = fromqstr(result.second);
1293 // check selected filename
1294 if (filename.empty()) {
1295 message(_("Canceled."));
1301 // get absolute path of file and add ".lyx" to the filename if
1303 FileName const fullname =
1304 fileSearch(string(), filename, "lyx", support::may_not_exist);
1305 if (!fullname.empty())
1306 filename = fullname.absFilename();
1308 // if the file doesn't exist, let the user create one
1309 if (!fullname.exists()) {
1310 // the user specifically chose this name. Believe him.
1311 Buffer * const b = newFile(filename, string(), true);
1317 docstring const disp_fn = makeDisplayPath(filename);
1318 message(bformat(_("Opening document %1$s..."), disp_fn));
1321 Buffer * buf = loadDocument(fullname);
1326 buf->errors("Parse");
1327 str2 = bformat(_("Document %1$s opened."), disp_fn);
1329 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1334 // FIXME: clean that
1335 static bool import(GuiView * lv, FileName const & filename,
1336 string const & format, ErrorList & errorList)
1338 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1340 string loader_format;
1341 vector<string> loaders = theConverters().loaders();
1342 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1343 for (vector<string>::const_iterator it = loaders.begin();
1344 it != loaders.end(); ++it) {
1345 if (!theConverters().isReachable(format, *it))
1348 string const tofile =
1349 support::changeExtension(filename.absFilename(),
1350 formats.extension(*it));
1351 if (!theConverters().convert(0, filename, FileName(tofile),
1352 filename, format, *it, errorList))
1354 loader_format = *it;
1357 if (loader_format.empty()) {
1358 frontend::Alert::error(_("Couldn't import file"),
1359 bformat(_("No information for importing the format %1$s."),
1360 formats.prettyName(format)));
1364 loader_format = format;
1366 if (loader_format == "lyx") {
1367 Buffer * buf = lv->loadDocument(lyxfile);
1372 buf->errors("Parse");
1374 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1378 bool as_paragraphs = loader_format == "textparagraph";
1379 string filename2 = (loader_format == format) ? filename.absFilename()
1380 : support::changeExtension(filename.absFilename(),
1381 formats.extension(loader_format));
1382 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1383 theLyXFunc().setLyXView(lv);
1384 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1391 void GuiView::importDocument(string const & argument)
1394 string filename = split(argument, format, ' ');
1396 LYXERR(Debug::INFO, format << " file: " << filename);
1398 // need user interaction
1399 if (filename.empty()) {
1400 string initpath = lyxrc.document_path;
1402 Buffer const * buf = buffer();
1404 string const trypath = buf->filePath();
1405 // If directory is writeable, use this as default.
1406 if (FileName(trypath).isDirWritable())
1410 docstring const text = bformat(_("Select %1$s file to import"),
1411 formats.prettyName(format));
1413 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1414 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1415 dlg.setButton2(qt_("Examples|#E#e"),
1416 toqstr(addPath(package().system_support().absFilename(), "examples")));
1418 docstring filter = formats.prettyName(format);
1421 filter += from_utf8(formats.extension(format));
1424 FileDialog::Result result =
1425 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1427 if (result.first == FileDialog::Later)
1430 filename = fromqstr(result.second);
1432 // check selected filename
1433 if (filename.empty())
1434 message(_("Canceled."));
1437 if (filename.empty())
1440 // get absolute path of file
1441 FileName const fullname(support::makeAbsPath(filename));
1443 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1445 // Check if the document already is open
1446 Buffer * buf = theBufferList().getBuffer(lyxfile);
1449 if (!closeBuffer()) {
1450 message(_("Canceled."));
1455 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1457 // if the file exists already, and we didn't do
1458 // -i lyx thefile.lyx, warn
1459 if (lyxfile.exists() && fullname != lyxfile) {
1461 docstring text = bformat(_("The document %1$s already exists.\n\n"
1462 "Do you want to overwrite that document?"), displaypath);
1463 int const ret = Alert::prompt(_("Overwrite document?"),
1464 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1467 message(_("Canceled."));
1472 message(bformat(_("Importing %1$s..."), displaypath));
1473 ErrorList errorList;
1474 if (import(this, fullname, format, errorList))
1475 message(_("imported."));
1477 message(_("file not imported!"));
1479 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1483 void GuiView::newDocument(string const & filename, bool from_template)
1485 FileName initpath(lyxrc.document_path);
1486 Buffer * buf = buffer();
1488 FileName const trypath(buf->filePath());
1489 // If directory is writeable, use this as default.
1490 if (trypath.isDirWritable())
1494 string templatefile = from_template ?
1495 selectTemplateFile().absFilename() : string();
1497 if (filename.empty())
1498 b = newUnnamedFile(templatefile, initpath);
1500 b = newFile(filename, templatefile, true);
1504 // Ensure the cursor is correctly positionned on screen.
1505 view()->showCursor();
1509 void GuiView::insertLyXFile(docstring const & fname)
1511 BufferView * bv = view();
1516 FileName filename(to_utf8(fname));
1518 if (!filename.empty()) {
1519 bv->insertLyXFile(filename);
1523 // Launch a file browser
1525 string initpath = lyxrc.document_path;
1526 string const trypath = bv->buffer().filePath();
1527 // If directory is writeable, use this as default.
1528 if (FileName(trypath).isDirWritable())
1532 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1533 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1534 dlg.setButton2(qt_("Examples|#E#e"),
1535 toqstr(addPath(package().system_support().absFilename(),
1538 FileDialog::Result result = dlg.open(toqstr(initpath),
1539 QStringList(qt_("LyX Documents (*.lyx)")));
1541 if (result.first == FileDialog::Later)
1545 filename.set(fromqstr(result.second));
1547 // check selected filename
1548 if (filename.empty()) {
1549 // emit message signal.
1550 message(_("Canceled."));
1554 bv->insertLyXFile(filename);
1558 void GuiView::insertPlaintextFile(docstring const & fname,
1561 BufferView * bv = view();
1566 FileName filename(to_utf8(fname));
1568 if (!filename.empty()) {
1569 bv->insertPlaintextFile(filename, asParagraph);
1573 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1574 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1576 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1579 if (result.first == FileDialog::Later)
1583 filename.set(fromqstr(result.second));
1585 // check selected filename
1586 if (filename.empty()) {
1587 // emit message signal.
1588 message(_("Canceled."));
1592 bv->insertPlaintextFile(filename, asParagraph);
1596 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1598 FileName fname = b.fileName();
1599 FileName const oldname = fname;
1601 if (!newname.empty()) {
1603 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1605 // Switch to this Buffer.
1608 /// No argument? Ask user through dialog.
1610 FileDialog dlg(qt_("Choose a filename to save document as"),
1611 LFUN_BUFFER_WRITE_AS);
1612 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1613 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1615 if (!isLyXFilename(fname.absFilename()))
1616 fname.changeExtension(".lyx");
1618 FileDialog::Result result =
1619 dlg.save(toqstr(fname.onlyPath().absFilename()),
1620 QStringList(qt_("LyX Documents (*.lyx)")),
1621 toqstr(fname.onlyFileName()));
1623 if (result.first == FileDialog::Later)
1626 fname.set(fromqstr(result.second));
1631 if (!isLyXFilename(fname.absFilename()))
1632 fname.changeExtension(".lyx");
1635 if (FileName(fname).exists()) {
1636 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1637 docstring text = bformat(_("The document %1$s already "
1638 "exists.\n\nDo you want to "
1639 "overwrite that document?"),
1641 int const ret = Alert::prompt(_("Overwrite document?"),
1642 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1645 case 1: return renameBuffer(b, docstring());
1646 case 2: return false;
1650 // Ok, change the name of the buffer
1651 b.setFileName(fname.absFilename());
1653 bool unnamed = b.isUnnamed();
1654 b.setUnnamed(false);
1655 b.saveCheckSum(fname);
1657 if (!saveBuffer(b)) {
1658 b.setFileName(oldname.absFilename());
1659 b.setUnnamed(unnamed);
1660 b.saveCheckSum(oldname);
1668 bool GuiView::saveBuffer(Buffer & b)
1671 return renameBuffer(b, docstring());
1674 theSession().lastFiles().add(b.fileName());
1678 // Switch to this Buffer.
1681 // FIXME: we don't tell the user *WHY* the save failed !!
1682 docstring const file = makeDisplayPath(b.absFileName(), 30);
1683 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1684 "Do you want to rename the document and "
1685 "try again?"), file);
1686 int const ret = Alert::prompt(_("Rename and save?"),
1687 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1690 if (!renameBuffer(b, docstring()))
1699 return saveBuffer(b);
1703 bool GuiView::closeBuffer()
1705 Buffer * buf = buffer();
1706 return buf && closeBuffer(*buf);
1710 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1712 // goto bookmark to update bookmark pit.
1713 //FIXME: we should update only the bookmarks related to this buffer!
1714 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1715 theLyXFunc().gotoBookmark(i+1, false, false);
1717 if (buf.isClean() || buf.paragraphs().empty()) {
1718 if (buf.masterBuffer() == &buf && tolastopened)
1719 theSession().lastOpened().add(buf.fileName());
1720 theBufferList().release(&buf);
1723 // Switch to this Buffer.
1728 if (buf.isUnnamed())
1729 file = from_utf8(buf.fileName().onlyFileName());
1731 file = buf.fileName().displayName(30);
1733 // Bring this window to top before asking questions.
1737 docstring const text = bformat(_("The document %1$s has unsaved changes."
1738 "\n\nDo you want to save the document or discard the changes?"), file);
1739 int const ret = Alert::prompt(_("Save changed document?"),
1740 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1744 if (!saveBuffer(buf))
1748 // if we crash after this we could
1749 // have no autosave file but I guess
1750 // this is really improbable (Jug)
1751 removeAutosaveFile(buf.absFileName());
1757 // save file names to .lyx/session
1758 // if master/slave are both open, do not save slave since it
1759 // will be automatically loaded when the master is loaded
1760 if (buf.masterBuffer() == &buf && tolastopened)
1761 theSession().lastOpened().add(buf.fileName());
1764 // Don't close child documents.
1765 removeWorkArea(d.current_work_area_);
1767 theBufferList().release(&buf);
1773 bool GuiView::dispatch(FuncRequest const & cmd)
1775 BufferView * bv = view();
1776 // By default we won't need any update.
1778 bv->cursor().updateFlags(Update::None);
1779 bool dispatched = true;
1781 switch(cmd.action) {
1782 case LFUN_BUFFER_IMPORT:
1783 importDocument(to_utf8(cmd.argument()));
1786 case LFUN_BUFFER_SWITCH:
1787 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1790 case LFUN_BUFFER_NEXT:
1791 setBuffer(theBufferList().next(buffer()));
1794 case LFUN_BUFFER_PREVIOUS:
1795 setBuffer(theBufferList().previous(buffer()));
1798 case LFUN_COMMAND_EXECUTE: {
1799 bool const show_it = cmd.argument() != "off";
1800 // FIXME: this is a hack, "minibuffer" should not be
1802 if (GuiToolbar * t = toolbar("minibuffer")) {
1803 t->setVisible(show_it);
1804 if (show_it && t->commandBuffer())
1805 t->commandBuffer()->setFocus();
1809 case LFUN_DROP_LAYOUTS_CHOICE:
1811 d.layout_->showPopup();
1814 case LFUN_MENU_OPEN:
1815 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1816 menu->exec(QCursor::pos());
1819 case LFUN_FILE_INSERT:
1820 insertLyXFile(cmd.argument());
1822 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1823 insertPlaintextFile(cmd.argument(), true);
1826 case LFUN_FILE_INSERT_PLAINTEXT:
1827 insertPlaintextFile(cmd.argument(), false);
1830 case LFUN_BUFFER_WRITE:
1832 saveBuffer(bv->buffer());
1835 case LFUN_BUFFER_WRITE_AS:
1837 renameBuffer(bv->buffer(), cmd.argument());
1840 case LFUN_BUFFER_WRITE_ALL: {
1841 Buffer * first = theBufferList().first();
1844 message(_("Saving all documents..."));
1845 // We cannot use a for loop as the buffer list cycles.
1848 if (!b->isClean()) {
1850 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1852 b = theBufferList().next(b);
1853 } while (b != first);
1854 message(_("All documents saved."));
1858 case LFUN_TOOLBAR_TOGGLE: {
1859 string const name = cmd.getArg(0);
1860 if (GuiToolbar * t = toolbar(name))
1865 case LFUN_DIALOG_UPDATE: {
1866 string const name = to_utf8(cmd.argument());
1867 // Can only update a dialog connected to an existing inset
1868 Inset * inset = getOpenInset(name);
1870 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1871 inset->dispatch(view()->cursor(), fr);
1872 } else if (name == "paragraph") {
1873 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1874 } else if (name == "prefs") {
1875 updateDialog(name, string());
1880 case LFUN_DIALOG_TOGGLE: {
1881 if (isDialogVisible(cmd.getArg(0)))
1882 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1884 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1888 case LFUN_DIALOG_DISCONNECT_INSET:
1889 disconnectDialog(to_utf8(cmd.argument()));
1892 case LFUN_DIALOG_HIDE: {
1893 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1897 case LFUN_DIALOG_SHOW: {
1898 string const name = cmd.getArg(0);
1899 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1901 if (name == "character") {
1902 data = freefont2string();
1904 showDialog("character", data);
1905 } else if (name == "latexlog") {
1906 Buffer::LogType type;
1907 string const logfile = buffer()->logName(&type);
1909 case Buffer::latexlog:
1912 case Buffer::buildlog:
1916 data += Lexer::quoteString(logfile);
1917 showDialog("log", data);
1918 } else if (name == "vclog") {
1919 string const data = "vc " +
1920 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1921 showDialog("log", data);
1922 } else if (name == "symbols") {
1923 data = bv->cursor().getEncoding()->name();
1925 showDialog("symbols", data);
1927 showDialog(name, data);
1931 case LFUN_INSET_APPLY: {
1932 view()->cursor().recordUndoFullDocument();
1933 string const name = cmd.getArg(0);
1934 Inset * inset = getOpenInset(name);
1936 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1937 inset->dispatch(view()->cursor(), fr);
1939 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1945 case LFUN_UI_TOGGLE:
1947 // Make sure the keyboard focus stays in the work area.
1951 case LFUN_COMPLETION_INLINE:
1952 if (d.current_work_area_)
1953 d.current_work_area_->completer().showInline();
1956 case LFUN_SPLIT_VIEW:
1957 if (Buffer * buf = buffer()) {
1958 string const orientation = cmd.getArg(0);
1959 d.splitter_->setOrientation(orientation == "vertical"
1960 ? Qt::Vertical : Qt::Horizontal);
1961 TabWorkArea * twa = addTabWorkArea();
1962 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1963 setCurrentWorkArea(wa);
1967 case LFUN_CLOSE_TAB_GROUP:
1968 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1970 twa = d.currentTabWorkArea();
1971 // Switch to the next GuiWorkArea in the found TabWorkArea.
1973 d.current_work_area_ = twa->currentWorkArea();
1974 // Make sure the work area is up to date.
1975 twa->setCurrentWorkArea(d.current_work_area_);
1977 d.current_work_area_ = 0;
1979 if (d.splitter_->count() == 0)
1980 // No more work area, switch to the background widget.
1985 case LFUN_COMPLETION_POPUP:
1986 if (d.current_work_area_)
1987 d.current_work_area_->completer().showPopup();
1991 case LFUN_COMPLETION_COMPLETE:
1992 if (d.current_work_area_)
1993 d.current_work_area_->completer().tab();
2001 if (isFullScreen()) {
2002 if (menuBar()->isVisible())
2004 if (statusBar()->isVisible())
2005 statusBar()->hide();
2012 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2014 string const arg = cmd.getArg(0);
2015 if (arg == "scrollbar") {
2016 // hide() is of no help
2017 if (d.current_work_area_->verticalScrollBarPolicy() ==
2018 Qt::ScrollBarAlwaysOff)
2020 d.current_work_area_->setVerticalScrollBarPolicy(
2021 Qt::ScrollBarAsNeeded);
2023 d.current_work_area_->setVerticalScrollBarPolicy(
2024 Qt::ScrollBarAlwaysOff);
2027 if (arg == "statusbar") {
2028 statusBar()->setVisible(!statusBar()->isVisible());
2031 if (arg == "menubar") {
2032 menuBar()->setVisible(!menuBar()->isVisible());
2035 #if QT_VERSION >= 0x040300
2036 if (arg == "frame") {
2038 getContentsMargins(&l, &t, &r, &b);
2039 //are the frames in default state?
2040 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2042 setContentsMargins(-2, -2, -2, -2);
2044 setContentsMargins(0, 0, 0, 0);
2049 if (arg == "fullscreen") {
2054 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2058 void GuiView::toggleFullScreen()
2060 if (isFullScreen()) {
2061 for (int i = 0; i != d.splitter_->count(); ++i)
2062 d.tabWorkArea(i)->setFullScreen(false);
2063 #if QT_VERSION >= 0x040300
2064 setContentsMargins(0, 0, 0, 0);
2066 setWindowState(windowState() ^ Qt::WindowFullScreen);
2069 statusBar()->show();
2071 for (int i = 0; i != d.splitter_->count(); ++i)
2072 d.tabWorkArea(i)->setFullScreen(true);
2073 #if QT_VERSION >= 0x040300
2074 setContentsMargins(-2, -2, -2, -2);
2077 setWindowState(windowState() ^ Qt::WindowFullScreen);
2078 statusBar()->hide();
2080 if (lyxrc.full_screen_toolbars) {
2081 ToolbarMap::iterator end = d.toolbars_.end();
2082 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2089 Buffer const * GuiView::updateInset(Inset const * inset)
2091 if (!d.current_work_area_)
2095 d.current_work_area_->scheduleRedraw();
2097 return &d.current_work_area_->bufferView().buffer();
2101 void GuiView::restartCursor()
2103 /* When we move around, or type, it's nice to be able to see
2104 * the cursor immediately after the keypress.
2106 if (d.current_work_area_)
2107 d.current_work_area_->startBlinkingCursor();
2109 // Take this occasion to update the other GUI elements.
2115 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2117 if (d.current_work_area_)
2118 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2123 // This list should be kept in sync with the list of insets in
2124 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2125 // dialog should have the same name as the inset.
2126 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2127 // docs in LyXAction.cpp.
2129 char const * const dialognames[] = {
2130 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2131 "citation", "document", "errorlist", "ert", "external", "file",
2132 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2133 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2134 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2136 #ifdef HAVE_LIBAIKSAURUS
2140 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2142 char const * const * const end_dialognames =
2143 dialognames + (sizeof(dialognames) / sizeof(char *));
2147 cmpCStr(char const * name) : name_(name) {}
2148 bool operator()(char const * other) {
2149 return strcmp(other, name_) == 0;
2156 bool isValidName(string const & name)
2158 return find_if(dialognames, end_dialognames,
2159 cmpCStr(name.c_str())) != end_dialognames;
2165 void GuiView::resetDialogs()
2167 // Make sure that no LFUN uses any LyXView.
2168 theLyXFunc().setLyXView(0);
2171 constructToolbars();
2172 guiApp->menus().fillMenuBar(menuBar(), this, true);
2174 d.layout_->updateContents(true);
2175 // Now update controls with current buffer.
2176 theLyXFunc().setLyXView(this);
2182 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2184 if (!isValidName(name))
2187 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2189 if (it != d.dialogs_.end())
2190 return it->second.get();
2192 Dialog * dialog = build(name);
2193 d.dialogs_[name].reset(dialog);
2194 if (lyxrc.allow_geometry_session)
2195 dialog->restoreSession();
2202 void GuiView::showDialog(string const & name, string const & data,
2209 Dialog * dialog = findOrBuild(name, false);
2211 dialog->showData(data);
2213 d.open_insets_[name] = inset;
2219 bool GuiView::isDialogVisible(string const & name) const
2221 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2222 if (it == d.dialogs_.end())
2224 return it->second.get()->isVisibleView();
2228 void GuiView::hideDialog(string const & name, Inset * inset)
2230 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2231 if (it == d.dialogs_.end())
2234 if (inset && inset != getOpenInset(name))
2237 Dialog * const dialog = it->second.get();
2238 if (dialog->isVisibleView())
2240 d.open_insets_[name] = 0;
2244 void GuiView::disconnectDialog(string const & name)
2246 if (!isValidName(name))
2249 if (d.open_insets_.find(name) != d.open_insets_.end())
2250 d.open_insets_[name] = 0;
2254 Inset * GuiView::getOpenInset(string const & name) const
2256 if (!isValidName(name))
2259 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2260 return it == d.open_insets_.end() ? 0 : it->second;
2264 void GuiView::hideAll() const
2266 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2267 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2269 for(; it != end; ++it)
2270 it->second->hideView();
2274 void GuiView::updateDialogs()
2276 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2277 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2279 for(; it != end; ++it) {
2280 Dialog * dialog = it->second.get();
2281 if (dialog && dialog->isVisibleView())
2282 dialog->checkStatus();
2289 // will be replaced by a proper factory...
2290 Dialog * createGuiAbout(GuiView & lv);
2291 Dialog * createGuiBibitem(GuiView & lv);
2292 Dialog * createGuiBibtex(GuiView & lv);
2293 Dialog * createGuiBox(GuiView & lv);
2294 Dialog * createGuiBranch(GuiView & lv);
2295 Dialog * createGuiChanges(GuiView & lv);
2296 Dialog * createGuiCharacter(GuiView & lv);
2297 Dialog * createGuiCitation(GuiView & lv);
2298 Dialog * createGuiDelimiter(GuiView & lv);
2299 Dialog * createGuiDocument(GuiView & lv);
2300 Dialog * createGuiErrorList(GuiView & lv);
2301 Dialog * createGuiERT(GuiView & lv);
2302 Dialog * createGuiExternal(GuiView & lv);
2303 Dialog * createGuiFloat(GuiView & lv);
2304 Dialog * createGuiGraphics(GuiView & lv);
2305 Dialog * createGuiHSpace(GuiView & lv);
2306 Dialog * createGuiInclude(GuiView & lv);
2307 Dialog * createGuiInfo(GuiView & lv);
2308 Dialog * createGuiLabel(GuiView & lv);
2309 Dialog * createGuiListings(GuiView & lv);
2310 Dialog * createGuiLog(GuiView & lv);
2311 Dialog * createGuiMathMatrix(GuiView & lv);
2312 Dialog * createGuiNomenclature(GuiView & lv);
2313 Dialog * createGuiNote(GuiView & lv);
2314 Dialog * createGuiParagraph(GuiView & lv);
2315 Dialog * createGuiPreferences(GuiView & lv);
2316 Dialog * createGuiPrint(GuiView & lv);
2317 Dialog * createGuiRef(GuiView & lv);
2318 Dialog * createGuiSearch(GuiView & lv);
2319 Dialog * createGuiSendTo(GuiView & lv);
2320 Dialog * createGuiShowFile(GuiView & lv);
2321 Dialog * createGuiSpellchecker(GuiView & lv);
2322 Dialog * createGuiSymbols(GuiView & lv);
2323 Dialog * createGuiTabularCreate(GuiView & lv);
2324 Dialog * createGuiTabular(GuiView & lv);
2325 Dialog * createGuiTexInfo(GuiView & lv);
2326 Dialog * createGuiToc(GuiView & lv);
2327 Dialog * createGuiThesaurus(GuiView & lv);
2328 Dialog * createGuiHyperlink(GuiView & lv);
2329 Dialog * createGuiVSpace(GuiView & lv);
2330 Dialog * createGuiViewSource(GuiView & lv);
2331 Dialog * createGuiWrap(GuiView & lv);
2334 Dialog * GuiView::build(string const & name)
2336 LASSERT(isValidName(name), /**/);
2338 if (name == "aboutlyx")
2339 return createGuiAbout(*this);
2340 if (name == "bibitem")
2341 return createGuiBibitem(*this);
2342 if (name == "bibtex")
2343 return createGuiBibtex(*this);
2345 return createGuiBox(*this);
2346 if (name == "branch")
2347 return createGuiBranch(*this);
2348 if (name == "changes")
2349 return createGuiChanges(*this);
2350 if (name == "character")
2351 return createGuiCharacter(*this);
2352 if (name == "citation")
2353 return createGuiCitation(*this);
2354 if (name == "document")
2355 return createGuiDocument(*this);
2356 if (name == "errorlist")
2357 return createGuiErrorList(*this);
2359 return createGuiERT(*this);
2360 if (name == "external")
2361 return createGuiExternal(*this);
2363 return createGuiShowFile(*this);
2364 if (name == "findreplace")
2365 return createGuiSearch(*this);
2366 if (name == "float")
2367 return createGuiFloat(*this);
2368 if (name == "graphics")
2369 return createGuiGraphics(*this);
2370 if (name == "include")
2371 return createGuiInclude(*this);
2373 return createGuiInfo(*this);
2374 if (name == "nomenclature")
2375 return createGuiNomenclature(*this);
2376 if (name == "label")
2377 return createGuiLabel(*this);
2379 return createGuiLog(*this);
2380 if (name == "view-source")
2381 return createGuiViewSource(*this);
2382 if (name == "mathdelimiter")
2383 return createGuiDelimiter(*this);
2384 if (name == "mathmatrix")
2385 return createGuiMathMatrix(*this);
2387 return createGuiNote(*this);
2388 if (name == "paragraph")
2389 return createGuiParagraph(*this);
2390 if (name == "prefs")
2391 return createGuiPreferences(*this);
2392 if (name == "print")
2393 return createGuiPrint(*this);
2395 return createGuiRef(*this);
2396 if (name == "sendto")
2397 return createGuiSendTo(*this);
2398 if (name == "space")
2399 return createGuiHSpace(*this);
2400 if (name == "spellchecker")
2401 return createGuiSpellchecker(*this);
2402 if (name == "symbols")
2403 return createGuiSymbols(*this);
2404 if (name == "tabular")
2405 return createGuiTabular(*this);
2406 if (name == "tabularcreate")
2407 return createGuiTabularCreate(*this);
2408 if (name == "texinfo")
2409 return createGuiTexInfo(*this);
2410 #ifdef HAVE_LIBAIKSAURUS
2411 if (name == "thesaurus")
2412 return createGuiThesaurus(*this);
2415 return createGuiToc(*this);
2417 return createGuiHyperlink(*this);
2418 if (name == "vspace")
2419 return createGuiVSpace(*this);
2421 return createGuiWrap(*this);
2422 if (name == "listings")
2423 return createGuiListings(*this);
2429 } // namespace frontend
2432 #include "GuiView_moc.cpp"