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.
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();
942 theGuiApp()->setCurrentView(this);
943 d.current_work_area_ = wa;
944 for (int i = 0; i != d.splitter_->count(); ++i) {
945 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
946 //if (d.current_main_work_area_)
947 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
948 d.current_main_work_area_ = wa;
949 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
950 //d.current_main_work_area_->setLineWidth(2);
951 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
955 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
956 on_currentWorkAreaChanged(wa);
957 BufferView & bv = wa->bufferView();
958 bv.cursor().fixIfBroken();
960 wa->setUpdatesEnabled(true);
961 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
965 void GuiView::removeWorkArea(GuiWorkArea * wa)
968 if (wa == d.current_work_area_) {
970 disconnectBufferView();
971 d.current_work_area_ = 0;
972 d.current_main_work_area_ = 0;
975 bool found_twa = false;
976 for (int i = 0; i != d.splitter_->count(); ++i) {
977 TabWorkArea * twa = d.tabWorkArea(i);
978 if (twa->removeWorkArea(wa)) {
979 // Found in this tab group, and deleted the GuiWorkArea.
981 if (twa->count() != 0) {
982 if (d.current_work_area_ == 0)
983 // This means that we are closing the current GuiWorkArea, so
984 // switch to the next GuiWorkArea in the found TabWorkArea.
985 setCurrentWorkArea(twa->currentWorkArea());
987 // No more WorkAreas in this tab group, so delete it.
994 // It is not a tabbed work area (i.e., the search work area), so it
995 // should be deleted by other means.
996 LASSERT(found_twa, /* */);
998 if (d.current_work_area_ == 0) {
999 if (d.splitter_->count() != 0) {
1000 TabWorkArea * twa = d.currentTabWorkArea();
1001 setCurrentWorkArea(twa->currentWorkArea());
1003 // No more work areas, switch to the background widget.
1004 setCurrentWorkArea(0);
1010 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1016 void GuiView::updateLayoutList()
1019 d.layout_->updateContents(false);
1023 void GuiView::updateToolbars()
1025 ToolbarMap::iterator end = d.toolbars_.end();
1026 if (d.current_work_area_) {
1028 d.current_work_area_->bufferView().cursor().inMathed();
1030 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1032 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1033 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1034 bool const mathmacrotemplate =
1035 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1037 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1038 it->second->update(math, table, review, mathmacrotemplate);
1040 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1041 it->second->update(false, false, false, false);
1045 Buffer * GuiView::buffer()
1047 if (d.current_work_area_)
1048 return &d.current_work_area_->bufferView().buffer();
1053 Buffer const * GuiView::buffer() const
1055 if (d.current_work_area_)
1056 return &d.current_work_area_->bufferView().buffer();
1061 void GuiView::setBuffer(Buffer * newBuffer)
1063 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1064 LASSERT(newBuffer, return);
1067 GuiWorkArea * wa = workArea(*newBuffer);
1069 newBuffer->masterBuffer()->updateLabels();
1070 wa = addWorkArea(*newBuffer);
1072 //Disconnect the old buffer...there's no new one.
1075 connectBuffer(*newBuffer);
1076 connectBufferView(wa->bufferView());
1077 setCurrentWorkArea(wa);
1083 void GuiView::connectBuffer(Buffer & buf)
1085 buf.setGuiDelegate(this);
1089 void GuiView::disconnectBuffer()
1091 if (d.current_work_area_)
1092 d.current_work_area_->bufferView().setGuiDelegate(0);
1096 void GuiView::connectBufferView(BufferView & bv)
1098 bv.setGuiDelegate(this);
1102 void GuiView::disconnectBufferView()
1104 if (d.current_work_area_)
1105 d.current_work_area_->bufferView().setGuiDelegate(0);
1109 void GuiView::errors(string const & error_type)
1111 ErrorList & el = buffer()->errorList(error_type);
1113 showDialog("errorlist", error_type);
1117 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1119 d.toc_models_.updateItem(toqstr(type), dit);
1123 void GuiView::structureChanged()
1125 d.toc_models_.reset(view());
1126 // Navigator needs more than a simple update in this case. It needs to be
1128 updateDialog("toc", "");
1132 void GuiView::updateDialog(string const & name, string const & data)
1134 if (!isDialogVisible(name))
1137 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1138 if (it == d.dialogs_.end())
1141 Dialog * const dialog = it->second.get();
1142 if (dialog->isVisibleView())
1143 dialog->initialiseParams(data);
1147 BufferView * GuiView::view()
1149 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1153 void GuiView::autoSave()
1155 LYXERR(Debug::INFO, "Running autoSave()");
1158 view()->buffer().autoSave();
1162 void GuiView::resetAutosaveTimers()
1165 d.autosave_timeout_.restart();
1169 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1172 Buffer * buf = buffer();
1174 /* In LyX/Mac, when a dialog is open, the menus of the
1175 application can still be accessed without giving focus to
1176 the main window. In this case, we want to disable the menu
1177 entries that are buffer-related.
1179 Note that this code is not perfect, as bug 1941 attests:
1180 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1182 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1185 switch(cmd.action) {
1186 case LFUN_BUFFER_WRITE:
1187 enable = buf && (buf->isUnnamed() || !buf->isClean());
1190 case LFUN_BUFFER_WRITE_AS:
1194 case LFUN_SPLIT_VIEW:
1195 if (cmd.getArg(0) == "vertical")
1196 enable = buf && (d.splitter_->count() == 1 ||
1197 d.splitter_->orientation() == Qt::Vertical);
1199 enable = buf && (d.splitter_->count() == 1 ||
1200 d.splitter_->orientation() == Qt::Horizontal);
1203 case LFUN_CLOSE_TAB_GROUP:
1204 enable = d.currentTabWorkArea();
1207 case LFUN_TOOLBAR_TOGGLE:
1208 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1209 flag.setOnOff(t->isVisible());
1212 case LFUN_UI_TOGGLE:
1213 flag.setOnOff(isFullScreen());
1216 case LFUN_DIALOG_TOGGLE:
1217 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1218 // fall through to set "enable"
1219 case LFUN_DIALOG_SHOW: {
1220 string const name = cmd.getArg(0);
1222 enable = name == "aboutlyx"
1223 || name == "file" //FIXME: should be removed.
1225 || name == "texinfo";
1226 else if (name == "print")
1227 enable = buf->isExportable("dvi")
1228 && lyxrc.print_command != "none";
1229 else if (name == "character") {
1233 InsetCode ic = view()->cursor().inset().lyxCode();
1234 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1237 else if (name == "symbols") {
1238 if (!view() || view()->cursor().inMathed())
1241 InsetCode ic = view()->cursor().inset().lyxCode();
1242 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1245 else if (name == "latexlog")
1246 enable = FileName(buf->logName()).isReadableFile();
1247 else if (name == "spellchecker")
1248 #if defined (USE_ASPELL)
1249 enable = !buf->isReadonly();
1253 else if (name == "vclog")
1254 enable = buf->lyxvc().inUse();
1258 case LFUN_DIALOG_UPDATE: {
1259 string const name = cmd.getArg(0);
1261 enable = name == "prefs";
1265 case LFUN_INSET_APPLY: {
1266 string const name = cmd.getArg(0);
1267 Inset * inset = getOpenInset(name);
1269 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1271 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1272 // Every inset is supposed to handle this
1273 LASSERT(false, break);
1277 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1278 flag |= lyx::getStatus(fr);
1280 enable = flag.enabled();
1284 case LFUN_COMPLETION_INLINE:
1285 if (!d.current_work_area_
1286 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1290 case LFUN_COMPLETION_POPUP:
1291 if (!d.current_work_area_
1292 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1296 case LFUN_COMPLETION_COMPLETE:
1297 if (!d.current_work_area_
1298 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1302 case LFUN_COMPLETION_ACCEPT:
1303 if (!d.current_work_area_
1304 || (!d.current_work_area_->completer().popupVisible()
1305 && !d.current_work_area_->completer().inlineVisible()
1306 && !d.current_work_area_->completer().completionAvailable()))
1310 case LFUN_COMPLETION_CANCEL:
1311 if (!d.current_work_area_
1312 || (!d.current_work_area_->completer().popupVisible()
1313 && !d.current_work_area_->completer().inlineVisible()))
1322 flag.setEnabled(false);
1328 static FileName selectTemplateFile()
1330 FileDialog dlg(qt_("Select template file"));
1331 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1332 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1334 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1335 QStringList(qt_("LyX Documents (*.lyx)")));
1337 if (result.first == FileDialog::Later)
1339 if (result.second.isEmpty())
1341 return FileName(fromqstr(result.second));
1345 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1349 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1352 message(_("Document not loaded."));
1357 setBuffer(newBuffer);
1359 // scroll to the position when the file was last closed
1360 if (lyxrc.use_lastfilepos) {
1361 LastFilePosSection::FilePos filepos =
1362 theSession().lastFilePos().load(filename);
1363 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1367 theSession().lastFiles().add(filename);
1374 void GuiView::openDocument(string const & fname)
1376 string initpath = lyxrc.document_path;
1379 string const trypath = buffer()->filePath();
1380 // If directory is writeable, use this as default.
1381 if (FileName(trypath).isDirWritable())
1387 if (fname.empty()) {
1388 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1389 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1390 dlg.setButton2(qt_("Examples|#E#e"),
1391 toqstr(addPath(package().system_support().absFilename(), "examples")));
1393 QStringList filter(qt_("LyX Documents (*.lyx)"));
1394 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1395 << qt_("LyX-1.4.x Documents (*.lyx14)")
1396 << qt_("LyX-1.5.x Documents (*.lyx15)");
1397 FileDialog::Result result =
1398 dlg.open(toqstr(initpath), filter);
1400 if (result.first == FileDialog::Later)
1403 filename = fromqstr(result.second);
1405 // check selected filename
1406 if (filename.empty()) {
1407 message(_("Canceled."));
1413 // get absolute path of file and add ".lyx" to the filename if
1415 FileName const fullname =
1416 fileSearch(string(), filename, "lyx", support::may_not_exist);
1417 if (!fullname.empty())
1418 filename = fullname.absFilename();
1420 if (!fullname.onlyPath().isDirectory()) {
1421 Alert::warning(_("Invalid filename"),
1422 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1423 from_utf8(fullname.absFilename())));
1426 // if the file doesn't exist, let the user create one
1427 if (!fullname.exists()) {
1428 // the user specifically chose this name. Believe him.
1429 Buffer * const b = newFile(filename, string(), true);
1435 docstring const disp_fn = makeDisplayPath(filename);
1436 message(bformat(_("Opening document %1$s..."), disp_fn));
1439 Buffer * buf = loadDocument(fullname);
1441 buf->updateLabels();
1443 buf->errors("Parse");
1444 str2 = bformat(_("Document %1$s opened."), disp_fn);
1445 if (buf->lyxvc().inUse())
1446 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1447 " " + _("Version control detected.");
1449 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1454 // FIXME: clean that
1455 static bool import(GuiView * lv, FileName const & filename,
1456 string const & format, ErrorList & errorList)
1458 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1460 string loader_format;
1461 vector<string> loaders = theConverters().loaders();
1462 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1463 for (vector<string>::const_iterator it = loaders.begin();
1464 it != loaders.end(); ++it) {
1465 if (!theConverters().isReachable(format, *it))
1468 string const tofile =
1469 support::changeExtension(filename.absFilename(),
1470 formats.extension(*it));
1471 if (!theConverters().convert(0, filename, FileName(tofile),
1472 filename, format, *it, errorList))
1474 loader_format = *it;
1477 if (loader_format.empty()) {
1478 frontend::Alert::error(_("Couldn't import file"),
1479 bformat(_("No information for importing the format %1$s."),
1480 formats.prettyName(format)));
1484 loader_format = format;
1486 if (loader_format == "lyx") {
1487 Buffer * buf = lv->loadDocument(lyxfile);
1490 buf->updateLabels();
1492 buf->errors("Parse");
1494 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1498 bool as_paragraphs = loader_format == "textparagraph";
1499 string filename2 = (loader_format == format) ? filename.absFilename()
1500 : support::changeExtension(filename.absFilename(),
1501 formats.extension(loader_format));
1502 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1503 theLyXFunc().setLyXView(lv);
1504 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1511 void GuiView::importDocument(string const & argument)
1514 string filename = split(argument, format, ' ');
1516 LYXERR(Debug::INFO, format << " file: " << filename);
1518 // need user interaction
1519 if (filename.empty()) {
1520 string initpath = lyxrc.document_path;
1522 Buffer const * buf = buffer();
1524 string const trypath = buf->filePath();
1525 // If directory is writeable, use this as default.
1526 if (FileName(trypath).isDirWritable())
1530 docstring const text = bformat(_("Select %1$s file to import"),
1531 formats.prettyName(format));
1533 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1534 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1535 dlg.setButton2(qt_("Examples|#E#e"),
1536 toqstr(addPath(package().system_support().absFilename(), "examples")));
1538 docstring filter = formats.prettyName(format);
1541 filter += from_utf8(formats.extension(format));
1544 FileDialog::Result result =
1545 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1547 if (result.first == FileDialog::Later)
1550 filename = fromqstr(result.second);
1552 // check selected filename
1553 if (filename.empty())
1554 message(_("Canceled."));
1557 if (filename.empty())
1560 // get absolute path of file
1561 FileName const fullname(support::makeAbsPath(filename));
1563 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1565 // Check if the document already is open
1566 Buffer * buf = theBufferList().getBuffer(lyxfile);
1569 if (!closeBuffer()) {
1570 message(_("Canceled."));
1575 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1577 // if the file exists already, and we didn't do
1578 // -i lyx thefile.lyx, warn
1579 if (lyxfile.exists() && fullname != lyxfile) {
1581 docstring text = bformat(_("The document %1$s already exists.\n\n"
1582 "Do you want to overwrite that document?"), displaypath);
1583 int const ret = Alert::prompt(_("Overwrite document?"),
1584 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1587 message(_("Canceled."));
1592 message(bformat(_("Importing %1$s..."), displaypath));
1593 ErrorList errorList;
1594 if (import(this, fullname, format, errorList))
1595 message(_("imported."));
1597 message(_("file not imported!"));
1599 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1603 void GuiView::newDocument(string const & filename, bool from_template)
1605 FileName initpath(lyxrc.document_path);
1606 Buffer * buf = buffer();
1608 FileName const trypath(buf->filePath());
1609 // If directory is writeable, use this as default.
1610 if (trypath.isDirWritable())
1614 string templatefile = from_template ?
1615 selectTemplateFile().absFilename() : string();
1617 if (filename.empty())
1618 b = newUnnamedFile(templatefile, initpath);
1620 b = newFile(filename, templatefile, true);
1624 // Ensure the cursor is correctly positionned on screen.
1625 view()->showCursor();
1629 void GuiView::insertLyXFile(docstring const & fname)
1631 BufferView * bv = view();
1636 FileName filename(to_utf8(fname));
1638 if (!filename.empty()) {
1639 bv->insertLyXFile(filename);
1643 // Launch a file browser
1645 string initpath = lyxrc.document_path;
1646 string const trypath = bv->buffer().filePath();
1647 // If directory is writeable, use this as default.
1648 if (FileName(trypath).isDirWritable())
1652 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1653 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1654 dlg.setButton2(qt_("Examples|#E#e"),
1655 toqstr(addPath(package().system_support().absFilename(),
1658 FileDialog::Result result = dlg.open(toqstr(initpath),
1659 QStringList(qt_("LyX Documents (*.lyx)")));
1661 if (result.first == FileDialog::Later)
1665 filename.set(fromqstr(result.second));
1667 // check selected filename
1668 if (filename.empty()) {
1669 // emit message signal.
1670 message(_("Canceled."));
1674 bv->insertLyXFile(filename);
1678 void GuiView::insertPlaintextFile(docstring const & fname,
1681 BufferView * bv = view();
1686 FileName filename(to_utf8(fname));
1688 if (!filename.empty()) {
1689 bv->insertPlaintextFile(filename, asParagraph);
1693 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1694 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1696 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1699 if (result.first == FileDialog::Later)
1703 filename.set(fromqstr(result.second));
1705 // check selected filename
1706 if (filename.empty()) {
1707 // emit message signal.
1708 message(_("Canceled."));
1712 bv->insertPlaintextFile(filename, asParagraph);
1716 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1718 FileName fname = b.fileName();
1719 FileName const oldname = fname;
1721 if (!newname.empty()) {
1723 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1725 // Switch to this Buffer.
1728 /// No argument? Ask user through dialog.
1730 FileDialog dlg(qt_("Choose a filename to save document as"),
1731 LFUN_BUFFER_WRITE_AS);
1732 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1733 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1735 if (!isLyXFilename(fname.absFilename()))
1736 fname.changeExtension(".lyx");
1738 FileDialog::Result result =
1739 dlg.save(toqstr(fname.onlyPath().absFilename()),
1740 QStringList(qt_("LyX Documents (*.lyx)")),
1741 toqstr(fname.onlyFileName()));
1743 if (result.first == FileDialog::Later)
1746 fname.set(fromqstr(result.second));
1751 if (!isLyXFilename(fname.absFilename()))
1752 fname.changeExtension(".lyx");
1755 if (FileName(fname).exists()) {
1756 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1757 docstring text = bformat(_("The document %1$s already "
1758 "exists.\n\nDo you want to "
1759 "overwrite that document?"),
1761 int const ret = Alert::prompt(_("Overwrite document?"),
1762 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1765 case 1: return renameBuffer(b, docstring());
1766 case 2: return false;
1770 // Ok, change the name of the buffer
1771 b.setFileName(fname.absFilename());
1773 bool unnamed = b.isUnnamed();
1774 b.setUnnamed(false);
1775 b.saveCheckSum(fname);
1777 if (!saveBuffer(b)) {
1778 b.setFileName(oldname.absFilename());
1779 b.setUnnamed(unnamed);
1780 b.saveCheckSum(oldname);
1788 bool GuiView::saveBuffer(Buffer & b)
1790 if (workArea(b) && workArea(b)->inDialogMode())
1794 return renameBuffer(b, docstring());
1797 theSession().lastFiles().add(b.fileName());
1801 // Switch to this Buffer.
1804 // FIXME: we don't tell the user *WHY* the save failed !!
1805 docstring const file = makeDisplayPath(b.absFileName(), 30);
1806 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1807 "Do you want to rename the document and "
1808 "try again?"), file);
1809 int const ret = Alert::prompt(_("Rename and save?"),
1810 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1813 if (!renameBuffer(b, docstring()))
1822 return saveBuffer(b);
1826 bool GuiView::closeBuffer()
1828 Buffer * buf = buffer();
1829 return buf && closeBuffer(*buf);
1833 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1835 // goto bookmark to update bookmark pit.
1836 //FIXME: we should update only the bookmarks related to this buffer!
1837 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1838 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1839 theLyXFunc().gotoBookmark(i+1, false, false);
1841 if (buf.isClean() || buf.paragraphs().empty()) {
1842 if (buf.masterBuffer() == &buf && tolastopened)
1843 theSession().lastOpened().add(buf.fileName());
1845 // Don't close child documents.
1846 removeWorkArea(currentMainWorkArea());
1848 theBufferList().release(&buf);
1851 // Switch to this Buffer.
1856 if (buf.isUnnamed())
1857 file = from_utf8(buf.fileName().onlyFileName());
1859 file = buf.fileName().displayName(30);
1861 // Bring this window to top before asking questions.
1865 docstring const text = bformat(_("The document %1$s has unsaved changes."
1866 "\n\nDo you want to save the document or discard the changes?"), file);
1867 int const ret = Alert::prompt(_("Save changed document?"),
1868 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1872 if (!saveBuffer(buf))
1876 // if we crash after this we could
1877 // have no autosave file but I guess
1878 // this is really improbable (Jug)
1879 removeAutosaveFile(buf.absFileName());
1885 // save file names to .lyx/session
1886 // if master/slave are both open, do not save slave since it
1887 // will be automatically loaded when the master is loaded
1888 if (buf.masterBuffer() == &buf && tolastopened)
1889 theSession().lastOpened().add(buf.fileName());
1892 // Don't close child documents.
1893 removeWorkArea(currentMainWorkArea());
1895 theBufferList().release(&buf);
1901 bool GuiView::dispatch(FuncRequest const & cmd)
1903 BufferView * bv = view();
1904 // By default we won't need any update.
1906 bv->cursor().updateFlags(Update::None);
1907 bool dispatched = true;
1909 switch(cmd.action) {
1910 case LFUN_BUFFER_IMPORT:
1911 importDocument(to_utf8(cmd.argument()));
1914 case LFUN_BUFFER_SWITCH:
1915 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1918 case LFUN_BUFFER_NEXT:
1919 setBuffer(theBufferList().next(buffer()));
1922 case LFUN_BUFFER_PREVIOUS:
1923 setBuffer(theBufferList().previous(buffer()));
1926 case LFUN_COMMAND_EXECUTE: {
1927 bool const show_it = cmd.argument() != "off";
1928 // FIXME: this is a hack, "minibuffer" should not be
1930 if (GuiToolbar * t = toolbar("minibuffer")) {
1931 t->setVisible(show_it);
1932 if (show_it && t->commandBuffer())
1933 t->commandBuffer()->setFocus();
1937 case LFUN_DROP_LAYOUTS_CHOICE:
1939 d.layout_->showPopup();
1942 case LFUN_MENU_OPEN:
1943 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1944 menu->exec(QCursor::pos());
1947 case LFUN_FILE_INSERT:
1948 insertLyXFile(cmd.argument());
1950 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1951 insertPlaintextFile(cmd.argument(), true);
1954 case LFUN_FILE_INSERT_PLAINTEXT:
1955 insertPlaintextFile(cmd.argument(), false);
1958 case LFUN_BUFFER_WRITE:
1960 saveBuffer(bv->buffer());
1963 case LFUN_BUFFER_WRITE_AS:
1965 renameBuffer(bv->buffer(), cmd.argument());
1968 case LFUN_BUFFER_WRITE_ALL: {
1969 Buffer * first = theBufferList().first();
1972 message(_("Saving all documents..."));
1973 // We cannot use a for loop as the buffer list cycles.
1976 if (!b->isClean()) {
1978 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1980 b = theBufferList().next(b);
1981 } while (b != first);
1982 message(_("All documents saved."));
1986 case LFUN_TOOLBAR_TOGGLE: {
1987 string const name = cmd.getArg(0);
1988 if (GuiToolbar * t = toolbar(name))
1993 case LFUN_DIALOG_UPDATE: {
1994 string const name = to_utf8(cmd.argument());
1995 // Can only update a dialog connected to an existing inset
1996 Inset * inset = getOpenInset(name);
1998 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1999 inset->dispatch(view()->cursor(), fr);
2000 } else if (name == "paragraph") {
2001 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2002 } else if (name == "prefs" || name == "document") {
2003 updateDialog(name, string());
2008 case LFUN_DIALOG_TOGGLE: {
2009 if (isDialogVisible(cmd.getArg(0)))
2010 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2012 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2016 case LFUN_DIALOG_DISCONNECT_INSET:
2017 disconnectDialog(to_utf8(cmd.argument()));
2020 case LFUN_DIALOG_HIDE: {
2021 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2025 case LFUN_DIALOG_SHOW: {
2026 string const name = cmd.getArg(0);
2027 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2029 if (name == "character") {
2030 data = freefont2string();
2032 showDialog("character", data);
2033 } else if (name == "latexlog") {
2034 Buffer::LogType type;
2035 string const logfile = buffer()->logName(&type);
2037 case Buffer::latexlog:
2040 case Buffer::buildlog:
2044 data += Lexer::quoteString(logfile);
2045 showDialog("log", data);
2046 } else if (name == "vclog") {
2047 string const data = "vc " +
2048 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2049 showDialog("log", data);
2050 } else if (name == "symbols") {
2051 data = bv->cursor().getEncoding()->name();
2053 showDialog("symbols", data);
2055 } else if (name == "prefs" && isFullScreen()) {
2056 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2058 showDialog("prefs", data);
2060 showDialog(name, data);
2064 case LFUN_INSET_APPLY: {
2065 string const name = cmd.getArg(0);
2066 Inset * inset = getOpenInset(name);
2068 // put cursor in front of inset.
2069 if (!view()->setCursorFromInset(inset))
2070 LASSERT(false, break);
2072 // useful if we are called from a dialog.
2073 view()->cursor().beginUndoGroup();
2074 view()->cursor().recordUndo();
2075 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2076 inset->dispatch(view()->cursor(), fr);
2077 view()->cursor().endUndoGroup();
2079 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2085 case LFUN_UI_TOGGLE:
2087 // Make sure the keyboard focus stays in the work area.
2091 case LFUN_SPLIT_VIEW:
2092 if (Buffer * buf = buffer()) {
2093 string const orientation = cmd.getArg(0);
2094 d.splitter_->setOrientation(orientation == "vertical"
2095 ? Qt::Vertical : Qt::Horizontal);
2096 TabWorkArea * twa = addTabWorkArea();
2097 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2098 setCurrentWorkArea(wa);
2102 case LFUN_CLOSE_TAB_GROUP:
2103 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2105 twa = d.currentTabWorkArea();
2106 // Switch to the next GuiWorkArea in the found TabWorkArea.
2108 // Make sure the work area is up to date.
2109 setCurrentWorkArea(twa->currentWorkArea());
2111 setCurrentWorkArea(0);
2116 case LFUN_COMPLETION_INLINE:
2117 if (d.current_work_area_)
2118 d.current_work_area_->completer().showInline();
2121 case LFUN_COMPLETION_POPUP:
2122 if (d.current_work_area_)
2123 d.current_work_area_->completer().showPopup();
2127 case LFUN_COMPLETION_COMPLETE:
2128 if (d.current_work_area_)
2129 d.current_work_area_->completer().tab();
2132 case LFUN_COMPLETION_CANCEL:
2133 if (d.current_work_area_) {
2134 if (d.current_work_area_->completer().popupVisible())
2135 d.current_work_area_->completer().hidePopup();
2137 d.current_work_area_->completer().hideInline();
2141 case LFUN_COMPLETION_ACCEPT:
2142 if (d.current_work_area_)
2143 d.current_work_area_->completer().activate();
2152 // Part of automatic menu appearance feature.
2153 if (isFullScreen()) {
2154 if (menuBar()->isVisible())
2156 if (statusBar()->isVisible())
2157 statusBar()->hide();
2164 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2166 string const arg = cmd.getArg(0);
2167 if (arg == "scrollbar") {
2168 // hide() is of no help
2169 if (d.current_work_area_->verticalScrollBarPolicy() ==
2170 Qt::ScrollBarAlwaysOff)
2172 d.current_work_area_->setVerticalScrollBarPolicy(
2173 Qt::ScrollBarAsNeeded);
2175 d.current_work_area_->setVerticalScrollBarPolicy(
2176 Qt::ScrollBarAlwaysOff);
2179 if (arg == "statusbar") {
2180 statusBar()->setVisible(!statusBar()->isVisible());
2183 if (arg == "menubar") {
2184 menuBar()->setVisible(!menuBar()->isVisible());
2187 #if QT_VERSION >= 0x040300
2188 if (arg == "frame") {
2190 getContentsMargins(&l, &t, &r, &b);
2191 //are the frames in default state?
2192 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2194 setContentsMargins(-2, -2, -2, -2);
2196 setContentsMargins(0, 0, 0, 0);
2201 if (arg == "fullscreen") {
2206 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2210 void GuiView::toggleFullScreen()
2212 if (isFullScreen()) {
2213 for (int i = 0; i != d.splitter_->count(); ++i)
2214 d.tabWorkArea(i)->setFullScreen(false);
2215 #if QT_VERSION >= 0x040300
2216 setContentsMargins(0, 0, 0, 0);
2218 setWindowState(windowState() ^ Qt::WindowFullScreen);
2221 statusBar()->show();
2224 hideDialogs("prefs", 0);
2225 for (int i = 0; i != d.splitter_->count(); ++i)
2226 d.tabWorkArea(i)->setFullScreen(true);
2227 #if QT_VERSION >= 0x040300
2228 setContentsMargins(-2, -2, -2, -2);
2231 setWindowState(windowState() ^ Qt::WindowFullScreen);
2232 statusBar()->hide();
2234 if (lyxrc.full_screen_toolbars) {
2235 ToolbarMap::iterator end = d.toolbars_.end();
2236 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2241 // give dialogs like the TOC a chance to adapt
2246 Buffer const * GuiView::updateInset(Inset const * inset)
2248 if (!d.current_work_area_)
2252 d.current_work_area_->scheduleRedraw();
2254 return &d.current_work_area_->bufferView().buffer();
2258 void GuiView::restartCursor()
2260 /* When we move around, or type, it's nice to be able to see
2261 * the cursor immediately after the keypress.
2263 if (d.current_work_area_)
2264 d.current_work_area_->startBlinkingCursor();
2266 // Take this occasion to update the other GUI elements.
2272 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2274 if (d.current_work_area_)
2275 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2280 // This list should be kept in sync with the list of insets in
2281 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2282 // dialog should have the same name as the inset.
2283 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2284 // docs in LyXAction.cpp.
2286 char const * const dialognames[] = {
2287 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2288 "citation", "document", "errorlist", "ert", "external", "file",
2289 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2290 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2291 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2293 #ifdef HAVE_LIBAIKSAURUS
2297 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2299 char const * const * const end_dialognames =
2300 dialognames + (sizeof(dialognames) / sizeof(char *));
2304 cmpCStr(char const * name) : name_(name) {}
2305 bool operator()(char const * other) {
2306 return strcmp(other, name_) == 0;
2313 bool isValidName(string const & name)
2315 return find_if(dialognames, end_dialognames,
2316 cmpCStr(name.c_str())) != end_dialognames;
2322 void GuiView::resetDialogs()
2324 // Make sure that no LFUN uses any LyXView.
2325 theLyXFunc().setLyXView(0);
2328 constructToolbars();
2329 guiApp->menus().fillMenuBar(menuBar(), this, false);
2331 d.layout_->updateContents(true);
2332 // Now update controls with current buffer.
2333 theLyXFunc().setLyXView(this);
2339 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2341 if (!isValidName(name))
2344 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2346 if (it != d.dialogs_.end()) {
2348 it->second->hideView();
2349 return it->second.get();
2352 Dialog * dialog = build(name);
2353 d.dialogs_[name].reset(dialog);
2354 if (lyxrc.allow_geometry_session)
2355 dialog->restoreSession();
2362 void GuiView::showDialog(string const & name, string const & data,
2370 Dialog * dialog = findOrBuild(name, false);
2372 dialog->showData(data);
2374 d.open_insets_[name] = inset;
2377 catch (ExceptionMessage const & ex) {
2385 bool GuiView::isDialogVisible(string const & name) const
2387 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2388 if (it == d.dialogs_.end())
2390 return it->second.get()->isVisibleView();
2394 void GuiView::hideDialog(string const & name, Inset * inset)
2396 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2397 if (it == d.dialogs_.end())
2400 if (inset && inset != getOpenInset(name))
2403 Dialog * const dialog = it->second.get();
2404 if (dialog->isVisibleView())
2406 d.open_insets_[name] = 0;
2410 void GuiView::disconnectDialog(string const & name)
2412 if (!isValidName(name))
2415 if (d.open_insets_.find(name) != d.open_insets_.end())
2416 d.open_insets_[name] = 0;
2420 Inset * GuiView::getOpenInset(string const & name) const
2422 if (!isValidName(name))
2425 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2426 return it == d.open_insets_.end() ? 0 : it->second;
2430 void GuiView::hideAll() const
2432 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2433 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2435 for(; it != end; ++it)
2436 it->second->hideView();
2440 void GuiView::updateDialogs()
2442 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2443 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2445 for(; it != end; ++it) {
2446 Dialog * dialog = it->second.get();
2447 if (dialog && dialog->isVisibleView())
2448 dialog->checkStatus();
2455 // will be replaced by a proper factory...
2456 Dialog * createGuiAbout(GuiView & lv);
2457 Dialog * createGuiBibitem(GuiView & lv);
2458 Dialog * createGuiBibtex(GuiView & lv);
2459 Dialog * createGuiBox(GuiView & lv);
2460 Dialog * createGuiBranch(GuiView & lv);
2461 Dialog * createGuiChanges(GuiView & lv);
2462 Dialog * createGuiCharacter(GuiView & lv);
2463 Dialog * createGuiCitation(GuiView & lv);
2464 Dialog * createGuiDelimiter(GuiView & lv);
2465 Dialog * createGuiDocument(GuiView & lv);
2466 Dialog * createGuiErrorList(GuiView & lv);
2467 Dialog * createGuiERT(GuiView & lv);
2468 Dialog * createGuiExternal(GuiView & lv);
2469 Dialog * createGuiFloat(GuiView & lv);
2470 Dialog * createGuiGraphics(GuiView & lv);
2471 Dialog * createGuiHSpace(GuiView & lv);
2472 Dialog * createGuiInclude(GuiView & lv);
2473 Dialog * createGuiInfo(GuiView & lv);
2474 Dialog * createGuiLabel(GuiView & lv);
2475 Dialog * createGuiListings(GuiView & lv);
2476 Dialog * createGuiLog(GuiView & lv);
2477 Dialog * createGuiMathMatrix(GuiView & lv);
2478 Dialog * createGuiNomenclature(GuiView & lv);
2479 Dialog * createGuiNote(GuiView & lv);
2480 Dialog * createGuiParagraph(GuiView & lv);
2481 Dialog * createGuiPreferences(GuiView & lv);
2482 Dialog * createGuiPrint(GuiView & lv);
2483 Dialog * createGuiRef(GuiView & lv);
2484 Dialog * createGuiSearch(GuiView & lv);
2485 Dialog * createGuiSearchAdv(GuiView & lv);
2486 Dialog * createGuiSendTo(GuiView & lv);
2487 Dialog * createGuiShowFile(GuiView & lv);
2488 Dialog * createGuiSpellchecker(GuiView & lv);
2489 Dialog * createGuiSymbols(GuiView & lv);
2490 Dialog * createGuiTabularCreate(GuiView & lv);
2491 Dialog * createGuiTabular(GuiView & lv);
2492 Dialog * createGuiTexInfo(GuiView & lv);
2493 Dialog * createGuiToc(GuiView & lv);
2494 Dialog * createGuiThesaurus(GuiView & lv);
2495 Dialog * createGuiHyperlink(GuiView & lv);
2496 Dialog * createGuiVSpace(GuiView & lv);
2497 Dialog * createGuiViewSource(GuiView & lv);
2498 Dialog * createGuiWrap(GuiView & lv);
2501 Dialog * GuiView::build(string const & name)
2503 LASSERT(isValidName(name), return 0);
2505 if (name == "aboutlyx")
2506 return createGuiAbout(*this);
2507 if (name == "bibitem")
2508 return createGuiBibitem(*this);
2509 if (name == "bibtex")
2510 return createGuiBibtex(*this);
2512 return createGuiBox(*this);
2513 if (name == "branch")
2514 return createGuiBranch(*this);
2515 if (name == "changes")
2516 return createGuiChanges(*this);
2517 if (name == "character")
2518 return createGuiCharacter(*this);
2519 if (name == "citation")
2520 return createGuiCitation(*this);
2521 if (name == "document")
2522 return createGuiDocument(*this);
2523 if (name == "errorlist")
2524 return createGuiErrorList(*this);
2526 return createGuiERT(*this);
2527 if (name == "external")
2528 return createGuiExternal(*this);
2530 return createGuiShowFile(*this);
2531 if (name == "findreplace")
2532 return createGuiSearch(*this);
2533 if (name == "findreplaceadv")
2534 return createGuiSearchAdv(*this);
2535 if (name == "float")
2536 return createGuiFloat(*this);
2537 if (name == "graphics")
2538 return createGuiGraphics(*this);
2539 if (name == "include")
2540 return createGuiInclude(*this);
2542 return createGuiInfo(*this);
2543 if (name == "nomenclature")
2544 return createGuiNomenclature(*this);
2545 if (name == "label")
2546 return createGuiLabel(*this);
2548 return createGuiLog(*this);
2549 if (name == "view-source")
2550 return createGuiViewSource(*this);
2551 if (name == "mathdelimiter")
2552 return createGuiDelimiter(*this);
2553 if (name == "mathmatrix")
2554 return createGuiMathMatrix(*this);
2556 return createGuiNote(*this);
2557 if (name == "paragraph")
2558 return createGuiParagraph(*this);
2559 if (name == "prefs")
2560 return createGuiPreferences(*this);
2561 if (name == "print")
2562 return createGuiPrint(*this);
2564 return createGuiRef(*this);
2565 if (name == "sendto")
2566 return createGuiSendTo(*this);
2567 if (name == "space")
2568 return createGuiHSpace(*this);
2569 if (name == "spellchecker")
2570 return createGuiSpellchecker(*this);
2571 if (name == "symbols")
2572 return createGuiSymbols(*this);
2573 if (name == "tabular")
2574 return createGuiTabular(*this);
2575 if (name == "tabularcreate")
2576 return createGuiTabularCreate(*this);
2577 if (name == "texinfo")
2578 return createGuiTexInfo(*this);
2579 #ifdef HAVE_LIBAIKSAURUS
2580 if (name == "thesaurus")
2581 return createGuiThesaurus(*this);
2584 return createGuiToc(*this);
2586 return createGuiHyperlink(*this);
2587 if (name == "vspace")
2588 return createGuiVSpace(*this);
2590 return createGuiWrap(*this);
2591 if (name == "listings")
2592 return createGuiListings(*this);
2598 } // namespace frontend
2601 #include "moc_GuiView.cpp"