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), 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.
391 findOrBuild("toc", true);
392 findOrBuild("view-source", true);
394 if (!restoreState(settings.value("layout").toByteArray(), 0))
401 GuiToolbar * GuiView::toolbar(string const & name)
403 ToolbarMap::iterator it = d.toolbars_.find(name);
404 if (it != d.toolbars_.end())
407 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
408 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
413 void GuiView::constructToolbars()
415 ToolbarMap::iterator it = d.toolbars_.begin();
416 for (; it != d.toolbars_.end(); ++it)
421 // extracts the toolbars from the backend
422 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
423 Toolbars::Infos::iterator end = guiApp->toolbars().end();
424 for (; cit != end; ++cit)
425 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
429 void GuiView::initToolbars()
431 // extracts the toolbars from the backend
432 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
433 Toolbars::Infos::iterator end = guiApp->toolbars().end();
434 for (; cit != end; ++cit) {
435 GuiToolbar * tb = toolbar(cit->name);
438 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
440 tb->setVisible(false);
441 tb->setVisibility(visibility);
443 if (visibility & Toolbars::TOP) {
445 addToolBarBreak(Qt::TopToolBarArea);
446 addToolBar(Qt::TopToolBarArea, tb);
449 if (visibility & Toolbars::BOTTOM) {
450 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
451 #if (QT_VERSION >= 0x040202)
452 addToolBarBreak(Qt::BottomToolBarArea);
454 addToolBar(Qt::BottomToolBarArea, tb);
457 if (visibility & Toolbars::LEFT) {
458 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
459 #if (QT_VERSION >= 0x040202)
460 addToolBarBreak(Qt::LeftToolBarArea);
462 addToolBar(Qt::LeftToolBarArea, tb);
465 if (visibility & Toolbars::RIGHT) {
466 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
467 #if (QT_VERSION >= 0x040202)
468 addToolBarBreak(Qt::RightToolBarArea);
470 addToolBar(Qt::RightToolBarArea, tb);
473 if (visibility & Toolbars::ON)
474 tb->setVisible(true);
479 TocModels & GuiView::tocModels()
481 return d.toc_models_;
485 void GuiView::setFocus()
487 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
488 // Make sure LyXFunc points to the correct view.
489 guiApp->setCurrentView(this);
490 theLyXFunc().setLyXView(this);
491 QMainWindow::setFocus();
492 if (d.current_work_area_)
493 d.current_work_area_->setFocus();
497 QMenu * GuiView::createPopupMenu()
499 return d.toolBarPopup(this);
503 void GuiView::showEvent(QShowEvent * e)
505 LYXERR(Debug::GUI, "Passed Geometry "
506 << size().height() << "x" << size().width()
507 << "+" << pos().x() << "+" << pos().y());
509 if (d.splitter_->count() == 0)
510 // No work area, switch to the background widget.
513 QMainWindow::showEvent(e);
517 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
518 ** is responsibility of the container (e.g., dialog)
520 void GuiView::closeEvent(QCloseEvent * close_event)
522 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
525 // it can happen that this event arrives without selecting the view,
526 // e.g. when clicking the close button on a background window.
528 setCurrentWorkArea(currentMainWorkArea());
529 while (GuiWorkArea * wa = currentMainWorkArea()) {
530 Buffer * b = &wa->bufferView().buffer();
532 // This is a child document, just close the tab after saving
533 // but keep the file loaded.
534 if (!saveBuffer(*b)) {
536 close_event->ignore();
543 QList<int> const ids = guiApp->viewIds();
544 for (int i = 0; i != ids.size(); ++i) {
547 if (guiApp->view(ids[i]).workArea(*b)) {
548 // FIXME 1: should we put an alert box here that the buffer
549 // is viewed elsewhere?
550 // FIXME 2: should we try to save this buffer in any case?
553 // This buffer is also opened in another view, so
554 // close the associated work area...
556 // ... but don't close the buffer.
561 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
562 if (b && !closeBuffer(*b, true)) {
564 close_event->ignore();
569 // Make sure that nothing will use this close to be closed View.
570 guiApp->unregisterView(this);
572 if (isFullScreen()) {
573 // Switch off fullscreen before closing.
578 // Make sure the timer time out will not trigger a statusbar update.
579 d.statusbar_timer_.stop();
581 // Saving fullscreen requires additional tweaks in the toolbar code.
582 // It wouldn't also work under linux natively.
583 if (lyxrc.allow_geometry_session) {
584 // Save this window geometry and layout.
586 // Then the toolbar private states.
587 ToolbarMap::iterator end = d.toolbars_.end();
588 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
589 it->second->saveSession();
590 // Now take care of all other dialogs:
591 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
592 for (; it!= d.dialogs_.end(); ++it)
593 it->second->saveSession();
596 close_event->accept();
600 void GuiView::dragEnterEvent(QDragEnterEvent * event)
602 if (event->mimeData()->hasUrls())
604 /// \todo Ask lyx-devel is this is enough:
605 /// if (event->mimeData()->hasFormat("text/plain"))
606 /// event->acceptProposedAction();
610 void GuiView::dropEvent(QDropEvent * event)
612 QList<QUrl> files = event->mimeData()->urls();
616 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
617 for (int i = 0; i != files.size(); ++i) {
618 string const file = os::internal_path(fromqstr(
619 files.at(i).toLocalFile()));
621 // Asynchronously post the event. DropEvent usually come
622 // from the BufferView. But reloading a file might close
623 // the BufferView from within its own event handler.
624 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
631 void GuiView::message(docstring const & str)
633 if (ForkedProcess::iAmAChild())
636 statusBar()->showMessage(toqstr(str));
637 d.statusbar_timer_.stop();
638 d.statusbar_timer_.start(3000);
642 void GuiView::smallSizedIcons()
644 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
648 void GuiView::normalSizedIcons()
650 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
654 void GuiView::bigSizedIcons()
656 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
660 void GuiView::clearMessage()
664 theLyXFunc().setLyXView(this);
665 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
666 d.statusbar_timer_.stop();
670 void GuiView::updateWindowTitle(GuiWorkArea * wa)
672 if (wa != d.current_work_area_)
674 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
675 setWindowIconText(wa->windowIconText());
679 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
682 disconnectBufferView();
683 connectBufferView(wa->bufferView());
684 connectBuffer(wa->bufferView().buffer());
685 d.current_work_area_ = wa;
686 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
687 this, SLOT(updateWindowTitle(GuiWorkArea *)));
688 updateWindowTitle(wa);
692 // The document settings needs to be reinitialised.
693 updateDialog("document", "");
695 // Buffer-dependent dialogs must be updated. This is done here because
696 // some dialogs require buffer()->text.
701 void GuiView::on_lastWorkAreaRemoved()
704 // We already are in a close event. Nothing more to do.
707 if (d.splitter_->count() > 1)
708 // We have a splitter so don't close anything.
711 // Reset and updates the dialogs.
712 d.toc_models_.reset(0);
713 updateDialog("document", "");
716 resetWindowTitleAndIconText();
718 if (lyxrc.open_buffers_in_tabs)
719 // Nothing more to do, the window should stay open.
722 if (guiApp->viewIds().size() > 1) {
728 // On Mac we also close the last window because the application stay
729 // resident in memory. On other platforms we don't close the last
730 // window because this would quit the application.
736 void GuiView::updateStatusBar()
738 // let the user see the explicit message
739 if (d.statusbar_timer_.isActive())
742 theLyXFunc().setLyXView(this);
743 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
747 bool GuiView::hasFocus() const
749 return qApp->activeWindow() == this;
753 bool GuiView::event(QEvent * e)
757 // Useful debug code:
758 //case QEvent::ActivationChange:
759 //case QEvent::WindowDeactivate:
760 //case QEvent::Paint:
761 //case QEvent::Enter:
762 //case QEvent::Leave:
763 //case QEvent::HoverEnter:
764 //case QEvent::HoverLeave:
765 //case QEvent::HoverMove:
766 //case QEvent::StatusTip:
767 //case QEvent::DragEnter:
768 //case QEvent::DragLeave:
772 case QEvent::WindowActivate: {
773 if (this == guiApp->currentView()) {
775 return QMainWindow::event(e);
777 guiApp->setCurrentView(this);
778 theLyXFunc().setLyXView(this);
779 if (d.current_work_area_) {
780 BufferView & bv = d.current_work_area_->bufferView();
781 connectBufferView(bv);
782 connectBuffer(bv.buffer());
783 // The document structure, name and dialogs might have
784 // changed in another view.
786 // The document settings needs to be reinitialised.
787 updateDialog("document", "");
790 resetWindowTitleAndIconText();
793 return QMainWindow::event(e);
796 case QEvent::ShortcutOverride: {
800 if (isFullScreen() && menuBar()->isHidden()) {
801 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
802 // FIXME: we should also try to detect special LyX shortcut such as
803 // Alt-P and Alt-M. Right now there is a hack in
804 // GuiWorkArea::processKeySym() that hides again the menubar for
806 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
808 return QMainWindow::event(e);
812 if (d.current_work_area_)
813 // Nothing special to do.
814 return QMainWindow::event(e);
816 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
817 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
819 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
820 || ke->key() == Qt::Key_Backtab)
821 return QMainWindow::event(e);
823 // Allow processing of shortcuts that are allowed even when no Buffer
825 theLyXFunc().setLyXView(this);
827 setKeySymbol(&sym, ke);
828 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
834 return QMainWindow::event(e);
838 void GuiView::resetWindowTitleAndIconText()
840 setWindowTitle(qt_("LyX"));
841 setWindowIconText(qt_("LyX"));
844 bool GuiView::focusNextPrevChild(bool /*next*/)
851 void GuiView::setBusy(bool busy)
853 if (d.current_work_area_) {
854 d.current_work_area_->setUpdatesEnabled(!busy);
856 d.current_work_area_->stopBlinkingCursor();
858 d.current_work_area_->startBlinkingCursor();
862 QApplication::setOverrideCursor(Qt::WaitCursor);
864 QApplication::restoreOverrideCursor();
868 GuiWorkArea * GuiView::workArea(Buffer & buffer)
870 if (currentWorkArea()
871 && ¤tWorkArea()->bufferView().buffer() == &buffer)
872 return (GuiWorkArea *) currentWorkArea();
873 if (TabWorkArea * twa = d.currentTabWorkArea())
874 return twa->workArea(buffer);
879 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
881 // Automatically create a TabWorkArea if there are none yet.
882 TabWorkArea * tab_widget = d.splitter_->count()
883 ? d.currentTabWorkArea() : addTabWorkArea();
884 return tab_widget->addWorkArea(buffer, *this);
888 TabWorkArea * GuiView::addTabWorkArea()
890 TabWorkArea * twa = new TabWorkArea;
891 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
892 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
893 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
894 this, SLOT(on_lastWorkAreaRemoved()));
896 d.splitter_->addWidget(twa);
897 d.stack_widget_->setCurrentWidget(d.splitter_);
902 GuiWorkArea const * GuiView::currentWorkArea() const
904 return d.current_work_area_;
908 GuiWorkArea * GuiView::currentWorkArea()
910 return d.current_work_area_;
914 GuiWorkArea const * GuiView::currentMainWorkArea() const
916 if (d.currentTabWorkArea() == NULL)
918 return d.currentTabWorkArea()->currentWorkArea();
922 GuiWorkArea * GuiView::currentMainWorkArea()
924 if (d.currentTabWorkArea() == NULL)
926 return d.currentTabWorkArea()->currentWorkArea();
930 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
932 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
934 d.current_work_area_ = NULL;
938 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
940 theGuiApp()->setCurrentView(this);
941 d.current_work_area_ = wa;
942 for (int i = 0; i != d.splitter_->count(); ++i) {
943 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
944 if (d.current_main_work_area_)
945 d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
946 d.current_main_work_area_ = wa;
947 d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
948 //d.current_main_work_area_->setLineWidth(2);
949 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
953 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
954 on_currentWorkAreaChanged(wa);
955 BufferView & bv = wa->bufferView();
956 bv.cursor().fixIfBroken();
958 wa->setUpdatesEnabled(true);
960 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
964 void GuiView::removeWorkArea(GuiWorkArea * wa)
967 if (wa == d.current_work_area_) {
969 disconnectBufferView();
970 d.current_work_area_ = 0;
971 d.current_main_work_area_ = 0;
974 bool found_twa = false;
975 for (int i = 0; i != d.splitter_->count(); ++i) {
976 TabWorkArea * twa = d.tabWorkArea(i);
977 if (twa->removeWorkArea(wa)) {
978 // Found in this tab group, and deleted the GuiWorkArea.
980 if (twa->count() != 0) {
981 if (d.current_work_area_ == 0)
982 // This means that we are closing the current GuiWorkArea, so
983 // switch to the next GuiWorkArea in the found TabWorkArea.
984 setCurrentWorkArea(twa->currentWorkArea());
986 // No more WorkAreas in this tab group, so delete it.
993 // It is not a tabbed work area (i.e., the search work area), so it
994 // should be deleted by other means.
995 LASSERT(found_twa, /* */);
997 if (d.current_work_area_ == 0) {
998 if (d.splitter_->count() != 0) {
999 TabWorkArea * twa = d.currentTabWorkArea();
1000 setCurrentWorkArea(twa->currentWorkArea());
1002 // No more work areas, switch to the background widget.
1003 setCurrentWorkArea(0);
1009 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1015 void GuiView::updateLayoutList()
1018 d.layout_->updateContents(false);
1022 void GuiView::updateToolbars()
1024 ToolbarMap::iterator end = d.toolbars_.end();
1025 if (d.current_work_area_) {
1027 d.current_work_area_->bufferView().cursor().inMathed();
1029 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1031 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1032 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1033 bool const mathmacrotemplate =
1034 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1036 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1037 it->second->update(math, table, review, mathmacrotemplate);
1039 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1040 it->second->update(false, false, false, false);
1044 Buffer * GuiView::buffer()
1046 if (d.current_work_area_)
1047 return &d.current_work_area_->bufferView().buffer();
1052 Buffer const * GuiView::buffer() const
1054 if (d.current_work_area_)
1055 return &d.current_work_area_->bufferView().buffer();
1060 void GuiView::setBuffer(Buffer * newBuffer)
1062 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1063 LASSERT(newBuffer, return);
1066 GuiWorkArea * wa = workArea(*newBuffer);
1068 updateLabels(*newBuffer->masterBuffer());
1069 wa = addWorkArea(*newBuffer);
1071 //Disconnect the old buffer...there's no new one.
1074 connectBuffer(*newBuffer);
1075 connectBufferView(wa->bufferView());
1076 setCurrentWorkArea(wa);
1082 void GuiView::connectBuffer(Buffer & buf)
1084 buf.setGuiDelegate(this);
1088 void GuiView::disconnectBuffer()
1090 if (d.current_work_area_)
1091 d.current_work_area_->bufferView().setGuiDelegate(0);
1095 void GuiView::connectBufferView(BufferView & bv)
1097 bv.setGuiDelegate(this);
1101 void GuiView::disconnectBufferView()
1103 if (d.current_work_area_)
1104 d.current_work_area_->bufferView().setGuiDelegate(0);
1108 void GuiView::errors(string const & error_type)
1110 ErrorList & el = buffer()->errorList(error_type);
1112 showDialog("errorlist", error_type);
1116 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1118 d.toc_models_.updateItem(toqstr(type), dit);
1122 void GuiView::structureChanged()
1124 d.toc_models_.reset(view());
1125 // Navigator needs more than a simple update in this case. It needs to be
1127 updateDialog("toc", "");
1131 void GuiView::updateDialog(string const & name, string const & data)
1133 if (!isDialogVisible(name))
1136 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1137 if (it == d.dialogs_.end())
1140 Dialog * const dialog = it->second.get();
1141 if (dialog->isVisibleView())
1142 dialog->initialiseParams(data);
1146 BufferView * GuiView::view()
1148 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1152 void GuiView::autoSave()
1154 LYXERR(Debug::INFO, "Running autoSave()");
1157 view()->buffer().autoSave();
1161 void GuiView::resetAutosaveTimers()
1164 d.autosave_timeout_.restart();
1168 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1171 Buffer * buf = buffer();
1173 /* In LyX/Mac, when a dialog is open, the menus of the
1174 application can still be accessed without giving focus to
1175 the main window. In this case, we want to disable the menu
1176 entries that are buffer-related.
1178 Note that this code is not perfect, as bug 1941 attests:
1179 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1181 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1184 switch(cmd.action) {
1185 case LFUN_BUFFER_WRITE:
1186 enable = buf && (buf->isUnnamed() || !buf->isClean());
1189 case LFUN_BUFFER_WRITE_AS:
1193 case LFUN_SPLIT_VIEW:
1194 if (cmd.getArg(0) == "vertical")
1195 enable = buf && (d.splitter_->count() == 1 ||
1196 d.splitter_->orientation() == Qt::Vertical);
1198 enable = buf && (d.splitter_->count() == 1 ||
1199 d.splitter_->orientation() == Qt::Horizontal);
1202 case LFUN_CLOSE_TAB_GROUP:
1203 enable = d.currentTabWorkArea();
1206 case LFUN_TOOLBAR_TOGGLE:
1207 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1208 flag.setOnOff(t->isVisible());
1211 case LFUN_UI_TOGGLE:
1212 flag.setOnOff(isFullScreen());
1215 case LFUN_DIALOG_TOGGLE:
1216 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1217 // fall through to set "enable"
1218 case LFUN_DIALOG_SHOW: {
1219 string const name = cmd.getArg(0);
1221 enable = name == "aboutlyx"
1222 || name == "file" //FIXME: should be removed.
1224 || name == "texinfo";
1225 else if (name == "print")
1226 enable = buf->isExportable("dvi")
1227 && lyxrc.print_command != "none";
1228 else if (name == "character") {
1232 InsetCode ic = view()->cursor().inset().lyxCode();
1233 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1236 else if (name == "symbols") {
1237 if (!view() || view()->cursor().inMathed())
1240 InsetCode ic = view()->cursor().inset().lyxCode();
1241 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1244 else if (name == "latexlog")
1245 enable = FileName(buf->logName()).isReadableFile();
1246 else if (name == "spellchecker")
1247 #if defined (USE_ASPELL)
1248 enable = !buf->isReadonly();
1252 else if (name == "vclog")
1253 enable = buf->lyxvc().inUse();
1257 case LFUN_DIALOG_UPDATE: {
1258 string const name = cmd.getArg(0);
1260 enable = name == "prefs";
1264 case LFUN_INSET_APPLY: {
1265 string const name = cmd.getArg(0);
1266 Inset * inset = getOpenInset(name);
1268 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1270 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1271 // Every inset is supposed to handle this
1272 LASSERT(false, break);
1276 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1277 flag |= lyx::getStatus(fr);
1279 enable = flag.enabled();
1283 case LFUN_COMPLETION_INLINE:
1284 if (!d.current_work_area_
1285 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1289 case LFUN_COMPLETION_POPUP:
1290 if (!d.current_work_area_
1291 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1295 case LFUN_COMPLETION_COMPLETE:
1296 if (!d.current_work_area_
1297 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1301 case LFUN_COMPLETION_ACCEPT:
1302 case LFUN_COMPLETION_CANCEL:
1303 if (!d.current_work_area_
1304 || (!d.current_work_area_->completer().popupVisible()
1305 && !d.current_work_area_->completer().inlineVisible()))
1314 flag.setEnabled(false);
1320 static FileName selectTemplateFile()
1322 FileDialog dlg(qt_("Select template file"));
1323 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1324 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1326 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1327 QStringList(qt_("LyX Documents (*.lyx)")));
1329 if (result.first == FileDialog::Later)
1331 if (result.second.isEmpty())
1333 return FileName(fromqstr(result.second));
1337 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1341 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1344 message(_("Document not loaded."));
1349 setBuffer(newBuffer);
1351 // scroll to the position when the file was last closed
1352 if (lyxrc.use_lastfilepos) {
1353 LastFilePosSection::FilePos filepos =
1354 theSession().lastFilePos().load(filename);
1355 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1359 theSession().lastFiles().add(filename);
1366 void GuiView::openDocument(string const & fname)
1368 string initpath = lyxrc.document_path;
1371 string const trypath = buffer()->filePath();
1372 // If directory is writeable, use this as default.
1373 if (FileName(trypath).isDirWritable())
1379 if (fname.empty()) {
1380 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1381 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1382 dlg.setButton2(qt_("Examples|#E#e"),
1383 toqstr(addPath(package().system_support().absFilename(), "examples")));
1385 QStringList filter(qt_("LyX Documents (*.lyx)"));
1386 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1387 << qt_("LyX-1.4.x Documents (*.lyx14)")
1388 << qt_("LyX-1.5.x Documents (*.lyx15)");
1389 FileDialog::Result result =
1390 dlg.open(toqstr(initpath), filter);
1392 if (result.first == FileDialog::Later)
1395 filename = fromqstr(result.second);
1397 // check selected filename
1398 if (filename.empty()) {
1399 message(_("Canceled."));
1405 // get absolute path of file and add ".lyx" to the filename if
1407 FileName const fullname =
1408 fileSearch(string(), filename, "lyx", support::may_not_exist);
1409 if (!fullname.empty())
1410 filename = fullname.absFilename();
1412 if (!fullname.onlyPath().isDirectory()) {
1413 Alert::warning(_("Invalid filename"),
1414 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1415 from_utf8(fullname.absFilename())));
1418 // if the file doesn't exist, let the user create one
1419 if (!fullname.exists()) {
1420 // the user specifically chose this name. Believe him.
1421 Buffer * const b = newFile(filename, string(), true);
1427 docstring const disp_fn = makeDisplayPath(filename);
1428 message(bformat(_("Opening document %1$s..."), disp_fn));
1431 Buffer * buf = loadDocument(fullname);
1436 buf->errors("Parse");
1437 str2 = bformat(_("Document %1$s opened."), disp_fn);
1438 if (buf->lyxvc().inUse())
1439 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1440 " " + _("Version control detected.");
1442 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1447 // FIXME: clean that
1448 static bool import(GuiView * lv, FileName const & filename,
1449 string const & format, ErrorList & errorList)
1451 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1453 string loader_format;
1454 vector<string> loaders = theConverters().loaders();
1455 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1456 for (vector<string>::const_iterator it = loaders.begin();
1457 it != loaders.end(); ++it) {
1458 if (!theConverters().isReachable(format, *it))
1461 string const tofile =
1462 support::changeExtension(filename.absFilename(),
1463 formats.extension(*it));
1464 if (!theConverters().convert(0, filename, FileName(tofile),
1465 filename, format, *it, errorList))
1467 loader_format = *it;
1470 if (loader_format.empty()) {
1471 frontend::Alert::error(_("Couldn't import file"),
1472 bformat(_("No information for importing the format %1$s."),
1473 formats.prettyName(format)));
1477 loader_format = format;
1479 if (loader_format == "lyx") {
1480 Buffer * buf = lv->loadDocument(lyxfile);
1485 buf->errors("Parse");
1487 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1491 bool as_paragraphs = loader_format == "textparagraph";
1492 string filename2 = (loader_format == format) ? filename.absFilename()
1493 : support::changeExtension(filename.absFilename(),
1494 formats.extension(loader_format));
1495 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1496 theLyXFunc().setLyXView(lv);
1497 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1504 void GuiView::importDocument(string const & argument)
1507 string filename = split(argument, format, ' ');
1509 LYXERR(Debug::INFO, format << " file: " << filename);
1511 // need user interaction
1512 if (filename.empty()) {
1513 string initpath = lyxrc.document_path;
1515 Buffer const * buf = buffer();
1517 string const trypath = buf->filePath();
1518 // If directory is writeable, use this as default.
1519 if (FileName(trypath).isDirWritable())
1523 docstring const text = bformat(_("Select %1$s file to import"),
1524 formats.prettyName(format));
1526 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1527 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1528 dlg.setButton2(qt_("Examples|#E#e"),
1529 toqstr(addPath(package().system_support().absFilename(), "examples")));
1531 docstring filter = formats.prettyName(format);
1534 filter += from_utf8(formats.extension(format));
1537 FileDialog::Result result =
1538 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1540 if (result.first == FileDialog::Later)
1543 filename = fromqstr(result.second);
1545 // check selected filename
1546 if (filename.empty())
1547 message(_("Canceled."));
1550 if (filename.empty())
1553 // get absolute path of file
1554 FileName const fullname(support::makeAbsPath(filename));
1556 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1558 // Check if the document already is open
1559 Buffer * buf = theBufferList().getBuffer(lyxfile);
1562 if (!closeBuffer()) {
1563 message(_("Canceled."));
1568 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1570 // if the file exists already, and we didn't do
1571 // -i lyx thefile.lyx, warn
1572 if (lyxfile.exists() && fullname != lyxfile) {
1574 docstring text = bformat(_("The document %1$s already exists.\n\n"
1575 "Do you want to overwrite that document?"), displaypath);
1576 int const ret = Alert::prompt(_("Overwrite document?"),
1577 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1580 message(_("Canceled."));
1585 message(bformat(_("Importing %1$s..."), displaypath));
1586 ErrorList errorList;
1587 if (import(this, fullname, format, errorList))
1588 message(_("imported."));
1590 message(_("file not imported!"));
1592 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1596 void GuiView::newDocument(string const & filename, bool from_template)
1598 FileName initpath(lyxrc.document_path);
1599 Buffer * buf = buffer();
1601 FileName const trypath(buf->filePath());
1602 // If directory is writeable, use this as default.
1603 if (trypath.isDirWritable())
1607 string templatefile = from_template ?
1608 selectTemplateFile().absFilename() : string();
1610 if (filename.empty())
1611 b = newUnnamedFile(templatefile, initpath);
1613 b = newFile(filename, templatefile, true);
1617 // Ensure the cursor is correctly positionned on screen.
1618 view()->showCursor();
1622 void GuiView::insertLyXFile(docstring const & fname)
1624 BufferView * bv = view();
1629 FileName filename(to_utf8(fname));
1631 if (!filename.empty()) {
1632 bv->insertLyXFile(filename);
1636 // Launch a file browser
1638 string initpath = lyxrc.document_path;
1639 string const trypath = bv->buffer().filePath();
1640 // If directory is writeable, use this as default.
1641 if (FileName(trypath).isDirWritable())
1645 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1646 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1647 dlg.setButton2(qt_("Examples|#E#e"),
1648 toqstr(addPath(package().system_support().absFilename(),
1651 FileDialog::Result result = dlg.open(toqstr(initpath),
1652 QStringList(qt_("LyX Documents (*.lyx)")));
1654 if (result.first == FileDialog::Later)
1658 filename.set(fromqstr(result.second));
1660 // check selected filename
1661 if (filename.empty()) {
1662 // emit message signal.
1663 message(_("Canceled."));
1667 bv->insertLyXFile(filename);
1671 void GuiView::insertPlaintextFile(docstring const & fname,
1674 BufferView * bv = view();
1679 FileName filename(to_utf8(fname));
1681 if (!filename.empty()) {
1682 bv->insertPlaintextFile(filename, asParagraph);
1686 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1687 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1689 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1692 if (result.first == FileDialog::Later)
1696 filename.set(fromqstr(result.second));
1698 // check selected filename
1699 if (filename.empty()) {
1700 // emit message signal.
1701 message(_("Canceled."));
1705 bv->insertPlaintextFile(filename, asParagraph);
1709 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1711 FileName fname = b.fileName();
1712 FileName const oldname = fname;
1714 if (!newname.empty()) {
1716 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1718 // Switch to this Buffer.
1721 /// No argument? Ask user through dialog.
1723 FileDialog dlg(qt_("Choose a filename to save document as"),
1724 LFUN_BUFFER_WRITE_AS);
1725 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1726 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1728 if (!isLyXFilename(fname.absFilename()))
1729 fname.changeExtension(".lyx");
1731 FileDialog::Result result =
1732 dlg.save(toqstr(fname.onlyPath().absFilename()),
1733 QStringList(qt_("LyX Documents (*.lyx)")),
1734 toqstr(fname.onlyFileName()));
1736 if (result.first == FileDialog::Later)
1739 fname.set(fromqstr(result.second));
1744 if (!isLyXFilename(fname.absFilename()))
1745 fname.changeExtension(".lyx");
1748 if (FileName(fname).exists()) {
1749 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1750 docstring text = bformat(_("The document %1$s already "
1751 "exists.\n\nDo you want to "
1752 "overwrite that document?"),
1754 int const ret = Alert::prompt(_("Overwrite document?"),
1755 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1758 case 1: return renameBuffer(b, docstring());
1759 case 2: return false;
1763 // Ok, change the name of the buffer
1764 b.setFileName(fname.absFilename());
1766 bool unnamed = b.isUnnamed();
1767 b.setUnnamed(false);
1768 b.saveCheckSum(fname);
1770 if (!saveBuffer(b)) {
1771 b.setFileName(oldname.absFilename());
1772 b.setUnnamed(unnamed);
1773 b.saveCheckSum(oldname);
1781 bool GuiView::saveBuffer(Buffer & b)
1783 if (workArea(b) && workArea(b)->inDialogMode())
1787 return renameBuffer(b, docstring());
1790 theSession().lastFiles().add(b.fileName());
1794 // Switch to this Buffer.
1797 // FIXME: we don't tell the user *WHY* the save failed !!
1798 docstring const file = makeDisplayPath(b.absFileName(), 30);
1799 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1800 "Do you want to rename the document and "
1801 "try again?"), file);
1802 int const ret = Alert::prompt(_("Rename and save?"),
1803 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1806 if (!renameBuffer(b, docstring()))
1815 return saveBuffer(b);
1819 bool GuiView::closeBuffer()
1821 Buffer * buf = buffer();
1822 return buf && closeBuffer(*buf);
1826 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1828 // goto bookmark to update bookmark pit.
1829 //FIXME: we should update only the bookmarks related to this buffer!
1830 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1831 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1832 theLyXFunc().gotoBookmark(i+1, false, false);
1834 if (buf.isClean() || buf.paragraphs().empty()) {
1835 if (buf.masterBuffer() == &buf && tolastopened)
1836 theSession().lastOpened().add(buf.fileName());
1838 // Don't close child documents.
1839 removeWorkArea(currentMainWorkArea());
1841 theBufferList().release(&buf);
1844 // Switch to this Buffer.
1849 if (buf.isUnnamed())
1850 file = from_utf8(buf.fileName().onlyFileName());
1852 file = buf.fileName().displayName(30);
1854 // Bring this window to top before asking questions.
1858 docstring const text = bformat(_("The document %1$s has unsaved changes."
1859 "\n\nDo you want to save the document or discard the changes?"), file);
1860 int const ret = Alert::prompt(_("Save changed document?"),
1861 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1865 if (!saveBuffer(buf))
1869 // if we crash after this we could
1870 // have no autosave file but I guess
1871 // this is really improbable (Jug)
1872 removeAutosaveFile(buf.absFileName());
1878 // save file names to .lyx/session
1879 // if master/slave are both open, do not save slave since it
1880 // will be automatically loaded when the master is loaded
1881 if (buf.masterBuffer() == &buf && tolastopened)
1882 theSession().lastOpened().add(buf.fileName());
1885 // Don't close child documents.
1886 removeWorkArea(currentMainWorkArea());
1888 theBufferList().release(&buf);
1894 bool GuiView::dispatch(FuncRequest const & cmd)
1896 BufferView * bv = view();
1897 // By default we won't need any update.
1899 bv->cursor().updateFlags(Update::None);
1900 bool dispatched = true;
1902 switch(cmd.action) {
1903 case LFUN_BUFFER_IMPORT:
1904 importDocument(to_utf8(cmd.argument()));
1907 case LFUN_BUFFER_SWITCH:
1908 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1911 case LFUN_BUFFER_NEXT:
1912 setBuffer(theBufferList().next(buffer()));
1915 case LFUN_BUFFER_PREVIOUS:
1916 setBuffer(theBufferList().previous(buffer()));
1919 case LFUN_COMMAND_EXECUTE: {
1920 bool const show_it = cmd.argument() != "off";
1921 // FIXME: this is a hack, "minibuffer" should not be
1923 if (GuiToolbar * t = toolbar("minibuffer")) {
1924 t->setVisible(show_it);
1925 if (show_it && t->commandBuffer())
1926 t->commandBuffer()->setFocus();
1930 case LFUN_DROP_LAYOUTS_CHOICE:
1932 d.layout_->showPopup();
1935 case LFUN_MENU_OPEN:
1936 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1937 menu->exec(QCursor::pos());
1940 case LFUN_FILE_INSERT:
1941 insertLyXFile(cmd.argument());
1943 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1944 insertPlaintextFile(cmd.argument(), true);
1947 case LFUN_FILE_INSERT_PLAINTEXT:
1948 insertPlaintextFile(cmd.argument(), false);
1951 case LFUN_BUFFER_WRITE:
1953 saveBuffer(bv->buffer());
1956 case LFUN_BUFFER_WRITE_AS:
1958 renameBuffer(bv->buffer(), cmd.argument());
1961 case LFUN_BUFFER_WRITE_ALL: {
1962 Buffer * first = theBufferList().first();
1965 message(_("Saving all documents..."));
1966 // We cannot use a for loop as the buffer list cycles.
1969 if (!b->isClean()) {
1971 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1973 b = theBufferList().next(b);
1974 } while (b != first);
1975 message(_("All documents saved."));
1979 case LFUN_TOOLBAR_TOGGLE: {
1980 string const name = cmd.getArg(0);
1981 if (GuiToolbar * t = toolbar(name))
1986 case LFUN_DIALOG_UPDATE: {
1987 string const name = to_utf8(cmd.argument());
1988 // Can only update a dialog connected to an existing inset
1989 Inset * inset = getOpenInset(name);
1991 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1992 inset->dispatch(view()->cursor(), fr);
1993 } else if (name == "paragraph") {
1994 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1995 } else if (name == "prefs") {
1996 updateDialog(name, string());
2001 case LFUN_DIALOG_TOGGLE: {
2002 if (isDialogVisible(cmd.getArg(0)))
2003 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2005 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2009 case LFUN_DIALOG_DISCONNECT_INSET:
2010 disconnectDialog(to_utf8(cmd.argument()));
2013 case LFUN_DIALOG_HIDE: {
2014 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2018 case LFUN_DIALOG_SHOW: {
2019 string const name = cmd.getArg(0);
2020 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2022 if (name == "character") {
2023 data = freefont2string();
2025 showDialog("character", data);
2026 } else if (name == "latexlog") {
2027 Buffer::LogType type;
2028 string const logfile = buffer()->logName(&type);
2030 case Buffer::latexlog:
2033 case Buffer::buildlog:
2037 data += Lexer::quoteString(logfile);
2038 showDialog("log", data);
2039 } else if (name == "vclog") {
2040 string const data = "vc " +
2041 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2042 showDialog("log", data);
2043 } else if (name == "symbols") {
2044 data = bv->cursor().getEncoding()->name();
2046 showDialog("symbols", data);
2048 } else if (name == "prefs" && isFullScreen()) {
2049 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2051 showDialog("prefs", data);
2053 showDialog(name, data);
2057 case LFUN_INSET_APPLY: {
2058 string const name = cmd.getArg(0);
2059 Inset * inset = getOpenInset(name);
2061 // put cursor in front of inset.
2062 if (!view()->setCursorFromInset(inset))
2063 LASSERT(false, break);
2065 // useful if we are called from a dialog.
2066 view()->cursor().beginUndoGroup();
2067 view()->cursor().recordUndo();
2068 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2069 inset->dispatch(view()->cursor(), fr);
2070 view()->cursor().endUndoGroup();
2072 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2078 case LFUN_UI_TOGGLE:
2080 // Make sure the keyboard focus stays in the work area.
2084 case LFUN_SPLIT_VIEW:
2085 if (Buffer * buf = buffer()) {
2086 string const orientation = cmd.getArg(0);
2087 d.splitter_->setOrientation(orientation == "vertical"
2088 ? Qt::Vertical : Qt::Horizontal);
2089 TabWorkArea * twa = addTabWorkArea();
2090 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2091 setCurrentWorkArea(wa);
2095 case LFUN_CLOSE_TAB_GROUP:
2096 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2098 twa = d.currentTabWorkArea();
2099 // Switch to the next GuiWorkArea in the found TabWorkArea.
2101 // Make sure the work area is up to date.
2102 setCurrentWorkArea(twa->currentWorkArea());
2104 setCurrentWorkArea(0);
2109 case LFUN_COMPLETION_INLINE:
2110 if (d.current_work_area_)
2111 d.current_work_area_->completer().showInline();
2114 case LFUN_COMPLETION_POPUP:
2115 if (d.current_work_area_)
2116 d.current_work_area_->completer().showPopup();
2120 case LFUN_COMPLETION_COMPLETE:
2121 if (d.current_work_area_)
2122 d.current_work_area_->completer().tab();
2125 case LFUN_COMPLETION_CANCEL:
2126 if (d.current_work_area_) {
2127 if (d.current_work_area_->completer().popupVisible())
2128 d.current_work_area_->completer().hidePopup();
2130 d.current_work_area_->completer().hideInline();
2134 case LFUN_COMPLETION_ACCEPT:
2135 if (d.current_work_area_)
2136 d.current_work_area_->completer().activate();
2145 // Part of automatic menu appearance feature.
2146 if (isFullScreen()) {
2147 if (menuBar()->isVisible())
2149 if (statusBar()->isVisible())
2150 statusBar()->hide();
2157 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2159 string const arg = cmd.getArg(0);
2160 if (arg == "scrollbar") {
2161 // hide() is of no help
2162 if (d.current_work_area_->verticalScrollBarPolicy() ==
2163 Qt::ScrollBarAlwaysOff)
2165 d.current_work_area_->setVerticalScrollBarPolicy(
2166 Qt::ScrollBarAsNeeded);
2168 d.current_work_area_->setVerticalScrollBarPolicy(
2169 Qt::ScrollBarAlwaysOff);
2172 if (arg == "statusbar") {
2173 statusBar()->setVisible(!statusBar()->isVisible());
2176 if (arg == "menubar") {
2177 menuBar()->setVisible(!menuBar()->isVisible());
2180 #if QT_VERSION >= 0x040300
2181 if (arg == "frame") {
2183 getContentsMargins(&l, &t, &r, &b);
2184 //are the frames in default state?
2185 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2187 setContentsMargins(-2, -2, -2, -2);
2189 setContentsMargins(0, 0, 0, 0);
2194 if (arg == "fullscreen") {
2199 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2203 void GuiView::toggleFullScreen()
2205 if (isFullScreen()) {
2206 for (int i = 0; i != d.splitter_->count(); ++i)
2207 d.tabWorkArea(i)->setFullScreen(false);
2208 #if QT_VERSION >= 0x040300
2209 setContentsMargins(0, 0, 0, 0);
2211 setWindowState(windowState() ^ Qt::WindowFullScreen);
2214 statusBar()->show();
2217 hideDialogs("prefs", 0);
2218 for (int i = 0; i != d.splitter_->count(); ++i)
2219 d.tabWorkArea(i)->setFullScreen(true);
2220 #if QT_VERSION >= 0x040300
2221 setContentsMargins(-2, -2, -2, -2);
2224 setWindowState(windowState() ^ Qt::WindowFullScreen);
2225 statusBar()->hide();
2227 if (lyxrc.full_screen_toolbars) {
2228 ToolbarMap::iterator end = d.toolbars_.end();
2229 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2234 // give dialogs like the TOC a chance to adapt
2239 Buffer const * GuiView::updateInset(Inset const * inset)
2241 if (!d.current_work_area_)
2245 d.current_work_area_->scheduleRedraw();
2247 return &d.current_work_area_->bufferView().buffer();
2251 void GuiView::restartCursor()
2253 /* When we move around, or type, it's nice to be able to see
2254 * the cursor immediately after the keypress.
2256 if (d.current_work_area_)
2257 d.current_work_area_->startBlinkingCursor();
2259 // Take this occasion to update the other GUI elements.
2265 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2267 if (d.current_work_area_)
2268 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2273 // This list should be kept in sync with the list of insets in
2274 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2275 // dialog should have the same name as the inset.
2276 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2277 // docs in LyXAction.cpp.
2279 char const * const dialognames[] = {
2280 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2281 "citation", "document", "errorlist", "ert", "external", "file",
2282 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2283 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2284 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2286 #ifdef HAVE_LIBAIKSAURUS
2290 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2292 char const * const * const end_dialognames =
2293 dialognames + (sizeof(dialognames) / sizeof(char *));
2297 cmpCStr(char const * name) : name_(name) {}
2298 bool operator()(char const * other) {
2299 return strcmp(other, name_) == 0;
2306 bool isValidName(string const & name)
2308 return find_if(dialognames, end_dialognames,
2309 cmpCStr(name.c_str())) != end_dialognames;
2315 void GuiView::resetDialogs()
2317 // Make sure that no LFUN uses any LyXView.
2318 theLyXFunc().setLyXView(0);
2321 constructToolbars();
2322 guiApp->menus().fillMenuBar(menuBar(), this, false);
2324 d.layout_->updateContents(true);
2325 // Now update controls with current buffer.
2326 theLyXFunc().setLyXView(this);
2332 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2334 if (!isValidName(name))
2337 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2339 if (it != d.dialogs_.end()) {
2341 it->second->hideView();
2342 return it->second.get();
2345 Dialog * dialog = build(name);
2346 d.dialogs_[name].reset(dialog);
2347 if (lyxrc.allow_geometry_session)
2348 dialog->restoreSession();
2355 void GuiView::showDialog(string const & name, string const & data,
2363 Dialog * dialog = findOrBuild(name, false);
2365 dialog->showData(data);
2367 d.open_insets_[name] = inset;
2370 catch (ExceptionMessage const & ex) {
2378 bool GuiView::isDialogVisible(string const & name) const
2380 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2381 if (it == d.dialogs_.end())
2383 return it->second.get()->isVisibleView();
2387 void GuiView::hideDialog(string const & name, Inset * inset)
2389 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2390 if (it == d.dialogs_.end())
2393 if (inset && inset != getOpenInset(name))
2396 Dialog * const dialog = it->second.get();
2397 if (dialog->isVisibleView())
2399 d.open_insets_[name] = 0;
2403 void GuiView::disconnectDialog(string const & name)
2405 if (!isValidName(name))
2408 if (d.open_insets_.find(name) != d.open_insets_.end())
2409 d.open_insets_[name] = 0;
2413 Inset * GuiView::getOpenInset(string const & name) const
2415 if (!isValidName(name))
2418 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2419 return it == d.open_insets_.end() ? 0 : it->second;
2423 void GuiView::hideAll() const
2425 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2426 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2428 for(; it != end; ++it)
2429 it->second->hideView();
2433 void GuiView::updateDialogs()
2435 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2436 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2438 for(; it != end; ++it) {
2439 Dialog * dialog = it->second.get();
2440 if (dialog && dialog->isVisibleView())
2441 dialog->checkStatus();
2448 // will be replaced by a proper factory...
2449 Dialog * createGuiAbout(GuiView & lv);
2450 Dialog * createGuiBibitem(GuiView & lv);
2451 Dialog * createGuiBibtex(GuiView & lv);
2452 Dialog * createGuiBox(GuiView & lv);
2453 Dialog * createGuiBranch(GuiView & lv);
2454 Dialog * createGuiChanges(GuiView & lv);
2455 Dialog * createGuiCharacter(GuiView & lv);
2456 Dialog * createGuiCitation(GuiView & lv);
2457 Dialog * createGuiDelimiter(GuiView & lv);
2458 Dialog * createGuiDocument(GuiView & lv);
2459 Dialog * createGuiErrorList(GuiView & lv);
2460 Dialog * createGuiERT(GuiView & lv);
2461 Dialog * createGuiExternal(GuiView & lv);
2462 Dialog * createGuiFloat(GuiView & lv);
2463 Dialog * createGuiGraphics(GuiView & lv);
2464 Dialog * createGuiHSpace(GuiView & lv);
2465 Dialog * createGuiInclude(GuiView & lv);
2466 Dialog * createGuiInfo(GuiView & lv);
2467 Dialog * createGuiLabel(GuiView & lv);
2468 Dialog * createGuiListings(GuiView & lv);
2469 Dialog * createGuiLog(GuiView & lv);
2470 Dialog * createGuiMathMatrix(GuiView & lv);
2471 Dialog * createGuiNomenclature(GuiView & lv);
2472 Dialog * createGuiNote(GuiView & lv);
2473 Dialog * createGuiParagraph(GuiView & lv);
2474 Dialog * createGuiPreferences(GuiView & lv);
2475 Dialog * createGuiPrint(GuiView & lv);
2476 Dialog * createGuiRef(GuiView & lv);
2477 Dialog * createGuiSearch(GuiView & lv);
2478 Dialog * createGuiSearchAdv(GuiView & lv);
2479 Dialog * createGuiSendTo(GuiView & lv);
2480 Dialog * createGuiShowFile(GuiView & lv);
2481 Dialog * createGuiSpellchecker(GuiView & lv);
2482 Dialog * createGuiSymbols(GuiView & lv);
2483 Dialog * createGuiTabularCreate(GuiView & lv);
2484 Dialog * createGuiTabular(GuiView & lv);
2485 Dialog * createGuiTexInfo(GuiView & lv);
2486 Dialog * createGuiToc(GuiView & lv);
2487 Dialog * createGuiThesaurus(GuiView & lv);
2488 Dialog * createGuiHyperlink(GuiView & lv);
2489 Dialog * createGuiVSpace(GuiView & lv);
2490 Dialog * createGuiViewSource(GuiView & lv);
2491 Dialog * createGuiWrap(GuiView & lv);
2494 Dialog * GuiView::build(string const & name)
2496 LASSERT(isValidName(name), return 0);
2498 if (name == "aboutlyx")
2499 return createGuiAbout(*this);
2500 if (name == "bibitem")
2501 return createGuiBibitem(*this);
2502 if (name == "bibtex")
2503 return createGuiBibtex(*this);
2505 return createGuiBox(*this);
2506 if (name == "branch")
2507 return createGuiBranch(*this);
2508 if (name == "changes")
2509 return createGuiChanges(*this);
2510 if (name == "character")
2511 return createGuiCharacter(*this);
2512 if (name == "citation")
2513 return createGuiCitation(*this);
2514 if (name == "document")
2515 return createGuiDocument(*this);
2516 if (name == "errorlist")
2517 return createGuiErrorList(*this);
2519 return createGuiERT(*this);
2520 if (name == "external")
2521 return createGuiExternal(*this);
2523 return createGuiShowFile(*this);
2524 if (name == "findreplace")
2525 return createGuiSearch(*this);
2526 if (name == "findreplaceadv")
2527 return createGuiSearchAdv(*this);
2528 if (name == "float")
2529 return createGuiFloat(*this);
2530 if (name == "graphics")
2531 return createGuiGraphics(*this);
2532 if (name == "include")
2533 return createGuiInclude(*this);
2535 return createGuiInfo(*this);
2536 if (name == "nomenclature")
2537 return createGuiNomenclature(*this);
2538 if (name == "label")
2539 return createGuiLabel(*this);
2541 return createGuiLog(*this);
2542 if (name == "view-source")
2543 return createGuiViewSource(*this);
2544 if (name == "mathdelimiter")
2545 return createGuiDelimiter(*this);
2546 if (name == "mathmatrix")
2547 return createGuiMathMatrix(*this);
2549 return createGuiNote(*this);
2550 if (name == "paragraph")
2551 return createGuiParagraph(*this);
2552 if (name == "prefs")
2553 return createGuiPreferences(*this);
2554 if (name == "print")
2555 return createGuiPrint(*this);
2557 return createGuiRef(*this);
2558 if (name == "sendto")
2559 return createGuiSendTo(*this);
2560 if (name == "space")
2561 return createGuiHSpace(*this);
2562 if (name == "spellchecker")
2563 return createGuiSpellchecker(*this);
2564 if (name == "symbols")
2565 return createGuiSymbols(*this);
2566 if (name == "tabular")
2567 return createGuiTabular(*this);
2568 if (name == "tabularcreate")
2569 return createGuiTabularCreate(*this);
2570 if (name == "texinfo")
2571 return createGuiTexInfo(*this);
2572 #ifdef HAVE_LIBAIKSAURUS
2573 if (name == "thesaurus")
2574 return createGuiThesaurus(*this);
2577 return createGuiToc(*this);
2579 return createGuiHyperlink(*this);
2580 if (name == "vspace")
2581 return createGuiVSpace(*this);
2583 return createGuiWrap(*this);
2584 if (name == "listings")
2585 return createGuiListings(*this);
2591 } // namespace frontend
2594 #include "moc_GuiView.cpp"