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 settings.beginGroup("views");
351 settings.beginGroup(QString::number(id_));
353 settings.setValue("pos", pos());
354 settings.setValue("size", size());
356 settings.setValue("geometry", saveGeometry());
358 settings.setValue("layout", saveState(0));
359 settings.setValue("icon_size", iconSize());
363 bool GuiView::restoreLayout()
366 settings.beginGroup("views");
367 settings.beginGroup(QString::number(id_));
368 QString const icon_key = "icon_size";
369 if (!settings.contains(icon_key))
372 setIconSize(settings.value(icon_key).toSize());
374 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
375 QSize size = settings.value("size", QSize(690, 510)).toSize();
379 if (!restoreGeometry(settings.value("geometry").toByteArray()))
380 setGeometry(50, 50, 690, 510);
382 // Make sure layout is correctly oriented.
383 setLayoutDirection(qApp->layoutDirection());
385 // Allow the toc and view-source dock widget to be restored if needed.
387 if ((tmp = findOrBuild("toc", true)))
389 if ((tmp = findOrBuild("view-source", true)))
392 if (!restoreState(settings.value("layout").toByteArray(), 0))
399 GuiToolbar * GuiView::toolbar(string const & name)
401 ToolbarMap::iterator it = d.toolbars_.find(name);
402 if (it != d.toolbars_.end())
405 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
406 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
411 void GuiView::constructToolbars()
413 ToolbarMap::iterator it = d.toolbars_.begin();
414 for (; it != d.toolbars_.end(); ++it)
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 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
427 void GuiView::initToolbars()
429 // extracts the toolbars from the backend
430 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
431 Toolbars::Infos::iterator end = guiApp->toolbars().end();
432 for (; cit != end; ++cit) {
433 GuiToolbar * tb = toolbar(cit->name);
436 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
438 tb->setVisible(false);
439 tb->setVisibility(visibility);
441 if (visibility & Toolbars::TOP) {
443 addToolBarBreak(Qt::TopToolBarArea);
444 addToolBar(Qt::TopToolBarArea, tb);
447 if (visibility & Toolbars::BOTTOM) {
448 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
449 #if (QT_VERSION >= 0x040202)
450 addToolBarBreak(Qt::BottomToolBarArea);
452 addToolBar(Qt::BottomToolBarArea, tb);
455 if (visibility & Toolbars::LEFT) {
456 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
457 #if (QT_VERSION >= 0x040202)
458 addToolBarBreak(Qt::LeftToolBarArea);
460 addToolBar(Qt::LeftToolBarArea, tb);
463 if (visibility & Toolbars::RIGHT) {
464 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
465 #if (QT_VERSION >= 0x040202)
466 addToolBarBreak(Qt::RightToolBarArea);
468 addToolBar(Qt::RightToolBarArea, tb);
471 if (visibility & Toolbars::ON)
472 tb->setVisible(true);
477 TocModels & GuiView::tocModels()
479 return d.toc_models_;
483 void GuiView::setFocus()
485 // Make sure LyXFunc points to the correct view.
486 theLyXFunc().setLyXView(this);
487 if (d.current_work_area_)
488 d.current_work_area_->setFocus();
494 QMenu * GuiView::createPopupMenu()
496 return d.toolBarPopup(this);
500 void GuiView::showEvent(QShowEvent * e)
502 LYXERR(Debug::GUI, "Passed Geometry "
503 << size().height() << "x" << size().width()
504 << "+" << pos().x() << "+" << pos().y());
506 if (d.splitter_->count() == 0)
507 // No work area, switch to the background widget.
510 QMainWindow::showEvent(e);
514 void GuiView::closeEvent(QCloseEvent * close_event)
518 // it can happen that this event arrives without selecting the view,
519 // e.g. when clicking the close button on a background window.
520 theLyXFunc().setLyXView(this);
522 while (Buffer * b = buffer()) {
524 // This is a child document, just close the tab after saving
525 // but keep the file loaded.
526 if (!saveBuffer(*b)) {
528 close_event->ignore();
531 removeWorkArea(d.current_work_area_);
535 QList<int> const ids = guiApp->viewIds();
536 for (int i = 0; i != ids.size(); ++i) {
539 if (guiApp->view(ids[i]).workArea(*b)) {
540 // FIXME 1: should we put an alert box here that the buffer
541 // is viewed elsewhere?
542 // FIXME 2: should we try to save this buffer in any case?
545 // This buffer is also opened in another view, so
546 // close the associated work area...
547 removeWorkArea(d.current_work_area_);
548 // ... but don't close the buffer.
553 if (b && !closeBuffer(*b, true)) {
555 close_event->ignore();
560 // Make sure that nothing will use this close to be closed View.
561 guiApp->unregisterView(this);
563 if (isFullScreen()) {
564 // Switch off fullscreen before closing.
569 // Make sure the timer time out will not trigger a statusbar update.
570 d.statusbar_timer_.stop();
572 // Saving fullscreen requires additional tweaks in the toolbar code.
573 // It wouldn't also work under linux natively.
574 if (lyxrc.allow_geometry_session) {
575 // Save this window geometry and layout.
577 // Then the toolbar private states.
578 ToolbarMap::iterator end = d.toolbars_.end();
579 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
580 it->second->saveSession();
581 // Now take care of all other dialogs:
582 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
583 for (; it!= d.dialogs_.end(); ++it)
584 it->second->saveSession();
587 close_event->accept();
591 void GuiView::dragEnterEvent(QDragEnterEvent * event)
593 if (event->mimeData()->hasUrls())
595 /// \todo Ask lyx-devel is this is enough:
596 /// if (event->mimeData()->hasFormat("text/plain"))
597 /// event->acceptProposedAction();
601 void GuiView::dropEvent(QDropEvent* event)
603 QList<QUrl> files = event->mimeData()->urls();
607 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
608 for (int i = 0; i != files.size(); ++i) {
609 string const file = os::internal_path(fromqstr(
610 files.at(i).toLocalFile()));
612 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
617 void GuiView::message(docstring const & str)
619 if (ForkedProcess::iAmAChild())
622 statusBar()->showMessage(toqstr(str));
623 d.statusbar_timer_.stop();
624 d.statusbar_timer_.start(3000);
628 void GuiView::smallSizedIcons()
630 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
634 void GuiView::normalSizedIcons()
636 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
640 void GuiView::bigSizedIcons()
642 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
646 void GuiView::clearMessage()
650 theLyXFunc().setLyXView(this);
651 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
652 d.statusbar_timer_.stop();
656 void GuiView::updateWindowTitle(GuiWorkArea * wa)
658 if (wa != d.current_work_area_)
660 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
661 setWindowIconText(wa->windowIconText());
665 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
668 disconnectBufferView();
669 connectBufferView(wa->bufferView());
670 connectBuffer(wa->bufferView().buffer());
671 d.current_work_area_ = wa;
672 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
673 this, SLOT(updateWindowTitle(GuiWorkArea *)));
674 updateWindowTitle(wa);
678 // The document settings needs to be reinitialised.
679 updateDialog("document", "");
681 // Buffer-dependent dialogs must be updated. This is done here because
682 // some dialogs require buffer()->text.
687 void GuiView::on_lastWorkAreaRemoved()
690 // We already are in a close event. Nothing more to do.
693 if (d.splitter_->count() > 1)
694 // We have a splitter so don't close anything.
697 // Reset and updates the dialogs.
698 d.toc_models_.reset(0);
699 updateDialog("document", "");
702 resetWindowTitleAndIconText();
704 if (lyxrc.open_buffers_in_tabs)
705 // Nothing more to do, the window should stay open.
708 if (guiApp->viewIds().size() > 1) {
714 // On Mac we also close the last window because the application stay
715 // resident in memory. On other platforms we don't close the last
716 // window because this would quit the application.
722 void GuiView::updateStatusBar()
724 // let the user see the explicit message
725 if (d.statusbar_timer_.isActive())
728 theLyXFunc().setLyXView(this);
729 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
733 bool GuiView::hasFocus() const
735 return qApp->activeWindow() == this;
739 bool GuiView::event(QEvent * e)
743 // Useful debug code:
744 //case QEvent::ActivationChange:
745 //case QEvent::WindowDeactivate:
746 //case QEvent::Paint:
747 //case QEvent::Enter:
748 //case QEvent::Leave:
749 //case QEvent::HoverEnter:
750 //case QEvent::HoverLeave:
751 //case QEvent::HoverMove:
752 //case QEvent::StatusTip:
753 //case QEvent::DragEnter:
754 //case QEvent::DragLeave:
758 case QEvent::WindowActivate: {
759 if (this == guiApp->currentView()) {
761 return QMainWindow::event(e);
763 guiApp->setCurrentView(this);
764 theLyXFunc().setLyXView(this);
765 if (d.current_work_area_) {
766 BufferView & bv = d.current_work_area_->bufferView();
767 connectBufferView(bv);
768 connectBuffer(bv.buffer());
769 // The document structure, name and dialogs might have
770 // changed in another view.
772 // The document settings needs to be reinitialised.
773 updateDialog("document", "");
776 resetWindowTitleAndIconText();
779 return QMainWindow::event(e);
782 case QEvent::ShortcutOverride: {
786 if (isFullScreen() && menuBar()->isHidden()) {
787 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
788 // FIXME: we should also try to detect special LyX shortcut such as
789 // Alt-P and Alt-M. Right now there is a hack in
790 // GuiWorkArea::processKeySym() that hides again the menubar for
792 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
794 return QMainWindow::event(e);
798 if (d.current_work_area_)
799 // Nothing special to do.
800 return QMainWindow::event(e);
802 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
803 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
805 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
806 || ke->key() == Qt::Key_Backtab)
807 return QMainWindow::event(e);
809 // Allow processing of shortcuts that are allowed even when no Buffer
811 theLyXFunc().setLyXView(this);
813 setKeySymbol(&sym, ke);
814 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
820 return QMainWindow::event(e);
824 void GuiView::resetWindowTitleAndIconText()
826 setWindowTitle(qt_("LyX"));
827 setWindowIconText(qt_("LyX"));
830 bool GuiView::focusNextPrevChild(bool /*next*/)
837 void GuiView::setBusy(bool busy)
839 if (d.current_work_area_) {
840 d.current_work_area_->setUpdatesEnabled(!busy);
842 d.current_work_area_->stopBlinkingCursor();
844 d.current_work_area_->startBlinkingCursor();
848 QApplication::setOverrideCursor(Qt::WaitCursor);
850 QApplication::restoreOverrideCursor();
854 GuiWorkArea * GuiView::workArea(Buffer & buffer)
856 if (TabWorkArea * twa = d.currentTabWorkArea())
857 return twa->workArea(buffer);
862 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
864 // Automatically create a TabWorkArea if there are none yet.
865 TabWorkArea * tab_widget = d.splitter_->count()
866 ? d.currentTabWorkArea() : addTabWorkArea();
867 return tab_widget->addWorkArea(buffer, *this);
871 TabWorkArea * GuiView::addTabWorkArea()
873 TabWorkArea * twa = new TabWorkArea;
874 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
875 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
876 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
877 this, SLOT(on_lastWorkAreaRemoved()));
879 d.splitter_->addWidget(twa);
880 d.stack_widget_->setCurrentWidget(d.splitter_);
885 GuiWorkArea const * GuiView::currentWorkArea() const
887 return d.current_work_area_;
891 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
894 d.current_work_area_ = wa;
895 for (int i = 0; i != d.splitter_->count(); ++i) {
896 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
902 void GuiView::removeWorkArea(GuiWorkArea * wa)
905 if (wa == d.current_work_area_) {
907 disconnectBufferView();
908 d.current_work_area_ = 0;
911 for (int i = 0; i != d.splitter_->count(); ++i) {
912 TabWorkArea * twa = d.tabWorkArea(i);
913 if (!twa->removeWorkArea(wa))
914 // Not found in this tab group.
917 // We found and removed the GuiWorkArea.
919 // No more WorkAreas in this tab group, so delete it.
924 if (d.current_work_area_)
925 // This means that we are not closing the current GuiWorkArea;
928 // Switch to the next GuiWorkArea in the found TabWorkArea.
929 d.current_work_area_ = twa->currentWorkArea();
933 if (d.splitter_->count() == 0)
934 // No more work area, switch to the background widget.
939 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
945 void GuiView::updateLayoutList()
948 d.layout_->updateContents(false);
952 void GuiView::updateToolbars()
954 ToolbarMap::iterator end = d.toolbars_.end();
955 if (d.current_work_area_) {
957 d.current_work_area_->bufferView().cursor().inMathed();
959 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
961 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
962 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
963 bool const mathmacrotemplate =
964 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
966 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
967 it->second->update(math, table, review, mathmacrotemplate);
969 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
970 it->second->update(false, false, false, false);
974 Buffer * GuiView::buffer()
976 if (d.current_work_area_)
977 return &d.current_work_area_->bufferView().buffer();
982 Buffer const * GuiView::buffer() const
984 if (d.current_work_area_)
985 return &d.current_work_area_->bufferView().buffer();
990 void GuiView::setBuffer(Buffer * newBuffer)
992 LASSERT(newBuffer, /**/);
995 GuiWorkArea * wa = workArea(*newBuffer);
997 updateLabels(*newBuffer->masterBuffer());
998 wa = addWorkArea(*newBuffer);
1000 //Disconnect the old buffer...there's no new one.
1003 connectBuffer(*newBuffer);
1004 connectBufferView(wa->bufferView());
1005 setCurrentWorkArea(wa);
1011 void GuiView::connectBuffer(Buffer & buf)
1013 buf.setGuiDelegate(this);
1017 void GuiView::disconnectBuffer()
1019 if (d.current_work_area_)
1020 d.current_work_area_->bufferView().setGuiDelegate(0);
1024 void GuiView::connectBufferView(BufferView & bv)
1026 bv.setGuiDelegate(this);
1030 void GuiView::disconnectBufferView()
1032 if (d.current_work_area_)
1033 d.current_work_area_->bufferView().setGuiDelegate(0);
1037 void GuiView::errors(string const & error_type)
1039 ErrorList & el = buffer()->errorList(error_type);
1041 showDialog("errorlist", error_type);
1045 void GuiView::structureChanged()
1047 d.toc_models_.reset(view());
1048 // Navigator needs more than a simple update in this case. It needs to be
1050 updateDialog("toc", "");
1054 void GuiView::updateDialog(string const & name, string const & data)
1056 if (!isDialogVisible(name))
1059 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1060 if (it == d.dialogs_.end())
1063 Dialog * const dialog = it->second.get();
1064 if (dialog->isVisibleView())
1065 dialog->initialiseParams(data);
1069 BufferView * GuiView::view()
1071 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1075 void GuiView::autoSave()
1077 LYXERR(Debug::INFO, "Running autoSave()");
1080 view()->buffer().autoSave();
1084 void GuiView::resetAutosaveTimers()
1087 d.autosave_timeout_.restart();
1091 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1094 Buffer * buf = buffer();
1096 /* In LyX/Mac, when a dialog is open, the menus of the
1097 application can still be accessed without giving focus to
1098 the main window. In this case, we want to disable the menu
1099 entries that are buffer-related.
1101 Note that this code is not perfect, as bug 1941 attests:
1102 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1104 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1107 switch(cmd.action) {
1108 case LFUN_BUFFER_WRITE:
1109 enable = buf && (buf->isUnnamed() || !buf->isClean());
1112 case LFUN_BUFFER_WRITE_AS:
1116 case LFUN_SPLIT_VIEW:
1117 if (cmd.getArg(0) == "vertical")
1118 enable = buf && (d.splitter_->count() == 1 ||
1119 d.splitter_->orientation() == Qt::Vertical);
1121 enable = buf && (d.splitter_->count() == 1 ||
1122 d.splitter_->orientation() == Qt::Horizontal);
1125 case LFUN_CLOSE_TAB_GROUP:
1126 enable = d.currentTabWorkArea();
1129 case LFUN_TOOLBAR_TOGGLE:
1130 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1131 flag.setOnOff(t->isVisible());
1134 case LFUN_UI_TOGGLE:
1135 flag.setOnOff(isFullScreen());
1138 case LFUN_DIALOG_TOGGLE:
1139 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1140 // fall through to set "enable"
1141 case LFUN_DIALOG_SHOW: {
1142 string const name = cmd.getArg(0);
1144 enable = name == "aboutlyx"
1145 || name == "file" //FIXME: should be removed.
1147 || name == "texinfo";
1148 else if (name == "print")
1149 enable = buf->isExportable("dvi")
1150 && lyxrc.print_command != "none";
1151 else if (name == "character") {
1155 InsetCode ic = view()->cursor().inset().lyxCode();
1156 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1159 else if (name == "symbols") {
1160 if (!view() || view()->cursor().inMathed())
1163 InsetCode ic = view()->cursor().inset().lyxCode();
1164 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1167 else if (name == "latexlog")
1168 enable = FileName(buf->logName()).isReadableFile();
1169 else if (name == "spellchecker")
1170 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1171 enable = !buf->isReadonly();
1175 else if (name == "vclog")
1176 enable = buf->lyxvc().inUse();
1180 case LFUN_DIALOG_UPDATE: {
1181 string const name = cmd.getArg(0);
1183 enable = name == "prefs";
1187 case LFUN_INSET_APPLY: {
1188 string const name = cmd.getArg(0);
1189 Inset * inset = getOpenInset(name);
1191 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1193 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1194 // Every inset is supposed to handle this
1195 LASSERT(false, /**/);
1199 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1200 flag |= lyx::getStatus(fr);
1202 enable = flag.enabled();
1206 case LFUN_COMPLETION_INLINE:
1207 if (!d.current_work_area_
1208 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1212 case LFUN_COMPLETION_POPUP:
1213 if (!d.current_work_area_
1214 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1218 case LFUN_COMPLETION_COMPLETE:
1219 if (!d.current_work_area_
1220 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1224 case LFUN_COMPLETION_ACCEPT:
1225 case LFUN_COMPLETION_CANCEL:
1226 if (!d.current_work_area_
1227 || (!d.current_work_area_->completer().popupVisible()
1228 && !d.current_work_area_->completer().inlineVisible()))
1237 flag.setEnabled(false);
1243 static FileName selectTemplateFile()
1245 FileDialog dlg(qt_("Select template file"));
1246 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1247 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1249 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1250 QStringList(qt_("LyX Documents (*.lyx)")));
1252 if (result.first == FileDialog::Later)
1254 if (result.second.isEmpty())
1256 return FileName(fromqstr(result.second));
1260 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1264 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1267 message(_("Document not loaded."));
1272 setBuffer(newBuffer);
1274 // scroll to the position when the file was last closed
1275 if (lyxrc.use_lastfilepos) {
1276 LastFilePosSection::FilePos filepos =
1277 theSession().lastFilePos().load(filename);
1278 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1282 theSession().lastFiles().add(filename);
1289 void GuiView::openDocument(string const & fname)
1291 string initpath = lyxrc.document_path;
1294 string const trypath = buffer()->filePath();
1295 // If directory is writeable, use this as default.
1296 if (FileName(trypath).isDirWritable())
1302 if (fname.empty()) {
1303 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1304 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1305 dlg.setButton2(qt_("Examples|#E#e"),
1306 toqstr(addPath(package().system_support().absFilename(), "examples")));
1308 QStringList filter(qt_("LyX Documents (*.lyx)"));
1309 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1310 << qt_("LyX-1.4.x Documents (*.lyx14)")
1311 << qt_("LyX-1.5.x Documents (*.lyx15)");
1312 FileDialog::Result result =
1313 dlg.open(toqstr(initpath), filter);
1315 if (result.first == FileDialog::Later)
1318 filename = fromqstr(result.second);
1320 // check selected filename
1321 if (filename.empty()) {
1322 message(_("Canceled."));
1328 // get absolute path of file and add ".lyx" to the filename if
1330 FileName const fullname =
1331 fileSearch(string(), filename, "lyx", support::may_not_exist);
1332 if (!fullname.empty())
1333 filename = fullname.absFilename();
1335 if (!fullname.onlyPath().isDirectory()) {
1336 Alert::warning(_("Invalid filename"),
1337 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1338 from_utf8(fullname.absFilename())));
1341 // if the file doesn't exist, let the user create one
1342 if (!fullname.exists()) {
1343 // the user specifically chose this name. Believe him.
1344 Buffer * const b = newFile(filename, string(), true);
1350 docstring const disp_fn = makeDisplayPath(filename);
1351 message(bformat(_("Opening document %1$s..."), disp_fn));
1354 Buffer * buf = loadDocument(fullname);
1359 buf->errors("Parse");
1360 str2 = bformat(_("Document %1$s opened."), disp_fn);
1361 if (buf->lyxvc().inUse())
1362 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1363 " " + _("Version control detected.");
1365 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1370 // FIXME: clean that
1371 static bool import(GuiView * lv, FileName const & filename,
1372 string const & format, ErrorList & errorList)
1374 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1376 string loader_format;
1377 vector<string> loaders = theConverters().loaders();
1378 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1379 for (vector<string>::const_iterator it = loaders.begin();
1380 it != loaders.end(); ++it) {
1381 if (!theConverters().isReachable(format, *it))
1384 string const tofile =
1385 support::changeExtension(filename.absFilename(),
1386 formats.extension(*it));
1387 if (!theConverters().convert(0, filename, FileName(tofile),
1388 filename, format, *it, errorList))
1390 loader_format = *it;
1393 if (loader_format.empty()) {
1394 frontend::Alert::error(_("Couldn't import file"),
1395 bformat(_("No information for importing the format %1$s."),
1396 formats.prettyName(format)));
1400 loader_format = format;
1402 if (loader_format == "lyx") {
1403 Buffer * buf = lv->loadDocument(lyxfile);
1408 buf->errors("Parse");
1410 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1414 bool as_paragraphs = loader_format == "textparagraph";
1415 string filename2 = (loader_format == format) ? filename.absFilename()
1416 : support::changeExtension(filename.absFilename(),
1417 formats.extension(loader_format));
1418 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1419 theLyXFunc().setLyXView(lv);
1420 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1427 void GuiView::importDocument(string const & argument)
1430 string filename = split(argument, format, ' ');
1432 LYXERR(Debug::INFO, format << " file: " << filename);
1434 // need user interaction
1435 if (filename.empty()) {
1436 string initpath = lyxrc.document_path;
1438 Buffer const * buf = buffer();
1440 string const trypath = buf->filePath();
1441 // If directory is writeable, use this as default.
1442 if (FileName(trypath).isDirWritable())
1446 docstring const text = bformat(_("Select %1$s file to import"),
1447 formats.prettyName(format));
1449 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1450 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1451 dlg.setButton2(qt_("Examples|#E#e"),
1452 toqstr(addPath(package().system_support().absFilename(), "examples")));
1454 docstring filter = formats.prettyName(format);
1457 filter += from_utf8(formats.extension(format));
1460 FileDialog::Result result =
1461 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1463 if (result.first == FileDialog::Later)
1466 filename = fromqstr(result.second);
1468 // check selected filename
1469 if (filename.empty())
1470 message(_("Canceled."));
1473 if (filename.empty())
1476 // get absolute path of file
1477 FileName const fullname(support::makeAbsPath(filename));
1479 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1481 // Check if the document already is open
1482 Buffer * buf = theBufferList().getBuffer(lyxfile);
1485 if (!closeBuffer()) {
1486 message(_("Canceled."));
1491 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1493 // if the file exists already, and we didn't do
1494 // -i lyx thefile.lyx, warn
1495 if (lyxfile.exists() && fullname != lyxfile) {
1497 docstring text = bformat(_("The document %1$s already exists.\n\n"
1498 "Do you want to overwrite that document?"), displaypath);
1499 int const ret = Alert::prompt(_("Overwrite document?"),
1500 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1503 message(_("Canceled."));
1508 message(bformat(_("Importing %1$s..."), displaypath));
1509 ErrorList errorList;
1510 if (import(this, fullname, format, errorList))
1511 message(_("imported."));
1513 message(_("file not imported!"));
1515 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1519 void GuiView::newDocument(string const & filename, bool from_template)
1521 FileName initpath(lyxrc.document_path);
1522 Buffer * buf = buffer();
1524 FileName const trypath(buf->filePath());
1525 // If directory is writeable, use this as default.
1526 if (trypath.isDirWritable())
1530 string templatefile = from_template ?
1531 selectTemplateFile().absFilename() : string();
1533 if (filename.empty())
1534 b = newUnnamedFile(templatefile, initpath);
1536 b = newFile(filename, templatefile, true);
1540 // Ensure the cursor is correctly positionned on screen.
1541 view()->showCursor();
1545 void GuiView::insertLyXFile(docstring const & fname)
1547 BufferView * bv = view();
1552 FileName filename(to_utf8(fname));
1554 if (!filename.empty()) {
1555 bv->insertLyXFile(filename);
1559 // Launch a file browser
1561 string initpath = lyxrc.document_path;
1562 string const trypath = bv->buffer().filePath();
1563 // If directory is writeable, use this as default.
1564 if (FileName(trypath).isDirWritable())
1568 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1569 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1570 dlg.setButton2(qt_("Examples|#E#e"),
1571 toqstr(addPath(package().system_support().absFilename(),
1574 FileDialog::Result result = dlg.open(toqstr(initpath),
1575 QStringList(qt_("LyX Documents (*.lyx)")));
1577 if (result.first == FileDialog::Later)
1581 filename.set(fromqstr(result.second));
1583 // check selected filename
1584 if (filename.empty()) {
1585 // emit message signal.
1586 message(_("Canceled."));
1590 bv->insertLyXFile(filename);
1594 void GuiView::insertPlaintextFile(docstring const & fname,
1597 BufferView * bv = view();
1602 FileName filename(to_utf8(fname));
1604 if (!filename.empty()) {
1605 bv->insertPlaintextFile(filename, asParagraph);
1609 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1610 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1612 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1615 if (result.first == FileDialog::Later)
1619 filename.set(fromqstr(result.second));
1621 // check selected filename
1622 if (filename.empty()) {
1623 // emit message signal.
1624 message(_("Canceled."));
1628 bv->insertPlaintextFile(filename, asParagraph);
1632 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1634 FileName fname = b.fileName();
1635 FileName const oldname = fname;
1637 if (!newname.empty()) {
1639 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1641 // Switch to this Buffer.
1644 /// No argument? Ask user through dialog.
1646 FileDialog dlg(qt_("Choose a filename to save document as"),
1647 LFUN_BUFFER_WRITE_AS);
1648 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1649 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1651 if (!isLyXFilename(fname.absFilename()))
1652 fname.changeExtension(".lyx");
1654 FileDialog::Result result =
1655 dlg.save(toqstr(fname.onlyPath().absFilename()),
1656 QStringList(qt_("LyX Documents (*.lyx)")),
1657 toqstr(fname.onlyFileName()));
1659 if (result.first == FileDialog::Later)
1662 fname.set(fromqstr(result.second));
1667 if (!isLyXFilename(fname.absFilename()))
1668 fname.changeExtension(".lyx");
1671 if (FileName(fname).exists()) {
1672 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1673 docstring text = bformat(_("The document %1$s already "
1674 "exists.\n\nDo you want to "
1675 "overwrite that document?"),
1677 int const ret = Alert::prompt(_("Overwrite document?"),
1678 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1681 case 1: return renameBuffer(b, docstring());
1682 case 2: return false;
1686 // Ok, change the name of the buffer
1687 b.setFileName(fname.absFilename());
1689 bool unnamed = b.isUnnamed();
1690 b.setUnnamed(false);
1691 b.saveCheckSum(fname);
1693 if (!saveBuffer(b)) {
1694 b.setFileName(oldname.absFilename());
1695 b.setUnnamed(unnamed);
1696 b.saveCheckSum(oldname);
1704 bool GuiView::saveBuffer(Buffer & b)
1707 return renameBuffer(b, docstring());
1710 theSession().lastFiles().add(b.fileName());
1714 // Switch to this Buffer.
1717 // FIXME: we don't tell the user *WHY* the save failed !!
1718 docstring const file = makeDisplayPath(b.absFileName(), 30);
1719 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1720 "Do you want to rename the document and "
1721 "try again?"), file);
1722 int const ret = Alert::prompt(_("Rename and save?"),
1723 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1726 if (!renameBuffer(b, docstring()))
1735 return saveBuffer(b);
1739 bool GuiView::closeBuffer()
1741 Buffer * buf = buffer();
1742 return buf && closeBuffer(*buf);
1746 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1748 // goto bookmark to update bookmark pit.
1749 //FIXME: we should update only the bookmarks related to this buffer!
1750 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1751 theLyXFunc().gotoBookmark(i+1, false, false);
1753 if (buf.isClean() || buf.paragraphs().empty()) {
1754 if (buf.masterBuffer() == &buf && tolastopened)
1755 theSession().lastOpened().add(buf.fileName());
1756 theBufferList().release(&buf);
1759 // Switch to this Buffer.
1764 if (buf.isUnnamed())
1765 file = from_utf8(buf.fileName().onlyFileName());
1767 file = buf.fileName().displayName(30);
1769 // Bring this window to top before asking questions.
1773 docstring const text = bformat(_("The document %1$s has unsaved changes."
1774 "\n\nDo you want to save the document or discard the changes?"), file);
1775 int const ret = Alert::prompt(_("Save changed document?"),
1776 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1780 if (!saveBuffer(buf))
1784 // if we crash after this we could
1785 // have no autosave file but I guess
1786 // this is really improbable (Jug)
1787 removeAutosaveFile(buf.absFileName());
1793 // save file names to .lyx/session
1794 // if master/slave are both open, do not save slave since it
1795 // will be automatically loaded when the master is loaded
1796 if (buf.masterBuffer() == &buf && tolastopened)
1797 theSession().lastOpened().add(buf.fileName());
1800 // Don't close child documents.
1801 removeWorkArea(d.current_work_area_);
1803 theBufferList().release(&buf);
1809 bool GuiView::dispatch(FuncRequest const & cmd)
1811 BufferView * bv = view();
1812 // By default we won't need any update.
1814 bv->cursor().updateFlags(Update::None);
1815 bool dispatched = true;
1817 switch(cmd.action) {
1818 case LFUN_BUFFER_IMPORT:
1819 importDocument(to_utf8(cmd.argument()));
1822 case LFUN_BUFFER_SWITCH:
1823 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1826 case LFUN_BUFFER_NEXT:
1827 setBuffer(theBufferList().next(buffer()));
1830 case LFUN_BUFFER_PREVIOUS:
1831 setBuffer(theBufferList().previous(buffer()));
1834 case LFUN_COMMAND_EXECUTE: {
1835 bool const show_it = cmd.argument() != "off";
1836 // FIXME: this is a hack, "minibuffer" should not be
1838 if (GuiToolbar * t = toolbar("minibuffer")) {
1839 t->setVisible(show_it);
1840 if (show_it && t->commandBuffer())
1841 t->commandBuffer()->setFocus();
1845 case LFUN_DROP_LAYOUTS_CHOICE:
1847 d.layout_->showPopup();
1850 case LFUN_MENU_OPEN:
1851 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1852 menu->exec(QCursor::pos());
1855 case LFUN_FILE_INSERT:
1856 insertLyXFile(cmd.argument());
1858 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1859 insertPlaintextFile(cmd.argument(), true);
1862 case LFUN_FILE_INSERT_PLAINTEXT:
1863 insertPlaintextFile(cmd.argument(), false);
1866 case LFUN_BUFFER_WRITE:
1868 saveBuffer(bv->buffer());
1871 case LFUN_BUFFER_WRITE_AS:
1873 renameBuffer(bv->buffer(), cmd.argument());
1876 case LFUN_BUFFER_WRITE_ALL: {
1877 Buffer * first = theBufferList().first();
1880 message(_("Saving all documents..."));
1881 // We cannot use a for loop as the buffer list cycles.
1884 if (!b->isClean()) {
1886 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1888 b = theBufferList().next(b);
1889 } while (b != first);
1890 message(_("All documents saved."));
1894 case LFUN_TOOLBAR_TOGGLE: {
1895 string const name = cmd.getArg(0);
1896 if (GuiToolbar * t = toolbar(name))
1901 case LFUN_DIALOG_UPDATE: {
1902 string const name = to_utf8(cmd.argument());
1903 // Can only update a dialog connected to an existing inset
1904 Inset * inset = getOpenInset(name);
1906 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1907 inset->dispatch(view()->cursor(), fr);
1908 } else if (name == "paragraph") {
1909 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1910 } else if (name == "prefs") {
1911 updateDialog(name, string());
1916 case LFUN_DIALOG_TOGGLE: {
1917 if (isDialogVisible(cmd.getArg(0)))
1918 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1920 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1924 case LFUN_DIALOG_DISCONNECT_INSET:
1925 disconnectDialog(to_utf8(cmd.argument()));
1928 case LFUN_DIALOG_HIDE: {
1929 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1933 case LFUN_DIALOG_SHOW: {
1934 string const name = cmd.getArg(0);
1935 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1937 if (name == "character") {
1938 data = freefont2string();
1940 showDialog("character", data);
1941 } else if (name == "latexlog") {
1942 Buffer::LogType type;
1943 string const logfile = buffer()->logName(&type);
1945 case Buffer::latexlog:
1948 case Buffer::buildlog:
1952 data += Lexer::quoteString(logfile);
1953 showDialog("log", data);
1954 } else if (name == "vclog") {
1955 string const data = "vc " +
1956 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1957 showDialog("log", data);
1958 } else if (name == "symbols") {
1959 data = bv->cursor().getEncoding()->name();
1961 showDialog("symbols", data);
1963 showDialog(name, data);
1967 case LFUN_INSET_APPLY: {
1968 string const name = cmd.getArg(0);
1969 Inset * inset = getOpenInset(name);
1971 // put cursor in front of inset.
1972 if (!view()->setCursorFromInset(inset))
1973 LASSERT(false, /**/);
1975 // useful if we are called from a dialog.
1976 view()->cursor().beginUndoGroup();
1977 view()->cursor().recordUndo();
1978 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1979 inset->dispatch(view()->cursor(), fr);
1980 view()->cursor().endUndoGroup();
1982 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1988 case LFUN_UI_TOGGLE:
1990 // Make sure the keyboard focus stays in the work area.
1994 case LFUN_SPLIT_VIEW:
1995 if (Buffer * buf = buffer()) {
1996 string const orientation = cmd.getArg(0);
1997 d.splitter_->setOrientation(orientation == "vertical"
1998 ? Qt::Vertical : Qt::Horizontal);
1999 TabWorkArea * twa = addTabWorkArea();
2000 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2001 setCurrentWorkArea(wa);
2005 case LFUN_CLOSE_TAB_GROUP:
2006 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2008 twa = d.currentTabWorkArea();
2009 // Switch to the next GuiWorkArea in the found TabWorkArea.
2011 d.current_work_area_ = twa->currentWorkArea();
2012 // Make sure the work area is up to date.
2013 twa->setCurrentWorkArea(d.current_work_area_);
2015 d.current_work_area_ = 0;
2017 if (d.splitter_->count() == 0)
2018 // No more work area, switch to the background widget.
2023 case LFUN_COMPLETION_INLINE:
2024 if (d.current_work_area_)
2025 d.current_work_area_->completer().showInline();
2028 case LFUN_COMPLETION_POPUP:
2029 if (d.current_work_area_)
2030 d.current_work_area_->completer().showPopup();
2034 case LFUN_COMPLETION_COMPLETE:
2035 if (d.current_work_area_)
2036 d.current_work_area_->completer().tab();
2039 case LFUN_COMPLETION_CANCEL:
2040 if (d.current_work_area_) {
2041 if (d.current_work_area_->completer().popupVisible())
2042 d.current_work_area_->completer().hidePopup();
2044 d.current_work_area_->completer().hideInline();
2048 case LFUN_COMPLETION_ACCEPT:
2049 if (d.current_work_area_)
2050 d.current_work_area_->completer().activate();
2059 // Part of automatic menu appearance feature.
2060 if (isFullScreen()) {
2061 if (menuBar()->isVisible())
2063 if (statusBar()->isVisible())
2064 statusBar()->hide();
2071 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2073 string const arg = cmd.getArg(0);
2074 if (arg == "scrollbar") {
2075 // hide() is of no help
2076 if (d.current_work_area_->verticalScrollBarPolicy() ==
2077 Qt::ScrollBarAlwaysOff)
2079 d.current_work_area_->setVerticalScrollBarPolicy(
2080 Qt::ScrollBarAsNeeded);
2082 d.current_work_area_->setVerticalScrollBarPolicy(
2083 Qt::ScrollBarAlwaysOff);
2086 if (arg == "statusbar") {
2087 statusBar()->setVisible(!statusBar()->isVisible());
2090 if (arg == "menubar") {
2091 menuBar()->setVisible(!menuBar()->isVisible());
2094 #if QT_VERSION >= 0x040300
2095 if (arg == "frame") {
2097 getContentsMargins(&l, &t, &r, &b);
2098 //are the frames in default state?
2099 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2101 setContentsMargins(-2, -2, -2, -2);
2103 setContentsMargins(0, 0, 0, 0);
2108 if (arg == "fullscreen") {
2113 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2117 void GuiView::toggleFullScreen()
2119 if (isFullScreen()) {
2120 for (int i = 0; i != d.splitter_->count(); ++i)
2121 d.tabWorkArea(i)->setFullScreen(false);
2122 #if QT_VERSION >= 0x040300
2123 setContentsMargins(0, 0, 0, 0);
2125 setWindowState(windowState() ^ Qt::WindowFullScreen);
2128 statusBar()->show();
2130 for (int i = 0; i != d.splitter_->count(); ++i)
2131 d.tabWorkArea(i)->setFullScreen(true);
2132 #if QT_VERSION >= 0x040300
2133 setContentsMargins(-2, -2, -2, -2);
2136 setWindowState(windowState() ^ Qt::WindowFullScreen);
2137 statusBar()->hide();
2139 if (lyxrc.full_screen_toolbars) {
2140 ToolbarMap::iterator end = d.toolbars_.end();
2141 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2148 Buffer const * GuiView::updateInset(Inset const * inset)
2150 if (!d.current_work_area_)
2154 d.current_work_area_->scheduleRedraw();
2156 return &d.current_work_area_->bufferView().buffer();
2160 void GuiView::restartCursor()
2162 /* When we move around, or type, it's nice to be able to see
2163 * the cursor immediately after the keypress.
2165 if (d.current_work_area_)
2166 d.current_work_area_->startBlinkingCursor();
2168 // Take this occasion to update the other GUI elements.
2174 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2176 if (d.current_work_area_)
2177 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2182 // This list should be kept in sync with the list of insets in
2183 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2184 // dialog should have the same name as the inset.
2185 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2186 // docs in LyXAction.cpp.
2188 char const * const dialognames[] = {
2189 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2190 "citation", "document", "errorlist", "ert", "external", "file",
2191 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2192 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2193 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2195 #ifdef HAVE_LIBAIKSAURUS
2199 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2201 char const * const * const end_dialognames =
2202 dialognames + (sizeof(dialognames) / sizeof(char *));
2206 cmpCStr(char const * name) : name_(name) {}
2207 bool operator()(char const * other) {
2208 return strcmp(other, name_) == 0;
2215 bool isValidName(string const & name)
2217 return find_if(dialognames, end_dialognames,
2218 cmpCStr(name.c_str())) != end_dialognames;
2224 void GuiView::resetDialogs()
2226 // Make sure that no LFUN uses any LyXView.
2227 theLyXFunc().setLyXView(0);
2230 constructToolbars();
2231 guiApp->menus().fillMenuBar(menuBar(), this, true);
2233 d.layout_->updateContents(true);
2234 // Now update controls with current buffer.
2235 theLyXFunc().setLyXView(this);
2241 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2243 if (!isValidName(name))
2246 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2248 if (it != d.dialogs_.end())
2249 return it->second.get();
2251 Dialog * dialog = build(name);
2252 d.dialogs_[name].reset(dialog);
2253 if (lyxrc.allow_geometry_session)
2254 dialog->restoreSession();
2261 void GuiView::showDialog(string const & name, string const & data,
2269 Dialog * dialog = findOrBuild(name, false);
2271 dialog->showData(data);
2273 d.open_insets_[name] = inset;
2276 catch (ExceptionMessage const & ex) {
2284 bool GuiView::isDialogVisible(string const & name) const
2286 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2287 if (it == d.dialogs_.end())
2289 return it->second.get()->isVisibleView();
2293 void GuiView::hideDialog(string const & name, Inset * inset)
2295 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2296 if (it == d.dialogs_.end())
2299 if (inset && inset != getOpenInset(name))
2302 Dialog * const dialog = it->second.get();
2303 if (dialog->isVisibleView())
2305 d.open_insets_[name] = 0;
2309 void GuiView::disconnectDialog(string const & name)
2311 if (!isValidName(name))
2314 if (d.open_insets_.find(name) != d.open_insets_.end())
2315 d.open_insets_[name] = 0;
2319 Inset * GuiView::getOpenInset(string const & name) const
2321 if (!isValidName(name))
2324 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2325 return it == d.open_insets_.end() ? 0 : it->second;
2329 void GuiView::hideAll() const
2331 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2332 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2334 for(; it != end; ++it)
2335 it->second->hideView();
2339 void GuiView::updateDialogs()
2341 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2342 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2344 for(; it != end; ++it) {
2345 Dialog * dialog = it->second.get();
2346 if (dialog && dialog->isVisibleView())
2347 dialog->checkStatus();
2354 // will be replaced by a proper factory...
2355 Dialog * createGuiAbout(GuiView & lv);
2356 Dialog * createGuiBibitem(GuiView & lv);
2357 Dialog * createGuiBibtex(GuiView & lv);
2358 Dialog * createGuiBox(GuiView & lv);
2359 Dialog * createGuiBranch(GuiView & lv);
2360 Dialog * createGuiChanges(GuiView & lv);
2361 Dialog * createGuiCharacter(GuiView & lv);
2362 Dialog * createGuiCitation(GuiView & lv);
2363 Dialog * createGuiDelimiter(GuiView & lv);
2364 Dialog * createGuiDocument(GuiView & lv);
2365 Dialog * createGuiErrorList(GuiView & lv);
2366 Dialog * createGuiERT(GuiView & lv);
2367 Dialog * createGuiExternal(GuiView & lv);
2368 Dialog * createGuiFloat(GuiView & lv);
2369 Dialog * createGuiGraphics(GuiView & lv);
2370 Dialog * createGuiHSpace(GuiView & lv);
2371 Dialog * createGuiInclude(GuiView & lv);
2372 Dialog * createGuiInfo(GuiView & lv);
2373 Dialog * createGuiLabel(GuiView & lv);
2374 Dialog * createGuiListings(GuiView & lv);
2375 Dialog * createGuiLog(GuiView & lv);
2376 Dialog * createGuiMathMatrix(GuiView & lv);
2377 Dialog * createGuiNomenclature(GuiView & lv);
2378 Dialog * createGuiNote(GuiView & lv);
2379 Dialog * createGuiParagraph(GuiView & lv);
2380 Dialog * createGuiPreferences(GuiView & lv);
2381 Dialog * createGuiPrint(GuiView & lv);
2382 Dialog * createGuiRef(GuiView & lv);
2383 Dialog * createGuiSearch(GuiView & lv);
2384 Dialog * createGuiSendTo(GuiView & lv);
2385 Dialog * createGuiShowFile(GuiView & lv);
2386 Dialog * createGuiSpellchecker(GuiView & lv);
2387 Dialog * createGuiSymbols(GuiView & lv);
2388 Dialog * createGuiTabularCreate(GuiView & lv);
2389 Dialog * createGuiTabular(GuiView & lv);
2390 Dialog * createGuiTexInfo(GuiView & lv);
2391 Dialog * createGuiToc(GuiView & lv);
2392 Dialog * createGuiThesaurus(GuiView & lv);
2393 Dialog * createGuiHyperlink(GuiView & lv);
2394 Dialog * createGuiVSpace(GuiView & lv);
2395 Dialog * createGuiViewSource(GuiView & lv);
2396 Dialog * createGuiWrap(GuiView & lv);
2399 Dialog * GuiView::build(string const & name)
2401 LASSERT(isValidName(name), /**/);
2403 if (name == "aboutlyx")
2404 return createGuiAbout(*this);
2405 if (name == "bibitem")
2406 return createGuiBibitem(*this);
2407 if (name == "bibtex")
2408 return createGuiBibtex(*this);
2410 return createGuiBox(*this);
2411 if (name == "branch")
2412 return createGuiBranch(*this);
2413 if (name == "changes")
2414 return createGuiChanges(*this);
2415 if (name == "character")
2416 return createGuiCharacter(*this);
2417 if (name == "citation")
2418 return createGuiCitation(*this);
2419 if (name == "document")
2420 return createGuiDocument(*this);
2421 if (name == "errorlist")
2422 return createGuiErrorList(*this);
2424 return createGuiERT(*this);
2425 if (name == "external")
2426 return createGuiExternal(*this);
2428 return createGuiShowFile(*this);
2429 if (name == "findreplace")
2430 return createGuiSearch(*this);
2431 if (name == "float")
2432 return createGuiFloat(*this);
2433 if (name == "graphics")
2434 return createGuiGraphics(*this);
2435 if (name == "include")
2436 return createGuiInclude(*this);
2438 return createGuiInfo(*this);
2439 if (name == "nomenclature")
2440 return createGuiNomenclature(*this);
2441 if (name == "label")
2442 return createGuiLabel(*this);
2444 return createGuiLog(*this);
2445 if (name == "view-source")
2446 return createGuiViewSource(*this);
2447 if (name == "mathdelimiter")
2448 return createGuiDelimiter(*this);
2449 if (name == "mathmatrix")
2450 return createGuiMathMatrix(*this);
2452 return createGuiNote(*this);
2453 if (name == "paragraph")
2454 return createGuiParagraph(*this);
2455 if (name == "prefs")
2456 return createGuiPreferences(*this);
2457 if (name == "print")
2458 return createGuiPrint(*this);
2460 return createGuiRef(*this);
2461 if (name == "sendto")
2462 return createGuiSendTo(*this);
2463 if (name == "space")
2464 return createGuiHSpace(*this);
2465 if (name == "spellchecker")
2466 return createGuiSpellchecker(*this);
2467 if (name == "symbols")
2468 return createGuiSymbols(*this);
2469 if (name == "tabular")
2470 return createGuiTabular(*this);
2471 if (name == "tabularcreate")
2472 return createGuiTabularCreate(*this);
2473 if (name == "texinfo")
2474 return createGuiTexInfo(*this);
2475 #ifdef HAVE_LIBAIKSAURUS
2476 if (name == "thesaurus")
2477 return createGuiThesaurus(*this);
2480 return createGuiToc(*this);
2482 return createGuiHyperlink(*this);
2483 if (name == "vspace")
2484 return createGuiVSpace(*this);
2486 return createGuiWrap(*this);
2487 if (name == "listings")
2488 return createGuiListings(*this);
2494 } // namespace frontend
2497 #include "GuiView_moc.cpp"