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(260, 15, 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), current_main_work_area_(0),
158 layout_(0), autosave_timeout_(5000),
161 // hardcode here the platform specific icon size
162 smallIconSize = 14; // scaling problems
163 normalIconSize = 20; // ok, default
164 bigIconSize = 26; // better for some math icons
166 splitter_ = new QSplitter;
167 bg_widget_ = new BackgroundWidget;
168 stack_widget_ = new QStackedWidget;
169 stack_widget_->addWidget(bg_widget_);
170 stack_widget_->addWidget(splitter_);
178 delete stack_widget_;
181 QMenu * toolBarPopup(GuiView * parent)
183 // FIXME: translation
184 QMenu * menu = new QMenu(parent);
185 QActionGroup * iconSizeGroup = new QActionGroup(parent);
187 QAction * smallIcons = new QAction(iconSizeGroup);
188 smallIcons->setText(qt_("Small-sized icons"));
189 smallIcons->setCheckable(true);
190 QObject::connect(smallIcons, SIGNAL(triggered()),
191 parent, SLOT(smallSizedIcons()));
192 menu->addAction(smallIcons);
194 QAction * normalIcons = new QAction(iconSizeGroup);
195 normalIcons->setText(qt_("Normal-sized icons"));
196 normalIcons->setCheckable(true);
197 QObject::connect(normalIcons, SIGNAL(triggered()),
198 parent, SLOT(normalSizedIcons()));
199 menu->addAction(normalIcons);
201 QAction * bigIcons = new QAction(iconSizeGroup);
202 bigIcons->setText(qt_("Big-sized icons"));
203 bigIcons->setCheckable(true);
204 QObject::connect(bigIcons, SIGNAL(triggered()),
205 parent, SLOT(bigSizedIcons()));
206 menu->addAction(bigIcons);
208 unsigned int cur = parent->iconSize().width();
209 if ( cur == parent->d.smallIconSize)
210 smallIcons->setChecked(true);
211 else if (cur == parent->d.normalIconSize)
212 normalIcons->setChecked(true);
213 else if (cur == parent->d.bigIconSize)
214 bigIcons->setChecked(true);
221 stack_widget_->setCurrentWidget(bg_widget_);
222 bg_widget_->setUpdatesEnabled(true);
225 TabWorkArea * tabWorkArea(int i)
227 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
230 TabWorkArea * currentTabWorkArea()
232 if (splitter_->count() == 1)
233 // The first TabWorkArea is always the first one, if any.
234 return tabWorkArea(0);
236 for (int i = 0; i != splitter_->count(); ++i) {
237 TabWorkArea * twa = tabWorkArea(i);
238 if (current_main_work_area_ == twa->currentWorkArea())
242 // None has the focus so we just take the first one.
243 return tabWorkArea(0);
247 GuiWorkArea * current_work_area_;
248 GuiWorkArea * current_main_work_area_;
249 QSplitter * splitter_;
250 QStackedWidget * stack_widget_;
251 BackgroundWidget * bg_widget_;
253 ToolbarMap toolbars_;
254 /// The main layout box.
256 * \warning Don't Delete! The layout box is actually owned by
257 * whichever toolbar contains it. All the GuiView class needs is a
258 * means of accessing it.
260 * FIXME: replace that with a proper model so that we are not limited
261 * to only one dialog.
263 GuiLayoutBox * layout_;
266 map<string, Inset *> open_insets_;
269 map<string, DialogPtr> dialogs_;
271 unsigned int smallIconSize;
272 unsigned int normalIconSize;
273 unsigned int bigIconSize;
275 QTimer statusbar_timer_;
276 /// auto-saving of buffers
277 Timeout autosave_timeout_;
278 /// flag against a race condition due to multiclicks, see bug #1119
282 TocModels toc_models_;
286 GuiView::GuiView(int id)
287 : d(*new GuiViewPrivate), id_(id), closing_(false)
289 // GuiToolbars *must* be initialised before the menu bar.
290 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
293 // set ourself as the current view. This is needed for the menu bar
294 // filling, at least for the static special menu item on Mac. Otherwise
295 // they are greyed out.
296 theLyXFunc().setLyXView(this);
298 // Fill up the menu bar.
299 guiApp->menus().fillMenuBar(menuBar(), this, true);
301 setCentralWidget(d.stack_widget_);
303 // Start autosave timer
304 if (lyxrc.autosave) {
305 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
306 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
307 d.autosave_timeout_.start();
309 connect(&d.statusbar_timer_, SIGNAL(timeout()),
310 this, SLOT(clearMessage()));
312 // We don't want to keep the window in memory if it is closed.
313 setAttribute(Qt::WA_DeleteOnClose, true);
315 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
316 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
317 // since the icon is provided in the application bundle.
318 setWindowIcon(QPixmap(":/images/lyx.png"));
322 setAcceptDrops(true);
324 statusBar()->setSizeGripEnabled(true);
326 // Forbid too small unresizable window because it can happen
327 // with some window manager under X11.
328 setMinimumSize(300, 200);
330 if (lyxrc.allow_geometry_session) {
331 // Now take care of session management.
336 // no session handling, default to a sane size.
337 setGeometry(50, 50, 690, 510);
340 // clear session data if any.
342 settings.remove("views");
352 void GuiView::saveLayout() const
355 settings.beginGroup("views");
356 settings.beginGroup(QString::number(id_));
358 settings.setValue("pos", pos());
359 settings.setValue("size", size());
361 settings.setValue("geometry", saveGeometry());
363 settings.setValue("layout", saveState(0));
364 settings.setValue("icon_size", iconSize());
368 bool GuiView::restoreLayout()
371 settings.beginGroup("views");
372 settings.beginGroup(QString::number(id_));
373 QString const icon_key = "icon_size";
374 if (!settings.contains(icon_key))
377 setIconSize(settings.value(icon_key).toSize());
379 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
380 QSize size = settings.value("size", QSize(690, 510)).toSize();
384 if (!restoreGeometry(settings.value("geometry").toByteArray()))
385 setGeometry(50, 50, 690, 510);
387 // Make sure layout is correctly oriented.
388 setLayoutDirection(qApp->layoutDirection());
390 // Allow the toc and view-source dock widget to be restored if needed.
392 if ((d = findOrBuild("toc", true)))
395 if ((d = findOrBuild("view-source", true)))
398 if (!restoreState(settings.value("layout").toByteArray(), 0))
405 GuiToolbar * GuiView::toolbar(string const & name)
407 ToolbarMap::iterator it = d.toolbars_.find(name);
408 if (it != d.toolbars_.end())
411 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
412 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
417 void GuiView::constructToolbars()
419 ToolbarMap::iterator it = d.toolbars_.begin();
420 for (; it != d.toolbars_.end(); ++it)
425 // extracts the toolbars from the backend
426 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
427 Toolbars::Infos::iterator end = guiApp->toolbars().end();
428 for (; cit != end; ++cit)
429 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
433 void GuiView::initToolbars()
435 // extracts the toolbars from the backend
436 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
437 Toolbars::Infos::iterator end = guiApp->toolbars().end();
438 for (; cit != end; ++cit) {
439 GuiToolbar * tb = toolbar(cit->name);
442 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
444 tb->setVisible(false);
445 tb->setVisibility(visibility);
447 if (visibility & Toolbars::TOP) {
449 addToolBarBreak(Qt::TopToolBarArea);
450 addToolBar(Qt::TopToolBarArea, tb);
453 if (visibility & Toolbars::BOTTOM) {
454 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456 addToolBarBreak(Qt::BottomToolBarArea);
458 addToolBar(Qt::BottomToolBarArea, tb);
461 if (visibility & Toolbars::LEFT) {
462 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
463 #if (QT_VERSION >= 0x040202)
464 addToolBarBreak(Qt::LeftToolBarArea);
466 addToolBar(Qt::LeftToolBarArea, tb);
469 if (visibility & Toolbars::RIGHT) {
470 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
471 #if (QT_VERSION >= 0x040202)
472 addToolBarBreak(Qt::RightToolBarArea);
474 addToolBar(Qt::RightToolBarArea, tb);
477 if (visibility & Toolbars::ON)
478 tb->setVisible(true);
483 TocModels & GuiView::tocModels()
485 return d.toc_models_;
489 void GuiView::setFocus()
491 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
492 // Make sure LyXFunc points to the correct view.
493 guiApp->setCurrentView(this);
494 theLyXFunc().setLyXView(this);
495 QMainWindow::setFocus();
496 if (d.current_work_area_)
497 d.current_work_area_->setFocus();
501 QMenu * GuiView::createPopupMenu()
503 return d.toolBarPopup(this);
507 void GuiView::showEvent(QShowEvent * e)
509 LYXERR(Debug::GUI, "Passed Geometry "
510 << size().height() << "x" << size().width()
511 << "+" << pos().x() << "+" << pos().y());
513 if (d.splitter_->count() == 0)
514 // No work area, switch to the background widget.
517 QMainWindow::showEvent(e);
521 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
522 ** is responsibility of the container (e.g., dialog)
524 void GuiView::closeEvent(QCloseEvent * close_event)
526 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
529 // it can happen that this event arrives without selecting the view,
530 // e.g. when clicking the close button on a background window.
532 setCurrentWorkArea(currentMainWorkArea());
533 while (GuiWorkArea * wa = currentMainWorkArea()) {
534 Buffer * b = &wa->bufferView().buffer();
536 // This is a child document, just close the tab after saving
537 // but keep the file loaded.
538 if (!saveBuffer(*b)) {
540 close_event->ignore();
547 QList<int> const ids = guiApp->viewIds();
548 for (int i = 0; i != ids.size(); ++i) {
551 if (guiApp->view(ids[i]).workArea(*b)) {
552 // FIXME 1: should we put an alert box here that the buffer
553 // is viewed elsewhere?
554 // FIXME 2: should we try to save this buffer in any case?
557 // This buffer is also opened in another view, so
558 // close the associated work area...
560 // ... but don't close the buffer.
565 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
566 if (b && !closeBuffer(*b, true)) {
568 close_event->ignore();
573 // Make sure that nothing will use this close to be closed View.
574 guiApp->unregisterView(this);
576 if (isFullScreen()) {
577 // Switch off fullscreen before closing.
582 // Make sure the timer time out will not trigger a statusbar update.
583 d.statusbar_timer_.stop();
585 // Saving fullscreen requires additional tweaks in the toolbar code.
586 // It wouldn't also work under linux natively.
587 if (lyxrc.allow_geometry_session) {
588 // Save this window geometry and layout.
590 // Then the toolbar private states.
591 ToolbarMap::iterator end = d.toolbars_.end();
592 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
593 it->second->saveSession();
594 // Now take care of all other dialogs:
595 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
596 for (; it!= d.dialogs_.end(); ++it)
597 it->second->saveSession();
600 close_event->accept();
604 void GuiView::dragEnterEvent(QDragEnterEvent * event)
606 if (event->mimeData()->hasUrls())
608 /// \todo Ask lyx-devel is this is enough:
609 /// if (event->mimeData()->hasFormat("text/plain"))
610 /// event->acceptProposedAction();
614 void GuiView::dropEvent(QDropEvent * event)
616 QList<QUrl> files = event->mimeData()->urls();
620 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
621 for (int i = 0; i != files.size(); ++i) {
622 string const file = os::internal_path(fromqstr(
623 files.at(i).toLocalFile()));
625 // Asynchronously post the event. DropEvent usually come
626 // from the BufferView. But reloading a file might close
627 // the BufferView from within its own event handler.
628 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
635 void GuiView::message(docstring const & str)
637 if (ForkedProcess::iAmAChild())
640 statusBar()->showMessage(toqstr(str));
641 d.statusbar_timer_.stop();
642 d.statusbar_timer_.start(3000);
646 void GuiView::smallSizedIcons()
648 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
652 void GuiView::normalSizedIcons()
654 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
658 void GuiView::bigSizedIcons()
660 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
664 void GuiView::clearMessage()
668 theLyXFunc().setLyXView(this);
669 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
670 d.statusbar_timer_.stop();
674 void GuiView::updateWindowTitle(GuiWorkArea * wa)
676 if (wa != d.current_work_area_)
678 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
679 setWindowIconText(wa->windowIconText());
683 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
686 disconnectBufferView();
687 connectBufferView(wa->bufferView());
688 connectBuffer(wa->bufferView().buffer());
689 d.current_work_area_ = wa;
690 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
691 this, SLOT(updateWindowTitle(GuiWorkArea *)));
692 updateWindowTitle(wa);
696 // The document settings needs to be reinitialised.
697 updateDialog("document", "");
699 // Buffer-dependent dialogs must be updated. This is done here because
700 // some dialogs require buffer()->text.
705 void GuiView::on_lastWorkAreaRemoved()
708 // We already are in a close event. Nothing more to do.
711 if (d.splitter_->count() > 1)
712 // We have a splitter so don't close anything.
715 // Reset and updates the dialogs.
716 d.toc_models_.reset(0);
717 updateDialog("document", "");
720 resetWindowTitleAndIconText();
722 if (lyxrc.open_buffers_in_tabs)
723 // Nothing more to do, the window should stay open.
726 if (guiApp->viewIds().size() > 1) {
732 // On Mac we also close the last window because the application stay
733 // resident in memory. On other platforms we don't close the last
734 // window because this would quit the application.
740 void GuiView::updateStatusBar()
742 // let the user see the explicit message
743 if (d.statusbar_timer_.isActive())
746 theLyXFunc().setLyXView(this);
747 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
751 bool GuiView::hasFocus() const
753 return qApp->activeWindow() == this;
757 bool GuiView::event(QEvent * e)
761 // Useful debug code:
762 //case QEvent::ActivationChange:
763 //case QEvent::WindowDeactivate:
764 //case QEvent::Paint:
765 //case QEvent::Enter:
766 //case QEvent::Leave:
767 //case QEvent::HoverEnter:
768 //case QEvent::HoverLeave:
769 //case QEvent::HoverMove:
770 //case QEvent::StatusTip:
771 //case QEvent::DragEnter:
772 //case QEvent::DragLeave:
776 case QEvent::WindowActivate: {
777 if (this == guiApp->currentView()) {
779 return QMainWindow::event(e);
781 guiApp->setCurrentView(this);
782 theLyXFunc().setLyXView(this);
783 if (d.current_work_area_) {
784 BufferView & bv = d.current_work_area_->bufferView();
785 connectBufferView(bv);
786 connectBuffer(bv.buffer());
787 // The document structure, name and dialogs might have
788 // changed in another view.
790 // The document settings needs to be reinitialised.
791 updateDialog("document", "");
794 resetWindowTitleAndIconText();
797 return QMainWindow::event(e);
800 case QEvent::ShortcutOverride: {
804 if (isFullScreen() && menuBar()->isHidden()) {
805 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
806 // FIXME: we should also try to detect special LyX shortcut such as
807 // Alt-P and Alt-M. Right now there is a hack in
808 // GuiWorkArea::processKeySym() that hides again the menubar for
810 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
812 return QMainWindow::event(e);
817 if (d.current_work_area_)
818 // Nothing special to do.
819 return QMainWindow::event(e);
821 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
822 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
824 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
825 || ke->key() == Qt::Key_Backtab)
826 return QMainWindow::event(e);
828 // Allow processing of shortcuts that are allowed even when no Buffer
830 theLyXFunc().setLyXView(this);
832 setKeySymbol(&sym, ke);
833 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
839 return QMainWindow::event(e);
843 void GuiView::resetWindowTitleAndIconText()
845 setWindowTitle(qt_("LyX"));
846 setWindowIconText(qt_("LyX"));
849 bool GuiView::focusNextPrevChild(bool /*next*/)
856 void GuiView::setBusy(bool busy)
858 if (d.current_work_area_) {
859 d.current_work_area_->setUpdatesEnabled(!busy);
861 d.current_work_area_->stopBlinkingCursor();
863 d.current_work_area_->startBlinkingCursor();
867 QApplication::setOverrideCursor(Qt::WaitCursor);
869 QApplication::restoreOverrideCursor();
873 GuiWorkArea * GuiView::workArea(Buffer & buffer)
875 if (currentWorkArea()
876 && ¤tWorkArea()->bufferView().buffer() == &buffer)
877 return (GuiWorkArea *) currentWorkArea();
878 if (TabWorkArea * twa = d.currentTabWorkArea())
879 return twa->workArea(buffer);
884 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
886 // Automatically create a TabWorkArea if there are none yet.
887 TabWorkArea * tab_widget = d.splitter_->count()
888 ? d.currentTabWorkArea() : addTabWorkArea();
889 return tab_widget->addWorkArea(buffer, *this);
893 TabWorkArea * GuiView::addTabWorkArea()
895 TabWorkArea * twa = new TabWorkArea;
896 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
897 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
898 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
899 this, SLOT(on_lastWorkAreaRemoved()));
901 d.splitter_->addWidget(twa);
902 d.stack_widget_->setCurrentWidget(d.splitter_);
907 GuiWorkArea const * GuiView::currentWorkArea() const
909 return d.current_work_area_;
913 GuiWorkArea * GuiView::currentWorkArea()
915 return d.current_work_area_;
919 GuiWorkArea const * GuiView::currentMainWorkArea() const
921 if (d.currentTabWorkArea() == NULL)
923 return d.currentTabWorkArea()->currentWorkArea();
927 GuiWorkArea * GuiView::currentMainWorkArea()
929 if (d.currentTabWorkArea() == NULL)
931 return d.currentTabWorkArea()->currentWorkArea();
935 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
937 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
939 d.current_work_area_ = NULL;
943 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
947 theGuiApp()->setCurrentView(this);
948 d.current_work_area_ = wa;
949 for (int i = 0; i != d.splitter_->count(); ++i) {
950 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
951 //if (d.current_main_work_area_)
952 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
953 d.current_main_work_area_ = wa;
954 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
955 //d.current_main_work_area_->setLineWidth(2);
956 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
960 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
961 on_currentWorkAreaChanged(wa);
962 BufferView & bv = wa->bufferView();
963 bv.cursor().fixIfBroken();
965 wa->setUpdatesEnabled(true);
966 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
970 void GuiView::removeWorkArea(GuiWorkArea * wa)
973 if (wa == d.current_work_area_) {
975 disconnectBufferView();
976 d.current_work_area_ = 0;
977 d.current_main_work_area_ = 0;
980 bool found_twa = false;
981 for (int i = 0; i != d.splitter_->count(); ++i) {
982 TabWorkArea * twa = d.tabWorkArea(i);
983 if (twa->removeWorkArea(wa)) {
984 // Found in this tab group, and deleted the GuiWorkArea.
986 if (twa->count() != 0) {
987 if (d.current_work_area_ == 0)
988 // This means that we are closing the current GuiWorkArea, so
989 // switch to the next GuiWorkArea in the found TabWorkArea.
990 setCurrentWorkArea(twa->currentWorkArea());
992 // No more WorkAreas in this tab group, so delete it.
999 // It is not a tabbed work area (i.e., the search work area), so it
1000 // should be deleted by other means.
1001 LASSERT(found_twa, /* */);
1003 if (d.current_work_area_ == 0) {
1004 if (d.splitter_->count() != 0) {
1005 TabWorkArea * twa = d.currentTabWorkArea();
1006 setCurrentWorkArea(twa->currentWorkArea());
1008 // No more work areas, switch to the background widget.
1009 setCurrentWorkArea(0);
1015 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1021 void GuiView::updateLayoutList()
1024 d.layout_->updateContents(false);
1028 void GuiView::updateToolbars()
1030 ToolbarMap::iterator end = d.toolbars_.end();
1031 if (d.current_work_area_) {
1033 d.current_work_area_->bufferView().cursor().inMathed();
1035 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1037 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1038 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1039 bool const mathmacrotemplate =
1040 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1042 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1043 it->second->update(math, table, review, mathmacrotemplate);
1045 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1046 it->second->update(false, false, false, false);
1050 Buffer * GuiView::buffer()
1052 if (d.current_work_area_)
1053 return &d.current_work_area_->bufferView().buffer();
1058 Buffer const * GuiView::buffer() const
1060 if (d.current_work_area_)
1061 return &d.current_work_area_->bufferView().buffer();
1066 void GuiView::setBuffer(Buffer * newBuffer)
1068 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1069 LASSERT(newBuffer, return);
1072 GuiWorkArea * wa = workArea(*newBuffer);
1074 newBuffer->masterBuffer()->updateLabels();
1075 wa = addWorkArea(*newBuffer);
1077 //Disconnect the old buffer...there's no new one.
1080 connectBuffer(*newBuffer);
1081 connectBufferView(wa->bufferView());
1082 setCurrentWorkArea(wa);
1088 void GuiView::connectBuffer(Buffer & buf)
1090 buf.setGuiDelegate(this);
1094 void GuiView::disconnectBuffer()
1096 if (d.current_work_area_)
1097 d.current_work_area_->bufferView().setGuiDelegate(0);
1101 void GuiView::connectBufferView(BufferView & bv)
1103 bv.setGuiDelegate(this);
1107 void GuiView::disconnectBufferView()
1109 if (d.current_work_area_)
1110 d.current_work_area_->bufferView().setGuiDelegate(0);
1114 void GuiView::errors(string const & error_type)
1116 ErrorList & el = buffer()->errorList(error_type);
1118 showDialog("errorlist", error_type);
1122 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1124 d.toc_models_.updateItem(toqstr(type), dit);
1128 void GuiView::structureChanged()
1130 d.toc_models_.reset(view());
1131 // Navigator needs more than a simple update in this case. It needs to be
1133 updateDialog("toc", "");
1137 void GuiView::updateDialog(string const & name, string const & data)
1139 if (!isDialogVisible(name))
1142 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1143 if (it == d.dialogs_.end())
1146 Dialog * const dialog = it->second.get();
1147 if (dialog->isVisibleView())
1148 dialog->initialiseParams(data);
1152 BufferView * GuiView::view()
1154 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1158 void GuiView::autoSave()
1160 LYXERR(Debug::INFO, "Running autoSave()");
1163 view()->buffer().autoSave();
1167 void GuiView::resetAutosaveTimers()
1170 d.autosave_timeout_.restart();
1174 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1177 Buffer * buf = buffer();
1179 /* In LyX/Mac, when a dialog is open, the menus of the
1180 application can still be accessed without giving focus to
1181 the main window. In this case, we want to disable the menu
1182 entries that are buffer-related.
1184 Note that this code is not perfect, as bug 1941 attests:
1185 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1187 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1190 switch(cmd.action) {
1191 case LFUN_BUFFER_WRITE:
1192 enable = buf && (buf->isUnnamed() || !buf->isClean());
1195 case LFUN_BUFFER_WRITE_AS:
1199 case LFUN_SPLIT_VIEW:
1200 if (cmd.getArg(0) == "vertical")
1201 enable = buf && (d.splitter_->count() == 1 ||
1202 d.splitter_->orientation() == Qt::Vertical);
1204 enable = buf && (d.splitter_->count() == 1 ||
1205 d.splitter_->orientation() == Qt::Horizontal);
1208 case LFUN_CLOSE_TAB_GROUP:
1209 enable = d.currentTabWorkArea();
1212 case LFUN_TOOLBAR_TOGGLE:
1213 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1214 flag.setOnOff(t->isVisible());
1217 case LFUN_UI_TOGGLE:
1218 flag.setOnOff(isFullScreen());
1221 case LFUN_DIALOG_TOGGLE:
1222 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1223 // fall through to set "enable"
1224 case LFUN_DIALOG_SHOW: {
1225 string const name = cmd.getArg(0);
1227 enable = name == "aboutlyx"
1228 || name == "file" //FIXME: should be removed.
1230 || name == "texinfo";
1231 else if (name == "print")
1232 enable = buf->isExportable("dvi")
1233 && lyxrc.print_command != "none";
1234 else if (name == "character") {
1238 InsetCode ic = view()->cursor().inset().lyxCode();
1239 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1242 else if (name == "symbols") {
1243 if (!view() || view()->cursor().inMathed())
1246 InsetCode ic = view()->cursor().inset().lyxCode();
1247 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1250 else if (name == "latexlog")
1251 enable = FileName(buf->logName()).isReadableFile();
1252 else if (name == "spellchecker")
1253 #if defined (USE_ASPELL)
1254 enable = !buf->isReadonly();
1258 else if (name == "vclog")
1259 enable = buf->lyxvc().inUse();
1263 case LFUN_DIALOG_UPDATE: {
1264 string const name = cmd.getArg(0);
1266 enable = name == "prefs";
1270 case LFUN_INSET_APPLY: {
1271 string const name = cmd.getArg(0);
1272 Inset * inset = getOpenInset(name);
1274 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1276 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1277 // Every inset is supposed to handle this
1278 LASSERT(false, break);
1282 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1283 flag |= lyx::getStatus(fr);
1285 enable = flag.enabled();
1289 case LFUN_COMPLETION_INLINE:
1290 if (!d.current_work_area_
1291 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1295 case LFUN_COMPLETION_POPUP:
1296 if (!d.current_work_area_
1297 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1301 case LFUN_COMPLETION_COMPLETE:
1302 if (!d.current_work_area_
1303 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1307 case LFUN_COMPLETION_ACCEPT:
1308 if (!d.current_work_area_
1309 || (!d.current_work_area_->completer().popupVisible()
1310 && !d.current_work_area_->completer().inlineVisible()
1311 && !d.current_work_area_->completer().completionAvailable()))
1315 case LFUN_COMPLETION_CANCEL:
1316 if (!d.current_work_area_
1317 || (!d.current_work_area_->completer().popupVisible()
1318 && !d.current_work_area_->completer().inlineVisible()))
1327 flag.setEnabled(false);
1333 static FileName selectTemplateFile()
1335 FileDialog dlg(qt_("Select template file"));
1336 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1337 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1339 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1340 QStringList(qt_("LyX Documents (*.lyx)")));
1342 if (result.first == FileDialog::Later)
1344 if (result.second.isEmpty())
1346 return FileName(fromqstr(result.second));
1350 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1354 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1357 message(_("Document not loaded."));
1362 setBuffer(newBuffer);
1364 // scroll to the position when the file was last closed
1365 if (lyxrc.use_lastfilepos) {
1366 LastFilePosSection::FilePos filepos =
1367 theSession().lastFilePos().load(filename);
1368 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1372 theSession().lastFiles().add(filename);
1379 void GuiView::openDocument(string const & fname)
1381 string initpath = lyxrc.document_path;
1384 string const trypath = buffer()->filePath();
1385 // If directory is writeable, use this as default.
1386 if (FileName(trypath).isDirWritable())
1392 if (fname.empty()) {
1393 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1394 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1395 dlg.setButton2(qt_("Examples|#E#e"),
1396 toqstr(addPath(package().system_support().absFilename(), "examples")));
1398 QStringList filter(qt_("LyX Documents (*.lyx)"));
1399 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1400 << qt_("LyX-1.4.x Documents (*.lyx14)")
1401 << qt_("LyX-1.5.x Documents (*.lyx15)")
1402 << qt_("LyX-1.6.x Documents (*.lyx16)");
1403 FileDialog::Result result =
1404 dlg.open(toqstr(initpath), filter);
1406 if (result.first == FileDialog::Later)
1409 filename = fromqstr(result.second);
1411 // check selected filename
1412 if (filename.empty()) {
1413 message(_("Canceled."));
1419 // get absolute path of file and add ".lyx" to the filename if
1421 FileName const fullname =
1422 fileSearch(string(), filename, "lyx", support::may_not_exist);
1423 if (!fullname.empty())
1424 filename = fullname.absFilename();
1426 if (!fullname.onlyPath().isDirectory()) {
1427 Alert::warning(_("Invalid filename"),
1428 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1429 from_utf8(fullname.absFilename())));
1432 // if the file doesn't exist, let the user create one
1433 if (!fullname.exists()) {
1434 // the user specifically chose this name. Believe him.
1435 Buffer * const b = newFile(filename, string(), true);
1441 docstring const disp_fn = makeDisplayPath(filename);
1442 message(bformat(_("Opening document %1$s..."), disp_fn));
1445 Buffer * buf = loadDocument(fullname);
1447 buf->updateLabels();
1449 buf->errors("Parse");
1450 str2 = bformat(_("Document %1$s opened."), disp_fn);
1451 if (buf->lyxvc().inUse())
1452 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1453 " " + _("Version control detected.");
1455 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1460 // FIXME: clean that
1461 static bool import(GuiView * lv, FileName const & filename,
1462 string const & format, ErrorList & errorList)
1464 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1466 string loader_format;
1467 vector<string> loaders = theConverters().loaders();
1468 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1469 for (vector<string>::const_iterator it = loaders.begin();
1470 it != loaders.end(); ++it) {
1471 if (!theConverters().isReachable(format, *it))
1474 string const tofile =
1475 support::changeExtension(filename.absFilename(),
1476 formats.extension(*it));
1477 if (!theConverters().convert(0, filename, FileName(tofile),
1478 filename, format, *it, errorList))
1480 loader_format = *it;
1483 if (loader_format.empty()) {
1484 frontend::Alert::error(_("Couldn't import file"),
1485 bformat(_("No information for importing the format %1$s."),
1486 formats.prettyName(format)));
1490 loader_format = format;
1492 if (loader_format == "lyx") {
1493 Buffer * buf = lv->loadDocument(lyxfile);
1496 buf->updateLabels();
1498 buf->errors("Parse");
1500 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1504 bool as_paragraphs = loader_format == "textparagraph";
1505 string filename2 = (loader_format == format) ? filename.absFilename()
1506 : support::changeExtension(filename.absFilename(),
1507 formats.extension(loader_format));
1508 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1509 theLyXFunc().setLyXView(lv);
1510 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1517 void GuiView::importDocument(string const & argument)
1520 string filename = split(argument, format, ' ');
1522 LYXERR(Debug::INFO, format << " file: " << filename);
1524 // need user interaction
1525 if (filename.empty()) {
1526 string initpath = lyxrc.document_path;
1528 Buffer const * buf = buffer();
1530 string const trypath = buf->filePath();
1531 // If directory is writeable, use this as default.
1532 if (FileName(trypath).isDirWritable())
1536 docstring const text = bformat(_("Select %1$s file to import"),
1537 formats.prettyName(format));
1539 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1540 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1541 dlg.setButton2(qt_("Examples|#E#e"),
1542 toqstr(addPath(package().system_support().absFilename(), "examples")));
1544 docstring filter = formats.prettyName(format);
1547 filter += from_utf8(formats.extension(format));
1550 FileDialog::Result result =
1551 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1553 if (result.first == FileDialog::Later)
1556 filename = fromqstr(result.second);
1558 // check selected filename
1559 if (filename.empty())
1560 message(_("Canceled."));
1563 if (filename.empty())
1566 // get absolute path of file
1567 FileName const fullname(support::makeAbsPath(filename));
1569 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1571 // Check if the document already is open
1572 Buffer * buf = theBufferList().getBuffer(lyxfile);
1575 if (!closeBuffer()) {
1576 message(_("Canceled."));
1581 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1583 // if the file exists already, and we didn't do
1584 // -i lyx thefile.lyx, warn
1585 if (lyxfile.exists() && fullname != lyxfile) {
1587 docstring text = bformat(_("The document %1$s already exists.\n\n"
1588 "Do you want to overwrite that document?"), displaypath);
1589 int const ret = Alert::prompt(_("Overwrite document?"),
1590 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1593 message(_("Canceled."));
1598 message(bformat(_("Importing %1$s..."), displaypath));
1599 ErrorList errorList;
1600 if (import(this, fullname, format, errorList))
1601 message(_("imported."));
1603 message(_("file not imported!"));
1605 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1609 void GuiView::newDocument(string const & filename, bool from_template)
1611 FileName initpath(lyxrc.document_path);
1612 Buffer * buf = buffer();
1614 FileName const trypath(buf->filePath());
1615 // If directory is writeable, use this as default.
1616 if (trypath.isDirWritable())
1620 string templatefile;
1621 if (from_template) {
1622 templatefile = selectTemplateFile().absFilename();
1623 if (templatefile.empty())
1628 if (filename.empty())
1629 b = newUnnamedFile(templatefile, initpath);
1631 b = newFile(filename, templatefile, true);
1636 // If no new document could be created, it is unsure
1637 // whether there is a valid BufferView.
1639 // Ensure the cursor is correctly positioned on screen.
1640 view()->showCursor();
1644 void GuiView::insertLyXFile(docstring const & fname)
1646 BufferView * bv = view();
1651 FileName filename(to_utf8(fname));
1653 if (!filename.empty()) {
1654 bv->insertLyXFile(filename);
1658 // Launch a file browser
1660 string initpath = lyxrc.document_path;
1661 string const trypath = bv->buffer().filePath();
1662 // If directory is writeable, use this as default.
1663 if (FileName(trypath).isDirWritable())
1667 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1668 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1669 dlg.setButton2(qt_("Examples|#E#e"),
1670 toqstr(addPath(package().system_support().absFilename(),
1673 FileDialog::Result result = dlg.open(toqstr(initpath),
1674 QStringList(qt_("LyX Documents (*.lyx)")));
1676 if (result.first == FileDialog::Later)
1680 filename.set(fromqstr(result.second));
1682 // check selected filename
1683 if (filename.empty()) {
1684 // emit message signal.
1685 message(_("Canceled."));
1689 bv->insertLyXFile(filename);
1693 void GuiView::insertPlaintextFile(docstring const & fname,
1696 BufferView * bv = view();
1701 FileName filename(to_utf8(fname));
1703 if (!filename.empty()) {
1704 bv->insertPlaintextFile(filename, asParagraph);
1708 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1709 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1711 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1712 QStringList(qt_("All Files (*)")));
1714 if (result.first == FileDialog::Later)
1718 filename.set(fromqstr(result.second));
1720 // check selected filename
1721 if (filename.empty()) {
1722 // emit message signal.
1723 message(_("Canceled."));
1727 bv->insertPlaintextFile(filename, asParagraph);
1731 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1733 FileName fname = b.fileName();
1734 FileName const oldname = fname;
1736 if (!newname.empty()) {
1738 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1740 // Switch to this Buffer.
1743 /// No argument? Ask user through dialog.
1745 FileDialog dlg(qt_("Choose a filename to save document as"),
1746 LFUN_BUFFER_WRITE_AS);
1747 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1748 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1750 if (!isLyXFilename(fname.absFilename()))
1751 fname.changeExtension(".lyx");
1753 FileDialog::Result result =
1754 dlg.save(toqstr(fname.onlyPath().absFilename()),
1755 QStringList(qt_("LyX Documents (*.lyx)")),
1756 toqstr(fname.onlyFileName()));
1758 if (result.first == FileDialog::Later)
1761 fname.set(fromqstr(result.second));
1766 if (!isLyXFilename(fname.absFilename()))
1767 fname.changeExtension(".lyx");
1770 if (FileName(fname).exists()) {
1771 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1772 docstring text = bformat(_("The document %1$s already "
1773 "exists.\n\nDo you want to "
1774 "overwrite that document?"),
1776 int const ret = Alert::prompt(_("Overwrite document?"),
1777 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1780 case 1: return renameBuffer(b, docstring());
1781 case 2: return false;
1785 // Ok, change the name of the buffer
1786 b.setFileName(fname.absFilename());
1788 bool unnamed = b.isUnnamed();
1789 b.setUnnamed(false);
1790 b.saveCheckSum(fname);
1792 if (!saveBuffer(b)) {
1793 b.setFileName(oldname.absFilename());
1794 b.setUnnamed(unnamed);
1795 b.saveCheckSum(oldname);
1803 bool GuiView::saveBuffer(Buffer & b)
1805 if (workArea(b) && workArea(b)->inDialogMode())
1809 return renameBuffer(b, docstring());
1812 theSession().lastFiles().add(b.fileName());
1816 // Switch to this Buffer.
1819 // FIXME: we don't tell the user *WHY* the save failed !!
1820 docstring const file = makeDisplayPath(b.absFileName(), 30);
1821 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1822 "Do you want to rename the document and "
1823 "try again?"), file);
1824 int const ret = Alert::prompt(_("Rename and save?"),
1825 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1828 if (!renameBuffer(b, docstring()))
1837 return saveBuffer(b);
1841 bool GuiView::closeBuffer()
1843 Buffer * buf = buffer();
1844 return buf && closeBuffer(*buf);
1848 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1850 // goto bookmark to update bookmark pit.
1851 //FIXME: we should update only the bookmarks related to this buffer!
1852 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1853 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1854 theLyXFunc().gotoBookmark(i+1, false, false);
1856 if (buf.isClean() || buf.paragraphs().empty()) {
1857 if (buf.masterBuffer() == &buf && tolastopened)
1858 theSession().lastOpened().add(buf.fileName());
1860 // Don't close child documents.
1861 removeWorkArea(currentMainWorkArea());
1863 theBufferList().release(&buf);
1866 // Switch to this Buffer.
1871 if (buf.isUnnamed())
1872 file = from_utf8(buf.fileName().onlyFileName());
1874 file = buf.fileName().displayName(30);
1876 // Bring this window to top before asking questions.
1880 docstring const text = bformat(_("The document %1$s has unsaved changes."
1881 "\n\nDo you want to save the document or discard the changes?"), file);
1882 int const ret = Alert::prompt(_("Save changed document?"),
1883 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1887 if (!saveBuffer(buf))
1891 // if we crash after this we could
1892 // have no autosave file but I guess
1893 // this is really improbable (Jug)
1894 removeAutosaveFile(buf.absFileName());
1900 // save file names to .lyx/session
1901 // if master/slave are both open, do not save slave since it
1902 // will be automatically loaded when the master is loaded
1903 if (buf.masterBuffer() == &buf && tolastopened)
1904 theSession().lastOpened().add(buf.fileName());
1907 // Don't close child documents.
1908 removeWorkArea(currentMainWorkArea());
1910 theBufferList().release(&buf);
1916 bool GuiView::dispatch(FuncRequest const & cmd)
1918 BufferView * bv = view();
1919 // By default we won't need any update.
1921 bv->cursor().updateFlags(Update::None);
1922 bool dispatched = true;
1924 switch(cmd.action) {
1925 case LFUN_BUFFER_IMPORT:
1926 importDocument(to_utf8(cmd.argument()));
1929 case LFUN_BUFFER_SWITCH:
1930 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1933 case LFUN_BUFFER_NEXT:
1934 setBuffer(theBufferList().next(buffer()));
1937 case LFUN_BUFFER_PREVIOUS:
1938 setBuffer(theBufferList().previous(buffer()));
1941 case LFUN_COMMAND_EXECUTE: {
1942 bool const show_it = cmd.argument() != "off";
1943 // FIXME: this is a hack, "minibuffer" should not be
1945 if (GuiToolbar * t = toolbar("minibuffer")) {
1946 t->setVisible(show_it);
1947 if (show_it && t->commandBuffer())
1948 t->commandBuffer()->setFocus();
1952 case LFUN_DROP_LAYOUTS_CHOICE:
1954 d.layout_->showPopup();
1957 case LFUN_MENU_OPEN:
1958 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1959 menu->exec(QCursor::pos());
1962 case LFUN_FILE_INSERT:
1963 insertLyXFile(cmd.argument());
1965 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1966 insertPlaintextFile(cmd.argument(), true);
1969 case LFUN_FILE_INSERT_PLAINTEXT:
1970 insertPlaintextFile(cmd.argument(), false);
1973 case LFUN_BUFFER_WRITE:
1975 saveBuffer(bv->buffer());
1978 case LFUN_BUFFER_WRITE_AS:
1980 renameBuffer(bv->buffer(), cmd.argument());
1983 case LFUN_BUFFER_WRITE_ALL: {
1984 Buffer * first = theBufferList().first();
1987 message(_("Saving all documents..."));
1988 // We cannot use a for loop as the buffer list cycles.
1991 if (!b->isClean()) {
1993 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1995 b = theBufferList().next(b);
1996 } while (b != first);
1997 message(_("All documents saved."));
2001 case LFUN_TOOLBAR_TOGGLE: {
2002 string const name = cmd.getArg(0);
2003 if (GuiToolbar * t = toolbar(name))
2008 case LFUN_DIALOG_UPDATE: {
2009 string const name = to_utf8(cmd.argument());
2010 // Can only update a dialog connected to an existing inset
2011 Inset * inset = getOpenInset(name);
2013 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2014 inset->dispatch(view()->cursor(), fr);
2015 } else if (name == "paragraph") {
2016 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2017 } else if (name == "prefs" || name == "document") {
2018 updateDialog(name, string());
2023 case LFUN_DIALOG_TOGGLE: {
2024 if (isDialogVisible(cmd.getArg(0)))
2025 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2027 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2031 case LFUN_DIALOG_DISCONNECT_INSET:
2032 disconnectDialog(to_utf8(cmd.argument()));
2035 case LFUN_DIALOG_HIDE: {
2036 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2040 case LFUN_DIALOG_SHOW: {
2041 string const name = cmd.getArg(0);
2042 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2044 if (name == "character") {
2045 data = freefont2string();
2047 showDialog("character", data);
2048 } else if (name == "latexlog") {
2049 Buffer::LogType type;
2050 string const logfile = buffer()->logName(&type);
2052 case Buffer::latexlog:
2055 case Buffer::buildlog:
2059 data += Lexer::quoteString(logfile);
2060 showDialog("log", data);
2061 } else if (name == "vclog") {
2062 string const data = "vc " +
2063 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2064 showDialog("log", data);
2065 } else if (name == "symbols") {
2066 data = bv->cursor().getEncoding()->name();
2068 showDialog("symbols", data);
2070 } else if (name == "prefs" && isFullScreen()) {
2071 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2073 showDialog("prefs", data);
2075 showDialog(name, data);
2079 case LFUN_INSET_APPLY: {
2080 string const name = cmd.getArg(0);
2081 Inset * inset = getOpenInset(name);
2083 // put cursor in front of inset.
2084 if (!view()->setCursorFromInset(inset)) {
2085 LASSERT(false, break);
2088 // useful if we are called from a dialog.
2089 view()->cursor().beginUndoGroup();
2090 view()->cursor().recordUndo();
2091 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2092 inset->dispatch(view()->cursor(), fr);
2093 view()->cursor().endUndoGroup();
2095 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2101 case LFUN_UI_TOGGLE:
2103 // Make sure the keyboard focus stays in the work area.
2107 case LFUN_SPLIT_VIEW:
2108 if (Buffer * buf = buffer()) {
2109 string const orientation = cmd.getArg(0);
2110 d.splitter_->setOrientation(orientation == "vertical"
2111 ? Qt::Vertical : Qt::Horizontal);
2112 TabWorkArea * twa = addTabWorkArea();
2113 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2114 setCurrentWorkArea(wa);
2118 case LFUN_CLOSE_TAB_GROUP:
2119 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2121 twa = d.currentTabWorkArea();
2122 // Switch to the next GuiWorkArea in the found TabWorkArea.
2124 // Make sure the work area is up to date.
2125 setCurrentWorkArea(twa->currentWorkArea());
2127 setCurrentWorkArea(0);
2132 case LFUN_COMPLETION_INLINE:
2133 if (d.current_work_area_)
2134 d.current_work_area_->completer().showInline();
2137 case LFUN_COMPLETION_POPUP:
2138 if (d.current_work_area_)
2139 d.current_work_area_->completer().showPopup();
2143 case LFUN_COMPLETION_COMPLETE:
2144 if (d.current_work_area_)
2145 d.current_work_area_->completer().tab();
2148 case LFUN_COMPLETION_CANCEL:
2149 if (d.current_work_area_) {
2150 if (d.current_work_area_->completer().popupVisible())
2151 d.current_work_area_->completer().hidePopup();
2153 d.current_work_area_->completer().hideInline();
2157 case LFUN_COMPLETION_ACCEPT:
2158 if (d.current_work_area_)
2159 d.current_work_area_->completer().activate();
2168 // Part of automatic menu appearance feature.
2169 if (isFullScreen()) {
2170 if (menuBar()->isVisible())
2172 if (statusBar()->isVisible())
2173 statusBar()->hide();
2180 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2182 string const arg = cmd.getArg(0);
2183 if (arg == "scrollbar") {
2184 // hide() is of no help
2185 if (d.current_work_area_->verticalScrollBarPolicy() ==
2186 Qt::ScrollBarAlwaysOff)
2188 d.current_work_area_->setVerticalScrollBarPolicy(
2189 Qt::ScrollBarAsNeeded);
2191 d.current_work_area_->setVerticalScrollBarPolicy(
2192 Qt::ScrollBarAlwaysOff);
2195 if (arg == "statusbar") {
2196 statusBar()->setVisible(!statusBar()->isVisible());
2199 if (arg == "menubar") {
2200 menuBar()->setVisible(!menuBar()->isVisible());
2203 #if QT_VERSION >= 0x040300
2204 if (arg == "frame") {
2206 getContentsMargins(&l, &t, &r, &b);
2207 //are the frames in default state?
2208 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2210 setContentsMargins(-2, -2, -2, -2);
2212 setContentsMargins(0, 0, 0, 0);
2217 if (arg == "fullscreen") {
2222 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2226 void GuiView::toggleFullScreen()
2228 if (isFullScreen()) {
2229 for (int i = 0; i != d.splitter_->count(); ++i)
2230 d.tabWorkArea(i)->setFullScreen(false);
2231 #if QT_VERSION >= 0x040300
2232 setContentsMargins(0, 0, 0, 0);
2234 setWindowState(windowState() ^ Qt::WindowFullScreen);
2237 statusBar()->show();
2240 hideDialogs("prefs", 0);
2241 for (int i = 0; i != d.splitter_->count(); ++i)
2242 d.tabWorkArea(i)->setFullScreen(true);
2243 #if QT_VERSION >= 0x040300
2244 setContentsMargins(-2, -2, -2, -2);
2247 setWindowState(windowState() ^ Qt::WindowFullScreen);
2248 statusBar()->hide();
2250 if (lyxrc.full_screen_toolbars) {
2251 ToolbarMap::iterator end = d.toolbars_.end();
2252 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2257 // give dialogs like the TOC a chance to adapt
2262 Buffer const * GuiView::updateInset(Inset const * inset)
2264 if (!d.current_work_area_)
2268 d.current_work_area_->scheduleRedraw();
2270 return &d.current_work_area_->bufferView().buffer();
2274 void GuiView::restartCursor()
2276 /* When we move around, or type, it's nice to be able to see
2277 * the cursor immediately after the keypress.
2279 if (d.current_work_area_)
2280 d.current_work_area_->startBlinkingCursor();
2282 // Take this occasion to update the other GUI elements.
2288 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2290 if (d.current_work_area_)
2291 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2296 // This list should be kept in sync with the list of insets in
2297 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2298 // dialog should have the same name as the inset.
2299 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2300 // docs in LyXAction.cpp.
2302 char const * const dialognames[] = {
2303 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2304 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2305 "float", "graphics", "include", "index", "info", "nomenclature", "label",
2306 "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2307 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2308 "symbols", "tabular", "tabularcreate",
2310 #ifdef HAVE_LIBAIKSAURUS
2314 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2316 char const * const * const end_dialognames =
2317 dialognames + (sizeof(dialognames) / sizeof(char *));
2321 cmpCStr(char const * name) : name_(name) {}
2322 bool operator()(char const * other) {
2323 return strcmp(other, name_) == 0;
2330 bool isValidName(string const & name)
2332 return find_if(dialognames, end_dialognames,
2333 cmpCStr(name.c_str())) != end_dialognames;
2339 void GuiView::resetDialogs()
2341 // Make sure that no LFUN uses any LyXView.
2342 theLyXFunc().setLyXView(0);
2345 constructToolbars();
2346 guiApp->menus().fillMenuBar(menuBar(), this, false);
2348 d.layout_->updateContents(true);
2349 // Now update controls with current buffer.
2350 theLyXFunc().setLyXView(this);
2356 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2358 if (!isValidName(name))
2361 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2363 if (it != d.dialogs_.end()) {
2365 it->second->hideView();
2366 return it->second.get();
2369 Dialog * dialog = build(name);
2370 d.dialogs_[name].reset(dialog);
2371 if (lyxrc.allow_geometry_session)
2372 dialog->restoreSession();
2379 void GuiView::showDialog(string const & name, string const & data,
2387 Dialog * dialog = findOrBuild(name, false);
2389 dialog->showData(data);
2391 d.open_insets_[name] = inset;
2394 catch (ExceptionMessage const & ex) {
2402 bool GuiView::isDialogVisible(string const & name) const
2404 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2405 if (it == d.dialogs_.end())
2407 return it->second.get()->isVisibleView();
2411 void GuiView::hideDialog(string const & name, Inset * inset)
2413 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2414 if (it == d.dialogs_.end())
2417 if (inset && inset != getOpenInset(name))
2420 Dialog * const dialog = it->second.get();
2421 if (dialog->isVisibleView())
2423 d.open_insets_[name] = 0;
2427 void GuiView::disconnectDialog(string const & name)
2429 if (!isValidName(name))
2432 if (d.open_insets_.find(name) != d.open_insets_.end())
2433 d.open_insets_[name] = 0;
2437 Inset * GuiView::getOpenInset(string const & name) const
2439 if (!isValidName(name))
2442 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2443 return it == d.open_insets_.end() ? 0 : it->second;
2447 void GuiView::hideAll() const
2449 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2450 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2452 for(; it != end; ++it)
2453 it->second->hideView();
2457 void GuiView::updateDialogs()
2459 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2460 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2462 for(; it != end; ++it) {
2463 Dialog * dialog = it->second.get();
2464 if (dialog && dialog->isVisibleView())
2465 dialog->checkStatus();
2472 // will be replaced by a proper factory...
2473 Dialog * createGuiAbout(GuiView & lv);
2474 Dialog * createGuiBibitem(GuiView & lv);
2475 Dialog * createGuiBibtex(GuiView & lv);
2476 Dialog * createGuiBox(GuiView & lv);
2477 Dialog * createGuiBranch(GuiView & lv);
2478 Dialog * createGuiChanges(GuiView & lv);
2479 Dialog * createGuiCharacter(GuiView & lv);
2480 Dialog * createGuiCitation(GuiView & lv);
2481 Dialog * createGuiDelimiter(GuiView & lv);
2482 Dialog * createGuiDocument(GuiView & lv);
2483 Dialog * createGuiErrorList(GuiView & lv);
2484 Dialog * createGuiERT(GuiView & lv);
2485 Dialog * createGuiExternal(GuiView & lv);
2486 Dialog * createGuiFloat(GuiView & lv);
2487 Dialog * createGuiGraphics(GuiView & lv);
2488 Dialog * createGuiInclude(GuiView & lv);
2489 Dialog * createGuiInfo(GuiView & lv);
2490 Dialog * createGuiLabel(GuiView & lv);
2491 Dialog * createGuiListings(GuiView & lv);
2492 Dialog * createGuiLog(GuiView & lv);
2493 Dialog * createGuiMathHSpace(GuiView & lv);
2494 Dialog * createGuiMathMatrix(GuiView & lv);
2495 Dialog * createGuiNomenclature(GuiView & lv);
2496 Dialog * createGuiNote(GuiView & lv);
2497 Dialog * createGuiParagraph(GuiView & lv);
2498 Dialog * createGuiPhantom(GuiView & lv);
2499 Dialog * createGuiPreferences(GuiView & lv);
2500 Dialog * createGuiPrint(GuiView & lv);
2501 Dialog * createGuiRef(GuiView & lv);
2502 Dialog * createGuiSearch(GuiView & lv);
2503 Dialog * createGuiSearchAdv(GuiView & lv);
2504 Dialog * createGuiSendTo(GuiView & lv);
2505 Dialog * createGuiShowFile(GuiView & lv);
2506 Dialog * createGuiSpellchecker(GuiView & lv);
2507 Dialog * createGuiSymbols(GuiView & lv);
2508 Dialog * createGuiTabularCreate(GuiView & lv);
2509 Dialog * createGuiTabular(GuiView & lv);
2510 Dialog * createGuiTexInfo(GuiView & lv);
2511 Dialog * createGuiTextHSpace(GuiView & lv);
2512 Dialog * createGuiToc(GuiView & lv);
2513 Dialog * createGuiThesaurus(GuiView & lv);
2514 Dialog * createGuiHyperlink(GuiView & lv);
2515 Dialog * createGuiVSpace(GuiView & lv);
2516 Dialog * createGuiViewSource(GuiView & lv);
2517 Dialog * createGuiWrap(GuiView & lv);
2520 Dialog * GuiView::build(string const & name)
2522 LASSERT(isValidName(name), return 0);
2524 if (name == "aboutlyx")
2525 return createGuiAbout(*this);
2526 if (name == "bibitem")
2527 return createGuiBibitem(*this);
2528 if (name == "bibtex")
2529 return createGuiBibtex(*this);
2531 return createGuiBox(*this);
2532 if (name == "branch")
2533 return createGuiBranch(*this);
2534 if (name == "changes")
2535 return createGuiChanges(*this);
2536 if (name == "character")
2537 return createGuiCharacter(*this);
2538 if (name == "citation")
2539 return createGuiCitation(*this);
2540 if (name == "document")
2541 return createGuiDocument(*this);
2542 if (name == "errorlist")
2543 return createGuiErrorList(*this);
2545 return createGuiERT(*this);
2546 if (name == "external")
2547 return createGuiExternal(*this);
2549 return createGuiShowFile(*this);
2550 if (name == "findreplace")
2551 return createGuiSearch(*this);
2552 if (name == "findreplaceadv")
2553 return createGuiSearchAdv(*this);
2554 if (name == "float")
2555 return createGuiFloat(*this);
2556 if (name == "graphics")
2557 return createGuiGraphics(*this);
2558 if (name == "include")
2559 return createGuiInclude(*this);
2561 return createGuiInfo(*this);
2562 if (name == "nomenclature")
2563 return createGuiNomenclature(*this);
2564 if (name == "label")
2565 return createGuiLabel(*this);
2567 return createGuiLog(*this);
2568 if (name == "mathdelimiter")
2569 return createGuiDelimiter(*this);
2570 if (name == "mathspace")
2571 return createGuiMathHSpace(*this);
2572 if (name == "mathmatrix")
2573 return createGuiMathMatrix(*this);
2575 return createGuiNote(*this);
2576 if (name == "paragraph")
2577 return createGuiParagraph(*this);
2578 if (name == "phantom")
2579 return createGuiPhantom(*this);
2580 if (name == "prefs")
2581 return createGuiPreferences(*this);
2582 if (name == "print")
2583 return createGuiPrint(*this);
2585 return createGuiRef(*this);
2586 if (name == "sendto")
2587 return createGuiSendTo(*this);
2588 if (name == "space")
2589 return createGuiTextHSpace(*this);
2590 if (name == "spellchecker")
2591 return createGuiSpellchecker(*this);
2592 if (name == "symbols")
2593 return createGuiSymbols(*this);
2594 if (name == "tabular")
2595 return createGuiTabular(*this);
2596 if (name == "tabularcreate")
2597 return createGuiTabularCreate(*this);
2598 if (name == "texinfo")
2599 return createGuiTexInfo(*this);
2600 if (name == "view-source")
2601 return createGuiViewSource(*this);
2602 #ifdef HAVE_LIBAIKSAURUS
2603 if (name == "thesaurus")
2604 return createGuiThesaurus(*this);
2607 return createGuiHyperlink(*this);
2608 if (name == "listings")
2609 return createGuiListings(*this);
2611 return createGuiToc(*this);
2612 if (name == "vspace")
2613 return createGuiVSpace(*this);
2615 return createGuiWrap(*this);
2621 } // namespace frontend
2624 #include "moc_GuiView.cpp"