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.
386 findOrBuild("toc", true);
387 findOrBuild("view-source", true);
389 if (!restoreState(settings.value("layout").toByteArray(), 0))
396 GuiToolbar * GuiView::toolbar(string const & name)
398 ToolbarMap::iterator it = d.toolbars_.find(name);
399 if (it != d.toolbars_.end())
402 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
403 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
408 void GuiView::constructToolbars()
410 ToolbarMap::iterator it = d.toolbars_.begin();
411 for (; it != d.toolbars_.end(); ++it)
416 // extracts the toolbars from the backend
417 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
418 Toolbars::Infos::iterator end = guiApp->toolbars().end();
419 for (; cit != end; ++cit)
420 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
424 void GuiView::initToolbars()
426 // extracts the toolbars from the backend
427 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
428 Toolbars::Infos::iterator end = guiApp->toolbars().end();
429 for (; cit != end; ++cit) {
430 GuiToolbar * tb = toolbar(cit->name);
433 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
435 tb->setVisible(false);
436 tb->setVisibility(visibility);
438 if (visibility & Toolbars::TOP) {
440 addToolBarBreak(Qt::TopToolBarArea);
441 addToolBar(Qt::TopToolBarArea, tb);
444 if (visibility & Toolbars::BOTTOM) {
445 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
446 #if (QT_VERSION >= 0x040202)
447 addToolBarBreak(Qt::BottomToolBarArea);
449 addToolBar(Qt::BottomToolBarArea, tb);
452 if (visibility & Toolbars::LEFT) {
453 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
454 #if (QT_VERSION >= 0x040202)
455 addToolBarBreak(Qt::LeftToolBarArea);
457 addToolBar(Qt::LeftToolBarArea, tb);
460 if (visibility & Toolbars::RIGHT) {
461 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
462 #if (QT_VERSION >= 0x040202)
463 addToolBarBreak(Qt::RightToolBarArea);
465 addToolBar(Qt::RightToolBarArea, tb);
468 if (visibility & Toolbars::ON)
469 tb->setVisible(true);
474 TocModels & GuiView::tocModels()
476 return d.toc_models_;
480 void GuiView::setFocus()
482 // Make sure LyXFunc points to the correct view.
483 guiApp->setCurrentView(this);
484 theLyXFunc().setLyXView(this);
485 QMainWindow::setFocus();
486 if (d.current_work_area_)
487 d.current_work_area_->setFocus();
491 QMenu * GuiView::createPopupMenu()
493 return d.toolBarPopup(this);
497 void GuiView::showEvent(QShowEvent * e)
499 LYXERR(Debug::GUI, "Passed Geometry "
500 << size().height() << "x" << size().width()
501 << "+" << pos().x() << "+" << pos().y());
503 if (d.splitter_->count() == 0)
504 // No work area, switch to the background widget.
507 QMainWindow::showEvent(e);
511 void GuiView::closeEvent(QCloseEvent * close_event)
515 // it can happen that this event arrives without selecting the view,
516 // e.g. when clicking the close button on a background window.
519 while (Buffer * b = buffer()) {
521 // This is a child document, just close the tab after saving
522 // but keep the file loaded.
523 if (!saveBuffer(*b)) {
525 close_event->ignore();
528 removeWorkArea(d.current_work_area_);
532 QList<int> const ids = guiApp->viewIds();
533 for (int i = 0; i != ids.size(); ++i) {
536 if (guiApp->view(ids[i]).workArea(*b)) {
537 // FIXME 1: should we put an alert box here that the buffer
538 // is viewed elsewhere?
539 // FIXME 2: should we try to save this buffer in any case?
542 // This buffer is also opened in another view, so
543 // close the associated work area...
544 removeWorkArea(d.current_work_area_);
545 // ... but don't close the buffer.
550 if (b && !closeBuffer(*b, true)) {
552 close_event->ignore();
557 // Make sure that nothing will use this close to be closed View.
558 guiApp->unregisterView(this);
560 if (isFullScreen()) {
561 // Switch off fullscreen before closing.
566 // Make sure the timer time out will not trigger a statusbar update.
567 d.statusbar_timer_.stop();
569 // Saving fullscreen requires additional tweaks in the toolbar code.
570 // It wouldn't also work under linux natively.
571 if (lyxrc.allow_geometry_session) {
572 // Save this window geometry and layout.
574 // Then the toolbar private states.
575 ToolbarMap::iterator end = d.toolbars_.end();
576 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
577 it->second->saveSession();
578 // Now take care of all other dialogs:
579 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
580 for (; it!= d.dialogs_.end(); ++it)
581 it->second->saveSession();
584 close_event->accept();
588 void GuiView::dragEnterEvent(QDragEnterEvent * event)
590 if (event->mimeData()->hasUrls())
592 /// \todo Ask lyx-devel is this is enough:
593 /// if (event->mimeData()->hasFormat("text/plain"))
594 /// event->acceptProposedAction();
598 void GuiView::dropEvent(QDropEvent* event)
600 QList<QUrl> files = event->mimeData()->urls();
604 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
605 for (int i = 0; i != files.size(); ++i) {
606 string const file = os::internal_path(fromqstr(
607 files.at(i).toLocalFile()));
609 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
614 void GuiView::message(docstring const & str)
616 if (ForkedProcess::iAmAChild())
619 statusBar()->showMessage(toqstr(str));
620 d.statusbar_timer_.stop();
621 d.statusbar_timer_.start(3000);
625 void GuiView::smallSizedIcons()
627 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
631 void GuiView::normalSizedIcons()
633 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
637 void GuiView::bigSizedIcons()
639 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
643 void GuiView::clearMessage()
647 theLyXFunc().setLyXView(this);
648 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
649 d.statusbar_timer_.stop();
653 void GuiView::updateWindowTitle(GuiWorkArea * wa)
655 if (wa != d.current_work_area_)
657 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
658 setWindowIconText(wa->windowIconText());
662 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
665 disconnectBufferView();
666 connectBufferView(wa->bufferView());
667 connectBuffer(wa->bufferView().buffer());
668 d.current_work_area_ = wa;
669 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
670 this, SLOT(updateWindowTitle(GuiWorkArea *)));
671 updateWindowTitle(wa);
675 // The document settings needs to be reinitialised.
676 updateDialog("document", "");
678 // Buffer-dependent dialogs must be updated. This is done here because
679 // some dialogs require buffer()->text.
684 void GuiView::on_lastWorkAreaRemoved()
687 // We already are in a close event. Nothing more to do.
690 if (d.splitter_->count() > 1)
691 // We have a splitter so don't close anything.
694 // Reset and updates the dialogs.
695 d.toc_models_.reset(0);
696 updateDialog("document", "");
699 resetWindowTitleAndIconText();
701 if (lyxrc.open_buffers_in_tabs)
702 // Nothing more to do, the window should stay open.
705 if (guiApp->viewIds().size() > 1) {
711 // On Mac we also close the last window because the application stay
712 // resident in memory. On other platforms we don't close the last
713 // window because this would quit the application.
719 void GuiView::updateStatusBar()
721 // let the user see the explicit message
722 if (d.statusbar_timer_.isActive())
725 theLyXFunc().setLyXView(this);
726 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
730 bool GuiView::hasFocus() const
732 return qApp->activeWindow() == this;
736 bool GuiView::event(QEvent * e)
740 // Useful debug code:
741 //case QEvent::ActivationChange:
742 //case QEvent::WindowDeactivate:
743 //case QEvent::Paint:
744 //case QEvent::Enter:
745 //case QEvent::Leave:
746 //case QEvent::HoverEnter:
747 //case QEvent::HoverLeave:
748 //case QEvent::HoverMove:
749 //case QEvent::StatusTip:
750 //case QEvent::DragEnter:
751 //case QEvent::DragLeave:
755 case QEvent::WindowActivate: {
756 if (this == guiApp->currentView()) {
758 return QMainWindow::event(e);
760 guiApp->setCurrentView(this);
761 theLyXFunc().setLyXView(this);
762 if (d.current_work_area_) {
763 BufferView & bv = d.current_work_area_->bufferView();
764 connectBufferView(bv);
765 connectBuffer(bv.buffer());
766 // The document structure, name and dialogs might have
767 // changed in another view.
769 // The document settings needs to be reinitialised.
770 updateDialog("document", "");
773 resetWindowTitleAndIconText();
776 return QMainWindow::event(e);
779 case QEvent::ShortcutOverride: {
783 if (isFullScreen() && menuBar()->isHidden()) {
784 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
785 // FIXME: we should also try to detect special LyX shortcut such as
786 // Alt-P and Alt-M. Right now there is a hack in
787 // GuiWorkArea::processKeySym() that hides again the menubar for
789 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
791 return QMainWindow::event(e);
795 if (d.current_work_area_)
796 // Nothing special to do.
797 return QMainWindow::event(e);
799 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
800 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
802 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
803 || ke->key() == Qt::Key_Backtab)
804 return QMainWindow::event(e);
806 // Allow processing of shortcuts that are allowed even when no Buffer
808 theLyXFunc().setLyXView(this);
810 setKeySymbol(&sym, ke);
811 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
817 return QMainWindow::event(e);
821 void GuiView::resetWindowTitleAndIconText()
823 setWindowTitle(qt_("LyX"));
824 setWindowIconText(qt_("LyX"));
827 bool GuiView::focusNextPrevChild(bool /*next*/)
834 void GuiView::setBusy(bool busy)
836 if (d.current_work_area_) {
837 d.current_work_area_->setUpdatesEnabled(!busy);
839 d.current_work_area_->stopBlinkingCursor();
841 d.current_work_area_->startBlinkingCursor();
845 QApplication::setOverrideCursor(Qt::WaitCursor);
847 QApplication::restoreOverrideCursor();
851 GuiWorkArea * GuiView::workArea(Buffer & buffer)
853 if (TabWorkArea * twa = d.currentTabWorkArea())
854 return twa->workArea(buffer);
859 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
861 // Automatically create a TabWorkArea if there are none yet.
862 TabWorkArea * tab_widget = d.splitter_->count()
863 ? d.currentTabWorkArea() : addTabWorkArea();
864 return tab_widget->addWorkArea(buffer, *this);
868 TabWorkArea * GuiView::addTabWorkArea()
870 TabWorkArea * twa = new TabWorkArea;
871 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
872 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
873 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
874 this, SLOT(on_lastWorkAreaRemoved()));
876 d.splitter_->addWidget(twa);
877 d.stack_widget_->setCurrentWidget(d.splitter_);
882 GuiWorkArea const * GuiView::currentWorkArea() const
884 return d.current_work_area_;
888 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
891 d.current_work_area_ = wa;
892 for (int i = 0; i != d.splitter_->count(); ++i) {
893 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
899 void GuiView::removeWorkArea(GuiWorkArea * wa)
902 if (wa == d.current_work_area_) {
904 disconnectBufferView();
905 d.current_work_area_ = 0;
908 for (int i = 0; i != d.splitter_->count(); ++i) {
909 TabWorkArea * twa = d.tabWorkArea(i);
910 if (!twa->removeWorkArea(wa))
911 // Not found in this tab group.
914 // We found and removed the GuiWorkArea.
916 // No more WorkAreas in this tab group, so delete it.
921 if (d.current_work_area_)
922 // This means that we are not closing the current GuiWorkArea;
925 // Switch to the next GuiWorkArea in the found TabWorkArea.
926 d.current_work_area_ = twa->currentWorkArea();
930 if (d.splitter_->count() == 0)
931 // No more work area, switch to the background widget.
936 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
942 void GuiView::updateLayoutList()
945 d.layout_->updateContents(false);
949 void GuiView::updateToolbars()
951 ToolbarMap::iterator end = d.toolbars_.end();
952 if (d.current_work_area_) {
954 d.current_work_area_->bufferView().cursor().inMathed();
956 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
958 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
959 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
960 bool const mathmacrotemplate =
961 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
963 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
964 it->second->update(math, table, review, mathmacrotemplate);
966 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
967 it->second->update(false, false, false, false);
971 Buffer * GuiView::buffer()
973 if (d.current_work_area_)
974 return &d.current_work_area_->bufferView().buffer();
979 Buffer const * GuiView::buffer() const
981 if (d.current_work_area_)
982 return &d.current_work_area_->bufferView().buffer();
987 void GuiView::setBuffer(Buffer * newBuffer)
989 LASSERT(newBuffer, return);
992 GuiWorkArea * wa = workArea(*newBuffer);
994 updateLabels(*newBuffer->masterBuffer());
995 wa = addWorkArea(*newBuffer);
997 //Disconnect the old buffer...there's no new one.
1000 connectBuffer(*newBuffer);
1001 connectBufferView(wa->bufferView());
1002 setCurrentWorkArea(wa);
1008 void GuiView::connectBuffer(Buffer & buf)
1010 buf.setGuiDelegate(this);
1014 void GuiView::disconnectBuffer()
1016 if (d.current_work_area_)
1017 d.current_work_area_->bufferView().setGuiDelegate(0);
1021 void GuiView::connectBufferView(BufferView & bv)
1023 bv.setGuiDelegate(this);
1027 void GuiView::disconnectBufferView()
1029 if (d.current_work_area_)
1030 d.current_work_area_->bufferView().setGuiDelegate(0);
1034 void GuiView::errors(string const & error_type)
1036 ErrorList & el = buffer()->errorList(error_type);
1038 showDialog("errorlist", error_type);
1042 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1044 d.toc_models_.updateItem(toqstr(type), dit);
1048 void GuiView::structureChanged()
1050 d.toc_models_.reset(view());
1051 // Navigator needs more than a simple update in this case. It needs to be
1053 updateDialog("toc", "");
1057 void GuiView::updateDialog(string const & name, string const & data)
1059 if (!isDialogVisible(name))
1062 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1063 if (it == d.dialogs_.end())
1066 Dialog * const dialog = it->second.get();
1067 if (dialog->isVisibleView())
1068 dialog->initialiseParams(data);
1072 BufferView * GuiView::view()
1074 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1078 void GuiView::autoSave()
1080 LYXERR(Debug::INFO, "Running autoSave()");
1083 view()->buffer().autoSave();
1087 void GuiView::resetAutosaveTimers()
1090 d.autosave_timeout_.restart();
1094 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1097 Buffer * buf = buffer();
1099 /* In LyX/Mac, when a dialog is open, the menus of the
1100 application can still be accessed without giving focus to
1101 the main window. In this case, we want to disable the menu
1102 entries that are buffer-related.
1104 Note that this code is not perfect, as bug 1941 attests:
1105 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1107 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1110 switch(cmd.action) {
1111 case LFUN_BUFFER_WRITE:
1112 enable = buf && (buf->isUnnamed() || !buf->isClean());
1115 case LFUN_BUFFER_WRITE_AS:
1119 case LFUN_SPLIT_VIEW:
1120 if (cmd.getArg(0) == "vertical")
1121 enable = buf && (d.splitter_->count() == 1 ||
1122 d.splitter_->orientation() == Qt::Vertical);
1124 enable = buf && (d.splitter_->count() == 1 ||
1125 d.splitter_->orientation() == Qt::Horizontal);
1128 case LFUN_CLOSE_TAB_GROUP:
1129 enable = d.currentTabWorkArea();
1132 case LFUN_TOOLBAR_TOGGLE:
1133 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1134 flag.setOnOff(t->isVisible());
1137 case LFUN_UI_TOGGLE:
1138 flag.setOnOff(isFullScreen());
1141 case LFUN_DIALOG_TOGGLE:
1142 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1143 // fall through to set "enable"
1144 case LFUN_DIALOG_SHOW: {
1145 string const name = cmd.getArg(0);
1147 enable = name == "aboutlyx"
1148 || name == "file" //FIXME: should be removed.
1150 || name == "texinfo";
1151 else if (name == "print")
1152 enable = buf->isExportable("dvi")
1153 && lyxrc.print_command != "none";
1154 else if (name == "character") {
1158 InsetCode ic = view()->cursor().inset().lyxCode();
1159 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1162 else if (name == "symbols") {
1163 if (!view() || view()->cursor().inMathed())
1166 InsetCode ic = view()->cursor().inset().lyxCode();
1167 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1170 else if (name == "latexlog")
1171 enable = FileName(buf->logName()).isReadableFile();
1172 else if (name == "spellchecker")
1173 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1174 enable = !buf->isReadonly();
1178 else if (name == "vclog")
1179 enable = buf->lyxvc().inUse();
1183 case LFUN_DIALOG_UPDATE: {
1184 string const name = cmd.getArg(0);
1186 enable = name == "prefs";
1190 case LFUN_INSET_APPLY: {
1191 string const name = cmd.getArg(0);
1192 Inset * inset = getOpenInset(name);
1194 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1196 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1197 // Every inset is supposed to handle this
1198 LASSERT(false, break);
1202 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1203 flag |= lyx::getStatus(fr);
1205 enable = flag.enabled();
1209 case LFUN_COMPLETION_INLINE:
1210 if (!d.current_work_area_
1211 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1215 case LFUN_COMPLETION_POPUP:
1216 if (!d.current_work_area_
1217 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1221 case LFUN_COMPLETION_COMPLETE:
1222 if (!d.current_work_area_
1223 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1227 case LFUN_COMPLETION_ACCEPT:
1228 case LFUN_COMPLETION_CANCEL:
1229 if (!d.current_work_area_
1230 || (!d.current_work_area_->completer().popupVisible()
1231 && !d.current_work_area_->completer().inlineVisible()))
1240 flag.setEnabled(false);
1246 static FileName selectTemplateFile()
1248 FileDialog dlg(qt_("Select template file"));
1249 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1250 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1252 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1253 QStringList(qt_("LyX Documents (*.lyx)")));
1255 if (result.first == FileDialog::Later)
1257 if (result.second.isEmpty())
1259 return FileName(fromqstr(result.second));
1263 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1267 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1270 message(_("Document not loaded."));
1275 setBuffer(newBuffer);
1277 // scroll to the position when the file was last closed
1278 if (lyxrc.use_lastfilepos) {
1279 LastFilePosSection::FilePos filepos =
1280 theSession().lastFilePos().load(filename);
1281 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1285 theSession().lastFiles().add(filename);
1292 void GuiView::openDocument(string const & fname)
1294 string initpath = lyxrc.document_path;
1297 string const trypath = buffer()->filePath();
1298 // If directory is writeable, use this as default.
1299 if (FileName(trypath).isDirWritable())
1305 if (fname.empty()) {
1306 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1307 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1308 dlg.setButton2(qt_("Examples|#E#e"),
1309 toqstr(addPath(package().system_support().absFilename(), "examples")));
1311 QStringList filter(qt_("LyX Documents (*.lyx)"));
1312 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1313 << qt_("LyX-1.4.x Documents (*.lyx14)")
1314 << qt_("LyX-1.5.x Documents (*.lyx15)");
1315 FileDialog::Result result =
1316 dlg.open(toqstr(initpath), filter);
1318 if (result.first == FileDialog::Later)
1321 filename = fromqstr(result.second);
1323 // check selected filename
1324 if (filename.empty()) {
1325 message(_("Canceled."));
1331 // get absolute path of file and add ".lyx" to the filename if
1333 FileName const fullname =
1334 fileSearch(string(), filename, "lyx", support::may_not_exist);
1335 if (!fullname.empty())
1336 filename = fullname.absFilename();
1338 if (!fullname.onlyPath().isDirectory()) {
1339 Alert::warning(_("Invalid filename"),
1340 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1341 from_utf8(fullname.absFilename())));
1344 // if the file doesn't exist, let the user create one
1345 if (!fullname.exists()) {
1346 // the user specifically chose this name. Believe him.
1347 Buffer * const b = newFile(filename, string(), true);
1353 docstring const disp_fn = makeDisplayPath(filename);
1354 message(bformat(_("Opening document %1$s..."), disp_fn));
1357 Buffer * buf = loadDocument(fullname);
1362 buf->errors("Parse");
1363 str2 = bformat(_("Document %1$s opened."), disp_fn);
1364 if (buf->lyxvc().inUse())
1365 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1366 " " + _("Version control detected.");
1368 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1373 // FIXME: clean that
1374 static bool import(GuiView * lv, FileName const & filename,
1375 string const & format, ErrorList & errorList)
1377 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1379 string loader_format;
1380 vector<string> loaders = theConverters().loaders();
1381 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1382 for (vector<string>::const_iterator it = loaders.begin();
1383 it != loaders.end(); ++it) {
1384 if (!theConverters().isReachable(format, *it))
1387 string const tofile =
1388 support::changeExtension(filename.absFilename(),
1389 formats.extension(*it));
1390 if (!theConverters().convert(0, filename, FileName(tofile),
1391 filename, format, *it, errorList))
1393 loader_format = *it;
1396 if (loader_format.empty()) {
1397 frontend::Alert::error(_("Couldn't import file"),
1398 bformat(_("No information for importing the format %1$s."),
1399 formats.prettyName(format)));
1403 loader_format = format;
1405 if (loader_format == "lyx") {
1406 Buffer * buf = lv->loadDocument(lyxfile);
1411 buf->errors("Parse");
1413 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1417 bool as_paragraphs = loader_format == "textparagraph";
1418 string filename2 = (loader_format == format) ? filename.absFilename()
1419 : support::changeExtension(filename.absFilename(),
1420 formats.extension(loader_format));
1421 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1422 theLyXFunc().setLyXView(lv);
1423 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1430 void GuiView::importDocument(string const & argument)
1433 string filename = split(argument, format, ' ');
1435 LYXERR(Debug::INFO, format << " file: " << filename);
1437 // need user interaction
1438 if (filename.empty()) {
1439 string initpath = lyxrc.document_path;
1441 Buffer const * buf = buffer();
1443 string const trypath = buf->filePath();
1444 // If directory is writeable, use this as default.
1445 if (FileName(trypath).isDirWritable())
1449 docstring const text = bformat(_("Select %1$s file to import"),
1450 formats.prettyName(format));
1452 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1453 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1454 dlg.setButton2(qt_("Examples|#E#e"),
1455 toqstr(addPath(package().system_support().absFilename(), "examples")));
1457 docstring filter = formats.prettyName(format);
1460 filter += from_utf8(formats.extension(format));
1463 FileDialog::Result result =
1464 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1466 if (result.first == FileDialog::Later)
1469 filename = fromqstr(result.second);
1471 // check selected filename
1472 if (filename.empty())
1473 message(_("Canceled."));
1476 if (filename.empty())
1479 // get absolute path of file
1480 FileName const fullname(support::makeAbsPath(filename));
1482 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1484 // Check if the document already is open
1485 Buffer * buf = theBufferList().getBuffer(lyxfile);
1488 if (!closeBuffer()) {
1489 message(_("Canceled."));
1494 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1496 // if the file exists already, and we didn't do
1497 // -i lyx thefile.lyx, warn
1498 if (lyxfile.exists() && fullname != lyxfile) {
1500 docstring text = bformat(_("The document %1$s already exists.\n\n"
1501 "Do you want to overwrite that document?"), displaypath);
1502 int const ret = Alert::prompt(_("Overwrite document?"),
1503 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1506 message(_("Canceled."));
1511 message(bformat(_("Importing %1$s..."), displaypath));
1512 ErrorList errorList;
1513 if (import(this, fullname, format, errorList))
1514 message(_("imported."));
1516 message(_("file not imported!"));
1518 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1522 void GuiView::newDocument(string const & filename, bool from_template)
1524 FileName initpath(lyxrc.document_path);
1525 Buffer * buf = buffer();
1527 FileName const trypath(buf->filePath());
1528 // If directory is writeable, use this as default.
1529 if (trypath.isDirWritable())
1533 string templatefile = from_template ?
1534 selectTemplateFile().absFilename() : string();
1536 if (filename.empty())
1537 b = newUnnamedFile(templatefile, initpath);
1539 b = newFile(filename, templatefile, true);
1543 // Ensure the cursor is correctly positionned on screen.
1544 view()->showCursor();
1548 void GuiView::insertLyXFile(docstring const & fname)
1550 BufferView * bv = view();
1555 FileName filename(to_utf8(fname));
1557 if (!filename.empty()) {
1558 bv->insertLyXFile(filename);
1562 // Launch a file browser
1564 string initpath = lyxrc.document_path;
1565 string const trypath = bv->buffer().filePath();
1566 // If directory is writeable, use this as default.
1567 if (FileName(trypath).isDirWritable())
1571 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1572 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1573 dlg.setButton2(qt_("Examples|#E#e"),
1574 toqstr(addPath(package().system_support().absFilename(),
1577 FileDialog::Result result = dlg.open(toqstr(initpath),
1578 QStringList(qt_("LyX Documents (*.lyx)")));
1580 if (result.first == FileDialog::Later)
1584 filename.set(fromqstr(result.second));
1586 // check selected filename
1587 if (filename.empty()) {
1588 // emit message signal.
1589 message(_("Canceled."));
1593 bv->insertLyXFile(filename);
1597 void GuiView::insertPlaintextFile(docstring const & fname,
1600 BufferView * bv = view();
1605 FileName filename(to_utf8(fname));
1607 if (!filename.empty()) {
1608 bv->insertPlaintextFile(filename, asParagraph);
1612 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1613 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1615 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1618 if (result.first == FileDialog::Later)
1622 filename.set(fromqstr(result.second));
1624 // check selected filename
1625 if (filename.empty()) {
1626 // emit message signal.
1627 message(_("Canceled."));
1631 bv->insertPlaintextFile(filename, asParagraph);
1635 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1637 FileName fname = b.fileName();
1638 FileName const oldname = fname;
1640 if (!newname.empty()) {
1642 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1644 // Switch to this Buffer.
1647 /// No argument? Ask user through dialog.
1649 FileDialog dlg(qt_("Choose a filename to save document as"),
1650 LFUN_BUFFER_WRITE_AS);
1651 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1652 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1654 if (!isLyXFilename(fname.absFilename()))
1655 fname.changeExtension(".lyx");
1657 FileDialog::Result result =
1658 dlg.save(toqstr(fname.onlyPath().absFilename()),
1659 QStringList(qt_("LyX Documents (*.lyx)")),
1660 toqstr(fname.onlyFileName()));
1662 if (result.first == FileDialog::Later)
1665 fname.set(fromqstr(result.second));
1670 if (!isLyXFilename(fname.absFilename()))
1671 fname.changeExtension(".lyx");
1674 if (FileName(fname).exists()) {
1675 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1676 docstring text = bformat(_("The document %1$s already "
1677 "exists.\n\nDo you want to "
1678 "overwrite that document?"),
1680 int const ret = Alert::prompt(_("Overwrite document?"),
1681 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1684 case 1: return renameBuffer(b, docstring());
1685 case 2: return false;
1689 // Ok, change the name of the buffer
1690 b.setFileName(fname.absFilename());
1692 bool unnamed = b.isUnnamed();
1693 b.setUnnamed(false);
1694 b.saveCheckSum(fname);
1696 if (!saveBuffer(b)) {
1697 b.setFileName(oldname.absFilename());
1698 b.setUnnamed(unnamed);
1699 b.saveCheckSum(oldname);
1707 bool GuiView::saveBuffer(Buffer & b)
1710 return renameBuffer(b, docstring());
1713 theSession().lastFiles().add(b.fileName());
1717 // Switch to this Buffer.
1720 // FIXME: we don't tell the user *WHY* the save failed !!
1721 docstring const file = makeDisplayPath(b.absFileName(), 30);
1722 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1723 "Do you want to rename the document and "
1724 "try again?"), file);
1725 int const ret = Alert::prompt(_("Rename and save?"),
1726 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1729 if (!renameBuffer(b, docstring()))
1738 return saveBuffer(b);
1742 bool GuiView::closeBuffer()
1744 Buffer * buf = buffer();
1745 return buf && closeBuffer(*buf);
1749 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1751 // goto bookmark to update bookmark pit.
1752 //FIXME: we should update only the bookmarks related to this buffer!
1753 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1754 theLyXFunc().gotoBookmark(i+1, false, false);
1756 if (buf.isClean() || buf.paragraphs().empty()) {
1757 if (buf.masterBuffer() == &buf && tolastopened)
1758 theSession().lastOpened().add(buf.fileName());
1760 // Don't close child documents.
1761 removeWorkArea(d.current_work_area_);
1763 theBufferList().release(&buf);
1766 // Switch to this Buffer.
1771 if (buf.isUnnamed())
1772 file = from_utf8(buf.fileName().onlyFileName());
1774 file = buf.fileName().displayName(30);
1776 // Bring this window to top before asking questions.
1780 docstring const text = bformat(_("The document %1$s has unsaved changes."
1781 "\n\nDo you want to save the document or discard the changes?"), file);
1782 int const ret = Alert::prompt(_("Save changed document?"),
1783 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1787 if (!saveBuffer(buf))
1791 // if we crash after this we could
1792 // have no autosave file but I guess
1793 // this is really improbable (Jug)
1794 removeAutosaveFile(buf.absFileName());
1800 // save file names to .lyx/session
1801 // if master/slave are both open, do not save slave since it
1802 // will be automatically loaded when the master is loaded
1803 if (buf.masterBuffer() == &buf && tolastopened)
1804 theSession().lastOpened().add(buf.fileName());
1807 // Don't close child documents.
1808 removeWorkArea(d.current_work_area_);
1810 theBufferList().release(&buf);
1816 bool GuiView::dispatch(FuncRequest const & cmd)
1818 BufferView * bv = view();
1819 // By default we won't need any update.
1821 bv->cursor().updateFlags(Update::None);
1822 bool dispatched = true;
1824 switch(cmd.action) {
1825 case LFUN_BUFFER_IMPORT:
1826 importDocument(to_utf8(cmd.argument()));
1829 case LFUN_BUFFER_SWITCH:
1830 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1833 case LFUN_BUFFER_NEXT:
1834 setBuffer(theBufferList().next(buffer()));
1837 case LFUN_BUFFER_PREVIOUS:
1838 setBuffer(theBufferList().previous(buffer()));
1841 case LFUN_COMMAND_EXECUTE: {
1842 bool const show_it = cmd.argument() != "off";
1843 // FIXME: this is a hack, "minibuffer" should not be
1845 if (GuiToolbar * t = toolbar("minibuffer")) {
1846 t->setVisible(show_it);
1847 if (show_it && t->commandBuffer())
1848 t->commandBuffer()->setFocus();
1852 case LFUN_DROP_LAYOUTS_CHOICE:
1854 d.layout_->showPopup();
1857 case LFUN_MENU_OPEN:
1858 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1859 menu->exec(QCursor::pos());
1862 case LFUN_FILE_INSERT:
1863 insertLyXFile(cmd.argument());
1865 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1866 insertPlaintextFile(cmd.argument(), true);
1869 case LFUN_FILE_INSERT_PLAINTEXT:
1870 insertPlaintextFile(cmd.argument(), false);
1873 case LFUN_BUFFER_WRITE:
1875 saveBuffer(bv->buffer());
1878 case LFUN_BUFFER_WRITE_AS:
1880 renameBuffer(bv->buffer(), cmd.argument());
1883 case LFUN_BUFFER_WRITE_ALL: {
1884 Buffer * first = theBufferList().first();
1887 message(_("Saving all documents..."));
1888 // We cannot use a for loop as the buffer list cycles.
1891 if (!b->isClean()) {
1893 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1895 b = theBufferList().next(b);
1896 } while (b != first);
1897 message(_("All documents saved."));
1901 case LFUN_TOOLBAR_TOGGLE: {
1902 string const name = cmd.getArg(0);
1903 if (GuiToolbar * t = toolbar(name))
1908 case LFUN_DIALOG_UPDATE: {
1909 string const name = to_utf8(cmd.argument());
1910 // Can only update a dialog connected to an existing inset
1911 Inset * inset = getOpenInset(name);
1913 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1914 inset->dispatch(view()->cursor(), fr);
1915 } else if (name == "paragraph") {
1916 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1917 } else if (name == "prefs") {
1918 updateDialog(name, string());
1923 case LFUN_DIALOG_TOGGLE: {
1924 if (isDialogVisible(cmd.getArg(0)))
1925 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1927 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1931 case LFUN_DIALOG_DISCONNECT_INSET:
1932 disconnectDialog(to_utf8(cmd.argument()));
1935 case LFUN_DIALOG_HIDE: {
1936 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1940 case LFUN_DIALOG_SHOW: {
1941 string const name = cmd.getArg(0);
1942 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1944 if (name == "character") {
1945 data = freefont2string();
1947 showDialog("character", data);
1948 } else if (name == "latexlog") {
1949 Buffer::LogType type;
1950 string const logfile = buffer()->logName(&type);
1952 case Buffer::latexlog:
1955 case Buffer::buildlog:
1959 data += Lexer::quoteString(logfile);
1960 showDialog("log", data);
1961 } else if (name == "vclog") {
1962 string const data = "vc " +
1963 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1964 showDialog("log", data);
1965 } else if (name == "symbols") {
1966 data = bv->cursor().getEncoding()->name();
1968 showDialog("symbols", data);
1970 showDialog(name, data);
1974 case LFUN_INSET_APPLY: {
1975 string const name = cmd.getArg(0);
1976 Inset * inset = getOpenInset(name);
1978 // put cursor in front of inset.
1979 if (!view()->setCursorFromInset(inset))
1980 LASSERT(false, break);
1982 // useful if we are called from a dialog.
1983 view()->cursor().beginUndoGroup();
1984 view()->cursor().recordUndo();
1985 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1986 inset->dispatch(view()->cursor(), fr);
1987 view()->cursor().endUndoGroup();
1989 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1995 case LFUN_UI_TOGGLE:
1997 // Make sure the keyboard focus stays in the work area.
2001 case LFUN_SPLIT_VIEW:
2002 if (Buffer * buf = buffer()) {
2003 string const orientation = cmd.getArg(0);
2004 d.splitter_->setOrientation(orientation == "vertical"
2005 ? Qt::Vertical : Qt::Horizontal);
2006 TabWorkArea * twa = addTabWorkArea();
2007 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2008 setCurrentWorkArea(wa);
2012 case LFUN_CLOSE_TAB_GROUP:
2013 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2015 twa = d.currentTabWorkArea();
2016 // Switch to the next GuiWorkArea in the found TabWorkArea.
2018 d.current_work_area_ = twa->currentWorkArea();
2019 // Make sure the work area is up to date.
2020 twa->setCurrentWorkArea(d.current_work_area_);
2022 d.current_work_area_ = 0;
2024 if (d.splitter_->count() == 0)
2025 // No more work area, switch to the background widget.
2030 case LFUN_COMPLETION_INLINE:
2031 if (d.current_work_area_)
2032 d.current_work_area_->completer().showInline();
2035 case LFUN_COMPLETION_POPUP:
2036 if (d.current_work_area_)
2037 d.current_work_area_->completer().showPopup();
2041 case LFUN_COMPLETION_COMPLETE:
2042 if (d.current_work_area_)
2043 d.current_work_area_->completer().tab();
2046 case LFUN_COMPLETION_CANCEL:
2047 if (d.current_work_area_) {
2048 if (d.current_work_area_->completer().popupVisible())
2049 d.current_work_area_->completer().hidePopup();
2051 d.current_work_area_->completer().hideInline();
2055 case LFUN_COMPLETION_ACCEPT:
2056 if (d.current_work_area_)
2057 d.current_work_area_->completer().activate();
2066 // Part of automatic menu appearance feature.
2067 if (isFullScreen()) {
2068 if (menuBar()->isVisible())
2070 if (statusBar()->isVisible())
2071 statusBar()->hide();
2078 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2080 string const arg = cmd.getArg(0);
2081 if (arg == "scrollbar") {
2082 // hide() is of no help
2083 if (d.current_work_area_->verticalScrollBarPolicy() ==
2084 Qt::ScrollBarAlwaysOff)
2086 d.current_work_area_->setVerticalScrollBarPolicy(
2087 Qt::ScrollBarAsNeeded);
2089 d.current_work_area_->setVerticalScrollBarPolicy(
2090 Qt::ScrollBarAlwaysOff);
2093 if (arg == "statusbar") {
2094 statusBar()->setVisible(!statusBar()->isVisible());
2097 if (arg == "menubar") {
2098 menuBar()->setVisible(!menuBar()->isVisible());
2101 #if QT_VERSION >= 0x040300
2102 if (arg == "frame") {
2104 getContentsMargins(&l, &t, &r, &b);
2105 //are the frames in default state?
2106 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2108 setContentsMargins(-2, -2, -2, -2);
2110 setContentsMargins(0, 0, 0, 0);
2115 if (arg == "fullscreen") {
2120 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2124 void GuiView::toggleFullScreen()
2126 if (isFullScreen()) {
2127 for (int i = 0; i != d.splitter_->count(); ++i)
2128 d.tabWorkArea(i)->setFullScreen(false);
2129 #if QT_VERSION >= 0x040300
2130 setContentsMargins(0, 0, 0, 0);
2132 setWindowState(windowState() ^ Qt::WindowFullScreen);
2135 statusBar()->show();
2137 for (int i = 0; i != d.splitter_->count(); ++i)
2138 d.tabWorkArea(i)->setFullScreen(true);
2139 #if QT_VERSION >= 0x040300
2140 setContentsMargins(-2, -2, -2, -2);
2143 setWindowState(windowState() ^ Qt::WindowFullScreen);
2144 statusBar()->hide();
2146 if (lyxrc.full_screen_toolbars) {
2147 ToolbarMap::iterator end = d.toolbars_.end();
2148 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2153 // give dialogs like the TOC a chance to adapt
2158 Buffer const * GuiView::updateInset(Inset const * inset)
2160 if (!d.current_work_area_)
2164 d.current_work_area_->scheduleRedraw();
2166 return &d.current_work_area_->bufferView().buffer();
2170 void GuiView::restartCursor()
2172 /* When we move around, or type, it's nice to be able to see
2173 * the cursor immediately after the keypress.
2175 if (d.current_work_area_)
2176 d.current_work_area_->startBlinkingCursor();
2178 // Take this occasion to update the other GUI elements.
2184 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2186 if (d.current_work_area_)
2187 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2192 // This list should be kept in sync with the list of insets in
2193 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2194 // dialog should have the same name as the inset.
2195 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2196 // docs in LyXAction.cpp.
2198 char const * const dialognames[] = {
2199 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2200 "citation", "document", "errorlist", "ert", "external", "file",
2201 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2202 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2203 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2205 #ifdef HAVE_LIBAIKSAURUS
2209 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2211 char const * const * const end_dialognames =
2212 dialognames + (sizeof(dialognames) / sizeof(char *));
2216 cmpCStr(char const * name) : name_(name) {}
2217 bool operator()(char const * other) {
2218 return strcmp(other, name_) == 0;
2225 bool isValidName(string const & name)
2227 return find_if(dialognames, end_dialognames,
2228 cmpCStr(name.c_str())) != end_dialognames;
2234 void GuiView::resetDialogs()
2236 // Make sure that no LFUN uses any LyXView.
2237 theLyXFunc().setLyXView(0);
2240 constructToolbars();
2241 guiApp->menus().fillMenuBar(menuBar(), this, false);
2243 d.layout_->updateContents(true);
2244 // Now update controls with current buffer.
2245 theLyXFunc().setLyXView(this);
2251 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2253 if (!isValidName(name))
2256 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2258 if (it != d.dialogs_.end()) {
2260 it->second->hideView();
2261 return it->second.get();
2264 Dialog * dialog = build(name);
2265 d.dialogs_[name].reset(dialog);
2266 if (lyxrc.allow_geometry_session)
2267 dialog->restoreSession();
2274 void GuiView::showDialog(string const & name, string const & data,
2282 Dialog * dialog = findOrBuild(name, false);
2284 dialog->showData(data);
2286 d.open_insets_[name] = inset;
2289 catch (ExceptionMessage const & ex) {
2297 bool GuiView::isDialogVisible(string const & name) const
2299 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2300 if (it == d.dialogs_.end())
2302 return it->second.get()->isVisibleView();
2306 void GuiView::hideDialog(string const & name, Inset * inset)
2308 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2309 if (it == d.dialogs_.end())
2312 if (inset && inset != getOpenInset(name))
2315 Dialog * const dialog = it->second.get();
2316 if (dialog->isVisibleView())
2318 d.open_insets_[name] = 0;
2322 void GuiView::disconnectDialog(string const & name)
2324 if (!isValidName(name))
2327 if (d.open_insets_.find(name) != d.open_insets_.end())
2328 d.open_insets_[name] = 0;
2332 Inset * GuiView::getOpenInset(string const & name) const
2334 if (!isValidName(name))
2337 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2338 return it == d.open_insets_.end() ? 0 : it->second;
2342 void GuiView::hideAll() const
2344 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2345 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2347 for(; it != end; ++it)
2348 it->second->hideView();
2352 void GuiView::updateDialogs()
2354 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2355 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2357 for(; it != end; ++it) {
2358 Dialog * dialog = it->second.get();
2359 if (dialog && dialog->isVisibleView())
2360 dialog->checkStatus();
2367 // will be replaced by a proper factory...
2368 Dialog * createGuiAbout(GuiView & lv);
2369 Dialog * createGuiBibitem(GuiView & lv);
2370 Dialog * createGuiBibtex(GuiView & lv);
2371 Dialog * createGuiBox(GuiView & lv);
2372 Dialog * createGuiBranch(GuiView & lv);
2373 Dialog * createGuiChanges(GuiView & lv);
2374 Dialog * createGuiCharacter(GuiView & lv);
2375 Dialog * createGuiCitation(GuiView & lv);
2376 Dialog * createGuiDelimiter(GuiView & lv);
2377 Dialog * createGuiDocument(GuiView & lv);
2378 Dialog * createGuiErrorList(GuiView & lv);
2379 Dialog * createGuiERT(GuiView & lv);
2380 Dialog * createGuiExternal(GuiView & lv);
2381 Dialog * createGuiFloat(GuiView & lv);
2382 Dialog * createGuiGraphics(GuiView & lv);
2383 Dialog * createGuiHSpace(GuiView & lv);
2384 Dialog * createGuiInclude(GuiView & lv);
2385 Dialog * createGuiInfo(GuiView & lv);
2386 Dialog * createGuiLabel(GuiView & lv);
2387 Dialog * createGuiListings(GuiView & lv);
2388 Dialog * createGuiLog(GuiView & lv);
2389 Dialog * createGuiMathMatrix(GuiView & lv);
2390 Dialog * createGuiNomenclature(GuiView & lv);
2391 Dialog * createGuiNote(GuiView & lv);
2392 Dialog * createGuiParagraph(GuiView & lv);
2393 Dialog * createGuiPreferences(GuiView & lv);
2394 Dialog * createGuiPrint(GuiView & lv);
2395 Dialog * createGuiRef(GuiView & lv);
2396 Dialog * createGuiSearch(GuiView & lv);
2397 Dialog * createGuiSendTo(GuiView & lv);
2398 Dialog * createGuiShowFile(GuiView & lv);
2399 Dialog * createGuiSpellchecker(GuiView & lv);
2400 Dialog * createGuiSymbols(GuiView & lv);
2401 Dialog * createGuiTabularCreate(GuiView & lv);
2402 Dialog * createGuiTabular(GuiView & lv);
2403 Dialog * createGuiTexInfo(GuiView & lv);
2404 Dialog * createGuiToc(GuiView & lv);
2405 Dialog * createGuiThesaurus(GuiView & lv);
2406 Dialog * createGuiHyperlink(GuiView & lv);
2407 Dialog * createGuiVSpace(GuiView & lv);
2408 Dialog * createGuiViewSource(GuiView & lv);
2409 Dialog * createGuiWrap(GuiView & lv);
2412 Dialog * GuiView::build(string const & name)
2414 LASSERT(isValidName(name), return 0);
2416 if (name == "aboutlyx")
2417 return createGuiAbout(*this);
2418 if (name == "bibitem")
2419 return createGuiBibitem(*this);
2420 if (name == "bibtex")
2421 return createGuiBibtex(*this);
2423 return createGuiBox(*this);
2424 if (name == "branch")
2425 return createGuiBranch(*this);
2426 if (name == "changes")
2427 return createGuiChanges(*this);
2428 if (name == "character")
2429 return createGuiCharacter(*this);
2430 if (name == "citation")
2431 return createGuiCitation(*this);
2432 if (name == "document")
2433 return createGuiDocument(*this);
2434 if (name == "errorlist")
2435 return createGuiErrorList(*this);
2437 return createGuiERT(*this);
2438 if (name == "external")
2439 return createGuiExternal(*this);
2441 return createGuiShowFile(*this);
2442 if (name == "findreplace")
2443 return createGuiSearch(*this);
2444 if (name == "float")
2445 return createGuiFloat(*this);
2446 if (name == "graphics")
2447 return createGuiGraphics(*this);
2448 if (name == "include")
2449 return createGuiInclude(*this);
2451 return createGuiInfo(*this);
2452 if (name == "nomenclature")
2453 return createGuiNomenclature(*this);
2454 if (name == "label")
2455 return createGuiLabel(*this);
2457 return createGuiLog(*this);
2458 if (name == "view-source")
2459 return createGuiViewSource(*this);
2460 if (name == "mathdelimiter")
2461 return createGuiDelimiter(*this);
2462 if (name == "mathmatrix")
2463 return createGuiMathMatrix(*this);
2465 return createGuiNote(*this);
2466 if (name == "paragraph")
2467 return createGuiParagraph(*this);
2468 if (name == "prefs")
2469 return createGuiPreferences(*this);
2470 if (name == "print")
2471 return createGuiPrint(*this);
2473 return createGuiRef(*this);
2474 if (name == "sendto")
2475 return createGuiSendTo(*this);
2476 if (name == "space")
2477 return createGuiHSpace(*this);
2478 if (name == "spellchecker")
2479 return createGuiSpellchecker(*this);
2480 if (name == "symbols")
2481 return createGuiSymbols(*this);
2482 if (name == "tabular")
2483 return createGuiTabular(*this);
2484 if (name == "tabularcreate")
2485 return createGuiTabularCreate(*this);
2486 if (name == "texinfo")
2487 return createGuiTexInfo(*this);
2488 #ifdef HAVE_LIBAIKSAURUS
2489 if (name == "thesaurus")
2490 return createGuiThesaurus(*this);
2493 return createGuiToc(*this);
2495 return createGuiHyperlink(*this);
2496 if (name == "vspace")
2497 return createGuiVSpace(*this);
2499 return createGuiWrap(*this);
2500 if (name == "listings")
2501 return createGuiListings(*this);
2507 } // namespace frontend
2510 #include "GuiView_moc.cpp"