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);
1328 if (buf->lyxvc().inUse())
1329 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1330 " " + _("Version control detected.");
1332 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1337 // FIXME: clean that
1338 static bool import(GuiView * lv, FileName const & filename,
1339 string const & format, ErrorList & errorList)
1341 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1343 string loader_format;
1344 vector<string> loaders = theConverters().loaders();
1345 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1346 for (vector<string>::const_iterator it = loaders.begin();
1347 it != loaders.end(); ++it) {
1348 if (!theConverters().isReachable(format, *it))
1351 string const tofile =
1352 support::changeExtension(filename.absFilename(),
1353 formats.extension(*it));
1354 if (!theConverters().convert(0, filename, FileName(tofile),
1355 filename, format, *it, errorList))
1357 loader_format = *it;
1360 if (loader_format.empty()) {
1361 frontend::Alert::error(_("Couldn't import file"),
1362 bformat(_("No information for importing the format %1$s."),
1363 formats.prettyName(format)));
1367 loader_format = format;
1369 if (loader_format == "lyx") {
1370 Buffer * buf = lv->loadDocument(lyxfile);
1375 buf->errors("Parse");
1377 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1381 bool as_paragraphs = loader_format == "textparagraph";
1382 string filename2 = (loader_format == format) ? filename.absFilename()
1383 : support::changeExtension(filename.absFilename(),
1384 formats.extension(loader_format));
1385 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1386 theLyXFunc().setLyXView(lv);
1387 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1394 void GuiView::importDocument(string const & argument)
1397 string filename = split(argument, format, ' ');
1399 LYXERR(Debug::INFO, format << " file: " << filename);
1401 // need user interaction
1402 if (filename.empty()) {
1403 string initpath = lyxrc.document_path;
1405 Buffer const * buf = buffer();
1407 string const trypath = buf->filePath();
1408 // If directory is writeable, use this as default.
1409 if (FileName(trypath).isDirWritable())
1413 docstring const text = bformat(_("Select %1$s file to import"),
1414 formats.prettyName(format));
1416 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1417 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1418 dlg.setButton2(qt_("Examples|#E#e"),
1419 toqstr(addPath(package().system_support().absFilename(), "examples")));
1421 docstring filter = formats.prettyName(format);
1424 filter += from_utf8(formats.extension(format));
1427 FileDialog::Result result =
1428 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1430 if (result.first == FileDialog::Later)
1433 filename = fromqstr(result.second);
1435 // check selected filename
1436 if (filename.empty())
1437 message(_("Canceled."));
1440 if (filename.empty())
1443 // get absolute path of file
1444 FileName const fullname(support::makeAbsPath(filename));
1446 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1448 // Check if the document already is open
1449 Buffer * buf = theBufferList().getBuffer(lyxfile);
1452 if (!closeBuffer()) {
1453 message(_("Canceled."));
1458 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1460 // if the file exists already, and we didn't do
1461 // -i lyx thefile.lyx, warn
1462 if (lyxfile.exists() && fullname != lyxfile) {
1464 docstring text = bformat(_("The document %1$s already exists.\n\n"
1465 "Do you want to overwrite that document?"), displaypath);
1466 int const ret = Alert::prompt(_("Overwrite document?"),
1467 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1470 message(_("Canceled."));
1475 message(bformat(_("Importing %1$s..."), displaypath));
1476 ErrorList errorList;
1477 if (import(this, fullname, format, errorList))
1478 message(_("imported."));
1480 message(_("file not imported!"));
1482 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1486 void GuiView::newDocument(string const & filename, bool from_template)
1488 FileName initpath(lyxrc.document_path);
1489 Buffer * buf = buffer();
1491 FileName const trypath(buf->filePath());
1492 // If directory is writeable, use this as default.
1493 if (trypath.isDirWritable())
1497 string templatefile = from_template ?
1498 selectTemplateFile().absFilename() : string();
1500 if (filename.empty())
1501 b = newUnnamedFile(templatefile, initpath);
1503 b = newFile(filename, templatefile, true);
1507 // Ensure the cursor is correctly positionned on screen.
1508 view()->showCursor();
1512 void GuiView::insertLyXFile(docstring const & fname)
1514 BufferView * bv = view();
1519 FileName filename(to_utf8(fname));
1521 if (!filename.empty()) {
1522 bv->insertLyXFile(filename);
1526 // Launch a file browser
1528 string initpath = lyxrc.document_path;
1529 string const trypath = bv->buffer().filePath();
1530 // If directory is writeable, use this as default.
1531 if (FileName(trypath).isDirWritable())
1535 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1536 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1537 dlg.setButton2(qt_("Examples|#E#e"),
1538 toqstr(addPath(package().system_support().absFilename(),
1541 FileDialog::Result result = dlg.open(toqstr(initpath),
1542 QStringList(qt_("LyX Documents (*.lyx)")));
1544 if (result.first == FileDialog::Later)
1548 filename.set(fromqstr(result.second));
1550 // check selected filename
1551 if (filename.empty()) {
1552 // emit message signal.
1553 message(_("Canceled."));
1557 bv->insertLyXFile(filename);
1561 void GuiView::insertPlaintextFile(docstring const & fname,
1564 BufferView * bv = view();
1569 FileName filename(to_utf8(fname));
1571 if (!filename.empty()) {
1572 bv->insertPlaintextFile(filename, asParagraph);
1576 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1577 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1579 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1582 if (result.first == FileDialog::Later)
1586 filename.set(fromqstr(result.second));
1588 // check selected filename
1589 if (filename.empty()) {
1590 // emit message signal.
1591 message(_("Canceled."));
1595 bv->insertPlaintextFile(filename, asParagraph);
1599 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1601 FileName fname = b.fileName();
1602 FileName const oldname = fname;
1604 if (!newname.empty()) {
1606 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1608 // Switch to this Buffer.
1611 /// No argument? Ask user through dialog.
1613 FileDialog dlg(qt_("Choose a filename to save document as"),
1614 LFUN_BUFFER_WRITE_AS);
1615 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1616 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1618 if (!isLyXFilename(fname.absFilename()))
1619 fname.changeExtension(".lyx");
1621 FileDialog::Result result =
1622 dlg.save(toqstr(fname.onlyPath().absFilename()),
1623 QStringList(qt_("LyX Documents (*.lyx)")),
1624 toqstr(fname.onlyFileName()));
1626 if (result.first == FileDialog::Later)
1629 fname.set(fromqstr(result.second));
1634 if (!isLyXFilename(fname.absFilename()))
1635 fname.changeExtension(".lyx");
1638 if (FileName(fname).exists()) {
1639 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1640 docstring text = bformat(_("The document %1$s already "
1641 "exists.\n\nDo you want to "
1642 "overwrite that document?"),
1644 int const ret = Alert::prompt(_("Overwrite document?"),
1645 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1648 case 1: return renameBuffer(b, docstring());
1649 case 2: return false;
1653 // Ok, change the name of the buffer
1654 b.setFileName(fname.absFilename());
1656 bool unnamed = b.isUnnamed();
1657 b.setUnnamed(false);
1658 b.saveCheckSum(fname);
1660 if (!saveBuffer(b)) {
1661 b.setFileName(oldname.absFilename());
1662 b.setUnnamed(unnamed);
1663 b.saveCheckSum(oldname);
1671 bool GuiView::saveBuffer(Buffer & b)
1674 return renameBuffer(b, docstring());
1677 theSession().lastFiles().add(b.fileName());
1681 // Switch to this Buffer.
1684 // FIXME: we don't tell the user *WHY* the save failed !!
1685 docstring const file = makeDisplayPath(b.absFileName(), 30);
1686 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1687 "Do you want to rename the document and "
1688 "try again?"), file);
1689 int const ret = Alert::prompt(_("Rename and save?"),
1690 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1693 if (!renameBuffer(b, docstring()))
1702 return saveBuffer(b);
1706 bool GuiView::closeBuffer()
1708 Buffer * buf = buffer();
1709 return buf && closeBuffer(*buf);
1713 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1715 // goto bookmark to update bookmark pit.
1716 //FIXME: we should update only the bookmarks related to this buffer!
1717 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1718 theLyXFunc().gotoBookmark(i+1, false, false);
1720 if (buf.isClean() || buf.paragraphs().empty()) {
1721 if (buf.masterBuffer() == &buf && tolastopened)
1722 theSession().lastOpened().add(buf.fileName());
1723 theBufferList().release(&buf);
1726 // Switch to this Buffer.
1731 if (buf.isUnnamed())
1732 file = from_utf8(buf.fileName().onlyFileName());
1734 file = buf.fileName().displayName(30);
1736 // Bring this window to top before asking questions.
1740 docstring const text = bformat(_("The document %1$s has unsaved changes."
1741 "\n\nDo you want to save the document or discard the changes?"), file);
1742 int const ret = Alert::prompt(_("Save changed document?"),
1743 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1747 if (!saveBuffer(buf))
1751 // if we crash after this we could
1752 // have no autosave file but I guess
1753 // this is really improbable (Jug)
1754 removeAutosaveFile(buf.absFileName());
1760 // save file names to .lyx/session
1761 // if master/slave are both open, do not save slave since it
1762 // will be automatically loaded when the master is loaded
1763 if (buf.masterBuffer() == &buf && tolastopened)
1764 theSession().lastOpened().add(buf.fileName());
1767 // Don't close child documents.
1768 removeWorkArea(d.current_work_area_);
1770 theBufferList().release(&buf);
1776 bool GuiView::dispatch(FuncRequest const & cmd)
1778 BufferView * bv = view();
1779 // By default we won't need any update.
1781 bv->cursor().updateFlags(Update::None);
1782 bool dispatched = true;
1784 switch(cmd.action) {
1785 case LFUN_BUFFER_IMPORT:
1786 importDocument(to_utf8(cmd.argument()));
1789 case LFUN_BUFFER_SWITCH:
1790 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1793 case LFUN_BUFFER_NEXT:
1794 setBuffer(theBufferList().next(buffer()));
1797 case LFUN_BUFFER_PREVIOUS:
1798 setBuffer(theBufferList().previous(buffer()));
1801 case LFUN_COMMAND_EXECUTE: {
1802 bool const show_it = cmd.argument() != "off";
1803 // FIXME: this is a hack, "minibuffer" should not be
1805 if (GuiToolbar * t = toolbar("minibuffer")) {
1806 t->setVisible(show_it);
1807 if (show_it && t->commandBuffer())
1808 t->commandBuffer()->setFocus();
1812 case LFUN_DROP_LAYOUTS_CHOICE:
1814 d.layout_->showPopup();
1817 case LFUN_MENU_OPEN:
1818 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1819 menu->exec(QCursor::pos());
1822 case LFUN_FILE_INSERT:
1823 insertLyXFile(cmd.argument());
1825 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1826 insertPlaintextFile(cmd.argument(), true);
1829 case LFUN_FILE_INSERT_PLAINTEXT:
1830 insertPlaintextFile(cmd.argument(), false);
1833 case LFUN_BUFFER_WRITE:
1835 saveBuffer(bv->buffer());
1838 case LFUN_BUFFER_WRITE_AS:
1840 renameBuffer(bv->buffer(), cmd.argument());
1843 case LFUN_BUFFER_WRITE_ALL: {
1844 Buffer * first = theBufferList().first();
1847 message(_("Saving all documents..."));
1848 // We cannot use a for loop as the buffer list cycles.
1851 if (!b->isClean()) {
1853 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1855 b = theBufferList().next(b);
1856 } while (b != first);
1857 message(_("All documents saved."));
1861 case LFUN_TOOLBAR_TOGGLE: {
1862 string const name = cmd.getArg(0);
1863 if (GuiToolbar * t = toolbar(name))
1868 case LFUN_DIALOG_UPDATE: {
1869 string const name = to_utf8(cmd.argument());
1870 // Can only update a dialog connected to an existing inset
1871 Inset * inset = getOpenInset(name);
1873 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1874 inset->dispatch(view()->cursor(), fr);
1875 } else if (name == "paragraph") {
1876 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1877 } else if (name == "prefs") {
1878 updateDialog(name, string());
1883 case LFUN_DIALOG_TOGGLE: {
1884 if (isDialogVisible(cmd.getArg(0)))
1885 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1887 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1891 case LFUN_DIALOG_DISCONNECT_INSET:
1892 disconnectDialog(to_utf8(cmd.argument()));
1895 case LFUN_DIALOG_HIDE: {
1896 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1900 case LFUN_DIALOG_SHOW: {
1901 string const name = cmd.getArg(0);
1902 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1904 if (name == "character") {
1905 data = freefont2string();
1907 showDialog("character", data);
1908 } else if (name == "latexlog") {
1909 Buffer::LogType type;
1910 string const logfile = buffer()->logName(&type);
1912 case Buffer::latexlog:
1915 case Buffer::buildlog:
1919 data += Lexer::quoteString(logfile);
1920 showDialog("log", data);
1921 } else if (name == "vclog") {
1922 string const data = "vc " +
1923 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1924 showDialog("log", data);
1925 } else if (name == "symbols") {
1926 data = bv->cursor().getEncoding()->name();
1928 showDialog("symbols", data);
1930 showDialog(name, data);
1934 case LFUN_INSET_APPLY: {
1935 view()->cursor().recordUndoFullDocument();
1936 string const name = cmd.getArg(0);
1937 Inset * inset = getOpenInset(name);
1939 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1940 inset->dispatch(view()->cursor(), fr);
1942 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1948 case LFUN_UI_TOGGLE:
1950 // Make sure the keyboard focus stays in the work area.
1954 case LFUN_COMPLETION_INLINE:
1955 if (d.current_work_area_)
1956 d.current_work_area_->completer().showInline();
1959 case LFUN_SPLIT_VIEW:
1960 if (Buffer * buf = buffer()) {
1961 string const orientation = cmd.getArg(0);
1962 d.splitter_->setOrientation(orientation == "vertical"
1963 ? Qt::Vertical : Qt::Horizontal);
1964 TabWorkArea * twa = addTabWorkArea();
1965 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1966 setCurrentWorkArea(wa);
1970 case LFUN_CLOSE_TAB_GROUP:
1971 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1973 twa = d.currentTabWorkArea();
1974 // Switch to the next GuiWorkArea in the found TabWorkArea.
1976 d.current_work_area_ = twa->currentWorkArea();
1977 // Make sure the work area is up to date.
1978 twa->setCurrentWorkArea(d.current_work_area_);
1980 d.current_work_area_ = 0;
1982 if (d.splitter_->count() == 0)
1983 // No more work area, switch to the background widget.
1988 case LFUN_COMPLETION_POPUP:
1989 if (d.current_work_area_)
1990 d.current_work_area_->completer().showPopup();
1994 case LFUN_COMPLETION_COMPLETE:
1995 if (d.current_work_area_)
1996 d.current_work_area_->completer().tab();
2004 if (isFullScreen()) {
2005 if (menuBar()->isVisible())
2007 if (statusBar()->isVisible())
2008 statusBar()->hide();
2015 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2017 string const arg = cmd.getArg(0);
2018 if (arg == "scrollbar") {
2019 // hide() is of no help
2020 if (d.current_work_area_->verticalScrollBarPolicy() ==
2021 Qt::ScrollBarAlwaysOff)
2023 d.current_work_area_->setVerticalScrollBarPolicy(
2024 Qt::ScrollBarAsNeeded);
2026 d.current_work_area_->setVerticalScrollBarPolicy(
2027 Qt::ScrollBarAlwaysOff);
2030 if (arg == "statusbar") {
2031 statusBar()->setVisible(!statusBar()->isVisible());
2034 if (arg == "menubar") {
2035 menuBar()->setVisible(!menuBar()->isVisible());
2038 #if QT_VERSION >= 0x040300
2039 if (arg == "frame") {
2041 getContentsMargins(&l, &t, &r, &b);
2042 //are the frames in default state?
2043 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2045 setContentsMargins(-2, -2, -2, -2);
2047 setContentsMargins(0, 0, 0, 0);
2052 if (arg == "fullscreen") {
2057 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2061 void GuiView::toggleFullScreen()
2063 if (isFullScreen()) {
2064 for (int i = 0; i != d.splitter_->count(); ++i)
2065 d.tabWorkArea(i)->setFullScreen(false);
2066 #if QT_VERSION >= 0x040300
2067 setContentsMargins(0, 0, 0, 0);
2069 setWindowState(windowState() ^ Qt::WindowFullScreen);
2072 statusBar()->show();
2074 for (int i = 0; i != d.splitter_->count(); ++i)
2075 d.tabWorkArea(i)->setFullScreen(true);
2076 #if QT_VERSION >= 0x040300
2077 setContentsMargins(-2, -2, -2, -2);
2080 setWindowState(windowState() ^ Qt::WindowFullScreen);
2081 statusBar()->hide();
2083 if (lyxrc.full_screen_toolbars) {
2084 ToolbarMap::iterator end = d.toolbars_.end();
2085 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2092 Buffer const * GuiView::updateInset(Inset const * inset)
2094 if (!d.current_work_area_)
2098 d.current_work_area_->scheduleRedraw();
2100 return &d.current_work_area_->bufferView().buffer();
2104 void GuiView::restartCursor()
2106 /* When we move around, or type, it's nice to be able to see
2107 * the cursor immediately after the keypress.
2109 if (d.current_work_area_)
2110 d.current_work_area_->startBlinkingCursor();
2112 // Take this occasion to update the other GUI elements.
2118 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2120 if (d.current_work_area_)
2121 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2126 // This list should be kept in sync with the list of insets in
2127 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2128 // dialog should have the same name as the inset.
2129 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2130 // docs in LyXAction.cpp.
2132 char const * const dialognames[] = {
2133 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2134 "citation", "document", "errorlist", "ert", "external", "file",
2135 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2136 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2137 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2139 #ifdef HAVE_LIBAIKSAURUS
2143 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2145 char const * const * const end_dialognames =
2146 dialognames + (sizeof(dialognames) / sizeof(char *));
2150 cmpCStr(char const * name) : name_(name) {}
2151 bool operator()(char const * other) {
2152 return strcmp(other, name_) == 0;
2159 bool isValidName(string const & name)
2161 return find_if(dialognames, end_dialognames,
2162 cmpCStr(name.c_str())) != end_dialognames;
2168 void GuiView::resetDialogs()
2170 // Make sure that no LFUN uses any LyXView.
2171 theLyXFunc().setLyXView(0);
2174 constructToolbars();
2175 guiApp->menus().fillMenuBar(menuBar(), this, true);
2177 d.layout_->updateContents(true);
2178 // Now update controls with current buffer.
2179 theLyXFunc().setLyXView(this);
2185 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2187 if (!isValidName(name))
2190 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2192 if (it != d.dialogs_.end())
2193 return it->second.get();
2195 Dialog * dialog = build(name);
2196 d.dialogs_[name].reset(dialog);
2197 if (lyxrc.allow_geometry_session)
2198 dialog->restoreSession();
2205 void GuiView::showDialog(string const & name, string const & data,
2212 Dialog * dialog = findOrBuild(name, false);
2214 dialog->showData(data);
2216 d.open_insets_[name] = inset;
2222 bool GuiView::isDialogVisible(string const & name) const
2224 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2225 if (it == d.dialogs_.end())
2227 return it->second.get()->isVisibleView();
2231 void GuiView::hideDialog(string const & name, Inset * inset)
2233 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2234 if (it == d.dialogs_.end())
2237 if (inset && inset != getOpenInset(name))
2240 Dialog * const dialog = it->second.get();
2241 if (dialog->isVisibleView())
2243 d.open_insets_[name] = 0;
2247 void GuiView::disconnectDialog(string const & name)
2249 if (!isValidName(name))
2252 if (d.open_insets_.find(name) != d.open_insets_.end())
2253 d.open_insets_[name] = 0;
2257 Inset * GuiView::getOpenInset(string const & name) const
2259 if (!isValidName(name))
2262 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2263 return it == d.open_insets_.end() ? 0 : it->second;
2267 void GuiView::hideAll() const
2269 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2270 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2272 for(; it != end; ++it)
2273 it->second->hideView();
2277 void GuiView::updateDialogs()
2279 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2280 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2282 for(; it != end; ++it) {
2283 Dialog * dialog = it->second.get();
2284 if (dialog && dialog->isVisibleView())
2285 dialog->checkStatus();
2292 // will be replaced by a proper factory...
2293 Dialog * createGuiAbout(GuiView & lv);
2294 Dialog * createGuiBibitem(GuiView & lv);
2295 Dialog * createGuiBibtex(GuiView & lv);
2296 Dialog * createGuiBox(GuiView & lv);
2297 Dialog * createGuiBranch(GuiView & lv);
2298 Dialog * createGuiChanges(GuiView & lv);
2299 Dialog * createGuiCharacter(GuiView & lv);
2300 Dialog * createGuiCitation(GuiView & lv);
2301 Dialog * createGuiDelimiter(GuiView & lv);
2302 Dialog * createGuiDocument(GuiView & lv);
2303 Dialog * createGuiErrorList(GuiView & lv);
2304 Dialog * createGuiERT(GuiView & lv);
2305 Dialog * createGuiExternal(GuiView & lv);
2306 Dialog * createGuiFloat(GuiView & lv);
2307 Dialog * createGuiGraphics(GuiView & lv);
2308 Dialog * createGuiHSpace(GuiView & lv);
2309 Dialog * createGuiInclude(GuiView & lv);
2310 Dialog * createGuiInfo(GuiView & lv);
2311 Dialog * createGuiLabel(GuiView & lv);
2312 Dialog * createGuiListings(GuiView & lv);
2313 Dialog * createGuiLog(GuiView & lv);
2314 Dialog * createGuiMathMatrix(GuiView & lv);
2315 Dialog * createGuiNomenclature(GuiView & lv);
2316 Dialog * createGuiNote(GuiView & lv);
2317 Dialog * createGuiParagraph(GuiView & lv);
2318 Dialog * createGuiPreferences(GuiView & lv);
2319 Dialog * createGuiPrint(GuiView & lv);
2320 Dialog * createGuiRef(GuiView & lv);
2321 Dialog * createGuiSearch(GuiView & lv);
2322 Dialog * createGuiSendTo(GuiView & lv);
2323 Dialog * createGuiShowFile(GuiView & lv);
2324 Dialog * createGuiSpellchecker(GuiView & lv);
2325 Dialog * createGuiSymbols(GuiView & lv);
2326 Dialog * createGuiTabularCreate(GuiView & lv);
2327 Dialog * createGuiTabular(GuiView & lv);
2328 Dialog * createGuiTexInfo(GuiView & lv);
2329 Dialog * createGuiToc(GuiView & lv);
2330 Dialog * createGuiThesaurus(GuiView & lv);
2331 Dialog * createGuiHyperlink(GuiView & lv);
2332 Dialog * createGuiVSpace(GuiView & lv);
2333 Dialog * createGuiViewSource(GuiView & lv);
2334 Dialog * createGuiWrap(GuiView & lv);
2337 Dialog * GuiView::build(string const & name)
2339 LASSERT(isValidName(name), /**/);
2341 if (name == "aboutlyx")
2342 return createGuiAbout(*this);
2343 if (name == "bibitem")
2344 return createGuiBibitem(*this);
2345 if (name == "bibtex")
2346 return createGuiBibtex(*this);
2348 return createGuiBox(*this);
2349 if (name == "branch")
2350 return createGuiBranch(*this);
2351 if (name == "changes")
2352 return createGuiChanges(*this);
2353 if (name == "character")
2354 return createGuiCharacter(*this);
2355 if (name == "citation")
2356 return createGuiCitation(*this);
2357 if (name == "document")
2358 return createGuiDocument(*this);
2359 if (name == "errorlist")
2360 return createGuiErrorList(*this);
2362 return createGuiERT(*this);
2363 if (name == "external")
2364 return createGuiExternal(*this);
2366 return createGuiShowFile(*this);
2367 if (name == "findreplace")
2368 return createGuiSearch(*this);
2369 if (name == "float")
2370 return createGuiFloat(*this);
2371 if (name == "graphics")
2372 return createGuiGraphics(*this);
2373 if (name == "include")
2374 return createGuiInclude(*this);
2376 return createGuiInfo(*this);
2377 if (name == "nomenclature")
2378 return createGuiNomenclature(*this);
2379 if (name == "label")
2380 return createGuiLabel(*this);
2382 return createGuiLog(*this);
2383 if (name == "view-source")
2384 return createGuiViewSource(*this);
2385 if (name == "mathdelimiter")
2386 return createGuiDelimiter(*this);
2387 if (name == "mathmatrix")
2388 return createGuiMathMatrix(*this);
2390 return createGuiNote(*this);
2391 if (name == "paragraph")
2392 return createGuiParagraph(*this);
2393 if (name == "prefs")
2394 return createGuiPreferences(*this);
2395 if (name == "print")
2396 return createGuiPrint(*this);
2398 return createGuiRef(*this);
2399 if (name == "sendto")
2400 return createGuiSendTo(*this);
2401 if (name == "space")
2402 return createGuiHSpace(*this);
2403 if (name == "spellchecker")
2404 return createGuiSpellchecker(*this);
2405 if (name == "symbols")
2406 return createGuiSymbols(*this);
2407 if (name == "tabular")
2408 return createGuiTabular(*this);
2409 if (name == "tabularcreate")
2410 return createGuiTabularCreate(*this);
2411 if (name == "texinfo")
2412 return createGuiTexInfo(*this);
2413 #ifdef HAVE_LIBAIKSAURUS
2414 if (name == "thesaurus")
2415 return createGuiThesaurus(*this);
2418 return createGuiToc(*this);
2420 return createGuiHyperlink(*this);
2421 if (name == "vspace")
2422 return createGuiVSpace(*this);
2424 return createGuiWrap(*this);
2425 if (name == "listings")
2426 return createGuiListings(*this);
2432 } // namespace frontend
2435 #include "GuiView_moc.cpp"