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/ExceptionMessage.h"
61 #include "support/FileName.h"
62 #include "support/filetools.h"
63 #include "support/gettext.h"
64 #include "support/ForkedCalls.h"
65 #include "support/lstrings.h"
66 #include "support/os.h"
67 #include "support/Package.h"
68 #include "support/Timeout.h"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
116 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
117 /// The text to be written on top of the pixmap
118 QString const text = lyx_version ?
119 qt_("version ") + lyx_version : qt_("unknown version");
120 splash_ = QPixmap(":/images/banner.png");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
125 // The font used to display the version info
126 font.setStyleHint(QFont::SansSerif);
127 font.setWeight(QFont::Bold);
128 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
130 pain.drawText(190, 225, text);
133 void paintEvent(QPaintEvent *)
135 int x = (width() - splash_.width()) / 2;
136 int y = (height() - splash_.height()) / 2;
138 pain.drawPixmap(x, y, splash_);
145 /// Toolbar store providing access to individual toolbars by name.
146 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
148 typedef boost::shared_ptr<Dialog> DialogPtr;
153 struct GuiView::GuiViewPrivate
156 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
159 // hardcode here the platform specific icon size
160 smallIconSize = 14; // scaling problems
161 normalIconSize = 20; // ok, default
162 bigIconSize = 26; // better for some math icons
164 splitter_ = new QSplitter;
165 bg_widget_ = new BackgroundWidget;
166 stack_widget_ = new QStackedWidget;
167 stack_widget_->addWidget(bg_widget_);
168 stack_widget_->addWidget(splitter_);
176 delete stack_widget_;
179 QMenu * toolBarPopup(GuiView * parent)
181 // FIXME: translation
182 QMenu * menu = new QMenu(parent);
183 QActionGroup * iconSizeGroup = new QActionGroup(parent);
185 QAction * smallIcons = new QAction(iconSizeGroup);
186 smallIcons->setText(qt_("Small-sized icons"));
187 smallIcons->setCheckable(true);
188 QObject::connect(smallIcons, SIGNAL(triggered()),
189 parent, SLOT(smallSizedIcons()));
190 menu->addAction(smallIcons);
192 QAction * normalIcons = new QAction(iconSizeGroup);
193 normalIcons->setText(qt_("Normal-sized icons"));
194 normalIcons->setCheckable(true);
195 QObject::connect(normalIcons, SIGNAL(triggered()),
196 parent, SLOT(normalSizedIcons()));
197 menu->addAction(normalIcons);
199 QAction * bigIcons = new QAction(iconSizeGroup);
200 bigIcons->setText(qt_("Big-sized icons"));
201 bigIcons->setCheckable(true);
202 QObject::connect(bigIcons, SIGNAL(triggered()),
203 parent, SLOT(bigSizedIcons()));
204 menu->addAction(bigIcons);
206 unsigned int cur = parent->iconSize().width();
207 if ( cur == parent->d.smallIconSize)
208 smallIcons->setChecked(true);
209 else if (cur == parent->d.normalIconSize)
210 normalIcons->setChecked(true);
211 else if (cur == parent->d.bigIconSize)
212 bigIcons->setChecked(true);
219 stack_widget_->setCurrentWidget(bg_widget_);
220 bg_widget_->setUpdatesEnabled(true);
223 TabWorkArea * tabWorkArea(int i)
225 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
228 TabWorkArea * currentTabWorkArea()
230 if (splitter_->count() == 1)
231 // The first TabWorkArea is always the first one, if any.
232 return tabWorkArea(0);
234 for (int i = 0; i != splitter_->count(); ++i) {
235 TabWorkArea * twa = tabWorkArea(i);
236 if (current_work_area_ == twa->currentWorkArea())
240 // None has the focus so we just take the first one.
241 return tabWorkArea(0);
245 GuiWorkArea * current_work_area_;
246 QSplitter * splitter_;
247 QStackedWidget * stack_widget_;
248 BackgroundWidget * bg_widget_;
250 ToolbarMap toolbars_;
251 /// The main layout box.
253 * \warning Don't Delete! The layout box is actually owned by
254 * whichever toolbar contains it. All the GuiView class needs is a
255 * means of accessing it.
257 * FIXME: replace that with a proper model so that we are not limited
258 * to only one dialog.
260 GuiLayoutBox * layout_;
263 map<string, Inset *> open_insets_;
266 map<string, DialogPtr> dialogs_;
268 unsigned int smallIconSize;
269 unsigned int normalIconSize;
270 unsigned int bigIconSize;
272 QTimer statusbar_timer_;
273 /// auto-saving of buffers
274 Timeout autosave_timeout_;
275 /// flag against a race condition due to multiclicks, see bug #1119
279 TocModels toc_models_;
283 GuiView::GuiView(int id)
284 : d(*new GuiViewPrivate), id_(id), closing_(false)
286 // GuiToolbars *must* be initialised before the menu bar.
289 // set ourself as the current view. This is needed for the menu bar
290 // filling, at least for the static special menu item on Mac. Otherwise
291 // they are greyed out.
292 theLyXFunc().setLyXView(this);
294 // Fill up the menu bar.
295 guiApp->menus().fillMenuBar(menuBar(), this, true);
297 setCentralWidget(d.stack_widget_);
299 // Start autosave timer
300 if (lyxrc.autosave) {
301 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
302 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
303 d.autosave_timeout_.start();
305 connect(&d.statusbar_timer_, SIGNAL(timeout()),
306 this, SLOT(clearMessage()));
308 // We don't want to keep the window in memory if it is closed.
309 setAttribute(Qt::WA_DeleteOnClose, true);
311 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
312 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
313 // since the icon is provided in the application bundle.
314 setWindowIcon(QPixmap(":/images/lyx.png"));
318 setAcceptDrops(true);
320 statusBar()->setSizeGripEnabled(true);
322 // Forbid too small unresizable window because it can happen
323 // with some window manager under X11.
324 setMinimumSize(300, 200);
326 if (lyxrc.allow_geometry_session) {
327 // Now take care of session management.
332 // No session handling, default to a sane size.
333 setGeometry(50, 50, 690, 510);
335 // This enables to clear session data if any.
337 settings.remove("views");
347 void GuiView::saveLayout() const
350 QString const key = "views/" + QString::number(id_);
352 settings.setValue(key + "/pos", pos());
353 settings.setValue(key + "/size", size());
355 settings.setValue(key + "/geometry", saveGeometry());
357 settings.setValue(key + "/layout", saveState(0));
358 settings.setValue(key + "/icon_size", iconSize());
362 bool GuiView::restoreLayout()
365 settings.beginGroup("views");
366 settings.beginGroup(QString::number(id_));
367 QString const icon_key = "icon_size";
368 if (!settings.contains(icon_key))
371 setIconSize(settings.value(icon_key).toSize());
373 QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
374 QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
378 if (!restoreGeometry(settings.value("geometry").toByteArray()))
379 setGeometry(50, 50, 690, 510);
381 // Make sure layout is correctly oriented.
382 setLayoutDirection(qApp->layoutDirection());
384 // Allow the toc and view-source dock widget to be restored if needed.
386 if ((tmp = findOrBuild("toc", true)))
388 if ((tmp = findOrBuild("view-source", true)))
391 if (!restoreState(settings.value("layout").toByteArray(), 0))
398 GuiToolbar * GuiView::toolbar(string const & name)
400 ToolbarMap::iterator it = d.toolbars_.find(name);
401 if (it != d.toolbars_.end())
404 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
405 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
410 void GuiView::constructToolbars()
412 ToolbarMap::iterator it = d.toolbars_.begin();
413 for (; it != d.toolbars_.end(); ++it)
418 // extracts the toolbars from the backend
419 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
420 Toolbars::Infos::iterator end = guiApp->toolbars().end();
421 for (; cit != end; ++cit)
422 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
426 void GuiView::initToolbars()
428 // extracts the toolbars from the backend
429 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
430 Toolbars::Infos::iterator end = guiApp->toolbars().end();
431 for (; cit != end; ++cit) {
432 GuiToolbar * tb = toolbar(cit->name);
435 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
437 tb->setVisible(false);
438 tb->setVisibility(visibility);
440 if (visibility & Toolbars::TOP) {
442 addToolBarBreak(Qt::TopToolBarArea);
443 addToolBar(Qt::TopToolBarArea, tb);
446 if (visibility & Toolbars::BOTTOM) {
447 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
448 #if (QT_VERSION >= 0x040202)
449 addToolBarBreak(Qt::BottomToolBarArea);
451 addToolBar(Qt::BottomToolBarArea, tb);
454 if (visibility & Toolbars::LEFT) {
455 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
456 #if (QT_VERSION >= 0x040202)
457 addToolBarBreak(Qt::LeftToolBarArea);
459 addToolBar(Qt::LeftToolBarArea, tb);
462 if (visibility & Toolbars::RIGHT) {
463 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
464 #if (QT_VERSION >= 0x040202)
465 addToolBarBreak(Qt::RightToolBarArea);
467 addToolBar(Qt::RightToolBarArea, tb);
470 if (visibility & Toolbars::ON)
471 tb->setVisible(true);
476 TocModels & GuiView::tocModels()
478 return d.toc_models_;
482 void GuiView::setFocus()
484 // Make sure LyXFunc points to the correct view.
485 theLyXFunc().setLyXView(this);
486 if (d.current_work_area_)
487 d.current_work_area_->setFocus();
493 QMenu * GuiView::createPopupMenu()
495 return d.toolBarPopup(this);
499 void GuiView::showEvent(QShowEvent * e)
501 LYXERR(Debug::GUI, "Passed Geometry "
502 << size().height() << "x" << size().width()
503 << "+" << pos().x() << "+" << pos().y());
505 if (d.splitter_->count() == 0)
506 // No work area, switch to the background widget.
509 QMainWindow::showEvent(e);
513 void GuiView::closeEvent(QCloseEvent * close_event)
517 // it can happen that this event arrives without selecting the view,
518 // e.g. when clicking the close button on a background window.
519 theLyXFunc().setLyXView(this);
521 while (Buffer * b = buffer()) {
523 // This is a child document, just close the tab after saving
524 // but keep the file loaded.
525 if (!saveBuffer(*b)) {
527 close_event->ignore();
530 removeWorkArea(d.current_work_area_);
534 QList<int> const ids = guiApp->viewIds();
535 for (int i = 0; i != ids.size(); ++i) {
538 if (guiApp->view(ids[i]).workArea(*b)) {
539 // FIXME 1: should we put an alert box here that the buffer
540 // is viewed elsewhere?
541 // FIXME 2: should we try to save this buffer in any case?
544 // This buffer is also opened in another view, so
545 // close the associated work area...
546 removeWorkArea(d.current_work_area_);
547 // ... but don't close the buffer.
552 if (b && !closeBuffer(*b, true)) {
554 close_event->ignore();
559 // Make sure that nothing will use this close to be closed View.
560 guiApp->unregisterView(this);
562 if (isFullScreen()) {
563 // Switch off fullscreen before closing.
568 // Make sure the timer time out will not trigger a statusbar update.
569 d.statusbar_timer_.stop();
571 // Saving fullscreen requires additional tweaks in the toolbar code.
572 // It wouldn't also work under linux natively.
573 if (lyxrc.allow_geometry_session) {
574 // Save this window geometry and layout.
576 // Then the toolbar private states.
577 ToolbarMap::iterator end = d.toolbars_.end();
578 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
579 it->second->saveSession();
580 // Now take care of all other dialogs:
581 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
582 for (; it!= d.dialogs_.end(); ++it)
583 it->second->saveSession();
586 close_event->accept();
590 void GuiView::dragEnterEvent(QDragEnterEvent * event)
592 if (event->mimeData()->hasUrls())
594 /// \todo Ask lyx-devel is this is enough:
595 /// if (event->mimeData()->hasFormat("text/plain"))
596 /// event->acceptProposedAction();
600 void GuiView::dropEvent(QDropEvent* event)
602 QList<QUrl> files = event->mimeData()->urls();
606 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
607 for (int i = 0; i != files.size(); ++i) {
608 string const file = os::internal_path(fromqstr(
609 files.at(i).toLocalFile()));
611 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
616 void GuiView::message(docstring const & str)
618 if (ForkedProcess::iAmAChild())
621 statusBar()->showMessage(toqstr(str));
622 d.statusbar_timer_.stop();
623 d.statusbar_timer_.start(3000);
627 void GuiView::smallSizedIcons()
629 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
633 void GuiView::normalSizedIcons()
635 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
639 void GuiView::bigSizedIcons()
641 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
645 void GuiView::clearMessage()
649 theLyXFunc().setLyXView(this);
650 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
651 d.statusbar_timer_.stop();
655 void GuiView::updateWindowTitle(GuiWorkArea * wa)
657 if (wa != d.current_work_area_)
659 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
660 setWindowIconText(wa->windowIconText());
664 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
667 disconnectBufferView();
668 connectBufferView(wa->bufferView());
669 connectBuffer(wa->bufferView().buffer());
670 d.current_work_area_ = wa;
671 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
672 this, SLOT(updateWindowTitle(GuiWorkArea *)));
673 updateWindowTitle(wa);
677 // The document settings needs to be reinitialised.
678 updateDialog("document", "");
680 // Buffer-dependent dialogs must be updated. This is done here because
681 // some dialogs require buffer()->text.
686 void GuiView::on_lastWorkAreaRemoved()
689 // We already are in a close event. Nothing more to do.
692 if (d.splitter_->count() > 1)
693 // We have a splitter so don't close anything.
696 // Reset and updates the dialogs.
697 d.toc_models_.reset(0);
698 updateDialog("document", "");
701 resetWindowTitleAndIconText();
703 if (lyxrc.open_buffers_in_tabs)
704 // Nothing more to do, the window should stay open.
707 if (guiApp->viewIds().size() > 1) {
713 // On Mac we also close the last window because the application stay
714 // resident in memory. On other platforms we don't close the last
715 // window because this would quit the application.
721 void GuiView::updateStatusBar()
723 // let the user see the explicit message
724 if (d.statusbar_timer_.isActive())
727 theLyXFunc().setLyXView(this);
728 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
732 bool GuiView::hasFocus() const
734 return qApp->activeWindow() == this;
738 bool GuiView::event(QEvent * e)
742 // Useful debug code:
743 //case QEvent::ActivationChange:
744 //case QEvent::WindowDeactivate:
745 //case QEvent::Paint:
746 //case QEvent::Enter:
747 //case QEvent::Leave:
748 //case QEvent::HoverEnter:
749 //case QEvent::HoverLeave:
750 //case QEvent::HoverMove:
751 //case QEvent::StatusTip:
752 //case QEvent::DragEnter:
753 //case QEvent::DragLeave:
757 case QEvent::WindowActivate: {
758 if (this == guiApp->currentView()) {
760 return QMainWindow::event(e);
762 guiApp->setCurrentView(this);
763 theLyXFunc().setLyXView(this);
764 if (d.current_work_area_) {
765 BufferView & bv = d.current_work_area_->bufferView();
766 connectBufferView(bv);
767 connectBuffer(bv.buffer());
768 // The document structure, name and dialogs might have
769 // changed in another view.
771 // The document settings needs to be reinitialised.
772 updateDialog("document", "");
775 resetWindowTitleAndIconText();
778 return QMainWindow::event(e);
781 case QEvent::ShortcutOverride: {
785 if (isFullScreen() && menuBar()->isHidden()) {
786 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
787 // FIXME: we should also try to detect special LyX shortcut such as
788 // Alt-P and Alt-M. Right now there is a hack in
789 // GuiWorkArea::processKeySym() that hides again the menubar for
791 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
793 return QMainWindow::event(e);
797 if (d.current_work_area_)
798 // Nothing special to do.
799 return QMainWindow::event(e);
801 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
802 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
804 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
805 || ke->key() == Qt::Key_Backtab)
806 return QMainWindow::event(e);
808 // Allow processing of shortcuts that are allowed even when no Buffer
810 theLyXFunc().setLyXView(this);
812 setKeySymbol(&sym, ke);
813 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
819 return QMainWindow::event(e);
823 void GuiView::resetWindowTitleAndIconText()
825 setWindowTitle(qt_("LyX"));
826 setWindowIconText(qt_("LyX"));
829 bool GuiView::focusNextPrevChild(bool /*next*/)
836 void GuiView::setBusy(bool busy)
838 if (d.current_work_area_) {
839 d.current_work_area_->setUpdatesEnabled(!busy);
841 d.current_work_area_->stopBlinkingCursor();
843 d.current_work_area_->startBlinkingCursor();
847 QApplication::setOverrideCursor(Qt::WaitCursor);
849 QApplication::restoreOverrideCursor();
853 GuiWorkArea * GuiView::workArea(Buffer & buffer)
855 if (TabWorkArea * twa = d.currentTabWorkArea())
856 return twa->workArea(buffer);
861 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
863 // Automatically create a TabWorkArea if there are none yet.
864 TabWorkArea * tab_widget = d.splitter_->count()
865 ? d.currentTabWorkArea() : addTabWorkArea();
866 return tab_widget->addWorkArea(buffer, *this);
870 TabWorkArea * GuiView::addTabWorkArea()
872 TabWorkArea * twa = new TabWorkArea;
873 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
874 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
875 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
876 this, SLOT(on_lastWorkAreaRemoved()));
878 d.splitter_->addWidget(twa);
879 d.stack_widget_->setCurrentWidget(d.splitter_);
884 GuiWorkArea const * GuiView::currentWorkArea() const
886 return d.current_work_area_;
890 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
893 d.current_work_area_ = wa;
894 for (int i = 0; i != d.splitter_->count(); ++i) {
895 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
901 void GuiView::removeWorkArea(GuiWorkArea * wa)
904 if (wa == d.current_work_area_) {
906 disconnectBufferView();
907 d.current_work_area_ = 0;
910 for (int i = 0; i != d.splitter_->count(); ++i) {
911 TabWorkArea * twa = d.tabWorkArea(i);
912 if (!twa->removeWorkArea(wa))
913 // Not found in this tab group.
916 // We found and removed the GuiWorkArea.
918 // No more WorkAreas in this tab group, so delete it.
923 if (d.current_work_area_)
924 // This means that we are not closing the current GuiWorkArea;
927 // Switch to the next GuiWorkArea in the found TabWorkArea.
928 d.current_work_area_ = twa->currentWorkArea();
932 if (d.splitter_->count() == 0)
933 // No more work area, switch to the background widget.
938 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
944 void GuiView::updateLayoutList()
947 d.layout_->updateContents(false);
951 void GuiView::updateToolbars()
953 ToolbarMap::iterator end = d.toolbars_.end();
954 if (d.current_work_area_) {
956 d.current_work_area_->bufferView().cursor().inMathed();
958 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
960 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
961 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
962 bool const mathmacrotemplate =
963 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
965 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
966 it->second->update(math, table, review, mathmacrotemplate);
968 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
969 it->second->update(false, false, false, false);
973 Buffer * GuiView::buffer()
975 if (d.current_work_area_)
976 return &d.current_work_area_->bufferView().buffer();
981 Buffer const * GuiView::buffer() const
983 if (d.current_work_area_)
984 return &d.current_work_area_->bufferView().buffer();
989 void GuiView::setBuffer(Buffer * newBuffer)
991 LASSERT(newBuffer, /**/);
994 GuiWorkArea * wa = workArea(*newBuffer);
996 updateLabels(*newBuffer->masterBuffer());
997 wa = addWorkArea(*newBuffer);
999 //Disconnect the old buffer...there's no new one.
1002 connectBuffer(*newBuffer);
1003 connectBufferView(wa->bufferView());
1004 setCurrentWorkArea(wa);
1010 void GuiView::connectBuffer(Buffer & buf)
1012 buf.setGuiDelegate(this);
1016 void GuiView::disconnectBuffer()
1018 if (d.current_work_area_)
1019 d.current_work_area_->bufferView().setGuiDelegate(0);
1023 void GuiView::connectBufferView(BufferView & bv)
1025 bv.setGuiDelegate(this);
1029 void GuiView::disconnectBufferView()
1031 if (d.current_work_area_)
1032 d.current_work_area_->bufferView().setGuiDelegate(0);
1036 void GuiView::errors(string const & error_type)
1038 ErrorList & el = buffer()->errorList(error_type);
1040 showDialog("errorlist", error_type);
1044 void GuiView::structureChanged()
1046 d.toc_models_.reset(view());
1047 // Navigator needs more than a simple update in this case. It needs to be
1049 updateDialog("toc", "");
1053 void GuiView::updateDialog(string const & name, string const & data)
1055 if (!isDialogVisible(name))
1058 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1059 if (it == d.dialogs_.end())
1062 Dialog * const dialog = it->second.get();
1063 if (dialog->isVisibleView())
1064 dialog->initialiseParams(data);
1068 BufferView * GuiView::view()
1070 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1074 void GuiView::autoSave()
1076 LYXERR(Debug::INFO, "Running autoSave()");
1079 view()->buffer().autoSave();
1083 void GuiView::resetAutosaveTimers()
1086 d.autosave_timeout_.restart();
1090 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1093 Buffer * buf = buffer();
1095 /* In LyX/Mac, when a dialog is open, the menus of the
1096 application can still be accessed without giving focus to
1097 the main window. In this case, we want to disable the menu
1098 entries that are buffer-related.
1100 Note that this code is not perfect, as bug 1941 attests:
1101 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1103 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1106 switch(cmd.action) {
1107 case LFUN_BUFFER_WRITE:
1108 enable = buf && (buf->isUnnamed() || !buf->isClean());
1111 case LFUN_BUFFER_WRITE_AS:
1115 case LFUN_SPLIT_VIEW:
1116 if (cmd.getArg(0) == "vertical")
1117 enable = buf && (d.splitter_->count() == 1 ||
1118 d.splitter_->orientation() == Qt::Vertical);
1120 enable = buf && (d.splitter_->count() == 1 ||
1121 d.splitter_->orientation() == Qt::Horizontal);
1124 case LFUN_CLOSE_TAB_GROUP:
1125 enable = d.currentTabWorkArea();
1128 case LFUN_TOOLBAR_TOGGLE:
1129 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1130 flag.setOnOff(t->isVisible());
1133 case LFUN_UI_TOGGLE:
1134 flag.setOnOff(isFullScreen());
1137 case LFUN_DIALOG_TOGGLE:
1138 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1139 // fall through to set "enable"
1140 case LFUN_DIALOG_SHOW: {
1141 string const name = cmd.getArg(0);
1143 enable = name == "aboutlyx"
1144 || name == "file" //FIXME: should be removed.
1146 || name == "texinfo";
1147 else if (name == "print")
1148 enable = buf->isExportable("dvi")
1149 && lyxrc.print_command != "none";
1150 else if (name == "character") {
1154 InsetCode ic = view()->cursor().inset().lyxCode();
1155 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1158 else if (name == "symbols") {
1159 if (!view() || view()->cursor().inMathed())
1162 InsetCode ic = view()->cursor().inset().lyxCode();
1163 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1166 else if (name == "latexlog")
1167 enable = FileName(buf->logName()).isReadableFile();
1168 else if (name == "spellchecker")
1169 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1170 enable = !buf->isReadonly();
1174 else if (name == "vclog")
1175 enable = buf->lyxvc().inUse();
1179 case LFUN_DIALOG_UPDATE: {
1180 string const name = cmd.getArg(0);
1182 enable = name == "prefs";
1186 case LFUN_INSET_APPLY: {
1187 string const name = cmd.getArg(0);
1188 Inset * inset = getOpenInset(name);
1190 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1192 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1193 // Every inset is supposed to handle this
1194 LASSERT(false, /**/);
1198 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1199 flag |= lyx::getStatus(fr);
1201 enable = flag.enabled();
1205 case LFUN_COMPLETION_INLINE:
1206 if (!d.current_work_area_
1207 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1211 case LFUN_COMPLETION_POPUP:
1212 if (!d.current_work_area_
1213 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1217 case LFUN_COMPLETION_COMPLETE:
1218 if (!d.current_work_area_
1219 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1223 case LFUN_COMPLETION_ACCEPT:
1224 case LFUN_COMPLETION_CANCEL:
1225 if (!d.current_work_area_
1226 || (!d.current_work_area_->completer().popupVisible()
1227 && !d.current_work_area_->completer().inlineVisible()))
1236 flag.setEnabled(false);
1242 static FileName selectTemplateFile()
1244 FileDialog dlg(qt_("Select template file"));
1245 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1246 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1248 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1249 QStringList(qt_("LyX Documents (*.lyx)")));
1251 if (result.first == FileDialog::Later)
1253 if (result.second.isEmpty())
1255 return FileName(fromqstr(result.second));
1259 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1263 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1266 message(_("Document not loaded."));
1271 setBuffer(newBuffer);
1273 // scroll to the position when the file was last closed
1274 if (lyxrc.use_lastfilepos) {
1275 LastFilePosSection::FilePos filepos =
1276 theSession().lastFilePos().load(filename);
1277 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1281 theSession().lastFiles().add(filename);
1288 void GuiView::openDocument(string const & fname)
1290 string initpath = lyxrc.document_path;
1293 string const trypath = buffer()->filePath();
1294 // If directory is writeable, use this as default.
1295 if (FileName(trypath).isDirWritable())
1301 if (fname.empty()) {
1302 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1303 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1304 dlg.setButton2(qt_("Examples|#E#e"),
1305 toqstr(addPath(package().system_support().absFilename(), "examples")));
1307 QStringList filter(qt_("LyX Documents (*.lyx)"));
1308 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1309 << qt_("LyX-1.4.x Documents (*.lyx14)")
1310 << qt_("LyX-1.5.x Documents (*.lyx15)");
1311 FileDialog::Result result =
1312 dlg.open(toqstr(initpath), filter);
1314 if (result.first == FileDialog::Later)
1317 filename = fromqstr(result.second);
1319 // check selected filename
1320 if (filename.empty()) {
1321 message(_("Canceled."));
1327 // get absolute path of file and add ".lyx" to the filename if
1329 FileName const fullname =
1330 fileSearch(string(), filename, "lyx", support::may_not_exist);
1331 if (!fullname.empty())
1332 filename = fullname.absFilename();
1334 if (!fullname.onlyPath().isDirectory()) {
1335 Alert::warning(_("Invalid filename"),
1336 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1337 from_utf8(fullname.absFilename())));
1340 // if the file doesn't exist, let the user create one
1341 if (!fullname.exists()) {
1342 // the user specifically chose this name. Believe him.
1343 Buffer * const b = newFile(filename, string(), true);
1349 docstring const disp_fn = makeDisplayPath(filename);
1350 message(bformat(_("Opening document %1$s..."), disp_fn));
1353 Buffer * buf = loadDocument(fullname);
1358 buf->errors("Parse");
1359 str2 = bformat(_("Document %1$s opened."), disp_fn);
1360 if (buf->lyxvc().inUse())
1361 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1362 " " + _("Version control detected.");
1364 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1369 // FIXME: clean that
1370 static bool import(GuiView * lv, FileName const & filename,
1371 string const & format, ErrorList & errorList)
1373 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1375 string loader_format;
1376 vector<string> loaders = theConverters().loaders();
1377 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1378 for (vector<string>::const_iterator it = loaders.begin();
1379 it != loaders.end(); ++it) {
1380 if (!theConverters().isReachable(format, *it))
1383 string const tofile =
1384 support::changeExtension(filename.absFilename(),
1385 formats.extension(*it));
1386 if (!theConverters().convert(0, filename, FileName(tofile),
1387 filename, format, *it, errorList))
1389 loader_format = *it;
1392 if (loader_format.empty()) {
1393 frontend::Alert::error(_("Couldn't import file"),
1394 bformat(_("No information for importing the format %1$s."),
1395 formats.prettyName(format)));
1399 loader_format = format;
1401 if (loader_format == "lyx") {
1402 Buffer * buf = lv->loadDocument(lyxfile);
1407 buf->errors("Parse");
1409 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1413 bool as_paragraphs = loader_format == "textparagraph";
1414 string filename2 = (loader_format == format) ? filename.absFilename()
1415 : support::changeExtension(filename.absFilename(),
1416 formats.extension(loader_format));
1417 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1418 theLyXFunc().setLyXView(lv);
1419 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1426 void GuiView::importDocument(string const & argument)
1429 string filename = split(argument, format, ' ');
1431 LYXERR(Debug::INFO, format << " file: " << filename);
1433 // need user interaction
1434 if (filename.empty()) {
1435 string initpath = lyxrc.document_path;
1437 Buffer const * buf = buffer();
1439 string const trypath = buf->filePath();
1440 // If directory is writeable, use this as default.
1441 if (FileName(trypath).isDirWritable())
1445 docstring const text = bformat(_("Select %1$s file to import"),
1446 formats.prettyName(format));
1448 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1449 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1450 dlg.setButton2(qt_("Examples|#E#e"),
1451 toqstr(addPath(package().system_support().absFilename(), "examples")));
1453 docstring filter = formats.prettyName(format);
1456 filter += from_utf8(formats.extension(format));
1459 FileDialog::Result result =
1460 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1462 if (result.first == FileDialog::Later)
1465 filename = fromqstr(result.second);
1467 // check selected filename
1468 if (filename.empty())
1469 message(_("Canceled."));
1472 if (filename.empty())
1475 // get absolute path of file
1476 FileName const fullname(support::makeAbsPath(filename));
1478 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1480 // Check if the document already is open
1481 Buffer * buf = theBufferList().getBuffer(lyxfile);
1484 if (!closeBuffer()) {
1485 message(_("Canceled."));
1490 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1492 // if the file exists already, and we didn't do
1493 // -i lyx thefile.lyx, warn
1494 if (lyxfile.exists() && fullname != lyxfile) {
1496 docstring text = bformat(_("The document %1$s already exists.\n\n"
1497 "Do you want to overwrite that document?"), displaypath);
1498 int const ret = Alert::prompt(_("Overwrite document?"),
1499 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1502 message(_("Canceled."));
1507 message(bformat(_("Importing %1$s..."), displaypath));
1508 ErrorList errorList;
1509 if (import(this, fullname, format, errorList))
1510 message(_("imported."));
1512 message(_("file not imported!"));
1514 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1518 void GuiView::newDocument(string const & filename, bool from_template)
1520 FileName initpath(lyxrc.document_path);
1521 Buffer * buf = buffer();
1523 FileName const trypath(buf->filePath());
1524 // If directory is writeable, use this as default.
1525 if (trypath.isDirWritable())
1529 string templatefile = from_template ?
1530 selectTemplateFile().absFilename() : string();
1532 if (filename.empty())
1533 b = newUnnamedFile(templatefile, initpath);
1535 b = newFile(filename, templatefile, true);
1539 // Ensure the cursor is correctly positionned on screen.
1540 view()->showCursor();
1544 void GuiView::insertLyXFile(docstring const & fname)
1546 BufferView * bv = view();
1551 FileName filename(to_utf8(fname));
1553 if (!filename.empty()) {
1554 bv->insertLyXFile(filename);
1558 // Launch a file browser
1560 string initpath = lyxrc.document_path;
1561 string const trypath = bv->buffer().filePath();
1562 // If directory is writeable, use this as default.
1563 if (FileName(trypath).isDirWritable())
1567 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1568 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1569 dlg.setButton2(qt_("Examples|#E#e"),
1570 toqstr(addPath(package().system_support().absFilename(),
1573 FileDialog::Result result = dlg.open(toqstr(initpath),
1574 QStringList(qt_("LyX Documents (*.lyx)")));
1576 if (result.first == FileDialog::Later)
1580 filename.set(fromqstr(result.second));
1582 // check selected filename
1583 if (filename.empty()) {
1584 // emit message signal.
1585 message(_("Canceled."));
1589 bv->insertLyXFile(filename);
1593 void GuiView::insertPlaintextFile(docstring const & fname,
1596 BufferView * bv = view();
1601 FileName filename(to_utf8(fname));
1603 if (!filename.empty()) {
1604 bv->insertPlaintextFile(filename, asParagraph);
1608 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1609 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1611 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1614 if (result.first == FileDialog::Later)
1618 filename.set(fromqstr(result.second));
1620 // check selected filename
1621 if (filename.empty()) {
1622 // emit message signal.
1623 message(_("Canceled."));
1627 bv->insertPlaintextFile(filename, asParagraph);
1631 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1633 FileName fname = b.fileName();
1634 FileName const oldname = fname;
1636 if (!newname.empty()) {
1638 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1640 // Switch to this Buffer.
1643 /// No argument? Ask user through dialog.
1645 FileDialog dlg(qt_("Choose a filename to save document as"),
1646 LFUN_BUFFER_WRITE_AS);
1647 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1648 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1650 if (!isLyXFilename(fname.absFilename()))
1651 fname.changeExtension(".lyx");
1653 FileDialog::Result result =
1654 dlg.save(toqstr(fname.onlyPath().absFilename()),
1655 QStringList(qt_("LyX Documents (*.lyx)")),
1656 toqstr(fname.onlyFileName()));
1658 if (result.first == FileDialog::Later)
1661 fname.set(fromqstr(result.second));
1666 if (!isLyXFilename(fname.absFilename()))
1667 fname.changeExtension(".lyx");
1670 if (FileName(fname).exists()) {
1671 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1672 docstring text = bformat(_("The document %1$s already "
1673 "exists.\n\nDo you want to "
1674 "overwrite that document?"),
1676 int const ret = Alert::prompt(_("Overwrite document?"),
1677 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1680 case 1: return renameBuffer(b, docstring());
1681 case 2: return false;
1685 // Ok, change the name of the buffer
1686 b.setFileName(fname.absFilename());
1688 bool unnamed = b.isUnnamed();
1689 b.setUnnamed(false);
1690 b.saveCheckSum(fname);
1692 if (!saveBuffer(b)) {
1693 b.setFileName(oldname.absFilename());
1694 b.setUnnamed(unnamed);
1695 b.saveCheckSum(oldname);
1703 bool GuiView::saveBuffer(Buffer & b)
1706 return renameBuffer(b, docstring());
1709 theSession().lastFiles().add(b.fileName());
1713 // Switch to this Buffer.
1716 // FIXME: we don't tell the user *WHY* the save failed !!
1717 docstring const file = makeDisplayPath(b.absFileName(), 30);
1718 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1719 "Do you want to rename the document and "
1720 "try again?"), file);
1721 int const ret = Alert::prompt(_("Rename and save?"),
1722 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1725 if (!renameBuffer(b, docstring()))
1734 return saveBuffer(b);
1738 bool GuiView::closeBuffer()
1740 Buffer * buf = buffer();
1741 return buf && closeBuffer(*buf);
1745 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1747 // goto bookmark to update bookmark pit.
1748 //FIXME: we should update only the bookmarks related to this buffer!
1749 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1750 theLyXFunc().gotoBookmark(i+1, false, false);
1752 if (buf.isClean() || buf.paragraphs().empty()) {
1753 if (buf.masterBuffer() == &buf && tolastopened)
1754 theSession().lastOpened().add(buf.fileName());
1755 theBufferList().release(&buf);
1758 // Switch to this Buffer.
1763 if (buf.isUnnamed())
1764 file = from_utf8(buf.fileName().onlyFileName());
1766 file = buf.fileName().displayName(30);
1768 // Bring this window to top before asking questions.
1772 docstring const text = bformat(_("The document %1$s has unsaved changes."
1773 "\n\nDo you want to save the document or discard the changes?"), file);
1774 int const ret = Alert::prompt(_("Save changed document?"),
1775 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1779 if (!saveBuffer(buf))
1783 // if we crash after this we could
1784 // have no autosave file but I guess
1785 // this is really improbable (Jug)
1786 removeAutosaveFile(buf.absFileName());
1792 // save file names to .lyx/session
1793 // if master/slave are both open, do not save slave since it
1794 // will be automatically loaded when the master is loaded
1795 if (buf.masterBuffer() == &buf && tolastopened)
1796 theSession().lastOpened().add(buf.fileName());
1799 // Don't close child documents.
1800 removeWorkArea(d.current_work_area_);
1802 theBufferList().release(&buf);
1808 bool GuiView::dispatch(FuncRequest const & cmd)
1810 BufferView * bv = view();
1811 // By default we won't need any update.
1813 bv->cursor().updateFlags(Update::None);
1814 bool dispatched = true;
1816 switch(cmd.action) {
1817 case LFUN_BUFFER_IMPORT:
1818 importDocument(to_utf8(cmd.argument()));
1821 case LFUN_BUFFER_SWITCH:
1822 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1825 case LFUN_BUFFER_NEXT:
1826 setBuffer(theBufferList().next(buffer()));
1829 case LFUN_BUFFER_PREVIOUS:
1830 setBuffer(theBufferList().previous(buffer()));
1833 case LFUN_COMMAND_EXECUTE: {
1834 bool const show_it = cmd.argument() != "off";
1835 // FIXME: this is a hack, "minibuffer" should not be
1837 if (GuiToolbar * t = toolbar("minibuffer")) {
1838 t->setVisible(show_it);
1839 if (show_it && t->commandBuffer())
1840 t->commandBuffer()->setFocus();
1844 case LFUN_DROP_LAYOUTS_CHOICE:
1846 d.layout_->showPopup();
1849 case LFUN_MENU_OPEN:
1850 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1851 menu->exec(QCursor::pos());
1854 case LFUN_FILE_INSERT:
1855 insertLyXFile(cmd.argument());
1857 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1858 insertPlaintextFile(cmd.argument(), true);
1861 case LFUN_FILE_INSERT_PLAINTEXT:
1862 insertPlaintextFile(cmd.argument(), false);
1865 case LFUN_BUFFER_WRITE:
1867 saveBuffer(bv->buffer());
1870 case LFUN_BUFFER_WRITE_AS:
1872 renameBuffer(bv->buffer(), cmd.argument());
1875 case LFUN_BUFFER_WRITE_ALL: {
1876 Buffer * first = theBufferList().first();
1879 message(_("Saving all documents..."));
1880 // We cannot use a for loop as the buffer list cycles.
1883 if (!b->isClean()) {
1885 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1887 b = theBufferList().next(b);
1888 } while (b != first);
1889 message(_("All documents saved."));
1893 case LFUN_TOOLBAR_TOGGLE: {
1894 string const name = cmd.getArg(0);
1895 if (GuiToolbar * t = toolbar(name))
1900 case LFUN_DIALOG_UPDATE: {
1901 string const name = to_utf8(cmd.argument());
1902 // Can only update a dialog connected to an existing inset
1903 Inset * inset = getOpenInset(name);
1905 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1906 inset->dispatch(view()->cursor(), fr);
1907 } else if (name == "paragraph") {
1908 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1909 } else if (name == "prefs") {
1910 updateDialog(name, string());
1915 case LFUN_DIALOG_TOGGLE: {
1916 if (isDialogVisible(cmd.getArg(0)))
1917 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1919 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1923 case LFUN_DIALOG_DISCONNECT_INSET:
1924 disconnectDialog(to_utf8(cmd.argument()));
1927 case LFUN_DIALOG_HIDE: {
1928 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1932 case LFUN_DIALOG_SHOW: {
1933 string const name = cmd.getArg(0);
1934 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1936 if (name == "character") {
1937 data = freefont2string();
1939 showDialog("character", data);
1940 } else if (name == "latexlog") {
1941 Buffer::LogType type;
1942 string const logfile = buffer()->logName(&type);
1944 case Buffer::latexlog:
1947 case Buffer::buildlog:
1951 data += Lexer::quoteString(logfile);
1952 showDialog("log", data);
1953 } else if (name == "vclog") {
1954 string const data = "vc " +
1955 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1956 showDialog("log", data);
1957 } else if (name == "symbols") {
1958 data = bv->cursor().getEncoding()->name();
1960 showDialog("symbols", data);
1962 showDialog(name, data);
1966 case LFUN_INSET_APPLY: {
1967 string const name = cmd.getArg(0);
1968 Inset * inset = getOpenInset(name);
1970 // put cursor in front of inset.
1971 if (!view()->setCursorFromInset(inset))
1972 LASSERT(false, /**/);
1974 view()->cursor().recordUndo();
1975 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1976 inset->dispatch(view()->cursor(), fr);
1978 view()->cursor().recordUndo();
1979 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1985 case LFUN_UI_TOGGLE:
1987 // Make sure the keyboard focus stays in the work area.
1991 case LFUN_SPLIT_VIEW:
1992 if (Buffer * buf = buffer()) {
1993 string const orientation = cmd.getArg(0);
1994 d.splitter_->setOrientation(orientation == "vertical"
1995 ? Qt::Vertical : Qt::Horizontal);
1996 TabWorkArea * twa = addTabWorkArea();
1997 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1998 setCurrentWorkArea(wa);
2002 case LFUN_CLOSE_TAB_GROUP:
2003 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2005 twa = d.currentTabWorkArea();
2006 // Switch to the next GuiWorkArea in the found TabWorkArea.
2008 d.current_work_area_ = twa->currentWorkArea();
2009 // Make sure the work area is up to date.
2010 twa->setCurrentWorkArea(d.current_work_area_);
2012 d.current_work_area_ = 0;
2014 if (d.splitter_->count() == 0)
2015 // No more work area, switch to the background widget.
2020 case LFUN_COMPLETION_INLINE:
2021 if (d.current_work_area_)
2022 d.current_work_area_->completer().showInline();
2025 case LFUN_COMPLETION_POPUP:
2026 if (d.current_work_area_)
2027 d.current_work_area_->completer().showPopup();
2031 case LFUN_COMPLETION_COMPLETE:
2032 if (d.current_work_area_)
2033 d.current_work_area_->completer().tab();
2036 case LFUN_COMPLETION_CANCEL:
2037 if (d.current_work_area_) {
2038 if (d.current_work_area_->completer().popupVisible())
2039 d.current_work_area_->completer().hidePopup();
2041 d.current_work_area_->completer().hideInline();
2045 case LFUN_COMPLETION_ACCEPT:
2046 if (d.current_work_area_)
2047 d.current_work_area_->completer().activate();
2056 // Part of automatic menu appearance feature.
2057 if (isFullScreen()) {
2058 if (menuBar()->isVisible())
2060 if (statusBar()->isVisible())
2061 statusBar()->hide();
2068 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2070 string const arg = cmd.getArg(0);
2071 if (arg == "scrollbar") {
2072 // hide() is of no help
2073 if (d.current_work_area_->verticalScrollBarPolicy() ==
2074 Qt::ScrollBarAlwaysOff)
2076 d.current_work_area_->setVerticalScrollBarPolicy(
2077 Qt::ScrollBarAsNeeded);
2079 d.current_work_area_->setVerticalScrollBarPolicy(
2080 Qt::ScrollBarAlwaysOff);
2083 if (arg == "statusbar") {
2084 statusBar()->setVisible(!statusBar()->isVisible());
2087 if (arg == "menubar") {
2088 menuBar()->setVisible(!menuBar()->isVisible());
2091 #if QT_VERSION >= 0x040300
2092 if (arg == "frame") {
2094 getContentsMargins(&l, &t, &r, &b);
2095 //are the frames in default state?
2096 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2098 setContentsMargins(-2, -2, -2, -2);
2100 setContentsMargins(0, 0, 0, 0);
2105 if (arg == "fullscreen") {
2110 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2114 void GuiView::toggleFullScreen()
2116 if (isFullScreen()) {
2117 for (int i = 0; i != d.splitter_->count(); ++i)
2118 d.tabWorkArea(i)->setFullScreen(false);
2119 #if QT_VERSION >= 0x040300
2120 setContentsMargins(0, 0, 0, 0);
2122 setWindowState(windowState() ^ Qt::WindowFullScreen);
2125 statusBar()->show();
2127 for (int i = 0; i != d.splitter_->count(); ++i)
2128 d.tabWorkArea(i)->setFullScreen(true);
2129 #if QT_VERSION >= 0x040300
2130 setContentsMargins(-2, -2, -2, -2);
2133 setWindowState(windowState() ^ Qt::WindowFullScreen);
2134 statusBar()->hide();
2136 if (lyxrc.full_screen_toolbars) {
2137 ToolbarMap::iterator end = d.toolbars_.end();
2138 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2145 Buffer const * GuiView::updateInset(Inset const * inset)
2147 if (!d.current_work_area_)
2151 d.current_work_area_->scheduleRedraw();
2153 return &d.current_work_area_->bufferView().buffer();
2157 void GuiView::restartCursor()
2159 /* When we move around, or type, it's nice to be able to see
2160 * the cursor immediately after the keypress.
2162 if (d.current_work_area_)
2163 d.current_work_area_->startBlinkingCursor();
2165 // Take this occasion to update the other GUI elements.
2171 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2173 if (d.current_work_area_)
2174 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2179 // This list should be kept in sync with the list of insets in
2180 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2181 // dialog should have the same name as the inset.
2182 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2183 // docs in LyXAction.cpp.
2185 char const * const dialognames[] = {
2186 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2187 "citation", "document", "errorlist", "ert", "external", "file",
2188 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2189 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2190 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2192 #ifdef HAVE_LIBAIKSAURUS
2196 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2198 char const * const * const end_dialognames =
2199 dialognames + (sizeof(dialognames) / sizeof(char *));
2203 cmpCStr(char const * name) : name_(name) {}
2204 bool operator()(char const * other) {
2205 return strcmp(other, name_) == 0;
2212 bool isValidName(string const & name)
2214 return find_if(dialognames, end_dialognames,
2215 cmpCStr(name.c_str())) != end_dialognames;
2221 void GuiView::resetDialogs()
2223 // Make sure that no LFUN uses any LyXView.
2224 theLyXFunc().setLyXView(0);
2227 constructToolbars();
2228 guiApp->menus().fillMenuBar(menuBar(), this, true);
2230 d.layout_->updateContents(true);
2231 // Now update controls with current buffer.
2232 theLyXFunc().setLyXView(this);
2238 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2240 if (!isValidName(name))
2243 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2245 if (it != d.dialogs_.end())
2246 return it->second.get();
2248 Dialog * dialog = build(name);
2249 d.dialogs_[name].reset(dialog);
2250 if (lyxrc.allow_geometry_session)
2251 dialog->restoreSession();
2258 void GuiView::showDialog(string const & name, string const & data,
2266 Dialog * dialog = findOrBuild(name, false);
2268 dialog->showData(data);
2270 d.open_insets_[name] = inset;
2273 catch (ExceptionMessage const & ex) {
2281 bool GuiView::isDialogVisible(string const & name) const
2283 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2284 if (it == d.dialogs_.end())
2286 return it->second.get()->isVisibleView();
2290 void GuiView::hideDialog(string const & name, Inset * inset)
2292 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2293 if (it == d.dialogs_.end())
2296 if (inset && inset != getOpenInset(name))
2299 Dialog * const dialog = it->second.get();
2300 if (dialog->isVisibleView())
2302 d.open_insets_[name] = 0;
2306 void GuiView::disconnectDialog(string const & name)
2308 if (!isValidName(name))
2311 if (d.open_insets_.find(name) != d.open_insets_.end())
2312 d.open_insets_[name] = 0;
2316 Inset * GuiView::getOpenInset(string const & name) const
2318 if (!isValidName(name))
2321 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2322 return it == d.open_insets_.end() ? 0 : it->second;
2326 void GuiView::hideAll() const
2328 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2329 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2331 for(; it != end; ++it)
2332 it->second->hideView();
2336 void GuiView::updateDialogs()
2338 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2339 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2341 for(; it != end; ++it) {
2342 Dialog * dialog = it->second.get();
2343 if (dialog && dialog->isVisibleView())
2344 dialog->checkStatus();
2351 // will be replaced by a proper factory...
2352 Dialog * createGuiAbout(GuiView & lv);
2353 Dialog * createGuiBibitem(GuiView & lv);
2354 Dialog * createGuiBibtex(GuiView & lv);
2355 Dialog * createGuiBox(GuiView & lv);
2356 Dialog * createGuiBranch(GuiView & lv);
2357 Dialog * createGuiChanges(GuiView & lv);
2358 Dialog * createGuiCharacter(GuiView & lv);
2359 Dialog * createGuiCitation(GuiView & lv);
2360 Dialog * createGuiDelimiter(GuiView & lv);
2361 Dialog * createGuiDocument(GuiView & lv);
2362 Dialog * createGuiErrorList(GuiView & lv);
2363 Dialog * createGuiERT(GuiView & lv);
2364 Dialog * createGuiExternal(GuiView & lv);
2365 Dialog * createGuiFloat(GuiView & lv);
2366 Dialog * createGuiGraphics(GuiView & lv);
2367 Dialog * createGuiHSpace(GuiView & lv);
2368 Dialog * createGuiInclude(GuiView & lv);
2369 Dialog * createGuiInfo(GuiView & lv);
2370 Dialog * createGuiLabel(GuiView & lv);
2371 Dialog * createGuiListings(GuiView & lv);
2372 Dialog * createGuiLog(GuiView & lv);
2373 Dialog * createGuiMathMatrix(GuiView & lv);
2374 Dialog * createGuiNomenclature(GuiView & lv);
2375 Dialog * createGuiNote(GuiView & lv);
2376 Dialog * createGuiParagraph(GuiView & lv);
2377 Dialog * createGuiPreferences(GuiView & lv);
2378 Dialog * createGuiPrint(GuiView & lv);
2379 Dialog * createGuiRef(GuiView & lv);
2380 Dialog * createGuiSearch(GuiView & lv);
2381 Dialog * createGuiSendTo(GuiView & lv);
2382 Dialog * createGuiShowFile(GuiView & lv);
2383 Dialog * createGuiSpellchecker(GuiView & lv);
2384 Dialog * createGuiSymbols(GuiView & lv);
2385 Dialog * createGuiTabularCreate(GuiView & lv);
2386 Dialog * createGuiTabular(GuiView & lv);
2387 Dialog * createGuiTexInfo(GuiView & lv);
2388 Dialog * createGuiToc(GuiView & lv);
2389 Dialog * createGuiThesaurus(GuiView & lv);
2390 Dialog * createGuiHyperlink(GuiView & lv);
2391 Dialog * createGuiVSpace(GuiView & lv);
2392 Dialog * createGuiViewSource(GuiView & lv);
2393 Dialog * createGuiWrap(GuiView & lv);
2396 Dialog * GuiView::build(string const & name)
2398 LASSERT(isValidName(name), /**/);
2400 if (name == "aboutlyx")
2401 return createGuiAbout(*this);
2402 if (name == "bibitem")
2403 return createGuiBibitem(*this);
2404 if (name == "bibtex")
2405 return createGuiBibtex(*this);
2407 return createGuiBox(*this);
2408 if (name == "branch")
2409 return createGuiBranch(*this);
2410 if (name == "changes")
2411 return createGuiChanges(*this);
2412 if (name == "character")
2413 return createGuiCharacter(*this);
2414 if (name == "citation")
2415 return createGuiCitation(*this);
2416 if (name == "document")
2417 return createGuiDocument(*this);
2418 if (name == "errorlist")
2419 return createGuiErrorList(*this);
2421 return createGuiERT(*this);
2422 if (name == "external")
2423 return createGuiExternal(*this);
2425 return createGuiShowFile(*this);
2426 if (name == "findreplace")
2427 return createGuiSearch(*this);
2428 if (name == "float")
2429 return createGuiFloat(*this);
2430 if (name == "graphics")
2431 return createGuiGraphics(*this);
2432 if (name == "include")
2433 return createGuiInclude(*this);
2435 return createGuiInfo(*this);
2436 if (name == "nomenclature")
2437 return createGuiNomenclature(*this);
2438 if (name == "label")
2439 return createGuiLabel(*this);
2441 return createGuiLog(*this);
2442 if (name == "view-source")
2443 return createGuiViewSource(*this);
2444 if (name == "mathdelimiter")
2445 return createGuiDelimiter(*this);
2446 if (name == "mathmatrix")
2447 return createGuiMathMatrix(*this);
2449 return createGuiNote(*this);
2450 if (name == "paragraph")
2451 return createGuiParagraph(*this);
2452 if (name == "prefs")
2453 return createGuiPreferences(*this);
2454 if (name == "print")
2455 return createGuiPrint(*this);
2457 return createGuiRef(*this);
2458 if (name == "sendto")
2459 return createGuiSendTo(*this);
2460 if (name == "space")
2461 return createGuiHSpace(*this);
2462 if (name == "spellchecker")
2463 return createGuiSpellchecker(*this);
2464 if (name == "symbols")
2465 return createGuiSymbols(*this);
2466 if (name == "tabular")
2467 return createGuiTabular(*this);
2468 if (name == "tabularcreate")
2469 return createGuiTabularCreate(*this);
2470 if (name == "texinfo")
2471 return createGuiTexInfo(*this);
2472 #ifdef HAVE_LIBAIKSAURUS
2473 if (name == "thesaurus")
2474 return createGuiThesaurus(*this);
2477 return createGuiToc(*this);
2479 return createGuiHyperlink(*this);
2480 if (name == "vspace")
2481 return createGuiVSpace(*this);
2483 return createGuiWrap(*this);
2484 if (name == "listings")
2485 return createGuiListings(*this);
2491 } // namespace frontend
2494 #include "GuiView_moc.cpp"