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_);
146 /// Toolbar store providing access to individual toolbars by name.
147 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
149 typedef boost::shared_ptr<Dialog> DialogPtr;
154 struct GuiView::GuiViewPrivate
157 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
160 // hardcode here the platform specific icon size
161 smallIconSize = 14; // scaling problems
162 normalIconSize = 20; // ok, default
163 bigIconSize = 26; // better for some math icons
165 splitter_ = new QSplitter;
166 bg_widget_ = new BackgroundWidget;
167 stack_widget_ = new QStackedWidget;
168 stack_widget_->addWidget(bg_widget_);
169 stack_widget_->addWidget(splitter_);
177 delete stack_widget_;
180 QMenu * toolBarPopup(GuiView * parent)
182 // FIXME: translation
183 QMenu * menu = new QMenu(parent);
184 QActionGroup * iconSizeGroup = new QActionGroup(parent);
186 QAction * smallIcons = new QAction(iconSizeGroup);
187 smallIcons->setText(qt_("Small-sized icons"));
188 smallIcons->setCheckable(true);
189 QObject::connect(smallIcons, SIGNAL(triggered()),
190 parent, SLOT(smallSizedIcons()));
191 menu->addAction(smallIcons);
193 QAction * normalIcons = new QAction(iconSizeGroup);
194 normalIcons->setText(qt_("Normal-sized icons"));
195 normalIcons->setCheckable(true);
196 QObject::connect(normalIcons, SIGNAL(triggered()),
197 parent, SLOT(normalSizedIcons()));
198 menu->addAction(normalIcons);
200 QAction * bigIcons = new QAction(iconSizeGroup);
201 bigIcons->setText(qt_("Big-sized icons"));
202 bigIcons->setCheckable(true);
203 QObject::connect(bigIcons, SIGNAL(triggered()),
204 parent, SLOT(bigSizedIcons()));
205 menu->addAction(bigIcons);
207 unsigned int cur = parent->iconSize().width();
208 if ( cur == parent->d.smallIconSize)
209 smallIcons->setChecked(true);
210 else if (cur == parent->d.normalIconSize)
211 normalIcons->setChecked(true);
212 else if (cur == parent->d.bigIconSize)
213 bigIcons->setChecked(true);
220 stack_widget_->setCurrentWidget(bg_widget_);
221 bg_widget_->setUpdatesEnabled(true);
224 TabWorkArea * tabWorkArea(int i)
226 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
229 TabWorkArea * currentTabWorkArea()
231 if (splitter_->count() == 1)
232 // The first TabWorkArea is always the first one, if any.
233 return tabWorkArea(0);
235 for (int i = 0; i != splitter_->count(); ++i) {
236 TabWorkArea * twa = tabWorkArea(i);
237 if (current_work_area_ == twa->currentWorkArea())
241 // None has the focus so we just take the first one.
242 return tabWorkArea(0);
246 GuiWorkArea * current_work_area_;
247 QSplitter * splitter_;
248 QStackedWidget * stack_widget_;
249 BackgroundWidget * bg_widget_;
251 ToolbarMap toolbars_;
252 /// The main layout box.
254 * \warning Don't Delete! The layout box is actually owned by
255 * whichever toolbar contains it. All the GuiView class needs is a
256 * means of accessing it.
258 * FIXME: replace that with a proper model so that we are not limited
259 * to only one dialog.
261 GuiLayoutBox * layout_;
264 map<string, Inset *> open_insets_;
267 map<string, DialogPtr> dialogs_;
269 unsigned int smallIconSize;
270 unsigned int normalIconSize;
271 unsigned int bigIconSize;
273 QTimer statusbar_timer_;
274 /// auto-saving of buffers
275 Timeout autosave_timeout_;
276 /// flag against a race condition due to multiclicks, see bug #1119
280 TocModels toc_models_;
284 GuiView::GuiView(int id)
285 : d(*new GuiViewPrivate), id_(id), closing_(false)
287 // GuiToolbars *must* be initialised before the menu bar.
288 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
291 // set ourself as the current view. This is needed for the menu bar
292 // filling, at least for the static special menu item on Mac. Otherwise
293 // they are greyed out.
294 theLyXFunc().setLyXView(this);
296 // Fill up the menu bar.
297 guiApp->menus().fillMenuBar(menuBar(), this, true);
299 setCentralWidget(d.stack_widget_);
301 // Start autosave timer
302 if (lyxrc.autosave) {
303 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
304 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
305 d.autosave_timeout_.start();
307 connect(&d.statusbar_timer_, SIGNAL(timeout()),
308 this, SLOT(clearMessage()));
310 // We don't want to keep the window in memory if it is closed.
311 setAttribute(Qt::WA_DeleteOnClose, true);
313 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
314 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
315 // since the icon is provided in the application bundle.
316 setWindowIcon(QPixmap(":/images/lyx.png"));
320 setAcceptDrops(true);
322 statusBar()->setSizeGripEnabled(true);
324 // Forbid too small unresizable window because it can happen
325 // with some window manager under X11.
326 setMinimumSize(300, 200);
328 if (lyxrc.allow_geometry_session) {
329 // Now take care of session management.
334 // no session handling, default to a sane size.
335 setGeometry(50, 50, 690, 510);
338 // clear session data if any.
340 settings.remove("views");
350 void GuiView::saveLayout() const
353 settings.beginGroup("views");
354 settings.beginGroup(QString::number(id_));
356 settings.setValue("pos", pos());
357 settings.setValue("size", size());
359 settings.setValue("geometry", saveGeometry());
361 settings.setValue("layout", saveState(0));
362 settings.setValue("icon_size", iconSize());
366 bool GuiView::restoreLayout()
369 settings.beginGroup("views");
370 settings.beginGroup(QString::number(id_));
371 QString const icon_key = "icon_size";
372 if (!settings.contains(icon_key))
375 setIconSize(settings.value(icon_key).toSize());
377 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
378 QSize size = settings.value("size", QSize(690, 510)).toSize();
382 if (!restoreGeometry(settings.value("geometry").toByteArray()))
383 setGeometry(50, 50, 690, 510);
385 // Make sure layout is correctly oriented.
386 setLayoutDirection(qApp->layoutDirection());
388 // Allow the toc and view-source dock widget to be restored if needed.
389 findOrBuild("toc", true);
390 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 guiApp->setCurrentView(this);
487 theLyXFunc().setLyXView(this);
488 QMainWindow::setFocus();
489 if (d.current_work_area_)
490 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.
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 // Asynchronously post the event. DropEvent usually come
613 // from the BufferView. But reloading a file might close
614 // the BufferView from within its own event handler.
615 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
622 void GuiView::message(docstring const & str)
624 if (ForkedProcess::iAmAChild())
627 statusBar()->showMessage(toqstr(str));
628 d.statusbar_timer_.stop();
629 d.statusbar_timer_.start(3000);
633 void GuiView::smallSizedIcons()
635 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
639 void GuiView::normalSizedIcons()
641 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
645 void GuiView::bigSizedIcons()
647 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
651 void GuiView::clearMessage()
655 theLyXFunc().setLyXView(this);
656 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
657 d.statusbar_timer_.stop();
661 void GuiView::updateWindowTitle(GuiWorkArea * wa)
663 if (wa != d.current_work_area_)
665 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
666 setWindowIconText(wa->windowIconText());
670 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
673 disconnectBufferView();
674 connectBufferView(wa->bufferView());
675 connectBuffer(wa->bufferView().buffer());
676 d.current_work_area_ = wa;
677 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
678 this, SLOT(updateWindowTitle(GuiWorkArea *)));
679 updateWindowTitle(wa);
683 // The document settings needs to be reinitialised.
684 updateDialog("document", "");
686 // Buffer-dependent dialogs must be updated. This is done here because
687 // some dialogs require buffer()->text.
692 void GuiView::on_lastWorkAreaRemoved()
695 // We already are in a close event. Nothing more to do.
698 if (d.splitter_->count() > 1)
699 // We have a splitter so don't close anything.
702 // Reset and updates the dialogs.
703 d.toc_models_.reset(0);
704 updateDialog("document", "");
707 resetWindowTitleAndIconText();
709 if (lyxrc.open_buffers_in_tabs)
710 // Nothing more to do, the window should stay open.
713 if (guiApp->viewIds().size() > 1) {
719 // On Mac we also close the last window because the application stay
720 // resident in memory. On other platforms we don't close the last
721 // window because this would quit the application.
727 void GuiView::updateStatusBar()
729 // let the user see the explicit message
730 if (d.statusbar_timer_.isActive())
733 theLyXFunc().setLyXView(this);
734 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
738 bool GuiView::hasFocus() const
740 return qApp->activeWindow() == this;
744 bool GuiView::event(QEvent * e)
748 // Useful debug code:
749 //case QEvent::ActivationChange:
750 //case QEvent::WindowDeactivate:
751 //case QEvent::Paint:
752 //case QEvent::Enter:
753 //case QEvent::Leave:
754 //case QEvent::HoverEnter:
755 //case QEvent::HoverLeave:
756 //case QEvent::HoverMove:
757 //case QEvent::StatusTip:
758 //case QEvent::DragEnter:
759 //case QEvent::DragLeave:
763 case QEvent::WindowActivate: {
764 if (this == guiApp->currentView()) {
766 return QMainWindow::event(e);
768 guiApp->setCurrentView(this);
769 theLyXFunc().setLyXView(this);
770 if (d.current_work_area_) {
771 BufferView & bv = d.current_work_area_->bufferView();
772 connectBufferView(bv);
773 connectBuffer(bv.buffer());
774 // The document structure, name and dialogs might have
775 // changed in another view.
777 // The document settings needs to be reinitialised.
778 updateDialog("document", "");
781 resetWindowTitleAndIconText();
784 return QMainWindow::event(e);
787 case QEvent::ShortcutOverride: {
791 if (isFullScreen() && menuBar()->isHidden()) {
792 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
793 // FIXME: we should also try to detect special LyX shortcut such as
794 // Alt-P and Alt-M. Right now there is a hack in
795 // GuiWorkArea::processKeySym() that hides again the menubar for
797 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
799 return QMainWindow::event(e);
803 if (d.current_work_area_)
804 // Nothing special to do.
805 return QMainWindow::event(e);
807 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
808 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
810 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
811 || ke->key() == Qt::Key_Backtab)
812 return QMainWindow::event(e);
814 // Allow processing of shortcuts that are allowed even when no Buffer
816 theLyXFunc().setLyXView(this);
818 setKeySymbol(&sym, ke);
819 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
825 return QMainWindow::event(e);
829 void GuiView::resetWindowTitleAndIconText()
831 setWindowTitle(qt_("LyX"));
832 setWindowIconText(qt_("LyX"));
835 bool GuiView::focusNextPrevChild(bool /*next*/)
842 void GuiView::setBusy(bool busy)
844 if (d.current_work_area_) {
845 d.current_work_area_->setUpdatesEnabled(!busy);
847 d.current_work_area_->stopBlinkingCursor();
849 d.current_work_area_->startBlinkingCursor();
853 QApplication::setOverrideCursor(Qt::WaitCursor);
855 QApplication::restoreOverrideCursor();
859 GuiWorkArea * GuiView::workArea(Buffer & buffer)
861 if (TabWorkArea * twa = d.currentTabWorkArea())
862 return twa->workArea(buffer);
867 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
869 // Automatically create a TabWorkArea if there are none yet.
870 TabWorkArea * tab_widget = d.splitter_->count()
871 ? d.currentTabWorkArea() : addTabWorkArea();
872 return tab_widget->addWorkArea(buffer, *this);
876 TabWorkArea * GuiView::addTabWorkArea()
878 TabWorkArea * twa = new TabWorkArea;
879 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
880 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
881 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
882 this, SLOT(on_lastWorkAreaRemoved()));
884 d.splitter_->addWidget(twa);
885 d.stack_widget_->setCurrentWidget(d.splitter_);
890 GuiWorkArea const * GuiView::currentWorkArea() const
892 return d.current_work_area_;
896 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
899 d.current_work_area_ = wa;
900 for (int i = 0; i != d.splitter_->count(); ++i) {
901 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
907 void GuiView::removeWorkArea(GuiWorkArea * wa)
910 if (wa == d.current_work_area_) {
912 disconnectBufferView();
913 d.current_work_area_ = 0;
916 for (int i = 0; i != d.splitter_->count(); ++i) {
917 TabWorkArea * twa = d.tabWorkArea(i);
918 if (!twa->removeWorkArea(wa))
919 // Not found in this tab group.
922 // We found and removed the GuiWorkArea.
924 // No more WorkAreas in this tab group, so delete it.
929 if (d.current_work_area_)
930 // This means that we are not closing the current GuiWorkArea;
933 // Switch to the next GuiWorkArea in the found TabWorkArea.
934 d.current_work_area_ = twa->currentWorkArea();
938 if (d.splitter_->count() == 0)
939 // No more work area, switch to the background widget.
944 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
950 void GuiView::updateLayoutList()
953 d.layout_->updateContents(false);
957 void GuiView::updateToolbars()
959 ToolbarMap::iterator end = d.toolbars_.end();
960 if (d.current_work_area_) {
962 d.current_work_area_->bufferView().cursor().inMathed();
964 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
966 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
967 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
968 bool const mathmacrotemplate =
969 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
971 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
972 it->second->update(math, table, review, mathmacrotemplate);
974 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
975 it->second->update(false, false, false, false);
979 Buffer * GuiView::buffer()
981 if (d.current_work_area_)
982 return &d.current_work_area_->bufferView().buffer();
987 Buffer const * GuiView::buffer() const
989 if (d.current_work_area_)
990 return &d.current_work_area_->bufferView().buffer();
995 void GuiView::setBuffer(Buffer * newBuffer)
997 LASSERT(newBuffer, return);
1000 GuiWorkArea * wa = workArea(*newBuffer);
1002 updateLabels(*newBuffer->masterBuffer());
1003 wa = addWorkArea(*newBuffer);
1005 //Disconnect the old buffer...there's no new one.
1008 connectBuffer(*newBuffer);
1009 connectBufferView(wa->bufferView());
1010 setCurrentWorkArea(wa);
1016 void GuiView::connectBuffer(Buffer & buf)
1018 buf.setGuiDelegate(this);
1022 void GuiView::disconnectBuffer()
1024 if (d.current_work_area_)
1025 d.current_work_area_->bufferView().setGuiDelegate(0);
1029 void GuiView::connectBufferView(BufferView & bv)
1031 bv.setGuiDelegate(this);
1035 void GuiView::disconnectBufferView()
1037 if (d.current_work_area_)
1038 d.current_work_area_->bufferView().setGuiDelegate(0);
1042 void GuiView::errors(string const & error_type)
1044 ErrorList & el = buffer()->errorList(error_type);
1046 showDialog("errorlist", error_type);
1050 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1052 d.toc_models_.updateItem(toqstr(type), dit);
1056 void GuiView::structureChanged()
1058 d.toc_models_.reset(view());
1059 // Navigator needs more than a simple update in this case. It needs to be
1061 updateDialog("toc", "");
1065 void GuiView::updateDialog(string const & name, string const & data)
1067 if (!isDialogVisible(name))
1070 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1071 if (it == d.dialogs_.end())
1074 Dialog * const dialog = it->second.get();
1075 if (dialog->isVisibleView())
1076 dialog->initialiseParams(data);
1080 BufferView * GuiView::view()
1082 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1086 void GuiView::autoSave()
1088 LYXERR(Debug::INFO, "Running autoSave()");
1091 view()->buffer().autoSave();
1095 void GuiView::resetAutosaveTimers()
1098 d.autosave_timeout_.restart();
1102 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1105 Buffer * buf = buffer();
1107 /* In LyX/Mac, when a dialog is open, the menus of the
1108 application can still be accessed without giving focus to
1109 the main window. In this case, we want to disable the menu
1110 entries that are buffer-related.
1112 Note that this code is not perfect, as bug 1941 attests:
1113 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1115 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1118 switch(cmd.action) {
1119 case LFUN_BUFFER_WRITE:
1120 enable = buf && (buf->isUnnamed() || !buf->isClean());
1123 case LFUN_BUFFER_WRITE_AS:
1127 case LFUN_SPLIT_VIEW:
1128 if (cmd.getArg(0) == "vertical")
1129 enable = buf && (d.splitter_->count() == 1 ||
1130 d.splitter_->orientation() == Qt::Vertical);
1132 enable = buf && (d.splitter_->count() == 1 ||
1133 d.splitter_->orientation() == Qt::Horizontal);
1136 case LFUN_CLOSE_TAB_GROUP:
1137 enable = d.currentTabWorkArea();
1140 case LFUN_TOOLBAR_TOGGLE:
1141 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1142 flag.setOnOff(t->isVisible());
1145 case LFUN_UI_TOGGLE:
1146 flag.setOnOff(isFullScreen());
1149 case LFUN_DIALOG_TOGGLE:
1150 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1151 // fall through to set "enable"
1152 case LFUN_DIALOG_SHOW: {
1153 string const name = cmd.getArg(0);
1155 enable = name == "aboutlyx"
1156 || name == "file" //FIXME: should be removed.
1158 || name == "texinfo";
1159 else if (name == "print")
1160 enable = buf->isExportable("dvi")
1161 && lyxrc.print_command != "none";
1162 else if (name == "character") {
1166 InsetCode ic = view()->cursor().inset().lyxCode();
1167 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1170 else if (name == "symbols") {
1171 if (!view() || view()->cursor().inMathed())
1174 InsetCode ic = view()->cursor().inset().lyxCode();
1175 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1178 else if (name == "latexlog")
1179 enable = FileName(buf->logName()).isReadableFile();
1180 else if (name == "spellchecker")
1181 #if defined (USE_ASPELL)
1182 enable = !buf->isReadonly();
1186 else if (name == "vclog")
1187 enable = buf->lyxvc().inUse();
1191 case LFUN_DIALOG_UPDATE: {
1192 string const name = cmd.getArg(0);
1194 enable = name == "prefs";
1198 case LFUN_INSET_APPLY: {
1199 string const name = cmd.getArg(0);
1200 Inset * inset = getOpenInset(name);
1202 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1204 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1205 // Every inset is supposed to handle this
1206 LASSERT(false, break);
1210 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1211 flag |= lyx::getStatus(fr);
1213 enable = flag.enabled();
1217 case LFUN_COMPLETION_INLINE:
1218 if (!d.current_work_area_
1219 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1223 case LFUN_COMPLETION_POPUP:
1224 if (!d.current_work_area_
1225 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1229 case LFUN_COMPLETION_COMPLETE:
1230 if (!d.current_work_area_
1231 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1235 case LFUN_COMPLETION_ACCEPT:
1236 case LFUN_COMPLETION_CANCEL:
1237 if (!d.current_work_area_
1238 || (!d.current_work_area_->completer().popupVisible()
1239 && !d.current_work_area_->completer().inlineVisible()))
1248 flag.setEnabled(false);
1254 static FileName selectTemplateFile()
1256 FileDialog dlg(qt_("Select template file"));
1257 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1258 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1260 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1261 QStringList(qt_("LyX Documents (*.lyx)")));
1263 if (result.first == FileDialog::Later)
1265 if (result.second.isEmpty())
1267 return FileName(fromqstr(result.second));
1271 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1275 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1278 message(_("Document not loaded."));
1283 setBuffer(newBuffer);
1285 // scroll to the position when the file was last closed
1286 if (lyxrc.use_lastfilepos) {
1287 LastFilePosSection::FilePos filepos =
1288 theSession().lastFilePos().load(filename);
1289 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1293 theSession().lastFiles().add(filename);
1300 void GuiView::openDocument(string const & fname)
1302 string initpath = lyxrc.document_path;
1305 string const trypath = buffer()->filePath();
1306 // If directory is writeable, use this as default.
1307 if (FileName(trypath).isDirWritable())
1313 if (fname.empty()) {
1314 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1315 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1316 dlg.setButton2(qt_("Examples|#E#e"),
1317 toqstr(addPath(package().system_support().absFilename(), "examples")));
1319 QStringList filter(qt_("LyX Documents (*.lyx)"));
1320 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1321 << qt_("LyX-1.4.x Documents (*.lyx14)")
1322 << qt_("LyX-1.5.x Documents (*.lyx15)");
1323 FileDialog::Result result =
1324 dlg.open(toqstr(initpath), filter);
1326 if (result.first == FileDialog::Later)
1329 filename = fromqstr(result.second);
1331 // check selected filename
1332 if (filename.empty()) {
1333 message(_("Canceled."));
1339 // get absolute path of file and add ".lyx" to the filename if
1341 FileName const fullname =
1342 fileSearch(string(), filename, "lyx", support::may_not_exist);
1343 if (!fullname.empty())
1344 filename = fullname.absFilename();
1346 if (!fullname.onlyPath().isDirectory()) {
1347 Alert::warning(_("Invalid filename"),
1348 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1349 from_utf8(fullname.absFilename())));
1352 // if the file doesn't exist, let the user create one
1353 if (!fullname.exists()) {
1354 // the user specifically chose this name. Believe him.
1355 Buffer * const b = newFile(filename, string(), true);
1361 docstring const disp_fn = makeDisplayPath(filename);
1362 message(bformat(_("Opening document %1$s..."), disp_fn));
1365 Buffer * buf = loadDocument(fullname);
1370 buf->errors("Parse");
1371 str2 = bformat(_("Document %1$s opened."), disp_fn);
1372 if (buf->lyxvc().inUse())
1373 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1374 " " + _("Version control detected.");
1376 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1381 // FIXME: clean that
1382 static bool import(GuiView * lv, FileName const & filename,
1383 string const & format, ErrorList & errorList)
1385 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1387 string loader_format;
1388 vector<string> loaders = theConverters().loaders();
1389 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1390 for (vector<string>::const_iterator it = loaders.begin();
1391 it != loaders.end(); ++it) {
1392 if (!theConverters().isReachable(format, *it))
1395 string const tofile =
1396 support::changeExtension(filename.absFilename(),
1397 formats.extension(*it));
1398 if (!theConverters().convert(0, filename, FileName(tofile),
1399 filename, format, *it, errorList))
1401 loader_format = *it;
1404 if (loader_format.empty()) {
1405 frontend::Alert::error(_("Couldn't import file"),
1406 bformat(_("No information for importing the format %1$s."),
1407 formats.prettyName(format)));
1411 loader_format = format;
1413 if (loader_format == "lyx") {
1414 Buffer * buf = lv->loadDocument(lyxfile);
1419 buf->errors("Parse");
1421 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1425 bool as_paragraphs = loader_format == "textparagraph";
1426 string filename2 = (loader_format == format) ? filename.absFilename()
1427 : support::changeExtension(filename.absFilename(),
1428 formats.extension(loader_format));
1429 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1430 theLyXFunc().setLyXView(lv);
1431 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1438 void GuiView::importDocument(string const & argument)
1441 string filename = split(argument, format, ' ');
1443 LYXERR(Debug::INFO, format << " file: " << filename);
1445 // need user interaction
1446 if (filename.empty()) {
1447 string initpath = lyxrc.document_path;
1449 Buffer const * buf = buffer();
1451 string const trypath = buf->filePath();
1452 // If directory is writeable, use this as default.
1453 if (FileName(trypath).isDirWritable())
1457 docstring const text = bformat(_("Select %1$s file to import"),
1458 formats.prettyName(format));
1460 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1461 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1462 dlg.setButton2(qt_("Examples|#E#e"),
1463 toqstr(addPath(package().system_support().absFilename(), "examples")));
1465 docstring filter = formats.prettyName(format);
1468 filter += from_utf8(formats.extension(format));
1471 FileDialog::Result result =
1472 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1474 if (result.first == FileDialog::Later)
1477 filename = fromqstr(result.second);
1479 // check selected filename
1480 if (filename.empty())
1481 message(_("Canceled."));
1484 if (filename.empty())
1487 // get absolute path of file
1488 FileName const fullname(support::makeAbsPath(filename));
1490 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1492 // Check if the document already is open
1493 Buffer * buf = theBufferList().getBuffer(lyxfile);
1496 if (!closeBuffer()) {
1497 message(_("Canceled."));
1502 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1504 // if the file exists already, and we didn't do
1505 // -i lyx thefile.lyx, warn
1506 if (lyxfile.exists() && fullname != lyxfile) {
1508 docstring text = bformat(_("The document %1$s already exists.\n\n"
1509 "Do you want to overwrite that document?"), displaypath);
1510 int const ret = Alert::prompt(_("Overwrite document?"),
1511 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1514 message(_("Canceled."));
1519 message(bformat(_("Importing %1$s..."), displaypath));
1520 ErrorList errorList;
1521 if (import(this, fullname, format, errorList))
1522 message(_("imported."));
1524 message(_("file not imported!"));
1526 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1530 void GuiView::newDocument(string const & filename, bool from_template)
1532 FileName initpath(lyxrc.document_path);
1533 Buffer * buf = buffer();
1535 FileName const trypath(buf->filePath());
1536 // If directory is writeable, use this as default.
1537 if (trypath.isDirWritable())
1541 string templatefile = from_template ?
1542 selectTemplateFile().absFilename() : string();
1544 if (filename.empty())
1545 b = newUnnamedFile(templatefile, initpath);
1547 b = newFile(filename, templatefile, true);
1551 // Ensure the cursor is correctly positionned on screen.
1552 view()->showCursor();
1556 void GuiView::insertLyXFile(docstring const & fname)
1558 BufferView * bv = view();
1563 FileName filename(to_utf8(fname));
1565 if (!filename.empty()) {
1566 bv->insertLyXFile(filename);
1570 // Launch a file browser
1572 string initpath = lyxrc.document_path;
1573 string const trypath = bv->buffer().filePath();
1574 // If directory is writeable, use this as default.
1575 if (FileName(trypath).isDirWritable())
1579 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1580 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1581 dlg.setButton2(qt_("Examples|#E#e"),
1582 toqstr(addPath(package().system_support().absFilename(),
1585 FileDialog::Result result = dlg.open(toqstr(initpath),
1586 QStringList(qt_("LyX Documents (*.lyx)")));
1588 if (result.first == FileDialog::Later)
1592 filename.set(fromqstr(result.second));
1594 // check selected filename
1595 if (filename.empty()) {
1596 // emit message signal.
1597 message(_("Canceled."));
1601 bv->insertLyXFile(filename);
1605 void GuiView::insertPlaintextFile(docstring const & fname,
1608 BufferView * bv = view();
1613 FileName filename(to_utf8(fname));
1615 if (!filename.empty()) {
1616 bv->insertPlaintextFile(filename, asParagraph);
1620 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1621 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1623 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1626 if (result.first == FileDialog::Later)
1630 filename.set(fromqstr(result.second));
1632 // check selected filename
1633 if (filename.empty()) {
1634 // emit message signal.
1635 message(_("Canceled."));
1639 bv->insertPlaintextFile(filename, asParagraph);
1643 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1645 FileName fname = b.fileName();
1646 FileName const oldname = fname;
1648 if (!newname.empty()) {
1650 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1652 // Switch to this Buffer.
1655 /// No argument? Ask user through dialog.
1657 FileDialog dlg(qt_("Choose a filename to save document as"),
1658 LFUN_BUFFER_WRITE_AS);
1659 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1660 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1662 if (!isLyXFilename(fname.absFilename()))
1663 fname.changeExtension(".lyx");
1665 FileDialog::Result result =
1666 dlg.save(toqstr(fname.onlyPath().absFilename()),
1667 QStringList(qt_("LyX Documents (*.lyx)")),
1668 toqstr(fname.onlyFileName()));
1670 if (result.first == FileDialog::Later)
1673 fname.set(fromqstr(result.second));
1678 if (!isLyXFilename(fname.absFilename()))
1679 fname.changeExtension(".lyx");
1682 if (FileName(fname).exists()) {
1683 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1684 docstring text = bformat(_("The document %1$s already "
1685 "exists.\n\nDo you want to "
1686 "overwrite that document?"),
1688 int const ret = Alert::prompt(_("Overwrite document?"),
1689 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1692 case 1: return renameBuffer(b, docstring());
1693 case 2: return false;
1697 // Ok, change the name of the buffer
1698 b.setFileName(fname.absFilename());
1700 bool unnamed = b.isUnnamed();
1701 b.setUnnamed(false);
1702 b.saveCheckSum(fname);
1704 if (!saveBuffer(b)) {
1705 b.setFileName(oldname.absFilename());
1706 b.setUnnamed(unnamed);
1707 b.saveCheckSum(oldname);
1715 bool GuiView::saveBuffer(Buffer & b)
1718 return renameBuffer(b, docstring());
1721 theSession().lastFiles().add(b.fileName());
1725 // Switch to this Buffer.
1728 // FIXME: we don't tell the user *WHY* the save failed !!
1729 docstring const file = makeDisplayPath(b.absFileName(), 30);
1730 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1731 "Do you want to rename the document and "
1732 "try again?"), file);
1733 int const ret = Alert::prompt(_("Rename and save?"),
1734 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1737 if (!renameBuffer(b, docstring()))
1746 return saveBuffer(b);
1750 bool GuiView::closeBuffer()
1752 Buffer * buf = buffer();
1753 return buf && closeBuffer(*buf);
1757 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1759 // goto bookmark to update bookmark pit.
1760 //FIXME: we should update only the bookmarks related to this buffer!
1761 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1762 theLyXFunc().gotoBookmark(i+1, false, false);
1764 if (buf.isClean() || buf.paragraphs().empty()) {
1765 if (buf.masterBuffer() == &buf && tolastopened)
1766 theSession().lastOpened().add(buf.fileName());
1768 // Don't close child documents.
1769 removeWorkArea(d.current_work_area_);
1771 theBufferList().release(&buf);
1774 // Switch to this Buffer.
1779 if (buf.isUnnamed())
1780 file = from_utf8(buf.fileName().onlyFileName());
1782 file = buf.fileName().displayName(30);
1784 // Bring this window to top before asking questions.
1788 docstring const text = bformat(_("The document %1$s has unsaved changes."
1789 "\n\nDo you want to save the document or discard the changes?"), file);
1790 int const ret = Alert::prompt(_("Save changed document?"),
1791 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1795 if (!saveBuffer(buf))
1799 // if we crash after this we could
1800 // have no autosave file but I guess
1801 // this is really improbable (Jug)
1802 removeAutosaveFile(buf.absFileName());
1808 // save file names to .lyx/session
1809 // if master/slave are both open, do not save slave since it
1810 // will be automatically loaded when the master is loaded
1811 if (buf.masterBuffer() == &buf && tolastopened)
1812 theSession().lastOpened().add(buf.fileName());
1815 // Don't close child documents.
1816 removeWorkArea(d.current_work_area_);
1818 theBufferList().release(&buf);
1824 bool GuiView::dispatch(FuncRequest const & cmd)
1826 BufferView * bv = view();
1827 // By default we won't need any update.
1829 bv->cursor().updateFlags(Update::None);
1830 bool dispatched = true;
1832 switch(cmd.action) {
1833 case LFUN_BUFFER_IMPORT:
1834 importDocument(to_utf8(cmd.argument()));
1837 case LFUN_BUFFER_SWITCH:
1838 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1841 case LFUN_BUFFER_NEXT:
1842 setBuffer(theBufferList().next(buffer()));
1845 case LFUN_BUFFER_PREVIOUS:
1846 setBuffer(theBufferList().previous(buffer()));
1849 case LFUN_COMMAND_EXECUTE: {
1850 bool const show_it = cmd.argument() != "off";
1851 // FIXME: this is a hack, "minibuffer" should not be
1853 if (GuiToolbar * t = toolbar("minibuffer")) {
1854 t->setVisible(show_it);
1855 if (show_it && t->commandBuffer())
1856 t->commandBuffer()->setFocus();
1860 case LFUN_DROP_LAYOUTS_CHOICE:
1862 d.layout_->showPopup();
1865 case LFUN_MENU_OPEN:
1866 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1867 menu->exec(QCursor::pos());
1870 case LFUN_FILE_INSERT:
1871 insertLyXFile(cmd.argument());
1873 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1874 insertPlaintextFile(cmd.argument(), true);
1877 case LFUN_FILE_INSERT_PLAINTEXT:
1878 insertPlaintextFile(cmd.argument(), false);
1881 case LFUN_BUFFER_WRITE:
1883 saveBuffer(bv->buffer());
1886 case LFUN_BUFFER_WRITE_AS:
1888 renameBuffer(bv->buffer(), cmd.argument());
1891 case LFUN_BUFFER_WRITE_ALL: {
1892 Buffer * first = theBufferList().first();
1895 message(_("Saving all documents..."));
1896 // We cannot use a for loop as the buffer list cycles.
1899 if (!b->isClean()) {
1901 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1903 b = theBufferList().next(b);
1904 } while (b != first);
1905 message(_("All documents saved."));
1909 case LFUN_TOOLBAR_TOGGLE: {
1910 string const name = cmd.getArg(0);
1911 if (GuiToolbar * t = toolbar(name))
1916 case LFUN_DIALOG_UPDATE: {
1917 string const name = to_utf8(cmd.argument());
1918 // Can only update a dialog connected to an existing inset
1919 Inset * inset = getOpenInset(name);
1921 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1922 inset->dispatch(view()->cursor(), fr);
1923 } else if (name == "paragraph") {
1924 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1925 } else if (name == "prefs") {
1926 updateDialog(name, string());
1931 case LFUN_DIALOG_TOGGLE: {
1932 if (isDialogVisible(cmd.getArg(0)))
1933 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1935 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1939 case LFUN_DIALOG_DISCONNECT_INSET:
1940 disconnectDialog(to_utf8(cmd.argument()));
1943 case LFUN_DIALOG_HIDE: {
1944 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1948 case LFUN_DIALOG_SHOW: {
1949 string const name = cmd.getArg(0);
1950 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1952 if (name == "character") {
1953 data = freefont2string();
1955 showDialog("character", data);
1956 } else if (name == "latexlog") {
1957 Buffer::LogType type;
1958 string const logfile = buffer()->logName(&type);
1960 case Buffer::latexlog:
1963 case Buffer::buildlog:
1967 data += Lexer::quoteString(logfile);
1968 showDialog("log", data);
1969 } else if (name == "vclog") {
1970 string const data = "vc " +
1971 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1972 showDialog("log", data);
1973 } else if (name == "symbols") {
1974 data = bv->cursor().getEncoding()->name();
1976 showDialog("symbols", data);
1978 } else if (name == "prefs" && isFullScreen()) {
1979 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
1981 showDialog("prefs", data);
1983 showDialog(name, data);
1987 case LFUN_INSET_APPLY: {
1988 string const name = cmd.getArg(0);
1989 Inset * inset = getOpenInset(name);
1991 // put cursor in front of inset.
1992 if (!view()->setCursorFromInset(inset))
1993 LASSERT(false, break);
1995 // useful if we are called from a dialog.
1996 view()->cursor().beginUndoGroup();
1997 view()->cursor().recordUndo();
1998 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1999 inset->dispatch(view()->cursor(), fr);
2000 view()->cursor().endUndoGroup();
2002 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2008 case LFUN_UI_TOGGLE:
2010 // Make sure the keyboard focus stays in the work area.
2014 case LFUN_SPLIT_VIEW:
2015 if (Buffer * buf = buffer()) {
2016 string const orientation = cmd.getArg(0);
2017 d.splitter_->setOrientation(orientation == "vertical"
2018 ? Qt::Vertical : Qt::Horizontal);
2019 TabWorkArea * twa = addTabWorkArea();
2020 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2021 setCurrentWorkArea(wa);
2025 case LFUN_CLOSE_TAB_GROUP:
2026 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2028 twa = d.currentTabWorkArea();
2029 // Switch to the next GuiWorkArea in the found TabWorkArea.
2031 d.current_work_area_ = twa->currentWorkArea();
2032 // Make sure the work area is up to date.
2033 twa->setCurrentWorkArea(d.current_work_area_);
2035 d.current_work_area_ = 0;
2037 if (d.splitter_->count() == 0)
2038 // No more work area, switch to the background widget.
2043 case LFUN_COMPLETION_INLINE:
2044 if (d.current_work_area_)
2045 d.current_work_area_->completer().showInline();
2048 case LFUN_COMPLETION_POPUP:
2049 if (d.current_work_area_)
2050 d.current_work_area_->completer().showPopup();
2054 case LFUN_COMPLETION_COMPLETE:
2055 if (d.current_work_area_)
2056 d.current_work_area_->completer().tab();
2059 case LFUN_COMPLETION_CANCEL:
2060 if (d.current_work_area_) {
2061 if (d.current_work_area_->completer().popupVisible())
2062 d.current_work_area_->completer().hidePopup();
2064 d.current_work_area_->completer().hideInline();
2068 case LFUN_COMPLETION_ACCEPT:
2069 if (d.current_work_area_)
2070 d.current_work_area_->completer().activate();
2079 // Part of automatic menu appearance feature.
2080 if (isFullScreen()) {
2081 if (menuBar()->isVisible())
2083 if (statusBar()->isVisible())
2084 statusBar()->hide();
2091 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2093 string const arg = cmd.getArg(0);
2094 if (arg == "scrollbar") {
2095 // hide() is of no help
2096 if (d.current_work_area_->verticalScrollBarPolicy() ==
2097 Qt::ScrollBarAlwaysOff)
2099 d.current_work_area_->setVerticalScrollBarPolicy(
2100 Qt::ScrollBarAsNeeded);
2102 d.current_work_area_->setVerticalScrollBarPolicy(
2103 Qt::ScrollBarAlwaysOff);
2106 if (arg == "statusbar") {
2107 statusBar()->setVisible(!statusBar()->isVisible());
2110 if (arg == "menubar") {
2111 menuBar()->setVisible(!menuBar()->isVisible());
2114 #if QT_VERSION >= 0x040300
2115 if (arg == "frame") {
2117 getContentsMargins(&l, &t, &r, &b);
2118 //are the frames in default state?
2119 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2121 setContentsMargins(-2, -2, -2, -2);
2123 setContentsMargins(0, 0, 0, 0);
2128 if (arg == "fullscreen") {
2133 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2137 void GuiView::toggleFullScreen()
2139 if (isFullScreen()) {
2140 for (int i = 0; i != d.splitter_->count(); ++i)
2141 d.tabWorkArea(i)->setFullScreen(false);
2142 #if QT_VERSION >= 0x040300
2143 setContentsMargins(0, 0, 0, 0);
2145 setWindowState(windowState() ^ Qt::WindowFullScreen);
2148 statusBar()->show();
2151 hideDialogs("prefs", 0);
2152 for (int i = 0; i != d.splitter_->count(); ++i)
2153 d.tabWorkArea(i)->setFullScreen(true);
2154 #if QT_VERSION >= 0x040300
2155 setContentsMargins(-2, -2, -2, -2);
2158 setWindowState(windowState() ^ Qt::WindowFullScreen);
2159 statusBar()->hide();
2161 if (lyxrc.full_screen_toolbars) {
2162 ToolbarMap::iterator end = d.toolbars_.end();
2163 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2168 // give dialogs like the TOC a chance to adapt
2173 Buffer const * GuiView::updateInset(Inset const * inset)
2175 if (!d.current_work_area_)
2179 d.current_work_area_->scheduleRedraw();
2181 return &d.current_work_area_->bufferView().buffer();
2185 void GuiView::restartCursor()
2187 /* When we move around, or type, it's nice to be able to see
2188 * the cursor immediately after the keypress.
2190 if (d.current_work_area_)
2191 d.current_work_area_->startBlinkingCursor();
2193 // Take this occasion to update the other GUI elements.
2199 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2201 if (d.current_work_area_)
2202 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2207 // This list should be kept in sync with the list of insets in
2208 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2209 // dialog should have the same name as the inset.
2210 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2211 // docs in LyXAction.cpp.
2213 char const * const dialognames[] = {
2214 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2215 "citation", "document", "errorlist", "ert", "external", "file",
2216 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2217 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2218 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2220 #ifdef HAVE_LIBAIKSAURUS
2224 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2226 char const * const * const end_dialognames =
2227 dialognames + (sizeof(dialognames) / sizeof(char *));
2231 cmpCStr(char const * name) : name_(name) {}
2232 bool operator()(char const * other) {
2233 return strcmp(other, name_) == 0;
2240 bool isValidName(string const & name)
2242 return find_if(dialognames, end_dialognames,
2243 cmpCStr(name.c_str())) != end_dialognames;
2249 void GuiView::resetDialogs()
2251 // Make sure that no LFUN uses any LyXView.
2252 theLyXFunc().setLyXView(0);
2255 constructToolbars();
2256 guiApp->menus().fillMenuBar(menuBar(), this, false);
2258 d.layout_->updateContents(true);
2259 // Now update controls with current buffer.
2260 theLyXFunc().setLyXView(this);
2266 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2268 if (!isValidName(name))
2271 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2273 if (it != d.dialogs_.end()) {
2275 it->second->hideView();
2276 return it->second.get();
2279 Dialog * dialog = build(name);
2280 d.dialogs_[name].reset(dialog);
2281 if (lyxrc.allow_geometry_session)
2282 dialog->restoreSession();
2289 void GuiView::showDialog(string const & name, string const & data,
2297 Dialog * dialog = findOrBuild(name, false);
2299 dialog->showData(data);
2301 d.open_insets_[name] = inset;
2304 catch (ExceptionMessage const & ex) {
2312 bool GuiView::isDialogVisible(string const & name) const
2314 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2315 if (it == d.dialogs_.end())
2317 return it->second.get()->isVisibleView();
2321 void GuiView::hideDialog(string const & name, Inset * inset)
2323 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2324 if (it == d.dialogs_.end())
2327 if (inset && inset != getOpenInset(name))
2330 Dialog * const dialog = it->second.get();
2331 if (dialog->isVisibleView())
2333 d.open_insets_[name] = 0;
2337 void GuiView::disconnectDialog(string const & name)
2339 if (!isValidName(name))
2342 if (d.open_insets_.find(name) != d.open_insets_.end())
2343 d.open_insets_[name] = 0;
2347 Inset * GuiView::getOpenInset(string const & name) const
2349 if (!isValidName(name))
2352 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2353 return it == d.open_insets_.end() ? 0 : it->second;
2357 void GuiView::hideAll() const
2359 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2360 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2362 for(; it != end; ++it)
2363 it->second->hideView();
2367 void GuiView::updateDialogs()
2369 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2370 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2372 for(; it != end; ++it) {
2373 Dialog * dialog = it->second.get();
2374 if (dialog && dialog->isVisibleView())
2375 dialog->checkStatus();
2382 // will be replaced by a proper factory...
2383 Dialog * createGuiAbout(GuiView & lv);
2384 Dialog * createGuiBibitem(GuiView & lv);
2385 Dialog * createGuiBibtex(GuiView & lv);
2386 Dialog * createGuiBox(GuiView & lv);
2387 Dialog * createGuiBranch(GuiView & lv);
2388 Dialog * createGuiChanges(GuiView & lv);
2389 Dialog * createGuiCharacter(GuiView & lv);
2390 Dialog * createGuiCitation(GuiView & lv);
2391 Dialog * createGuiDelimiter(GuiView & lv);
2392 Dialog * createGuiDocument(GuiView & lv);
2393 Dialog * createGuiErrorList(GuiView & lv);
2394 Dialog * createGuiERT(GuiView & lv);
2395 Dialog * createGuiExternal(GuiView & lv);
2396 Dialog * createGuiFloat(GuiView & lv);
2397 Dialog * createGuiGraphics(GuiView & lv);
2398 Dialog * createGuiHSpace(GuiView & lv);
2399 Dialog * createGuiInclude(GuiView & lv);
2400 Dialog * createGuiInfo(GuiView & lv);
2401 Dialog * createGuiLabel(GuiView & lv);
2402 Dialog * createGuiListings(GuiView & lv);
2403 Dialog * createGuiLog(GuiView & lv);
2404 Dialog * createGuiMathMatrix(GuiView & lv);
2405 Dialog * createGuiNomenclature(GuiView & lv);
2406 Dialog * createGuiNote(GuiView & lv);
2407 Dialog * createGuiParagraph(GuiView & lv);
2408 Dialog * createGuiPreferences(GuiView & lv);
2409 Dialog * createGuiPrint(GuiView & lv);
2410 Dialog * createGuiRef(GuiView & lv);
2411 Dialog * createGuiSearch(GuiView & lv);
2412 Dialog * createGuiSendTo(GuiView & lv);
2413 Dialog * createGuiShowFile(GuiView & lv);
2414 Dialog * createGuiSpellchecker(GuiView & lv);
2415 Dialog * createGuiSymbols(GuiView & lv);
2416 Dialog * createGuiTabularCreate(GuiView & lv);
2417 Dialog * createGuiTabular(GuiView & lv);
2418 Dialog * createGuiTexInfo(GuiView & lv);
2419 Dialog * createGuiToc(GuiView & lv);
2420 Dialog * createGuiThesaurus(GuiView & lv);
2421 Dialog * createGuiHyperlink(GuiView & lv);
2422 Dialog * createGuiVSpace(GuiView & lv);
2423 Dialog * createGuiViewSource(GuiView & lv);
2424 Dialog * createGuiWrap(GuiView & lv);
2427 Dialog * GuiView::build(string const & name)
2429 LASSERT(isValidName(name), return 0);
2431 if (name == "aboutlyx")
2432 return createGuiAbout(*this);
2433 if (name == "bibitem")
2434 return createGuiBibitem(*this);
2435 if (name == "bibtex")
2436 return createGuiBibtex(*this);
2438 return createGuiBox(*this);
2439 if (name == "branch")
2440 return createGuiBranch(*this);
2441 if (name == "changes")
2442 return createGuiChanges(*this);
2443 if (name == "character")
2444 return createGuiCharacter(*this);
2445 if (name == "citation")
2446 return createGuiCitation(*this);
2447 if (name == "document")
2448 return createGuiDocument(*this);
2449 if (name == "errorlist")
2450 return createGuiErrorList(*this);
2452 return createGuiERT(*this);
2453 if (name == "external")
2454 return createGuiExternal(*this);
2456 return createGuiShowFile(*this);
2457 if (name == "findreplace")
2458 return createGuiSearch(*this);
2459 if (name == "float")
2460 return createGuiFloat(*this);
2461 if (name == "graphics")
2462 return createGuiGraphics(*this);
2463 if (name == "include")
2464 return createGuiInclude(*this);
2466 return createGuiInfo(*this);
2467 if (name == "nomenclature")
2468 return createGuiNomenclature(*this);
2469 if (name == "label")
2470 return createGuiLabel(*this);
2472 return createGuiLog(*this);
2473 if (name == "view-source")
2474 return createGuiViewSource(*this);
2475 if (name == "mathdelimiter")
2476 return createGuiDelimiter(*this);
2477 if (name == "mathmatrix")
2478 return createGuiMathMatrix(*this);
2480 return createGuiNote(*this);
2481 if (name == "paragraph")
2482 return createGuiParagraph(*this);
2483 if (name == "prefs")
2484 return createGuiPreferences(*this);
2485 if (name == "print")
2486 return createGuiPrint(*this);
2488 return createGuiRef(*this);
2489 if (name == "sendto")
2490 return createGuiSendTo(*this);
2491 if (name == "space")
2492 return createGuiHSpace(*this);
2493 if (name == "spellchecker")
2494 return createGuiSpellchecker(*this);
2495 if (name == "symbols")
2496 return createGuiSymbols(*this);
2497 if (name == "tabular")
2498 return createGuiTabular(*this);
2499 if (name == "tabularcreate")
2500 return createGuiTabularCreate(*this);
2501 if (name == "texinfo")
2502 return createGuiTexInfo(*this);
2503 #ifdef HAVE_LIBAIKSAURUS
2504 if (name == "thesaurus")
2505 return createGuiThesaurus(*this);
2508 return createGuiToc(*this);
2510 return createGuiHyperlink(*this);
2511 if (name == "vspace")
2512 return createGuiVSpace(*this);
2514 return createGuiWrap(*this);
2515 if (name == "listings")
2516 return createGuiListings(*this);
2522 } // namespace frontend
2525 #include "moc_GuiView.cpp"