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: {
775 if (isFullScreen() && menuBar()->isHidden()) {
776 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
777 // FIXME: we should also try to detect special LyX shortcut such as
778 // Alt-P and Alt-M. Right now there is a hack in
779 // GuiWorkArea::processKeySym() that hides again the menubar for
781 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
783 return QMainWindow::event(e);
787 if (d.current_work_area_)
788 // Nothing special to do.
789 return QMainWindow::event(e);
791 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
792 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
794 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
795 || ke->key() == Qt::Key_Backtab)
796 return QMainWindow::event(e);
798 // Allow processing of shortcuts that are allowed even when no Buffer
800 theLyXFunc().setLyXView(this);
802 setKeySymbol(&sym, ke);
803 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
809 return QMainWindow::event(e);
814 bool GuiView::focusNextPrevChild(bool /*next*/)
821 void GuiView::setBusy(bool busy)
823 if (d.current_work_area_) {
824 d.current_work_area_->setUpdatesEnabled(!busy);
826 d.current_work_area_->stopBlinkingCursor();
828 d.current_work_area_->startBlinkingCursor();
832 QApplication::setOverrideCursor(Qt::WaitCursor);
834 QApplication::restoreOverrideCursor();
838 GuiWorkArea * GuiView::workArea(Buffer & buffer)
840 if (TabWorkArea * twa = d.currentTabWorkArea())
841 return twa->workArea(buffer);
846 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
848 // Automatically create a TabWorkArea if there are none yet.
849 TabWorkArea * tab_widget = d.splitter_->count()
850 ? d.currentTabWorkArea() : addTabWorkArea();
851 return tab_widget->addWorkArea(buffer, *this);
855 TabWorkArea * GuiView::addTabWorkArea()
857 TabWorkArea * twa = new TabWorkArea;
858 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
859 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
860 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
861 this, SLOT(on_lastWorkAreaRemoved()));
863 d.splitter_->addWidget(twa);
864 d.stack_widget_->setCurrentWidget(d.splitter_);
869 GuiWorkArea const * GuiView::currentWorkArea() const
871 return d.current_work_area_;
875 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
878 d.current_work_area_ = wa;
879 for (int i = 0; i != d.splitter_->count(); ++i) {
880 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
886 void GuiView::removeWorkArea(GuiWorkArea * wa)
889 if (wa == d.current_work_area_) {
891 disconnectBufferView();
892 d.current_work_area_ = 0;
895 for (int i = 0; i != d.splitter_->count(); ++i) {
896 TabWorkArea * twa = d.tabWorkArea(i);
897 if (!twa->removeWorkArea(wa))
898 // Not found in this tab group.
901 // We found and removed the GuiWorkArea.
903 // No more WorkAreas in this tab group, so delete it.
908 if (d.current_work_area_)
909 // This means that we are not closing the current GuiWorkArea;
912 // Switch to the next GuiWorkArea in the found TabWorkArea.
913 d.current_work_area_ = twa->currentWorkArea();
917 if (d.splitter_->count() == 0)
918 // No more work area, switch to the background widget.
923 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
929 void GuiView::updateLayoutList()
932 d.layout_->updateContents(false);
936 void GuiView::updateToolbars()
938 ToolbarMap::iterator end = d.toolbars_.end();
939 if (d.current_work_area_) {
941 d.current_work_area_->bufferView().cursor().inMathed();
943 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
945 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
946 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
947 bool const mathmacrotemplate =
948 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
950 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
951 it->second->update(math, table, review, mathmacrotemplate);
953 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
954 it->second->update(false, false, false, false);
958 Buffer * GuiView::buffer()
960 if (d.current_work_area_)
961 return &d.current_work_area_->bufferView().buffer();
966 Buffer const * GuiView::buffer() const
968 if (d.current_work_area_)
969 return &d.current_work_area_->bufferView().buffer();
974 void GuiView::setBuffer(Buffer * newBuffer)
976 LASSERT(newBuffer, /**/);
979 GuiWorkArea * wa = workArea(*newBuffer);
981 updateLabels(*newBuffer->masterBuffer());
982 wa = addWorkArea(*newBuffer);
984 //Disconnect the old buffer...there's no new one.
987 connectBuffer(*newBuffer);
988 connectBufferView(wa->bufferView());
989 setCurrentWorkArea(wa);
995 void GuiView::connectBuffer(Buffer & buf)
997 buf.setGuiDelegate(this);
1001 void GuiView::disconnectBuffer()
1003 if (d.current_work_area_)
1004 d.current_work_area_->bufferView().setGuiDelegate(0);
1008 void GuiView::connectBufferView(BufferView & bv)
1010 bv.setGuiDelegate(this);
1014 void GuiView::disconnectBufferView()
1016 if (d.current_work_area_)
1017 d.current_work_area_->bufferView().setGuiDelegate(0);
1021 void GuiView::errors(string const & error_type)
1023 ErrorList & el = buffer()->errorList(error_type);
1025 showDialog("errorlist", error_type);
1029 void GuiView::structureChanged()
1031 d.toc_models_.reset(view());
1032 // Navigator needs more than a simple update in this case. It needs to be
1034 updateDialog("toc", "");
1038 void GuiView::updateDialog(string const & name, string const & data)
1040 if (!isDialogVisible(name))
1043 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1044 if (it == d.dialogs_.end())
1047 Dialog * const dialog = it->second.get();
1048 if (dialog->isVisibleView())
1049 dialog->initialiseParams(data);
1053 BufferView * GuiView::view()
1055 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1059 void GuiView::autoSave()
1061 LYXERR(Debug::INFO, "Running autoSave()");
1064 view()->buffer().autoSave();
1068 void GuiView::resetAutosaveTimers()
1071 d.autosave_timeout_.restart();
1075 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1078 Buffer * buf = buffer();
1080 /* In LyX/Mac, when a dialog is open, the menus of the
1081 application can still be accessed without giving focus to
1082 the main window. In this case, we want to disable the menu
1083 entries that are buffer-related.
1085 Note that this code is not perfect, as bug 1941 attests:
1086 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1088 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1091 switch(cmd.action) {
1092 case LFUN_BUFFER_WRITE:
1093 enable = buf && (buf->isUnnamed() || !buf->isClean());
1096 case LFUN_BUFFER_WRITE_AS:
1100 case LFUN_SPLIT_VIEW:
1101 if (cmd.getArg(0) == "vertical")
1102 enable = buf && (d.splitter_->count() == 1 ||
1103 d.splitter_->orientation() == Qt::Vertical);
1105 enable = buf && (d.splitter_->count() == 1 ||
1106 d.splitter_->orientation() == Qt::Horizontal);
1109 case LFUN_CLOSE_TAB_GROUP:
1110 enable = d.currentTabWorkArea();
1113 case LFUN_TOOLBAR_TOGGLE:
1114 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1115 flag.setOnOff(t->isVisible());
1118 case LFUN_UI_TOGGLE:
1119 flag.setOnOff(isFullScreen());
1122 case LFUN_DIALOG_TOGGLE:
1123 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1124 // fall through to set "enable"
1125 case LFUN_DIALOG_SHOW: {
1126 string const name = cmd.getArg(0);
1128 enable = name == "aboutlyx"
1129 || name == "file" //FIXME: should be removed.
1131 || name == "texinfo";
1132 else if (name == "print")
1133 enable = buf->isExportable("dvi")
1134 && lyxrc.print_command != "none";
1135 else if (name == "character") {
1139 InsetCode ic = view()->cursor().inset().lyxCode();
1140 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1143 else if (name == "symbols") {
1144 if (!view() || view()->cursor().inMathed())
1147 InsetCode ic = view()->cursor().inset().lyxCode();
1148 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1151 else if (name == "latexlog")
1152 enable = FileName(buf->logName()).isReadableFile();
1153 else if (name == "spellchecker")
1154 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1155 enable = !buf->isReadonly();
1159 else if (name == "vclog")
1160 enable = buf->lyxvc().inUse();
1164 case LFUN_DIALOG_UPDATE: {
1165 string const name = cmd.getArg(0);
1167 enable = name == "prefs";
1171 case LFUN_INSET_APPLY: {
1172 string const name = cmd.getArg(0);
1173 Inset * inset = getOpenInset(name);
1175 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1177 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1178 // Every inset is supposed to handle this
1179 LASSERT(false, /**/);
1183 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1184 flag |= lyx::getStatus(fr);
1186 enable = flag.enabled();
1190 case LFUN_COMPLETION_INLINE:
1191 if (!d.current_work_area_
1192 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1196 case LFUN_COMPLETION_POPUP:
1197 if (!d.current_work_area_
1198 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1202 case LFUN_COMPLETION_COMPLETE:
1203 if (!d.current_work_area_
1204 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1213 flag.setEnabled(false);
1219 static FileName selectTemplateFile()
1221 FileDialog dlg(qt_("Select template file"));
1222 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1223 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1225 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1226 QStringList(qt_("LyX Documents (*.lyx)")));
1228 if (result.first == FileDialog::Later)
1230 if (result.second.isEmpty())
1232 return FileName(fromqstr(result.second));
1236 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1240 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1243 message(_("Document not loaded."));
1248 setBuffer(newBuffer);
1250 // scroll to the position when the file was last closed
1251 if (lyxrc.use_lastfilepos) {
1252 LastFilePosSection::FilePos filepos =
1253 theSession().lastFilePos().load(filename);
1254 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1258 theSession().lastFiles().add(filename);
1265 void GuiView::openDocument(string const & fname)
1267 string initpath = lyxrc.document_path;
1270 string const trypath = buffer()->filePath();
1271 // If directory is writeable, use this as default.
1272 if (FileName(trypath).isDirWritable())
1278 if (fname.empty()) {
1279 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1280 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1281 dlg.setButton2(qt_("Examples|#E#e"),
1282 toqstr(addPath(package().system_support().absFilename(), "examples")));
1284 QStringList filter(qt_("LyX Documents (*.lyx)"));
1285 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1286 << qt_("LyX-1.4.x Documents (*.lyx14)")
1287 << qt_("LyX-1.5.x Documents (*.lyx15)");
1288 FileDialog::Result result =
1289 dlg.open(toqstr(initpath), filter);
1291 if (result.first == FileDialog::Later)
1294 filename = fromqstr(result.second);
1296 // check selected filename
1297 if (filename.empty()) {
1298 message(_("Canceled."));
1304 // get absolute path of file and add ".lyx" to the filename if
1306 FileName const fullname =
1307 fileSearch(string(), filename, "lyx", support::may_not_exist);
1308 if (!fullname.empty())
1309 filename = fullname.absFilename();
1311 // if the file doesn't exist, let the user create one
1312 if (!fullname.exists()) {
1313 // the user specifically chose this name. Believe him.
1314 Buffer * const b = newFile(filename, string(), true);
1320 docstring const disp_fn = makeDisplayPath(filename);
1321 message(bformat(_("Opening document %1$s..."), disp_fn));
1324 Buffer * buf = loadDocument(fullname);
1329 buf->errors("Parse");
1330 str2 = bformat(_("Document %1$s opened."), disp_fn);
1331 if (buf->lyxvc().inUse())
1332 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1333 " " + _("Version control detected.");
1335 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1340 // FIXME: clean that
1341 static bool import(GuiView * lv, FileName const & filename,
1342 string const & format, ErrorList & errorList)
1344 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1346 string loader_format;
1347 vector<string> loaders = theConverters().loaders();
1348 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1349 for (vector<string>::const_iterator it = loaders.begin();
1350 it != loaders.end(); ++it) {
1351 if (!theConverters().isReachable(format, *it))
1354 string const tofile =
1355 support::changeExtension(filename.absFilename(),
1356 formats.extension(*it));
1357 if (!theConverters().convert(0, filename, FileName(tofile),
1358 filename, format, *it, errorList))
1360 loader_format = *it;
1363 if (loader_format.empty()) {
1364 frontend::Alert::error(_("Couldn't import file"),
1365 bformat(_("No information for importing the format %1$s."),
1366 formats.prettyName(format)));
1370 loader_format = format;
1372 if (loader_format == "lyx") {
1373 Buffer * buf = lv->loadDocument(lyxfile);
1378 buf->errors("Parse");
1380 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1384 bool as_paragraphs = loader_format == "textparagraph";
1385 string filename2 = (loader_format == format) ? filename.absFilename()
1386 : support::changeExtension(filename.absFilename(),
1387 formats.extension(loader_format));
1388 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1389 theLyXFunc().setLyXView(lv);
1390 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1397 void GuiView::importDocument(string const & argument)
1400 string filename = split(argument, format, ' ');
1402 LYXERR(Debug::INFO, format << " file: " << filename);
1404 // need user interaction
1405 if (filename.empty()) {
1406 string initpath = lyxrc.document_path;
1408 Buffer const * buf = buffer();
1410 string const trypath = buf->filePath();
1411 // If directory is writeable, use this as default.
1412 if (FileName(trypath).isDirWritable())
1416 docstring const text = bformat(_("Select %1$s file to import"),
1417 formats.prettyName(format));
1419 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1420 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1421 dlg.setButton2(qt_("Examples|#E#e"),
1422 toqstr(addPath(package().system_support().absFilename(), "examples")));
1424 docstring filter = formats.prettyName(format);
1427 filter += from_utf8(formats.extension(format));
1430 FileDialog::Result result =
1431 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1433 if (result.first == FileDialog::Later)
1436 filename = fromqstr(result.second);
1438 // check selected filename
1439 if (filename.empty())
1440 message(_("Canceled."));
1443 if (filename.empty())
1446 // get absolute path of file
1447 FileName const fullname(support::makeAbsPath(filename));
1449 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1451 // Check if the document already is open
1452 Buffer * buf = theBufferList().getBuffer(lyxfile);
1455 if (!closeBuffer()) {
1456 message(_("Canceled."));
1461 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1463 // if the file exists already, and we didn't do
1464 // -i lyx thefile.lyx, warn
1465 if (lyxfile.exists() && fullname != lyxfile) {
1467 docstring text = bformat(_("The document %1$s already exists.\n\n"
1468 "Do you want to overwrite that document?"), displaypath);
1469 int const ret = Alert::prompt(_("Overwrite document?"),
1470 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1473 message(_("Canceled."));
1478 message(bformat(_("Importing %1$s..."), displaypath));
1479 ErrorList errorList;
1480 if (import(this, fullname, format, errorList))
1481 message(_("imported."));
1483 message(_("file not imported!"));
1485 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1489 void GuiView::newDocument(string const & filename, bool from_template)
1491 FileName initpath(lyxrc.document_path);
1492 Buffer * buf = buffer();
1494 FileName const trypath(buf->filePath());
1495 // If directory is writeable, use this as default.
1496 if (trypath.isDirWritable())
1500 string templatefile = from_template ?
1501 selectTemplateFile().absFilename() : string();
1503 if (filename.empty())
1504 b = newUnnamedFile(templatefile, initpath);
1506 b = newFile(filename, templatefile, true);
1510 // Ensure the cursor is correctly positionned on screen.
1511 view()->showCursor();
1515 void GuiView::insertLyXFile(docstring const & fname)
1517 BufferView * bv = view();
1522 FileName filename(to_utf8(fname));
1524 if (!filename.empty()) {
1525 bv->insertLyXFile(filename);
1529 // Launch a file browser
1531 string initpath = lyxrc.document_path;
1532 string const trypath = bv->buffer().filePath();
1533 // If directory is writeable, use this as default.
1534 if (FileName(trypath).isDirWritable())
1538 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1539 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1540 dlg.setButton2(qt_("Examples|#E#e"),
1541 toqstr(addPath(package().system_support().absFilename(),
1544 FileDialog::Result result = dlg.open(toqstr(initpath),
1545 QStringList(qt_("LyX Documents (*.lyx)")));
1547 if (result.first == FileDialog::Later)
1551 filename.set(fromqstr(result.second));
1553 // check selected filename
1554 if (filename.empty()) {
1555 // emit message signal.
1556 message(_("Canceled."));
1560 bv->insertLyXFile(filename);
1564 void GuiView::insertPlaintextFile(docstring const & fname,
1567 BufferView * bv = view();
1572 FileName filename(to_utf8(fname));
1574 if (!filename.empty()) {
1575 bv->insertPlaintextFile(filename, asParagraph);
1579 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1580 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1582 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1585 if (result.first == FileDialog::Later)
1589 filename.set(fromqstr(result.second));
1591 // check selected filename
1592 if (filename.empty()) {
1593 // emit message signal.
1594 message(_("Canceled."));
1598 bv->insertPlaintextFile(filename, asParagraph);
1602 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1604 FileName fname = b.fileName();
1605 FileName const oldname = fname;
1607 if (!newname.empty()) {
1609 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1611 // Switch to this Buffer.
1614 /// No argument? Ask user through dialog.
1616 FileDialog dlg(qt_("Choose a filename to save document as"),
1617 LFUN_BUFFER_WRITE_AS);
1618 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1619 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1621 if (!isLyXFilename(fname.absFilename()))
1622 fname.changeExtension(".lyx");
1624 FileDialog::Result result =
1625 dlg.save(toqstr(fname.onlyPath().absFilename()),
1626 QStringList(qt_("LyX Documents (*.lyx)")),
1627 toqstr(fname.onlyFileName()));
1629 if (result.first == FileDialog::Later)
1632 fname.set(fromqstr(result.second));
1637 if (!isLyXFilename(fname.absFilename()))
1638 fname.changeExtension(".lyx");
1641 if (FileName(fname).exists()) {
1642 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1643 docstring text = bformat(_("The document %1$s already "
1644 "exists.\n\nDo you want to "
1645 "overwrite that document?"),
1647 int const ret = Alert::prompt(_("Overwrite document?"),
1648 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1651 case 1: return renameBuffer(b, docstring());
1652 case 2: return false;
1656 // Ok, change the name of the buffer
1657 b.setFileName(fname.absFilename());
1659 bool unnamed = b.isUnnamed();
1660 b.setUnnamed(false);
1661 b.saveCheckSum(fname);
1663 if (!saveBuffer(b)) {
1664 b.setFileName(oldname.absFilename());
1665 b.setUnnamed(unnamed);
1666 b.saveCheckSum(oldname);
1674 bool GuiView::saveBuffer(Buffer & b)
1677 return renameBuffer(b, docstring());
1680 theSession().lastFiles().add(b.fileName());
1684 // Switch to this Buffer.
1687 // FIXME: we don't tell the user *WHY* the save failed !!
1688 docstring const file = makeDisplayPath(b.absFileName(), 30);
1689 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1690 "Do you want to rename the document and "
1691 "try again?"), file);
1692 int const ret = Alert::prompt(_("Rename and save?"),
1693 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1696 if (!renameBuffer(b, docstring()))
1705 return saveBuffer(b);
1709 bool GuiView::closeBuffer()
1711 Buffer * buf = buffer();
1712 return buf && closeBuffer(*buf);
1716 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1718 // goto bookmark to update bookmark pit.
1719 //FIXME: we should update only the bookmarks related to this buffer!
1720 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1721 theLyXFunc().gotoBookmark(i+1, false, false);
1723 if (buf.isClean() || buf.paragraphs().empty()) {
1724 if (buf.masterBuffer() == &buf && tolastopened)
1725 theSession().lastOpened().add(buf.fileName());
1726 theBufferList().release(&buf);
1729 // Switch to this Buffer.
1734 if (buf.isUnnamed())
1735 file = from_utf8(buf.fileName().onlyFileName());
1737 file = buf.fileName().displayName(30);
1739 // Bring this window to top before asking questions.
1743 docstring const text = bformat(_("The document %1$s has unsaved changes."
1744 "\n\nDo you want to save the document or discard the changes?"), file);
1745 int const ret = Alert::prompt(_("Save changed document?"),
1746 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1750 if (!saveBuffer(buf))
1754 // if we crash after this we could
1755 // have no autosave file but I guess
1756 // this is really improbable (Jug)
1757 removeAutosaveFile(buf.absFileName());
1763 // save file names to .lyx/session
1764 // if master/slave are both open, do not save slave since it
1765 // will be automatically loaded when the master is loaded
1766 if (buf.masterBuffer() == &buf && tolastopened)
1767 theSession().lastOpened().add(buf.fileName());
1770 // Don't close child documents.
1771 removeWorkArea(d.current_work_area_);
1773 theBufferList().release(&buf);
1779 bool GuiView::dispatch(FuncRequest const & cmd)
1781 BufferView * bv = view();
1782 // By default we won't need any update.
1784 bv->cursor().updateFlags(Update::None);
1785 bool dispatched = true;
1787 switch(cmd.action) {
1788 case LFUN_BUFFER_IMPORT:
1789 importDocument(to_utf8(cmd.argument()));
1792 case LFUN_BUFFER_SWITCH:
1793 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1796 case LFUN_BUFFER_NEXT:
1797 setBuffer(theBufferList().next(buffer()));
1800 case LFUN_BUFFER_PREVIOUS:
1801 setBuffer(theBufferList().previous(buffer()));
1804 case LFUN_COMMAND_EXECUTE: {
1805 bool const show_it = cmd.argument() != "off";
1806 // FIXME: this is a hack, "minibuffer" should not be
1808 if (GuiToolbar * t = toolbar("minibuffer")) {
1809 t->setVisible(show_it);
1810 if (show_it && t->commandBuffer())
1811 t->commandBuffer()->setFocus();
1815 case LFUN_DROP_LAYOUTS_CHOICE:
1817 d.layout_->showPopup();
1820 case LFUN_MENU_OPEN:
1821 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1822 menu->exec(QCursor::pos());
1825 case LFUN_FILE_INSERT:
1826 insertLyXFile(cmd.argument());
1828 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1829 insertPlaintextFile(cmd.argument(), true);
1832 case LFUN_FILE_INSERT_PLAINTEXT:
1833 insertPlaintextFile(cmd.argument(), false);
1836 case LFUN_BUFFER_WRITE:
1838 saveBuffer(bv->buffer());
1841 case LFUN_BUFFER_WRITE_AS:
1843 renameBuffer(bv->buffer(), cmd.argument());
1846 case LFUN_BUFFER_WRITE_ALL: {
1847 Buffer * first = theBufferList().first();
1850 message(_("Saving all documents..."));
1851 // We cannot use a for loop as the buffer list cycles.
1854 if (!b->isClean()) {
1856 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1858 b = theBufferList().next(b);
1859 } while (b != first);
1860 message(_("All documents saved."));
1864 case LFUN_TOOLBAR_TOGGLE: {
1865 string const name = cmd.getArg(0);
1866 if (GuiToolbar * t = toolbar(name))
1871 case LFUN_DIALOG_UPDATE: {
1872 string const name = to_utf8(cmd.argument());
1873 // Can only update a dialog connected to an existing inset
1874 Inset * inset = getOpenInset(name);
1876 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1877 inset->dispatch(view()->cursor(), fr);
1878 } else if (name == "paragraph") {
1879 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1880 } else if (name == "prefs") {
1881 updateDialog(name, string());
1886 case LFUN_DIALOG_TOGGLE: {
1887 if (isDialogVisible(cmd.getArg(0)))
1888 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1890 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1894 case LFUN_DIALOG_DISCONNECT_INSET:
1895 disconnectDialog(to_utf8(cmd.argument()));
1898 case LFUN_DIALOG_HIDE: {
1899 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1903 case LFUN_DIALOG_SHOW: {
1904 string const name = cmd.getArg(0);
1905 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1907 if (name == "character") {
1908 data = freefont2string();
1910 showDialog("character", data);
1911 } else if (name == "latexlog") {
1912 Buffer::LogType type;
1913 string const logfile = buffer()->logName(&type);
1915 case Buffer::latexlog:
1918 case Buffer::buildlog:
1922 data += Lexer::quoteString(logfile);
1923 showDialog("log", data);
1924 } else if (name == "vclog") {
1925 string const data = "vc " +
1926 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1927 showDialog("log", data);
1928 } else if (name == "symbols") {
1929 data = bv->cursor().getEncoding()->name();
1931 showDialog("symbols", data);
1933 showDialog(name, data);
1937 case LFUN_INSET_APPLY: {
1938 view()->cursor().recordUndoFullDocument();
1939 string const name = cmd.getArg(0);
1940 Inset * inset = getOpenInset(name);
1942 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1943 inset->dispatch(view()->cursor(), fr);
1945 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1951 case LFUN_UI_TOGGLE:
1953 // Make sure the keyboard focus stays in the work area.
1957 case LFUN_COMPLETION_INLINE:
1958 if (d.current_work_area_)
1959 d.current_work_area_->completer().showInline();
1962 case LFUN_SPLIT_VIEW:
1963 if (Buffer * buf = buffer()) {
1964 string const orientation = cmd.getArg(0);
1965 d.splitter_->setOrientation(orientation == "vertical"
1966 ? Qt::Vertical : Qt::Horizontal);
1967 TabWorkArea * twa = addTabWorkArea();
1968 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1969 setCurrentWorkArea(wa);
1973 case LFUN_CLOSE_TAB_GROUP:
1974 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1976 twa = d.currentTabWorkArea();
1977 // Switch to the next GuiWorkArea in the found TabWorkArea.
1979 d.current_work_area_ = twa->currentWorkArea();
1980 // Make sure the work area is up to date.
1981 twa->setCurrentWorkArea(d.current_work_area_);
1983 d.current_work_area_ = 0;
1985 if (d.splitter_->count() == 0)
1986 // No more work area, switch to the background widget.
1991 case LFUN_COMPLETION_POPUP:
1992 if (d.current_work_area_)
1993 d.current_work_area_->completer().showPopup();
1997 case LFUN_COMPLETION_COMPLETE:
1998 if (d.current_work_area_)
1999 d.current_work_area_->completer().tab();
2007 // Part of automatic menu appearance feature.
2008 if (isFullScreen()) {
2009 if (menuBar()->isVisible())
2011 if (statusBar()->isVisible())
2012 statusBar()->hide();
2019 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2021 string const arg = cmd.getArg(0);
2022 if (arg == "scrollbar") {
2023 // hide() is of no help
2024 if (d.current_work_area_->verticalScrollBarPolicy() ==
2025 Qt::ScrollBarAlwaysOff)
2027 d.current_work_area_->setVerticalScrollBarPolicy(
2028 Qt::ScrollBarAsNeeded);
2030 d.current_work_area_->setVerticalScrollBarPolicy(
2031 Qt::ScrollBarAlwaysOff);
2034 if (arg == "statusbar") {
2035 statusBar()->setVisible(!statusBar()->isVisible());
2038 if (arg == "menubar") {
2039 menuBar()->setVisible(!menuBar()->isVisible());
2042 #if QT_VERSION >= 0x040300
2043 if (arg == "frame") {
2045 getContentsMargins(&l, &t, &r, &b);
2046 //are the frames in default state?
2047 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2049 setContentsMargins(-2, -2, -2, -2);
2051 setContentsMargins(0, 0, 0, 0);
2056 if (arg == "fullscreen") {
2061 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2065 void GuiView::toggleFullScreen()
2067 if (isFullScreen()) {
2068 for (int i = 0; i != d.splitter_->count(); ++i)
2069 d.tabWorkArea(i)->setFullScreen(false);
2070 #if QT_VERSION >= 0x040300
2071 setContentsMargins(0, 0, 0, 0);
2073 setWindowState(windowState() ^ Qt::WindowFullScreen);
2076 statusBar()->show();
2078 for (int i = 0; i != d.splitter_->count(); ++i)
2079 d.tabWorkArea(i)->setFullScreen(true);
2080 #if QT_VERSION >= 0x040300
2081 setContentsMargins(-2, -2, -2, -2);
2084 setWindowState(windowState() ^ Qt::WindowFullScreen);
2085 statusBar()->hide();
2087 if (lyxrc.full_screen_toolbars) {
2088 ToolbarMap::iterator end = d.toolbars_.end();
2089 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2096 Buffer const * GuiView::updateInset(Inset const * inset)
2098 if (!d.current_work_area_)
2102 d.current_work_area_->scheduleRedraw();
2104 return &d.current_work_area_->bufferView().buffer();
2108 void GuiView::restartCursor()
2110 /* When we move around, or type, it's nice to be able to see
2111 * the cursor immediately after the keypress.
2113 if (d.current_work_area_)
2114 d.current_work_area_->startBlinkingCursor();
2116 // Take this occasion to update the other GUI elements.
2122 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2124 if (d.current_work_area_)
2125 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2130 // This list should be kept in sync with the list of insets in
2131 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2132 // dialog should have the same name as the inset.
2133 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2134 // docs in LyXAction.cpp.
2136 char const * const dialognames[] = {
2137 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2138 "citation", "document", "errorlist", "ert", "external", "file",
2139 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2140 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2141 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2143 #ifdef HAVE_LIBAIKSAURUS
2147 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2149 char const * const * const end_dialognames =
2150 dialognames + (sizeof(dialognames) / sizeof(char *));
2154 cmpCStr(char const * name) : name_(name) {}
2155 bool operator()(char const * other) {
2156 return strcmp(other, name_) == 0;
2163 bool isValidName(string const & name)
2165 return find_if(dialognames, end_dialognames,
2166 cmpCStr(name.c_str())) != end_dialognames;
2172 void GuiView::resetDialogs()
2174 // Make sure that no LFUN uses any LyXView.
2175 theLyXFunc().setLyXView(0);
2178 constructToolbars();
2179 guiApp->menus().fillMenuBar(menuBar(), this, true);
2181 d.layout_->updateContents(true);
2182 // Now update controls with current buffer.
2183 theLyXFunc().setLyXView(this);
2189 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2191 if (!isValidName(name))
2194 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2196 if (it != d.dialogs_.end())
2197 return it->second.get();
2199 Dialog * dialog = build(name);
2200 d.dialogs_[name].reset(dialog);
2201 if (lyxrc.allow_geometry_session)
2202 dialog->restoreSession();
2209 void GuiView::showDialog(string const & name, string const & data,
2216 Dialog * dialog = findOrBuild(name, false);
2218 dialog->showData(data);
2220 d.open_insets_[name] = inset;
2226 bool GuiView::isDialogVisible(string const & name) const
2228 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2229 if (it == d.dialogs_.end())
2231 return it->second.get()->isVisibleView();
2235 void GuiView::hideDialog(string const & name, Inset * inset)
2237 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2238 if (it == d.dialogs_.end())
2241 if (inset && inset != getOpenInset(name))
2244 Dialog * const dialog = it->second.get();
2245 if (dialog->isVisibleView())
2247 d.open_insets_[name] = 0;
2251 void GuiView::disconnectDialog(string const & name)
2253 if (!isValidName(name))
2256 if (d.open_insets_.find(name) != d.open_insets_.end())
2257 d.open_insets_[name] = 0;
2261 Inset * GuiView::getOpenInset(string const & name) const
2263 if (!isValidName(name))
2266 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2267 return it == d.open_insets_.end() ? 0 : it->second;
2271 void GuiView::hideAll() const
2273 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2274 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2276 for(; it != end; ++it)
2277 it->second->hideView();
2281 void GuiView::updateDialogs()
2283 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2284 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2286 for(; it != end; ++it) {
2287 Dialog * dialog = it->second.get();
2288 if (dialog && dialog->isVisibleView())
2289 dialog->checkStatus();
2296 // will be replaced by a proper factory...
2297 Dialog * createGuiAbout(GuiView & lv);
2298 Dialog * createGuiBibitem(GuiView & lv);
2299 Dialog * createGuiBibtex(GuiView & lv);
2300 Dialog * createGuiBox(GuiView & lv);
2301 Dialog * createGuiBranch(GuiView & lv);
2302 Dialog * createGuiChanges(GuiView & lv);
2303 Dialog * createGuiCharacter(GuiView & lv);
2304 Dialog * createGuiCitation(GuiView & lv);
2305 Dialog * createGuiDelimiter(GuiView & lv);
2306 Dialog * createGuiDocument(GuiView & lv);
2307 Dialog * createGuiErrorList(GuiView & lv);
2308 Dialog * createGuiERT(GuiView & lv);
2309 Dialog * createGuiExternal(GuiView & lv);
2310 Dialog * createGuiFloat(GuiView & lv);
2311 Dialog * createGuiGraphics(GuiView & lv);
2312 Dialog * createGuiHSpace(GuiView & lv);
2313 Dialog * createGuiInclude(GuiView & lv);
2314 Dialog * createGuiInfo(GuiView & lv);
2315 Dialog * createGuiLabel(GuiView & lv);
2316 Dialog * createGuiListings(GuiView & lv);
2317 Dialog * createGuiLog(GuiView & lv);
2318 Dialog * createGuiMathMatrix(GuiView & lv);
2319 Dialog * createGuiNomenclature(GuiView & lv);
2320 Dialog * createGuiNote(GuiView & lv);
2321 Dialog * createGuiParagraph(GuiView & lv);
2322 Dialog * createGuiPreferences(GuiView & lv);
2323 Dialog * createGuiPrint(GuiView & lv);
2324 Dialog * createGuiRef(GuiView & lv);
2325 Dialog * createGuiSearch(GuiView & lv);
2326 Dialog * createGuiSendTo(GuiView & lv);
2327 Dialog * createGuiShowFile(GuiView & lv);
2328 Dialog * createGuiSpellchecker(GuiView & lv);
2329 Dialog * createGuiSymbols(GuiView & lv);
2330 Dialog * createGuiTabularCreate(GuiView & lv);
2331 Dialog * createGuiTabular(GuiView & lv);
2332 Dialog * createGuiTexInfo(GuiView & lv);
2333 Dialog * createGuiToc(GuiView & lv);
2334 Dialog * createGuiThesaurus(GuiView & lv);
2335 Dialog * createGuiHyperlink(GuiView & lv);
2336 Dialog * createGuiVSpace(GuiView & lv);
2337 Dialog * createGuiViewSource(GuiView & lv);
2338 Dialog * createGuiWrap(GuiView & lv);
2341 Dialog * GuiView::build(string const & name)
2343 LASSERT(isValidName(name), /**/);
2345 if (name == "aboutlyx")
2346 return createGuiAbout(*this);
2347 if (name == "bibitem")
2348 return createGuiBibitem(*this);
2349 if (name == "bibtex")
2350 return createGuiBibtex(*this);
2352 return createGuiBox(*this);
2353 if (name == "branch")
2354 return createGuiBranch(*this);
2355 if (name == "changes")
2356 return createGuiChanges(*this);
2357 if (name == "character")
2358 return createGuiCharacter(*this);
2359 if (name == "citation")
2360 return createGuiCitation(*this);
2361 if (name == "document")
2362 return createGuiDocument(*this);
2363 if (name == "errorlist")
2364 return createGuiErrorList(*this);
2366 return createGuiERT(*this);
2367 if (name == "external")
2368 return createGuiExternal(*this);
2370 return createGuiShowFile(*this);
2371 if (name == "findreplace")
2372 return createGuiSearch(*this);
2373 if (name == "float")
2374 return createGuiFloat(*this);
2375 if (name == "graphics")
2376 return createGuiGraphics(*this);
2377 if (name == "include")
2378 return createGuiInclude(*this);
2380 return createGuiInfo(*this);
2381 if (name == "nomenclature")
2382 return createGuiNomenclature(*this);
2383 if (name == "label")
2384 return createGuiLabel(*this);
2386 return createGuiLog(*this);
2387 if (name == "view-source")
2388 return createGuiViewSource(*this);
2389 if (name == "mathdelimiter")
2390 return createGuiDelimiter(*this);
2391 if (name == "mathmatrix")
2392 return createGuiMathMatrix(*this);
2394 return createGuiNote(*this);
2395 if (name == "paragraph")
2396 return createGuiParagraph(*this);
2397 if (name == "prefs")
2398 return createGuiPreferences(*this);
2399 if (name == "print")
2400 return createGuiPrint(*this);
2402 return createGuiRef(*this);
2403 if (name == "sendto")
2404 return createGuiSendTo(*this);
2405 if (name == "space")
2406 return createGuiHSpace(*this);
2407 if (name == "spellchecker")
2408 return createGuiSpellchecker(*this);
2409 if (name == "symbols")
2410 return createGuiSymbols(*this);
2411 if (name == "tabular")
2412 return createGuiTabular(*this);
2413 if (name == "tabularcreate")
2414 return createGuiTabularCreate(*this);
2415 if (name == "texinfo")
2416 return createGuiTexInfo(*this);
2417 #ifdef HAVE_LIBAIKSAURUS
2418 if (name == "thesaurus")
2419 return createGuiThesaurus(*this);
2422 return createGuiToc(*this);
2424 return createGuiHyperlink(*this);
2425 if (name == "vspace")
2426 return createGuiVSpace(*this);
2428 return createGuiWrap(*this);
2429 if (name == "listings")
2430 return createGuiListings(*this);
2436 } // namespace frontend
2439 #include "GuiView_moc.cpp"