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 QStringList filter(qt_("LyX Documents (*.lyx)"));
1277 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1278 << qt_("LyX-1.4.x Documents (*.lyx14)")
1279 << qt_("LyX-1.5.x Documents (*.lyx15)");
1280 FileDialog::Result result =
1281 dlg.open(toqstr(initpath), filter);
1283 if (result.first == FileDialog::Later)
1286 filename = fromqstr(result.second);
1288 // check selected filename
1289 if (filename.empty()) {
1290 message(_("Canceled."));
1296 // get absolute path of file and add ".lyx" to the filename if
1298 FileName const fullname =
1299 fileSearch(string(), filename, "lyx", support::may_not_exist);
1300 if (!fullname.empty())
1301 filename = fullname.absFilename();
1303 // if the file doesn't exist, let the user create one
1304 if (!fullname.exists()) {
1305 // the user specifically chose this name. Believe him.
1306 Buffer * const b = newFile(filename, string(), true);
1312 docstring const disp_fn = makeDisplayPath(filename);
1313 message(bformat(_("Opening document %1$s..."), disp_fn));
1316 Buffer * buf = loadDocument(fullname);
1321 buf->errors("Parse");
1322 str2 = bformat(_("Document %1$s opened."), disp_fn);
1324 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1329 // FIXME: clean that
1330 static bool import(GuiView * lv, FileName const & filename,
1331 string const & format, ErrorList & errorList)
1333 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1335 string loader_format;
1336 vector<string> loaders = theConverters().loaders();
1337 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1338 for (vector<string>::const_iterator it = loaders.begin();
1339 it != loaders.end(); ++it) {
1340 if (!theConverters().isReachable(format, *it))
1343 string const tofile =
1344 support::changeExtension(filename.absFilename(),
1345 formats.extension(*it));
1346 if (!theConverters().convert(0, filename, FileName(tofile),
1347 filename, format, *it, errorList))
1349 loader_format = *it;
1352 if (loader_format.empty()) {
1353 frontend::Alert::error(_("Couldn't import file"),
1354 bformat(_("No information for importing the format %1$s."),
1355 formats.prettyName(format)));
1359 loader_format = format;
1361 if (loader_format == "lyx") {
1362 Buffer * buf = lv->loadDocument(lyxfile);
1367 buf->errors("Parse");
1369 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1373 bool as_paragraphs = loader_format == "textparagraph";
1374 string filename2 = (loader_format == format) ? filename.absFilename()
1375 : support::changeExtension(filename.absFilename(),
1376 formats.extension(loader_format));
1377 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1378 theLyXFunc().setLyXView(lv);
1379 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1386 void GuiView::importDocument(string const & argument)
1389 string filename = split(argument, format, ' ');
1391 LYXERR(Debug::INFO, format << " file: " << filename);
1393 // need user interaction
1394 if (filename.empty()) {
1395 string initpath = lyxrc.document_path;
1397 Buffer const * buf = buffer();
1399 string const trypath = buf->filePath();
1400 // If directory is writeable, use this as default.
1401 if (FileName(trypath).isDirWritable())
1405 docstring const text = bformat(_("Select %1$s file to import"),
1406 formats.prettyName(format));
1408 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1409 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1410 dlg.setButton2(qt_("Examples|#E#e"),
1411 toqstr(addPath(package().system_support().absFilename(), "examples")));
1413 docstring filter = formats.prettyName(format);
1416 filter += from_utf8(formats.extension(format));
1419 FileDialog::Result result =
1420 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1422 if (result.first == FileDialog::Later)
1425 filename = fromqstr(result.second);
1427 // check selected filename
1428 if (filename.empty())
1429 message(_("Canceled."));
1432 if (filename.empty())
1435 // get absolute path of file
1436 FileName const fullname(support::makeAbsPath(filename));
1438 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1440 // Check if the document already is open
1441 Buffer * buf = theBufferList().getBuffer(lyxfile.absFilename());
1444 if (!closeBuffer()) {
1445 message(_("Canceled."));
1450 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1452 // if the file exists already, and we didn't do
1453 // -i lyx thefile.lyx, warn
1454 if (lyxfile.exists() && fullname != lyxfile) {
1456 docstring text = bformat(_("The document %1$s already exists.\n\n"
1457 "Do you want to overwrite that document?"), displaypath);
1458 int const ret = Alert::prompt(_("Overwrite document?"),
1459 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1462 message(_("Canceled."));
1467 message(bformat(_("Importing %1$s..."), displaypath));
1468 ErrorList errorList;
1469 if (import(this, fullname, format, errorList))
1470 message(_("imported."));
1472 message(_("file not imported!"));
1474 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1478 void GuiView::newDocument(string const & filename, bool from_template)
1480 FileName initpath(lyxrc.document_path);
1481 Buffer * buf = buffer();
1483 FileName const trypath(buf->filePath());
1484 // If directory is writeable, use this as default.
1485 if (trypath.isDirWritable())
1489 string templatefile = from_template ?
1490 selectTemplateFile().absFilename() : string();
1492 if (filename.empty())
1493 b = newUnnamedFile(templatefile, initpath);
1495 b = newFile(filename, templatefile, true);
1499 // Ensure the cursor is correctly positionned on screen.
1500 view()->showCursor();
1504 void GuiView::insertLyXFile(docstring const & fname)
1506 BufferView * bv = view();
1511 FileName filename(to_utf8(fname));
1513 if (!filename.empty()) {
1514 bv->insertLyXFile(filename);
1518 // Launch a file browser
1520 string initpath = lyxrc.document_path;
1521 string const trypath = bv->buffer().filePath();
1522 // If directory is writeable, use this as default.
1523 if (FileName(trypath).isDirWritable())
1527 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1528 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1529 dlg.setButton2(qt_("Examples|#E#e"),
1530 toqstr(addPath(package().system_support().absFilename(),
1533 FileDialog::Result result = dlg.open(toqstr(initpath),
1534 QStringList(qt_("LyX Documents (*.lyx)")));
1536 if (result.first == FileDialog::Later)
1540 filename.set(fromqstr(result.second));
1542 // check selected filename
1543 if (filename.empty()) {
1544 // emit message signal.
1545 message(_("Canceled."));
1549 bv->insertLyXFile(filename);
1553 void GuiView::insertPlaintextFile(docstring const & fname,
1556 BufferView * bv = view();
1561 FileName filename(to_utf8(fname));
1563 if (!filename.empty()) {
1564 bv->insertPlaintextFile(filename, asParagraph);
1568 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1569 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1571 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1574 if (result.first == FileDialog::Later)
1578 filename.set(fromqstr(result.second));
1580 // check selected filename
1581 if (filename.empty()) {
1582 // emit message signal.
1583 message(_("Canceled."));
1587 bv->insertPlaintextFile(filename, asParagraph);
1591 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1593 FileName fname = b.fileName();
1594 FileName const oldname = fname;
1596 if (!newname.empty()) {
1598 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1600 // Switch to this Buffer.
1603 /// No argument? Ask user through dialog.
1605 FileDialog dlg(qt_("Choose a filename to save document as"),
1606 LFUN_BUFFER_WRITE_AS);
1607 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1608 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1610 if (!isLyXFilename(fname.absFilename()))
1611 fname.changeExtension(".lyx");
1613 FileDialog::Result result =
1614 dlg.save(toqstr(fname.onlyPath().absFilename()),
1615 QStringList(qt_("LyX Documents (*.lyx)")),
1616 toqstr(fname.onlyFileName()));
1618 if (result.first == FileDialog::Later)
1621 fname.set(fromqstr(result.second));
1626 if (!isLyXFilename(fname.absFilename()))
1627 fname.changeExtension(".lyx");
1630 if (FileName(fname).exists()) {
1631 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1632 docstring text = bformat(_("The document %1$s already "
1633 "exists.\n\nDo you want to "
1634 "overwrite that document?"),
1636 int const ret = Alert::prompt(_("Overwrite document?"),
1637 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1640 case 1: return renameBuffer(b, docstring());
1641 case 2: return false;
1645 // Ok, change the name of the buffer
1646 b.setFileName(fname.absFilename());
1648 bool unnamed = b.isUnnamed();
1649 b.setUnnamed(false);
1650 b.saveCheckSum(fname);
1652 if (!saveBuffer(b)) {
1653 b.setFileName(oldname.absFilename());
1654 b.setUnnamed(unnamed);
1655 b.saveCheckSum(oldname);
1663 bool GuiView::saveBuffer(Buffer & b)
1666 return renameBuffer(b, docstring());
1669 LyX::ref().session().lastFiles().add(b.fileName());
1673 // Switch to this Buffer.
1676 // FIXME: we don't tell the user *WHY* the save failed !!
1677 docstring const file = makeDisplayPath(b.absFileName(), 30);
1678 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1679 "Do you want to rename the document and "
1680 "try again?"), file);
1681 int const ret = Alert::prompt(_("Rename and save?"),
1682 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1685 if (!renameBuffer(b, docstring()))
1694 return saveBuffer(b);
1698 bool GuiView::closeBuffer()
1700 Buffer * buf = buffer();
1701 return buf && closeBuffer(*buf);
1705 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1707 // goto bookmark to update bookmark pit.
1708 //FIXME: we should update only the bookmarks related to this buffer!
1709 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1710 theLyXFunc().gotoBookmark(i+1, false, false);
1712 if (buf.isClean() || buf.paragraphs().empty()) {
1713 if (buf.masterBuffer() == &buf && tolastopened)
1714 LyX::ref().session().lastOpened().add(buf.fileName());
1715 theBufferList().release(&buf);
1718 // Switch to this Buffer.
1723 if (buf.isUnnamed())
1724 file = from_utf8(buf.fileName().onlyFileName());
1726 file = buf.fileName().displayName(30);
1728 // Bring this window to top before asking questions.
1732 docstring const text = bformat(_("The document %1$s has unsaved changes."
1733 "\n\nDo you want to save the document or discard the changes?"), file);
1734 int const ret = Alert::prompt(_("Save changed document?"),
1735 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1739 if (!saveBuffer(buf))
1743 // if we crash after this we could
1744 // have no autosave file but I guess
1745 // this is really improbable (Jug)
1746 removeAutosaveFile(buf.absFileName());
1752 // save file names to .lyx/session
1753 // if master/slave are both open, do not save slave since it
1754 // will be automatically loaded when the master is loaded
1755 if (buf.masterBuffer() == &buf && tolastopened)
1756 LyX::ref().session().lastOpened().add(buf.fileName());
1759 // Don't close child documents.
1760 removeWorkArea(d.current_work_area_);
1762 theBufferList().release(&buf);
1768 bool GuiView::dispatch(FuncRequest const & cmd)
1770 BufferView * bv = view();
1771 // By default we won't need any update.
1773 bv->cursor().updateFlags(Update::None);
1774 bool dispatched = true;
1776 switch(cmd.action) {
1777 case LFUN_BUFFER_IMPORT:
1778 importDocument(to_utf8(cmd.argument()));
1781 case LFUN_BUFFER_SWITCH:
1782 setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1785 case LFUN_BUFFER_NEXT:
1786 setBuffer(theBufferList().next(buffer()));
1789 case LFUN_BUFFER_PREVIOUS:
1790 setBuffer(theBufferList().previous(buffer()));
1793 case LFUN_COMMAND_EXECUTE: {
1794 bool const show_it = cmd.argument() != "off";
1795 // FIXME: this is a hack, "minibuffer" should not be
1797 if (GuiToolbar * t = toolbar("minibuffer")) {
1798 t->setVisible(show_it);
1799 if (show_it && t->commandBuffer())
1800 t->commandBuffer()->setFocus();
1804 case LFUN_DROP_LAYOUTS_CHOICE:
1806 d.layout_->showPopup();
1809 case LFUN_MENU_OPEN:
1810 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1811 menu->exec(QCursor::pos());
1814 case LFUN_FILE_INSERT:
1815 insertLyXFile(cmd.argument());
1817 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1818 insertPlaintextFile(cmd.argument(), true);
1821 case LFUN_FILE_INSERT_PLAINTEXT:
1822 insertPlaintextFile(cmd.argument(), false);
1825 case LFUN_BUFFER_WRITE:
1827 saveBuffer(bv->buffer());
1830 case LFUN_BUFFER_WRITE_AS:
1832 renameBuffer(bv->buffer(), cmd.argument());
1835 case LFUN_BUFFER_WRITE_ALL: {
1836 Buffer * first = theBufferList().first();
1839 message(_("Saving all documents..."));
1840 // We cannot use a for loop as the buffer list cycles.
1843 if (!b->isClean()) {
1845 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1847 b = theBufferList().next(b);
1848 } while (b != first);
1849 message(_("All documents saved."));
1853 case LFUN_TOOLBAR_TOGGLE: {
1854 string const name = cmd.getArg(0);
1855 if (GuiToolbar * t = toolbar(name))
1860 case LFUN_DIALOG_UPDATE: {
1861 string const name = to_utf8(cmd.argument());
1862 // Can only update a dialog connected to an existing inset
1863 Inset * inset = getOpenInset(name);
1865 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1866 inset->dispatch(view()->cursor(), fr);
1867 } else if (name == "paragraph") {
1868 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1869 } else if (name == "prefs") {
1870 updateDialog(name, string());
1875 case LFUN_DIALOG_TOGGLE: {
1876 if (isDialogVisible(cmd.getArg(0)))
1877 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1879 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1883 case LFUN_DIALOG_DISCONNECT_INSET:
1884 disconnectDialog(to_utf8(cmd.argument()));
1887 case LFUN_DIALOG_HIDE: {
1888 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1892 case LFUN_DIALOG_SHOW: {
1893 string const name = cmd.getArg(0);
1894 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1896 if (name == "character") {
1897 data = freefont2string();
1899 showDialog("character", data);
1900 } else if (name == "latexlog") {
1901 Buffer::LogType type;
1902 string const logfile = buffer()->logName(&type);
1904 case Buffer::latexlog:
1907 case Buffer::buildlog:
1911 data += Lexer::quoteString(logfile);
1912 showDialog("log", data);
1913 } else if (name == "vclog") {
1914 string const data = "vc " +
1915 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1916 showDialog("log", data);
1917 } else if (name == "symbols") {
1918 data = bv->cursor().getEncoding()->name();
1920 showDialog("symbols", data);
1922 showDialog(name, data);
1926 case LFUN_INSET_APPLY: {
1927 view()->cursor().recordUndoFullDocument();
1928 string const name = cmd.getArg(0);
1929 Inset * inset = getOpenInset(name);
1931 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1932 inset->dispatch(view()->cursor(), fr);
1934 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1940 case LFUN_UI_TOGGLE:
1942 // Make sure the keyboard focus stays in the work area.
1946 case LFUN_COMPLETION_INLINE:
1947 if (d.current_work_area_)
1948 d.current_work_area_->completer().showInline();
1951 case LFUN_SPLIT_VIEW:
1952 if (Buffer * buf = buffer()) {
1953 string const orientation = cmd.getArg(0);
1954 d.splitter_->setOrientation(orientation == "vertical"
1955 ? Qt::Vertical : Qt::Horizontal);
1956 TabWorkArea * twa = addTabWorkArea();
1957 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1958 setCurrentWorkArea(wa);
1962 case LFUN_CLOSE_TAB_GROUP:
1963 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1965 twa = d.currentTabWorkArea();
1966 // Switch to the next GuiWorkArea in the found TabWorkArea.
1968 d.current_work_area_ = twa->currentWorkArea();
1969 // Make sure the work area is up to date.
1970 twa->setCurrentWorkArea(d.current_work_area_);
1972 d.current_work_area_ = 0;
1974 if (d.splitter_->count() == 0)
1975 // No more work area, switch to the background widget.
1980 case LFUN_COMPLETION_POPUP:
1981 if (d.current_work_area_)
1982 d.current_work_area_->completer().showPopup();
1986 case LFUN_COMPLETION_COMPLETE:
1987 if (d.current_work_area_)
1988 d.current_work_area_->completer().tab();
1996 if (isFullScreen()) {
1997 if (menuBar()->isVisible())
1999 if (statusBar()->isVisible())
2000 statusBar()->hide();
2007 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2009 string const arg = cmd.getArg(0);
2010 if (arg == "scrollbar") {
2011 // hide() is of no help
2012 if (d.current_work_area_->verticalScrollBarPolicy() ==
2013 Qt::ScrollBarAlwaysOff)
2015 d.current_work_area_->setVerticalScrollBarPolicy(
2016 Qt::ScrollBarAsNeeded);
2018 d.current_work_area_->setVerticalScrollBarPolicy(
2019 Qt::ScrollBarAlwaysOff);
2022 if (arg == "statusbar") {
2023 statusBar()->setVisible(!statusBar()->isVisible());
2026 if (arg == "menubar") {
2027 menuBar()->setVisible(!menuBar()->isVisible());
2030 #if QT_VERSION >= 0x040300
2031 if (arg == "frame") {
2033 getContentsMargins(&l, &t, &r, &b);
2034 //are the frames in default state?
2035 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2037 setContentsMargins(-2, -2, -2, -2);
2039 setContentsMargins(0, 0, 0, 0);
2044 if (arg == "fullscreen") {
2049 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2053 void GuiView::toggleFullScreen()
2055 if (isFullScreen()) {
2056 for (int i = 0; i != d.splitter_->count(); ++i)
2057 d.tabWorkArea(i)->setFullScreen(false);
2058 #if QT_VERSION >= 0x040300
2059 setContentsMargins(0, 0, 0, 0);
2061 setWindowState(windowState() ^ Qt::WindowFullScreen);
2064 statusBar()->show();
2066 for (int i = 0; i != d.splitter_->count(); ++i)
2067 d.tabWorkArea(i)->setFullScreen(true);
2068 #if QT_VERSION >= 0x040300
2069 setContentsMargins(-2, -2, -2, -2);
2072 setWindowState(windowState() ^ Qt::WindowFullScreen);
2073 statusBar()->hide();
2075 if (lyxrc.full_screen_toolbars) {
2076 ToolbarMap::iterator end = d.toolbars_.end();
2077 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2084 Buffer const * GuiView::updateInset(Inset const * inset)
2086 if (!d.current_work_area_)
2090 d.current_work_area_->scheduleRedraw();
2092 return &d.current_work_area_->bufferView().buffer();
2096 void GuiView::restartCursor()
2098 /* When we move around, or type, it's nice to be able to see
2099 * the cursor immediately after the keypress.
2101 if (d.current_work_area_)
2102 d.current_work_area_->startBlinkingCursor();
2104 // Take this occasion to update the other GUI elements.
2110 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2112 if (d.current_work_area_)
2113 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2118 // This list should be kept in sync with the list of insets in
2119 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2120 // dialog should have the same name as the inset.
2121 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2122 // docs in LyXAction.cpp.
2124 char const * const dialognames[] = {
2125 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2126 "citation", "document", "errorlist", "ert", "external", "file",
2127 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2128 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2129 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2131 #ifdef HAVE_LIBAIKSAURUS
2135 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2137 char const * const * const end_dialognames =
2138 dialognames + (sizeof(dialognames) / sizeof(char *));
2142 cmpCStr(char const * name) : name_(name) {}
2143 bool operator()(char const * other) {
2144 return strcmp(other, name_) == 0;
2151 bool isValidName(string const & name)
2153 return find_if(dialognames, end_dialognames,
2154 cmpCStr(name.c_str())) != end_dialognames;
2160 void GuiView::resetDialogs()
2162 // Make sure that no LFUN uses any LyXView.
2163 theLyXFunc().setLyXView(0);
2166 constructToolbars();
2167 guiApp->menus().fillMenuBar(menuBar(), this, true);
2169 d.layout_->updateContents(true);
2170 // Now update controls with current buffer.
2171 theLyXFunc().setLyXView(this);
2177 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2179 if (!isValidName(name))
2182 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2184 if (it != d.dialogs_.end())
2185 return it->second.get();
2187 Dialog * dialog = build(name);
2188 d.dialogs_[name].reset(dialog);
2189 if (lyxrc.allow_geometry_session)
2190 dialog->restoreSession();
2197 void GuiView::showDialog(string const & name, string const & data,
2204 Dialog * dialog = findOrBuild(name, false);
2206 dialog->showData(data);
2208 d.open_insets_[name] = inset;
2214 bool GuiView::isDialogVisible(string const & name) const
2216 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2217 if (it == d.dialogs_.end())
2219 return it->second.get()->isVisibleView();
2223 void GuiView::hideDialog(string const & name, Inset * inset)
2225 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2226 if (it == d.dialogs_.end())
2229 if (inset && inset != getOpenInset(name))
2232 Dialog * const dialog = it->second.get();
2233 if (dialog->isVisibleView())
2235 d.open_insets_[name] = 0;
2239 void GuiView::disconnectDialog(string const & name)
2241 if (!isValidName(name))
2244 if (d.open_insets_.find(name) != d.open_insets_.end())
2245 d.open_insets_[name] = 0;
2249 Inset * GuiView::getOpenInset(string const & name) const
2251 if (!isValidName(name))
2254 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2255 return it == d.open_insets_.end() ? 0 : it->second;
2259 void GuiView::hideAll() const
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 it->second->hideView();
2269 void GuiView::updateDialogs()
2271 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2272 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2274 for(; it != end; ++it) {
2275 Dialog * dialog = it->second.get();
2276 if (dialog && dialog->isVisibleView())
2277 dialog->checkStatus();
2284 // will be replaced by a proper factory...
2285 Dialog * createGuiAbout(GuiView & lv);
2286 Dialog * createGuiBibitem(GuiView & lv);
2287 Dialog * createGuiBibtex(GuiView & lv);
2288 Dialog * createGuiBox(GuiView & lv);
2289 Dialog * createGuiBranch(GuiView & lv);
2290 Dialog * createGuiChanges(GuiView & lv);
2291 Dialog * createGuiCharacter(GuiView & lv);
2292 Dialog * createGuiCitation(GuiView & lv);
2293 Dialog * createGuiDelimiter(GuiView & lv);
2294 Dialog * createGuiDocument(GuiView & lv);
2295 Dialog * createGuiErrorList(GuiView & lv);
2296 Dialog * createGuiERT(GuiView & lv);
2297 Dialog * createGuiExternal(GuiView & lv);
2298 Dialog * createGuiFloat(GuiView & lv);
2299 Dialog * createGuiGraphics(GuiView & lv);
2300 Dialog * createGuiHSpace(GuiView & lv);
2301 Dialog * createGuiInclude(GuiView & lv);
2302 Dialog * createGuiInfo(GuiView & lv);
2303 Dialog * createGuiLabel(GuiView & lv);
2304 Dialog * createGuiListings(GuiView & lv);
2305 Dialog * createGuiLog(GuiView & lv);
2306 Dialog * createGuiMathMatrix(GuiView & lv);
2307 Dialog * createGuiNomenclature(GuiView & lv);
2308 Dialog * createGuiNote(GuiView & lv);
2309 Dialog * createGuiParagraph(GuiView & lv);
2310 Dialog * createGuiPreferences(GuiView & lv);
2311 Dialog * createGuiPrint(GuiView & lv);
2312 Dialog * createGuiRef(GuiView & lv);
2313 Dialog * createGuiSearch(GuiView & lv);
2314 Dialog * createGuiSendTo(GuiView & lv);
2315 Dialog * createGuiShowFile(GuiView & lv);
2316 Dialog * createGuiSpellchecker(GuiView & lv);
2317 Dialog * createGuiSymbols(GuiView & lv);
2318 Dialog * createGuiTabularCreate(GuiView & lv);
2319 Dialog * createGuiTabular(GuiView & lv);
2320 Dialog * createGuiTexInfo(GuiView & lv);
2321 Dialog * createGuiToc(GuiView & lv);
2322 Dialog * createGuiThesaurus(GuiView & lv);
2323 Dialog * createGuiHyperlink(GuiView & lv);
2324 Dialog * createGuiVSpace(GuiView & lv);
2325 Dialog * createGuiViewSource(GuiView & lv);
2326 Dialog * createGuiWrap(GuiView & lv);
2329 Dialog * GuiView::build(string const & name)
2331 LASSERT(isValidName(name), /**/);
2333 if (name == "aboutlyx")
2334 return createGuiAbout(*this);
2335 if (name == "bibitem")
2336 return createGuiBibitem(*this);
2337 if (name == "bibtex")
2338 return createGuiBibtex(*this);
2340 return createGuiBox(*this);
2341 if (name == "branch")
2342 return createGuiBranch(*this);
2343 if (name == "changes")
2344 return createGuiChanges(*this);
2345 if (name == "character")
2346 return createGuiCharacter(*this);
2347 if (name == "citation")
2348 return createGuiCitation(*this);
2349 if (name == "document")
2350 return createGuiDocument(*this);
2351 if (name == "errorlist")
2352 return createGuiErrorList(*this);
2354 return createGuiERT(*this);
2355 if (name == "external")
2356 return createGuiExternal(*this);
2358 return createGuiShowFile(*this);
2359 if (name == "findreplace")
2360 return createGuiSearch(*this);
2361 if (name == "float")
2362 return createGuiFloat(*this);
2363 if (name == "graphics")
2364 return createGuiGraphics(*this);
2365 if (name == "include")
2366 return createGuiInclude(*this);
2368 return createGuiInfo(*this);
2369 if (name == "nomenclature")
2370 return createGuiNomenclature(*this);
2371 if (name == "label")
2372 return createGuiLabel(*this);
2374 return createGuiLog(*this);
2375 if (name == "view-source")
2376 return createGuiViewSource(*this);
2377 if (name == "mathdelimiter")
2378 return createGuiDelimiter(*this);
2379 if (name == "mathmatrix")
2380 return createGuiMathMatrix(*this);
2382 return createGuiNote(*this);
2383 if (name == "paragraph")
2384 return createGuiParagraph(*this);
2385 if (name == "prefs")
2386 return createGuiPreferences(*this);
2387 if (name == "print")
2388 return createGuiPrint(*this);
2390 return createGuiRef(*this);
2391 if (name == "sendto")
2392 return createGuiSendTo(*this);
2393 if (name == "space")
2394 return createGuiHSpace(*this);
2395 if (name == "spellchecker")
2396 return createGuiSpellchecker(*this);
2397 if (name == "symbols")
2398 return createGuiSymbols(*this);
2399 if (name == "tabular")
2400 return createGuiTabular(*this);
2401 if (name == "tabularcreate")
2402 return createGuiTabularCreate(*this);
2403 if (name == "texinfo")
2404 return createGuiTexInfo(*this);
2405 #ifdef HAVE_LIBAIKSAURUS
2406 if (name == "thesaurus")
2407 return createGuiThesaurus(*this);
2410 return createGuiToc(*this);
2412 return createGuiHyperlink(*this);
2413 if (name == "vspace")
2414 return createGuiVSpace(*this);
2416 return createGuiWrap(*this);
2417 if (name == "listings")
2418 return createGuiListings(*this);
2424 } // namespace frontend
2427 #include "GuiView_moc.cpp"