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 "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiWorkArea.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
31 #include "qt_helpers.h"
33 #include "frontends/alert.h"
35 #include "buffer_funcs.h"
37 #include "BufferList.h"
38 #include "BufferParams.h"
39 #include "BufferView.h"
40 #include "Converter.h"
43 #include "ErrorList.h"
45 #include "FuncStatus.h"
46 #include "FuncRequest.h"
54 #include "Paragraph.h"
55 #include "TextClass.h"
60 #include "support/convert.h"
61 #include "support/debug.h"
62 #include "support/ExceptionMessage.h"
63 #include "support/FileName.h"
64 #include "support/filetools.h"
65 #include "support/gettext.h"
66 #include "support/ForkedCalls.h"
67 #include "support/lassert.h"
68 #include "support/lstrings.h"
69 #include "support/os.h"
70 #include "support/Package.h"
71 #include "support/Timeout.h"
74 #include <QApplication>
75 #include <QCloseEvent>
77 #include <QDesktopWidget>
78 #include <QDragEnterEvent>
85 #include <QPixmapCache>
87 #include <QPushButton>
91 #include <QStackedWidget>
98 #include <boost/bind.hpp>
100 #ifdef HAVE_SYS_TIME_H
101 # include <sys/time.h>
108 using namespace lyx::support;
115 class BackgroundWidget : public QWidget
120 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
121 /// The text to be written on top of the pixmap
122 QString const text = lyx_version ?
123 qt_("version ") + lyx_version : qt_("unknown version");
124 splash_ = getPixmap("images/", "banner", "png");
126 QPainter pain(&splash_);
127 pain.setPen(QColor(0, 0, 0));
129 // The font used to display the version info
130 font.setStyleHint(QFont::SansSerif);
131 font.setWeight(QFont::Bold);
132 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
134 pain.drawText(260, 15, text);
137 void paintEvent(QPaintEvent *)
139 int x = (width() - splash_.width()) / 2;
140 int y = (height() - splash_.height()) / 2;
142 pain.drawPixmap(x, y, splash_);
150 /// Toolbar store providing access to individual toolbars by name.
151 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
153 typedef boost::shared_ptr<Dialog> DialogPtr;
158 struct GuiView::GuiViewPrivate
161 : current_work_area_(0), current_main_work_area_(0),
162 layout_(0), autosave_timeout_(5000),
165 // hardcode here the platform specific icon size
166 smallIconSize = 14; // scaling problems
167 normalIconSize = 20; // ok, default
168 bigIconSize = 26; // better for some math icons
170 splitter_ = new QSplitter;
171 bg_widget_ = new BackgroundWidget;
172 stack_widget_ = new QStackedWidget;
173 stack_widget_->addWidget(bg_widget_);
174 stack_widget_->addWidget(splitter_);
182 delete stack_widget_;
185 QMenu * toolBarPopup(GuiView * parent)
187 // FIXME: translation
188 QMenu * menu = new QMenu(parent);
189 QActionGroup * iconSizeGroup = new QActionGroup(parent);
191 QAction * smallIcons = new QAction(iconSizeGroup);
192 smallIcons->setText(qt_("Small-sized icons"));
193 smallIcons->setCheckable(true);
194 QObject::connect(smallIcons, SIGNAL(triggered()),
195 parent, SLOT(smallSizedIcons()));
196 menu->addAction(smallIcons);
198 QAction * normalIcons = new QAction(iconSizeGroup);
199 normalIcons->setText(qt_("Normal-sized icons"));
200 normalIcons->setCheckable(true);
201 QObject::connect(normalIcons, SIGNAL(triggered()),
202 parent, SLOT(normalSizedIcons()));
203 menu->addAction(normalIcons);
205 QAction * bigIcons = new QAction(iconSizeGroup);
206 bigIcons->setText(qt_("Big-sized icons"));
207 bigIcons->setCheckable(true);
208 QObject::connect(bigIcons, SIGNAL(triggered()),
209 parent, SLOT(bigSizedIcons()));
210 menu->addAction(bigIcons);
212 unsigned int cur = parent->iconSize().width();
213 if ( cur == parent->d.smallIconSize)
214 smallIcons->setChecked(true);
215 else if (cur == parent->d.normalIconSize)
216 normalIcons->setChecked(true);
217 else if (cur == parent->d.bigIconSize)
218 bigIcons->setChecked(true);
225 stack_widget_->setCurrentWidget(bg_widget_);
226 bg_widget_->setUpdatesEnabled(true);
229 TabWorkArea * tabWorkArea(int i)
231 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
234 TabWorkArea * currentTabWorkArea()
236 if (splitter_->count() == 1)
237 // The first TabWorkArea is always the first one, if any.
238 return tabWorkArea(0);
240 for (int i = 0; i != splitter_->count(); ++i) {
241 TabWorkArea * twa = tabWorkArea(i);
242 if (current_main_work_area_ == twa->currentWorkArea())
246 // None has the focus so we just take the first one.
247 return tabWorkArea(0);
251 GuiWorkArea * current_work_area_;
252 GuiWorkArea * current_main_work_area_;
253 QSplitter * splitter_;
254 QStackedWidget * stack_widget_;
255 BackgroundWidget * bg_widget_;
257 ToolbarMap toolbars_;
258 /// The main layout box.
260 * \warning Don't Delete! The layout box is actually owned by
261 * whichever toolbar contains it. All the GuiView class needs is a
262 * means of accessing it.
264 * FIXME: replace that with a proper model so that we are not limited
265 * to only one dialog.
267 GuiLayoutBox * layout_;
270 map<string, Inset *> open_insets_;
273 map<string, DialogPtr> dialogs_;
275 unsigned int smallIconSize;
276 unsigned int normalIconSize;
277 unsigned int bigIconSize;
279 QTimer statusbar_timer_;
280 /// auto-saving of buffers
281 Timeout autosave_timeout_;
282 /// flag against a race condition due to multiclicks, see bug #1119
286 TocModels toc_models_;
290 GuiView::GuiView(int id)
291 : d(*new GuiViewPrivate), id_(id), closing_(false)
293 // GuiToolbars *must* be initialised before the menu bar.
294 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
297 // set ourself as the current view. This is needed for the menu bar
298 // filling, at least for the static special menu item on Mac. Otherwise
299 // they are greyed out.
300 theLyXFunc().setLyXView(this);
302 // Fill up the menu bar.
303 guiApp->menus().fillMenuBar(menuBar(), this, true);
305 setCentralWidget(d.stack_widget_);
307 // Start autosave timer
308 if (lyxrc.autosave) {
309 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
310 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
311 d.autosave_timeout_.start();
313 connect(&d.statusbar_timer_, SIGNAL(timeout()),
314 this, SLOT(clearMessage()));
316 // We don't want to keep the window in memory if it is closed.
317 setAttribute(Qt::WA_DeleteOnClose, true);
319 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
320 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
321 // since the icon is provided in the application bundle.
322 setWindowIcon(getPixmap("images/", "lyx", "png"));
326 setAcceptDrops(true);
328 statusBar()->setSizeGripEnabled(true);
330 // Forbid too small unresizable window because it can happen
331 // with some window manager under X11.
332 setMinimumSize(300, 200);
334 if (lyxrc.allow_geometry_session) {
335 // Now take care of session management.
340 // no session handling, default to a sane size.
341 setGeometry(50, 50, 690, 510);
344 // clear session data if any.
346 settings.remove("views");
356 void GuiView::saveLayout() const
359 settings.beginGroup("views");
360 settings.beginGroup(QString::number(id_));
362 settings.setValue("pos", pos());
363 settings.setValue("size", size());
365 settings.setValue("geometry", saveGeometry());
367 settings.setValue("layout", saveState(0));
368 settings.setValue("icon_size", iconSize());
372 bool GuiView::restoreLayout()
375 settings.beginGroup("views");
376 settings.beginGroup(QString::number(id_));
377 QString const icon_key = "icon_size";
378 if (!settings.contains(icon_key))
381 //code below is skipped when when ~/.config/LyX is (re)created
382 setIconSize(settings.value(icon_key).toSize());
384 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
385 QSize size = settings.value("size", QSize(690, 510)).toSize();
389 if (!restoreGeometry(settings.value("geometry").toByteArray()))
390 setGeometry(50, 50, 690, 510);
392 // Make sure layout is correctly oriented.
393 setLayoutDirection(qApp->layoutDirection());
395 // Allow the toc and view-source dock widget to be restored if needed.
397 if ((d = findOrBuild("toc", true)));
398 // see bug 5082. At least setup title and enabled state.
399 // Visibility will be adjusted by restoreState below.
401 if ((d = findOrBuild("view-source", true)))
404 if (!restoreState(settings.value("layout").toByteArray(), 0))
411 GuiToolbar * GuiView::toolbar(string const & name)
413 ToolbarMap::iterator it = d.toolbars_.find(name);
414 if (it != d.toolbars_.end())
417 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
418 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
423 void GuiView::constructToolbars()
425 ToolbarMap::iterator it = d.toolbars_.begin();
426 for (; it != d.toolbars_.end(); ++it)
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 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
439 void GuiView::initToolbars()
441 // extracts the toolbars from the backend
442 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
443 Toolbars::Infos::iterator end = guiApp->toolbars().end();
444 for (; cit != end; ++cit) {
445 GuiToolbar * tb = toolbar(cit->name);
448 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
450 tb->setVisible(false);
451 tb->setVisibility(visibility);
453 if (visibility & Toolbars::TOP) {
455 addToolBarBreak(Qt::TopToolBarArea);
456 addToolBar(Qt::TopToolBarArea, tb);
459 if (visibility & Toolbars::BOTTOM) {
460 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
461 #if (QT_VERSION >= 0x040202)
462 addToolBarBreak(Qt::BottomToolBarArea);
464 addToolBar(Qt::BottomToolBarArea, tb);
467 if (visibility & Toolbars::LEFT) {
468 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
469 #if (QT_VERSION >= 0x040202)
470 addToolBarBreak(Qt::LeftToolBarArea);
472 addToolBar(Qt::LeftToolBarArea, tb);
475 if (visibility & Toolbars::RIGHT) {
476 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
477 #if (QT_VERSION >= 0x040202)
478 addToolBarBreak(Qt::RightToolBarArea);
480 addToolBar(Qt::RightToolBarArea, tb);
483 if (visibility & Toolbars::ON)
484 tb->setVisible(true);
489 TocModels & GuiView::tocModels()
491 return d.toc_models_;
495 void GuiView::setFocus()
497 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
498 // Make sure LyXFunc points to the correct view.
499 guiApp->setCurrentView(this);
500 theLyXFunc().setLyXView(this);
501 QMainWindow::setFocus();
502 if (d.current_work_area_)
503 d.current_work_area_->setFocus();
507 QMenu * GuiView::createPopupMenu()
509 return d.toolBarPopup(this);
513 void GuiView::showEvent(QShowEvent * e)
515 LYXERR(Debug::GUI, "Passed Geometry "
516 << size().height() << "x" << size().width()
517 << "+" << pos().x() << "+" << pos().y());
519 if (d.splitter_->count() == 0)
520 // No work area, switch to the background widget.
523 QMainWindow::showEvent(e);
527 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
528 ** is responsibility of the container (e.g., dialog)
530 void GuiView::closeEvent(QCloseEvent * close_event)
532 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
535 // it can happen that this event arrives without selecting the view,
536 // e.g. when clicking the close button on a background window.
538 GuiWorkArea * active_wa = currentMainWorkArea();
539 setCurrentWorkArea(active_wa);
541 int splitter_count = d.splitter_->count();
542 for (; splitter_count; --splitter_count) {
543 TabWorkArea * twa = d.tabWorkArea(0);
545 int twa_count = twa->count();
546 for (; twa_count; --twa_count) {
547 twa->setCurrentIndex(twa_count-1);
549 GuiWorkArea * wa = twa->currentWorkArea();
550 bool const is_active_wa = active_wa == wa;
551 Buffer * b = &wa->bufferView().buffer();
553 // This is a child document, just close the tab
554 // after saving but keep the file loaded.
555 if (!closeBuffer(*b, true, is_active_wa)) {
557 close_event->ignore();
563 vector<Buffer *> clist = b->getChildren();
564 for (vector<Buffer *>::const_iterator it = clist.begin();
565 it != clist.end(); ++it) {
566 if ((*it)->isClean())
569 // If a child is dirty, do not close
570 // without user intervention
571 if (!closeBuffer(*c, false)) {
573 close_event->ignore();
578 QList<int> const ids = guiApp->viewIds();
579 for (int i = 0; i != ids.size(); ++i) {
582 if (guiApp->view(ids[i]).workArea(*b)) {
583 // FIXME 1: should we put an alert box here
584 // that the buffer is viewed elsewhere?
585 // FIXME 2: should we try to save this buffer in any case?
588 // This buffer is also opened in another view, so
589 // close the associated work area...
591 // ... but don't close the buffer.
596 // closeBuffer() needs buffer workArea still alive and
597 // set as currrent one, and destroys it
598 if (b && !closeBuffer(*b, true, is_active_wa)) {
600 close_event->ignore();
605 // Make sure that nothing will use this close to be closed View.
606 guiApp->unregisterView(this);
608 if (isFullScreen()) {
609 // Switch off fullscreen before closing.
614 // Make sure the timer time out will not trigger a statusbar update.
615 d.statusbar_timer_.stop();
617 // Saving fullscreen requires additional tweaks in the toolbar code.
618 // It wouldn't also work under linux natively.
619 if (lyxrc.allow_geometry_session) {
620 // Save this window geometry and layout.
622 // Then the toolbar private states.
623 ToolbarMap::iterator end = d.toolbars_.end();
624 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
625 it->second->saveSession();
626 // Now take care of all other dialogs:
627 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
628 for (; it!= d.dialogs_.end(); ++it)
629 it->second->saveSession();
632 close_event->accept();
636 void GuiView::dragEnterEvent(QDragEnterEvent * event)
638 if (event->mimeData()->hasUrls())
640 /// \todo Ask lyx-devel is this is enough:
641 /// if (event->mimeData()->hasFormat("text/plain"))
642 /// event->acceptProposedAction();
646 void GuiView::dropEvent(QDropEvent * event)
648 QList<QUrl> files = event->mimeData()->urls();
652 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
653 for (int i = 0; i != files.size(); ++i) {
654 string const file = os::internal_path(fromqstr(
655 files.at(i).toLocalFile()));
657 // Asynchronously post the event. DropEvent usually come
658 // from the BufferView. But reloading a file might close
659 // the BufferView from within its own event handler.
660 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
667 void GuiView::message(docstring const & str)
669 if (ForkedProcess::iAmAChild())
672 statusBar()->showMessage(toqstr(str));
673 d.statusbar_timer_.stop();
674 d.statusbar_timer_.start(3000);
678 void GuiView::smallSizedIcons()
680 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
684 void GuiView::normalSizedIcons()
686 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
690 void GuiView::bigSizedIcons()
692 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
696 void GuiView::clearMessage()
700 theLyXFunc().setLyXView(this);
701 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
702 d.statusbar_timer_.stop();
706 void GuiView::updateWindowTitle(GuiWorkArea * wa)
708 if (wa != d.current_work_area_)
710 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
711 setWindowIconText(wa->windowIconText());
715 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
718 disconnectBufferView();
719 connectBufferView(wa->bufferView());
720 connectBuffer(wa->bufferView().buffer());
721 d.current_work_area_ = wa;
722 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
723 this, SLOT(updateWindowTitle(GuiWorkArea *)));
724 updateWindowTitle(wa);
728 // The document settings needs to be reinitialised.
729 updateDialog("document", "");
731 // Buffer-dependent dialogs must be updated. This is done here because
732 // some dialogs require buffer()->text.
737 void GuiView::on_lastWorkAreaRemoved()
740 // We already are in a close event. Nothing more to do.
743 if (d.splitter_->count() > 1)
744 // We have a splitter so don't close anything.
747 // Reset and updates the dialogs.
748 d.toc_models_.reset(0);
749 updateDialog("document", "");
752 resetWindowTitleAndIconText();
754 if (lyxrc.open_buffers_in_tabs)
755 // Nothing more to do, the window should stay open.
758 if (guiApp->viewIds().size() > 1) {
764 // On Mac we also close the last window because the application stay
765 // resident in memory. On other platforms we don't close the last
766 // window because this would quit the application.
772 void GuiView::updateStatusBar()
774 // let the user see the explicit message
775 if (d.statusbar_timer_.isActive())
778 theLyXFunc().setLyXView(this);
779 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
783 bool GuiView::hasFocus() const
785 return qApp->activeWindow() == this;
789 bool GuiView::event(QEvent * e)
793 // Useful debug code:
794 //case QEvent::ActivationChange:
795 //case QEvent::WindowDeactivate:
796 //case QEvent::Paint:
797 //case QEvent::Enter:
798 //case QEvent::Leave:
799 //case QEvent::HoverEnter:
800 //case QEvent::HoverLeave:
801 //case QEvent::HoverMove:
802 //case QEvent::StatusTip:
803 //case QEvent::DragEnter:
804 //case QEvent::DragLeave:
808 case QEvent::WindowActivate: {
809 if (this == guiApp->currentView()) {
811 return QMainWindow::event(e);
813 guiApp->setCurrentView(this);
814 theLyXFunc().setLyXView(this);
815 if (d.current_work_area_) {
816 BufferView & bv = d.current_work_area_->bufferView();
817 connectBufferView(bv);
818 connectBuffer(bv.buffer());
819 // The document structure, name and dialogs might have
820 // changed in another view.
822 // The document settings needs to be reinitialised.
823 updateDialog("document", "");
826 resetWindowTitleAndIconText();
829 return QMainWindow::event(e);
832 case QEvent::ShortcutOverride: {
835 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
836 if (isFullScreen() && menuBar()->isHidden()) {
837 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
838 // FIXME: we should also try to detect special LyX shortcut such as
839 // Alt-P and Alt-M. Right now there is a hack in
840 // GuiWorkArea::processKeySym() that hides again the menubar for
842 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
844 return QMainWindow::event(e);
849 if (d.current_work_area_)
850 // Nothing special to do.
851 return QMainWindow::event(e);
853 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
854 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
856 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
857 || ke->key() == Qt::Key_Backtab)
858 return QMainWindow::event(e);
860 // Allow processing of shortcuts that are allowed even when no Buffer
862 theLyXFunc().setLyXView(this);
864 setKeySymbol(&sym, ke);
865 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
871 return QMainWindow::event(e);
875 void GuiView::resetWindowTitleAndIconText()
877 setWindowTitle(qt_("LyX"));
878 setWindowIconText(qt_("LyX"));
881 bool GuiView::focusNextPrevChild(bool /*next*/)
888 void GuiView::setBusy(bool busy)
890 if (d.current_work_area_) {
891 d.current_work_area_->setUpdatesEnabled(!busy);
893 d.current_work_area_->stopBlinkingCursor();
895 d.current_work_area_->startBlinkingCursor();
899 QApplication::setOverrideCursor(Qt::WaitCursor);
901 QApplication::restoreOverrideCursor();
905 GuiWorkArea * GuiView::workArea(Buffer & buffer)
907 if (currentWorkArea()
908 && ¤tWorkArea()->bufferView().buffer() == &buffer)
909 return (GuiWorkArea *) currentWorkArea();
910 if (TabWorkArea * twa = d.currentTabWorkArea())
911 return twa->workArea(buffer);
916 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
918 // Automatically create a TabWorkArea if there are none yet.
919 TabWorkArea * tab_widget = d.splitter_->count()
920 ? d.currentTabWorkArea() : addTabWorkArea();
921 return tab_widget->addWorkArea(buffer, *this);
925 TabWorkArea * GuiView::addTabWorkArea()
927 TabWorkArea * twa = new TabWorkArea;
928 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
929 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
930 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
931 this, SLOT(on_lastWorkAreaRemoved()));
933 d.splitter_->addWidget(twa);
934 d.stack_widget_->setCurrentWidget(d.splitter_);
939 GuiWorkArea const * GuiView::currentWorkArea() const
941 return d.current_work_area_;
945 GuiWorkArea * GuiView::currentWorkArea()
947 return d.current_work_area_;
951 GuiWorkArea const * GuiView::currentMainWorkArea() const
953 if (d.currentTabWorkArea() == NULL)
955 return d.currentTabWorkArea()->currentWorkArea();
959 GuiWorkArea * GuiView::currentMainWorkArea()
961 if (d.currentTabWorkArea() == NULL)
963 return d.currentTabWorkArea()->currentWorkArea();
967 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
969 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
971 d.current_work_area_ = NULL;
975 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
979 theGuiApp()->setCurrentView(this);
980 d.current_work_area_ = wa;
981 for (int i = 0; i != d.splitter_->count(); ++i) {
982 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
983 //if (d.current_main_work_area_)
984 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
985 d.current_main_work_area_ = wa;
986 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
987 //d.current_main_work_area_->setLineWidth(2);
988 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
992 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
993 on_currentWorkAreaChanged(wa);
994 BufferView & bv = wa->bufferView();
995 bv.cursor().fixIfBroken();
997 wa->setUpdatesEnabled(true);
998 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1002 void GuiView::removeWorkArea(GuiWorkArea * wa)
1004 LASSERT(wa, return);
1005 if (wa == d.current_work_area_) {
1007 disconnectBufferView();
1008 d.current_work_area_ = 0;
1009 d.current_main_work_area_ = 0;
1012 bool found_twa = false;
1013 for (int i = 0; i != d.splitter_->count(); ++i) {
1014 TabWorkArea * twa = d.tabWorkArea(i);
1015 if (twa->removeWorkArea(wa)) {
1016 // Found in this tab group, and deleted the GuiWorkArea.
1018 if (twa->count() != 0) {
1019 if (d.current_work_area_ == 0)
1020 // This means that we are closing the current GuiWorkArea, so
1021 // switch to the next GuiWorkArea in the found TabWorkArea.
1022 setCurrentWorkArea(twa->currentWorkArea());
1024 // No more WorkAreas in this tab group, so delete it.
1031 // It is not a tabbed work area (i.e., the search work area), so it
1032 // should be deleted by other means.
1033 LASSERT(found_twa, /* */);
1035 if (d.current_work_area_ == 0) {
1036 if (d.splitter_->count() != 0) {
1037 TabWorkArea * twa = d.currentTabWorkArea();
1038 setCurrentWorkArea(twa->currentWorkArea());
1040 // No more work areas, switch to the background widget.
1041 setCurrentWorkArea(0);
1047 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1053 void GuiView::updateLayoutList()
1056 d.layout_->updateContents(false);
1060 void GuiView::updateToolbars()
1062 ToolbarMap::iterator end = d.toolbars_.end();
1063 if (d.current_work_area_) {
1065 d.current_work_area_->bufferView().cursor().inMathed();
1067 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1069 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1070 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1071 bool const mathmacrotemplate =
1072 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1074 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1075 it->second->update(math, table, review, mathmacrotemplate);
1077 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1078 it->second->update(false, false, false, false);
1082 Buffer * GuiView::buffer()
1084 if (d.current_work_area_)
1085 return &d.current_work_area_->bufferView().buffer();
1090 Buffer const * GuiView::buffer() const
1092 if (d.current_work_area_)
1093 return &d.current_work_area_->bufferView().buffer();
1098 void GuiView::setBuffer(Buffer * newBuffer)
1100 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1101 LASSERT(newBuffer, return);
1104 GuiWorkArea * wa = workArea(*newBuffer);
1106 newBuffer->masterBuffer()->updateLabels();
1107 wa = addWorkArea(*newBuffer);
1109 //Disconnect the old buffer...there's no new one.
1112 connectBuffer(*newBuffer);
1113 connectBufferView(wa->bufferView());
1114 setCurrentWorkArea(wa);
1120 void GuiView::connectBuffer(Buffer & buf)
1122 buf.setGuiDelegate(this);
1126 void GuiView::disconnectBuffer()
1128 if (d.current_work_area_)
1129 d.current_work_area_->bufferView().setGuiDelegate(0);
1133 void GuiView::connectBufferView(BufferView & bv)
1135 bv.setGuiDelegate(this);
1139 void GuiView::disconnectBufferView()
1141 if (d.current_work_area_)
1142 d.current_work_area_->bufferView().setGuiDelegate(0);
1146 void GuiView::errors(string const & error_type)
1148 ErrorList & el = buffer()->errorList(error_type);
1150 showDialog("errorlist", error_type);
1154 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1156 d.toc_models_.updateItem(toqstr(type), dit);
1160 void GuiView::structureChanged()
1162 d.toc_models_.reset(view());
1163 // Navigator needs more than a simple update in this case. It needs to be
1165 updateDialog("toc", "");
1169 void GuiView::updateDialog(string const & name, string const & data)
1171 if (!isDialogVisible(name))
1174 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1175 if (it == d.dialogs_.end())
1178 Dialog * const dialog = it->second.get();
1179 if (dialog->isVisibleView())
1180 dialog->initialiseParams(data);
1184 BufferView * GuiView::view()
1186 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1190 void GuiView::autoSave()
1192 LYXERR(Debug::INFO, "Running autoSave()");
1195 view()->buffer().autoSave();
1199 void GuiView::resetAutosaveTimers()
1202 d.autosave_timeout_.restart();
1206 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1209 Buffer * buf = buffer();
1211 /* In LyX/Mac, when a dialog is open, the menus of the
1212 application can still be accessed without giving focus to
1213 the main window. In this case, we want to disable the menu
1214 entries that are buffer-related.
1216 Note that this code is not perfect, as bug 1941 attests:
1217 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1219 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1222 if (cmd.origin == FuncRequest::TOC) {
1223 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1225 if (toc->getStatus(view()->cursor(), cmd, fs))
1228 flag.setEnabled(false);
1232 switch(cmd.action) {
1233 case LFUN_BUFFER_WRITE:
1234 enable = buf && (buf->isUnnamed() || !buf->isClean());
1237 case LFUN_BUFFER_WRITE_AS:
1241 case LFUN_SPLIT_VIEW:
1242 if (cmd.getArg(0) == "vertical")
1243 enable = buf && (d.splitter_->count() == 1 ||
1244 d.splitter_->orientation() == Qt::Vertical);
1246 enable = buf && (d.splitter_->count() == 1 ||
1247 d.splitter_->orientation() == Qt::Horizontal);
1250 case LFUN_CLOSE_TAB_GROUP:
1251 enable = d.currentTabWorkArea();
1254 case LFUN_TOOLBAR_TOGGLE:
1255 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1256 flag.setOnOff(t->isVisible());
1259 case LFUN_UI_TOGGLE:
1260 flag.setOnOff(isFullScreen());
1263 case LFUN_DIALOG_TOGGLE:
1264 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1265 // fall through to set "enable"
1266 case LFUN_DIALOG_SHOW: {
1267 string const name = cmd.getArg(0);
1269 enable = name == "aboutlyx"
1270 || name == "file" //FIXME: should be removed.
1272 || name == "texinfo";
1273 else if (name == "print")
1274 enable = buf->isExportable("dvi")
1275 && lyxrc.print_command != "none";
1276 else if (name == "character") {
1280 InsetCode ic = view()->cursor().inset().lyxCode();
1281 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1284 else if (name == "symbols") {
1285 if (!view() || view()->cursor().inMathed())
1288 InsetCode ic = view()->cursor().inset().lyxCode();
1289 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1292 else if (name == "latexlog")
1293 enable = FileName(buf->logName()).isReadableFile();
1294 else if (name == "spellchecker")
1295 #if defined (USE_ASPELL)
1296 enable = !buf->isReadonly();
1300 else if (name == "vclog")
1301 enable = buf->lyxvc().inUse();
1305 case LFUN_DIALOG_UPDATE: {
1306 string const name = cmd.getArg(0);
1308 enable = name == "prefs";
1312 case LFUN_INSET_APPLY: {
1313 string const name = cmd.getArg(0);
1314 Inset * inset = getOpenInset(name);
1316 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1318 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1319 // Every inset is supposed to handle this
1320 LASSERT(false, break);
1324 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1325 flag |= lyx::getStatus(fr);
1327 enable = flag.enabled();
1331 case LFUN_COMPLETION_INLINE:
1332 if (!d.current_work_area_
1333 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1337 case LFUN_COMPLETION_POPUP:
1338 if (!d.current_work_area_
1339 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1343 case LFUN_COMPLETION_COMPLETE:
1344 if (!d.current_work_area_
1345 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1349 case LFUN_COMPLETION_ACCEPT:
1350 if (!d.current_work_area_
1351 || (!d.current_work_area_->completer().popupVisible()
1352 && !d.current_work_area_->completer().inlineVisible()
1353 && !d.current_work_area_->completer().completionAvailable()))
1357 case LFUN_COMPLETION_CANCEL:
1358 if (!d.current_work_area_
1359 || (!d.current_work_area_->completer().popupVisible()
1360 && !d.current_work_area_->completer().inlineVisible()))
1364 case LFUN_BUFFER_ZOOM_OUT:
1365 enable = buf && lyxrc.zoom > 10;
1368 case LFUN_BUFFER_ZOOM_IN:
1377 flag.setEnabled(false);
1383 static FileName selectTemplateFile()
1385 FileDialog dlg(qt_("Select template file"));
1386 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1387 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1389 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1390 QStringList(qt_("LyX Documents (*.lyx)")));
1392 if (result.first == FileDialog::Later)
1394 if (result.second.isEmpty())
1396 return FileName(fromqstr(result.second));
1400 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1404 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1407 message(_("Document not loaded."));
1412 setBuffer(newBuffer);
1414 // scroll to the position when the file was last closed
1415 if (lyxrc.use_lastfilepos) {
1416 LastFilePosSection::FilePos filepos =
1417 theSession().lastFilePos().load(filename);
1418 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1422 theSession().lastFiles().add(filename);
1429 void GuiView::openDocument(string const & fname)
1431 string initpath = lyxrc.document_path;
1434 string const trypath = buffer()->filePath();
1435 // If directory is writeable, use this as default.
1436 if (FileName(trypath).isDirWritable())
1442 if (fname.empty()) {
1443 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1444 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1445 dlg.setButton2(qt_("Examples|#E#e"),
1446 toqstr(addPath(package().system_support().absFilename(), "examples")));
1448 QStringList filter(qt_("LyX Documents (*.lyx)"));
1449 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1450 << qt_("LyX-1.4.x Documents (*.lyx14)")
1451 << qt_("LyX-1.5.x Documents (*.lyx15)")
1452 << qt_("LyX-1.6.x Documents (*.lyx16)");
1453 FileDialog::Result result =
1454 dlg.open(toqstr(initpath), filter);
1456 if (result.first == FileDialog::Later)
1459 filename = fromqstr(result.second);
1461 // check selected filename
1462 if (filename.empty()) {
1463 message(_("Canceled."));
1469 // get absolute path of file and add ".lyx" to the filename if
1471 FileName const fullname =
1472 fileSearch(string(), filename, "lyx", support::may_not_exist);
1473 if (!fullname.empty())
1474 filename = fullname.absFilename();
1476 if (!fullname.onlyPath().isDirectory()) {
1477 Alert::warning(_("Invalid filename"),
1478 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1479 from_utf8(fullname.absFilename())));
1482 // if the file doesn't exist, let the user create one
1483 if (!fullname.exists()) {
1484 // the user specifically chose this name. Believe him.
1485 Buffer * const b = newFile(filename, string(), true);
1491 docstring const disp_fn = makeDisplayPath(filename);
1492 message(bformat(_("Opening document %1$s..."), disp_fn));
1495 Buffer * buf = loadDocument(fullname);
1497 buf->updateLabels();
1499 buf->errors("Parse");
1500 str2 = bformat(_("Document %1$s opened."), disp_fn);
1501 if (buf->lyxvc().inUse())
1502 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1503 " " + _("Version control detected.");
1505 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1510 // FIXME: clean that
1511 static bool import(GuiView * lv, FileName const & filename,
1512 string const & format, ErrorList & errorList)
1514 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1516 string loader_format;
1517 vector<string> loaders = theConverters().loaders();
1518 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1519 for (vector<string>::const_iterator it = loaders.begin();
1520 it != loaders.end(); ++it) {
1521 if (!theConverters().isReachable(format, *it))
1524 string const tofile =
1525 support::changeExtension(filename.absFilename(),
1526 formats.extension(*it));
1527 if (!theConverters().convert(0, filename, FileName(tofile),
1528 filename, format, *it, errorList))
1530 loader_format = *it;
1533 if (loader_format.empty()) {
1534 frontend::Alert::error(_("Couldn't import file"),
1535 bformat(_("No information for importing the format %1$s."),
1536 formats.prettyName(format)));
1540 loader_format = format;
1542 if (loader_format == "lyx") {
1543 Buffer * buf = lv->loadDocument(lyxfile);
1546 buf->updateLabels();
1548 buf->errors("Parse");
1550 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1554 bool as_paragraphs = loader_format == "textparagraph";
1555 string filename2 = (loader_format == format) ? filename.absFilename()
1556 : support::changeExtension(filename.absFilename(),
1557 formats.extension(loader_format));
1558 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1559 theLyXFunc().setLyXView(lv);
1560 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1567 void GuiView::importDocument(string const & argument)
1570 string filename = split(argument, format, ' ');
1572 LYXERR(Debug::INFO, format << " file: " << filename);
1574 // need user interaction
1575 if (filename.empty()) {
1576 string initpath = lyxrc.document_path;
1578 Buffer const * buf = buffer();
1580 string const trypath = buf->filePath();
1581 // If directory is writeable, use this as default.
1582 if (FileName(trypath).isDirWritable())
1586 docstring const text = bformat(_("Select %1$s file to import"),
1587 formats.prettyName(format));
1589 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1590 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1591 dlg.setButton2(qt_("Examples|#E#e"),
1592 toqstr(addPath(package().system_support().absFilename(), "examples")));
1594 docstring filter = formats.prettyName(format);
1597 filter += from_utf8(formats.extension(format));
1600 FileDialog::Result result =
1601 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1603 if (result.first == FileDialog::Later)
1606 filename = fromqstr(result.second);
1608 // check selected filename
1609 if (filename.empty())
1610 message(_("Canceled."));
1613 if (filename.empty())
1616 // get absolute path of file
1617 FileName const fullname(support::makeAbsPath(filename));
1619 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1621 // Check if the document already is open
1622 Buffer * buf = theBufferList().getBuffer(lyxfile);
1625 if (!closeBuffer()) {
1626 message(_("Canceled."));
1631 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1633 // if the file exists already, and we didn't do
1634 // -i lyx thefile.lyx, warn
1635 if (lyxfile.exists() && fullname != lyxfile) {
1637 docstring text = bformat(_("The document %1$s already exists.\n\n"
1638 "Do you want to overwrite that document?"), displaypath);
1639 int const ret = Alert::prompt(_("Overwrite document?"),
1640 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1643 message(_("Canceled."));
1648 message(bformat(_("Importing %1$s..."), displaypath));
1649 ErrorList errorList;
1650 if (import(this, fullname, format, errorList))
1651 message(_("imported."));
1653 message(_("file not imported!"));
1655 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1659 void GuiView::newDocument(string const & filename, bool from_template)
1661 FileName initpath(lyxrc.document_path);
1662 Buffer * buf = buffer();
1664 FileName const trypath(buf->filePath());
1665 // If directory is writeable, use this as default.
1666 if (trypath.isDirWritable())
1670 string templatefile;
1671 if (from_template) {
1672 templatefile = selectTemplateFile().absFilename();
1673 if (templatefile.empty())
1678 if (filename.empty())
1679 b = newUnnamedFile(templatefile, initpath);
1681 b = newFile(filename, templatefile, true);
1686 // If no new document could be created, it is unsure
1687 // whether there is a valid BufferView.
1689 // Ensure the cursor is correctly positioned on screen.
1690 view()->showCursor();
1694 void GuiView::insertLyXFile(docstring const & fname)
1696 BufferView * bv = view();
1701 FileName filename(to_utf8(fname));
1703 if (!filename.empty()) {
1704 bv->insertLyXFile(filename);
1708 // Launch a file browser
1710 string initpath = lyxrc.document_path;
1711 string const trypath = bv->buffer().filePath();
1712 // If directory is writeable, use this as default.
1713 if (FileName(trypath).isDirWritable())
1717 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1718 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1719 dlg.setButton2(qt_("Examples|#E#e"),
1720 toqstr(addPath(package().system_support().absFilename(),
1723 FileDialog::Result result = dlg.open(toqstr(initpath),
1724 QStringList(qt_("LyX Documents (*.lyx)")));
1726 if (result.first == FileDialog::Later)
1730 filename.set(fromqstr(result.second));
1732 // check selected filename
1733 if (filename.empty()) {
1734 // emit message signal.
1735 message(_("Canceled."));
1739 bv->insertLyXFile(filename);
1743 void GuiView::insertPlaintextFile(docstring const & fname,
1746 BufferView * bv = view();
1750 if (!fname.empty() && !FileName::isAbsolute(to_utf8(fname))) {
1751 message(_("Absolute filename expected."));
1756 FileName filename(to_utf8(fname));
1758 if (!filename.empty()) {
1759 bv->insertPlaintextFile(filename, asParagraph);
1763 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1764 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1766 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1767 QStringList(qt_("All Files (*)")));
1769 if (result.first == FileDialog::Later)
1773 filename.set(fromqstr(result.second));
1775 // check selected filename
1776 if (filename.empty()) {
1777 // emit message signal.
1778 message(_("Canceled."));
1782 bv->insertPlaintextFile(filename, asParagraph);
1786 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1788 FileName fname = b.fileName();
1789 FileName const oldname = fname;
1791 if (!newname.empty()) {
1793 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1795 // Switch to this Buffer.
1798 // No argument? Ask user through dialog.
1800 FileDialog dlg(qt_("Choose a filename to save document as"),
1801 LFUN_BUFFER_WRITE_AS);
1802 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1803 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1805 if (!isLyXFilename(fname.absFilename()))
1806 fname.changeExtension(".lyx");
1808 FileDialog::Result result =
1809 dlg.save(toqstr(fname.onlyPath().absFilename()),
1810 QStringList(qt_("LyX Documents (*.lyx)")),
1811 toqstr(fname.onlyFileName()));
1813 if (result.first == FileDialog::Later)
1816 fname.set(fromqstr(result.second));
1821 if (!isLyXFilename(fname.absFilename()))
1822 fname.changeExtension(".lyx");
1825 if (FileName(fname).exists()) {
1826 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1827 docstring text = bformat(_("The document %1$s already "
1828 "exists.\n\nDo you want to "
1829 "overwrite that document?"),
1831 int const ret = Alert::prompt(_("Overwrite document?"),
1832 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1835 case 1: return renameBuffer(b, docstring());
1836 case 2: return false;
1840 FileName oldauto = b.getAutosaveFilename();
1842 // Ok, change the name of the buffer
1843 b.setFileName(fname.absFilename());
1845 bool unnamed = b.isUnnamed();
1846 b.setUnnamed(false);
1847 b.saveCheckSum(fname);
1849 // bring the autosave file with us, just in case.
1850 b.moveAutosaveFile(oldauto);
1852 if (!saveBuffer(b)) {
1853 oldauto = b.getAutosaveFilename();
1854 b.setFileName(oldname.absFilename());
1855 b.setUnnamed(unnamed);
1856 b.saveCheckSum(oldname);
1857 b.moveAutosaveFile(oldauto);
1865 bool GuiView::saveBuffer(Buffer & b)
1867 if (workArea(b) && workArea(b)->inDialogMode())
1871 return renameBuffer(b, docstring());
1874 theSession().lastFiles().add(b.fileName());
1878 // Switch to this Buffer.
1881 // FIXME: we don't tell the user *WHY* the save failed !!
1882 docstring const file = makeDisplayPath(b.absFileName(), 30);
1883 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1884 "Do you want to rename the document and "
1885 "try again?"), file);
1886 int const ret = Alert::prompt(_("Rename and save?"),
1887 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1890 if (!renameBuffer(b, docstring()))
1899 return saveBuffer(b);
1903 bool GuiView::closeBuffer()
1905 Buffer * buf = buffer();
1906 return buf && closeBuffer(*buf);
1910 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
1912 // goto bookmark to update bookmark pit.
1913 //FIXME: we should update only the bookmarks related to this buffer!
1914 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1915 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1916 theLyXFunc().gotoBookmark(i+1, false, false);
1918 if (buf.isClean() || buf.paragraphs().empty()) {
1919 // save in sessions if requested
1920 // do not save childs if their master
1921 // is opened as well
1923 theSession().lastOpened().add(buf.fileName(), mark_active);
1925 // Don't close child documents.
1926 removeWorkArea(currentMainWorkArea());
1928 theBufferList().release(&buf);
1931 // Switch to this Buffer.
1936 if (buf.isUnnamed())
1937 file = from_utf8(buf.fileName().onlyFileName());
1939 file = buf.fileName().displayName(30);
1941 // Bring this window to top before asking questions.
1945 docstring const text = bformat(_("The document %1$s has unsaved changes."
1946 "\n\nDo you want to save the document or discard the changes?"), file);
1947 int const ret = Alert::prompt(_("Save changed document?"),
1948 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1952 if (!saveBuffer(buf))
1956 // if we crash after this we could
1957 // have no autosave file but I guess
1958 // this is really improbable (Jug)
1959 buf.removeAutosaveFile();
1965 // save file names to .lyx/session
1967 theSession().lastOpened().add(buf.fileName(), mark_active);
1970 // Don't close child documents.
1971 removeWorkArea(currentMainWorkArea());
1973 theBufferList().release(&buf);
1979 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1981 Buffer * const curbuf = buffer();
1982 Buffer * nextbuf = curbuf;
1984 if (np == NEXTBUFFER)
1985 nextbuf = theBufferList().next(nextbuf);
1987 nextbuf = theBufferList().previous(nextbuf);
1988 if (nextbuf == curbuf)
1994 if (workArea(*nextbuf))
2001 bool GuiView::dispatch(FuncRequest const & cmd)
2003 BufferView * bv = view();
2004 // By default we won't need any update.
2006 bv->cursor().updateFlags(Update::None);
2007 bool dispatched = true;
2009 if (cmd.origin == FuncRequest::TOC) {
2010 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2011 toc->doDispatch(bv->cursor(), cmd);
2015 switch(cmd.action) {
2016 case LFUN_BUFFER_IMPORT:
2017 importDocument(to_utf8(cmd.argument()));
2020 case LFUN_BUFFER_SWITCH:
2021 if (FileName::isAbsolute(to_utf8(cmd.argument()))) {
2023 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2027 bv->cursor().message(_("Document not loaded"));
2031 case LFUN_BUFFER_NEXT:
2032 gotoNextOrPreviousBuffer(NEXTBUFFER);
2035 case LFUN_BUFFER_PREVIOUS:
2036 gotoNextOrPreviousBuffer(PREVBUFFER);
2039 case LFUN_COMMAND_EXECUTE: {
2040 bool const show_it = cmd.argument() != "off";
2041 // FIXME: this is a hack, "minibuffer" should not be
2043 if (GuiToolbar * t = toolbar("minibuffer")) {
2044 t->setVisible(show_it);
2045 if (show_it && t->commandBuffer())
2046 t->commandBuffer()->setFocus();
2050 case LFUN_DROP_LAYOUTS_CHOICE:
2052 d.layout_->showPopup();
2055 case LFUN_MENU_OPEN:
2056 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2057 menu->exec(QCursor::pos());
2060 case LFUN_FILE_INSERT:
2061 insertLyXFile(cmd.argument());
2063 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2064 insertPlaintextFile(cmd.argument(), true);
2067 case LFUN_FILE_INSERT_PLAINTEXT:
2068 insertPlaintextFile(cmd.argument(), false);
2071 case LFUN_BUFFER_WRITE:
2073 saveBuffer(bv->buffer());
2076 case LFUN_BUFFER_WRITE_AS:
2078 renameBuffer(bv->buffer(), cmd.argument());
2081 case LFUN_BUFFER_WRITE_ALL: {
2082 Buffer * first = theBufferList().first();
2085 message(_("Saving all documents..."));
2086 // We cannot use a for loop as the buffer list cycles.
2089 if (!b->isClean()) {
2091 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2093 b = theBufferList().next(b);
2094 } while (b != first);
2095 message(_("All documents saved."));
2099 case LFUN_TOOLBAR_TOGGLE: {
2100 string const name = cmd.getArg(0);
2101 if (GuiToolbar * t = toolbar(name))
2106 case LFUN_DIALOG_UPDATE: {
2107 string const name = to_utf8(cmd.argument());
2108 // Can only update a dialog connected to an existing inset
2109 Inset * inset = getOpenInset(name);
2111 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2112 inset->dispatch(view()->cursor(), fr);
2113 } else if (name == "paragraph") {
2114 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2115 } else if (name == "prefs" || name == "document") {
2116 updateDialog(name, string());
2121 case LFUN_DIALOG_TOGGLE: {
2122 if (isDialogVisible(cmd.getArg(0)))
2123 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2125 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2129 case LFUN_DIALOG_DISCONNECT_INSET:
2130 disconnectDialog(to_utf8(cmd.argument()));
2133 case LFUN_DIALOG_HIDE: {
2134 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2138 case LFUN_DIALOG_SHOW: {
2139 string const name = cmd.getArg(0);
2140 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2142 if (name == "character") {
2143 data = freefont2string();
2145 showDialog("character", data);
2146 } else if (name == "latexlog") {
2147 Buffer::LogType type;
2148 string const logfile = buffer()->logName(&type);
2150 case Buffer::latexlog:
2153 case Buffer::buildlog:
2157 data += Lexer::quoteString(logfile);
2158 showDialog("log", data);
2159 } else if (name == "vclog") {
2160 string const data = "vc " +
2161 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2162 showDialog("log", data);
2163 } else if (name == "symbols") {
2164 data = bv->cursor().getEncoding()->name();
2166 showDialog("symbols", data);
2168 } else if (name == "prefs" && isFullScreen()) {
2169 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2171 showDialog("prefs", data);
2173 showDialog(name, data);
2177 case LFUN_INSET_APPLY: {
2178 string const name = cmd.getArg(0);
2179 Inset * inset = getOpenInset(name);
2181 // put cursor in front of inset.
2182 if (!view()->setCursorFromInset(inset)) {
2183 LASSERT(false, break);
2186 // useful if we are called from a dialog.
2187 view()->cursor().beginUndoGroup();
2188 view()->cursor().recordUndo();
2189 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2190 inset->dispatch(view()->cursor(), fr);
2191 view()->cursor().endUndoGroup();
2193 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2199 case LFUN_UI_TOGGLE:
2201 // Make sure the keyboard focus stays in the work area.
2205 case LFUN_SPLIT_VIEW:
2206 if (Buffer * buf = buffer()) {
2207 string const orientation = cmd.getArg(0);
2208 d.splitter_->setOrientation(orientation == "vertical"
2209 ? Qt::Vertical : Qt::Horizontal);
2210 TabWorkArea * twa = addTabWorkArea();
2211 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2212 setCurrentWorkArea(wa);
2216 case LFUN_CLOSE_TAB_GROUP:
2217 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2219 twa = d.currentTabWorkArea();
2220 // Switch to the next GuiWorkArea in the found TabWorkArea.
2222 // Make sure the work area is up to date.
2223 setCurrentWorkArea(twa->currentWorkArea());
2225 setCurrentWorkArea(0);
2230 case LFUN_COMPLETION_INLINE:
2231 if (d.current_work_area_)
2232 d.current_work_area_->completer().showInline();
2235 case LFUN_COMPLETION_POPUP:
2236 if (d.current_work_area_)
2237 d.current_work_area_->completer().showPopup();
2241 case LFUN_COMPLETION_COMPLETE:
2242 if (d.current_work_area_)
2243 d.current_work_area_->completer().tab();
2246 case LFUN_COMPLETION_CANCEL:
2247 if (d.current_work_area_) {
2248 if (d.current_work_area_->completer().popupVisible())
2249 d.current_work_area_->completer().hidePopup();
2251 d.current_work_area_->completer().hideInline();
2255 case LFUN_COMPLETION_ACCEPT:
2256 if (d.current_work_area_)
2257 d.current_work_area_->completer().activate();
2260 case LFUN_BUFFER_ZOOM_IN:
2261 case LFUN_BUFFER_ZOOM_OUT:
2262 if (cmd.argument().empty()) {
2263 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2268 lyxrc.zoom += convert<int>(cmd.argument());
2270 if (lyxrc.zoom < 10)
2273 // The global QPixmapCache is used in GuiPainter to cache text
2274 // painting so we must reset it.
2275 QPixmapCache::clear();
2276 guiApp->fontLoader().update();
2277 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2285 // Part of automatic menu appearance feature.
2286 if (isFullScreen()) {
2287 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
2289 if (statusBar()->isVisible())
2290 statusBar()->hide();
2297 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2299 string const arg = cmd.getArg(0);
2300 if (arg == "scrollbar") {
2301 // hide() is of no help
2302 if (d.current_work_area_->verticalScrollBarPolicy() ==
2303 Qt::ScrollBarAlwaysOff)
2305 d.current_work_area_->setVerticalScrollBarPolicy(
2306 Qt::ScrollBarAsNeeded);
2308 d.current_work_area_->setVerticalScrollBarPolicy(
2309 Qt::ScrollBarAlwaysOff);
2312 if (arg == "statusbar") {
2313 statusBar()->setVisible(!statusBar()->isVisible());
2316 if (arg == "menubar") {
2317 menuBar()->setVisible(!menuBar()->isVisible());
2320 #if QT_VERSION >= 0x040300
2321 if (arg == "frame") {
2323 getContentsMargins(&l, &t, &r, &b);
2324 //are the frames in default state?
2325 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2327 setContentsMargins(-2, -2, -2, -2);
2329 setContentsMargins(0, 0, 0, 0);
2334 if (arg == "fullscreen") {
2339 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2343 void GuiView::toggleFullScreen()
2345 if (isFullScreen()) {
2346 for (int i = 0; i != d.splitter_->count(); ++i)
2347 d.tabWorkArea(i)->setFullScreen(false);
2348 #if QT_VERSION >= 0x040300
2349 setContentsMargins(0, 0, 0, 0);
2351 setWindowState(windowState() ^ Qt::WindowFullScreen);
2354 statusBar()->show();
2357 hideDialogs("prefs", 0);
2358 for (int i = 0; i != d.splitter_->count(); ++i)
2359 d.tabWorkArea(i)->setFullScreen(true);
2360 #if QT_VERSION >= 0x040300
2361 setContentsMargins(-2, -2, -2, -2);
2364 setWindowState(windowState() ^ Qt::WindowFullScreen);
2365 statusBar()->hide();
2366 if (lyxrc.full_screen_menubar)
2368 if (lyxrc.full_screen_toolbars) {
2369 ToolbarMap::iterator end = d.toolbars_.end();
2370 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2375 // give dialogs like the TOC a chance to adapt
2380 Buffer const * GuiView::updateInset(Inset const * inset)
2382 if (!d.current_work_area_)
2386 d.current_work_area_->scheduleRedraw();
2388 return &d.current_work_area_->bufferView().buffer();
2392 void GuiView::restartCursor()
2394 /* When we move around, or type, it's nice to be able to see
2395 * the cursor immediately after the keypress.
2397 if (d.current_work_area_)
2398 d.current_work_area_->startBlinkingCursor();
2400 // Take this occasion to update the other GUI elements.
2406 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2408 if (d.current_work_area_)
2409 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2414 // This list should be kept in sync with the list of insets in
2415 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2416 // dialog should have the same name as the inset.
2417 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2418 // docs in LyXAction.cpp.
2420 char const * const dialognames[] = {
2421 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2422 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2423 "findreplaceadv", "float", "graphics", "href", "include", "index",
2424 "index_print", "info", "listings", "label", "log", "mathdelimiter",
2425 "mathmatrix", "mathspace", "nomenclature", "nomencl_print", "note",
2426 "paragraph", "phantom", "prefs", "print", "ref", "sendto", "space",
2427 "spellchecker", "symbols", "tabular", "tabularcreate", "thesaurus", "texinfo",
2428 "toc", "view-source", "vspace", "wrap" };
2430 char const * const * const end_dialognames =
2431 dialognames + (sizeof(dialognames) / sizeof(char *));
2435 cmpCStr(char const * name) : name_(name) {}
2436 bool operator()(char const * other) {
2437 return strcmp(other, name_) == 0;
2444 bool isValidName(string const & name)
2446 return find_if(dialognames, end_dialognames,
2447 cmpCStr(name.c_str())) != end_dialognames;
2453 void GuiView::resetDialogs()
2455 // Make sure that no LFUN uses any LyXView.
2456 theLyXFunc().setLyXView(0);
2459 constructToolbars();
2460 guiApp->menus().fillMenuBar(menuBar(), this, false);
2462 d.layout_->updateContents(true);
2463 // Now update controls with current buffer.
2464 theLyXFunc().setLyXView(this);
2470 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2472 if (!isValidName(name))
2475 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2477 if (it != d.dialogs_.end()) {
2479 it->second->hideView();
2480 return it->second.get();
2483 Dialog * dialog = build(name);
2484 d.dialogs_[name].reset(dialog);
2485 if (lyxrc.allow_geometry_session)
2486 dialog->restoreSession();
2493 void GuiView::showDialog(string const & name, string const & data,
2501 Dialog * dialog = findOrBuild(name, false);
2503 dialog->showData(data);
2505 d.open_insets_[name] = inset;
2508 catch (ExceptionMessage const & ex) {
2516 bool GuiView::isDialogVisible(string const & name) const
2518 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2519 if (it == d.dialogs_.end())
2521 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
2525 void GuiView::hideDialog(string const & name, Inset * inset)
2527 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2528 if (it == d.dialogs_.end())
2531 if (inset && inset != getOpenInset(name))
2534 Dialog * const dialog = it->second.get();
2535 if (dialog->isVisibleView())
2537 d.open_insets_[name] = 0;
2541 void GuiView::disconnectDialog(string const & name)
2543 if (!isValidName(name))
2546 if (d.open_insets_.find(name) != d.open_insets_.end())
2547 d.open_insets_[name] = 0;
2551 Inset * GuiView::getOpenInset(string const & name) const
2553 if (!isValidName(name))
2556 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2557 return it == d.open_insets_.end() ? 0 : it->second;
2561 void GuiView::hideAll() const
2563 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2564 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2566 for(; it != end; ++it)
2567 it->second->hideView();
2571 void GuiView::updateDialogs()
2573 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2574 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2576 for(; it != end; ++it) {
2577 Dialog * dialog = it->second.get();
2578 if (dialog && dialog->isVisibleView())
2579 dialog->checkStatus();
2586 // will be replaced by a proper factory...
2587 Dialog * createGuiAbout(GuiView & lv);
2588 Dialog * createGuiBibitem(GuiView & lv);
2589 Dialog * createGuiBibtex(GuiView & lv);
2590 Dialog * createGuiBox(GuiView & lv);
2591 Dialog * createGuiBranch(GuiView & lv);
2592 Dialog * createGuiChanges(GuiView & lv);
2593 Dialog * createGuiCharacter(GuiView & lv);
2594 Dialog * createGuiCitation(GuiView & lv);
2595 Dialog * createGuiDelimiter(GuiView & lv);
2596 Dialog * createGuiDocument(GuiView & lv);
2597 Dialog * createGuiErrorList(GuiView & lv);
2598 Dialog * createGuiERT(GuiView & lv);
2599 Dialog * createGuiExternal(GuiView & lv);
2600 Dialog * createGuiFloat(GuiView & lv);
2601 Dialog * createGuiGraphics(GuiView & lv);
2602 Dialog * createGuiInclude(GuiView & lv);
2603 Dialog * createGuiIndex(GuiView & lv);
2604 Dialog * createGuiInfo(GuiView & lv);
2605 Dialog * createGuiLabel(GuiView & lv);
2606 Dialog * createGuiListings(GuiView & lv);
2607 Dialog * createGuiLog(GuiView & lv);
2608 Dialog * createGuiMathHSpace(GuiView & lv);
2609 Dialog * createGuiMathMatrix(GuiView & lv);
2610 Dialog * createGuiNomenclature(GuiView & lv);
2611 Dialog * createGuiNote(GuiView & lv);
2612 Dialog * createGuiParagraph(GuiView & lv);
2613 Dialog * createGuiPhantom(GuiView & lv);
2614 Dialog * createGuiPreferences(GuiView & lv);
2615 Dialog * createGuiPrint(GuiView & lv);
2616 Dialog * createGuiPrintindex(GuiView & lv);
2617 Dialog * createGuiPrintNomencl(GuiView & lv);
2618 Dialog * createGuiRef(GuiView & lv);
2619 Dialog * createGuiSearch(GuiView & lv);
2620 Dialog * createGuiSearchAdv(GuiView & lv);
2621 Dialog * createGuiSendTo(GuiView & lv);
2622 Dialog * createGuiShowFile(GuiView & lv);
2623 Dialog * createGuiSpellchecker(GuiView & lv);
2624 Dialog * createGuiSymbols(GuiView & lv);
2625 Dialog * createGuiTabularCreate(GuiView & lv);
2626 Dialog * createGuiTabular(GuiView & lv);
2627 Dialog * createGuiTexInfo(GuiView & lv);
2628 Dialog * createGuiTextHSpace(GuiView & lv);
2629 Dialog * createGuiToc(GuiView & lv);
2630 Dialog * createGuiThesaurus(GuiView & lv);
2631 Dialog * createGuiHyperlink(GuiView & lv);
2632 Dialog * createGuiVSpace(GuiView & lv);
2633 Dialog * createGuiViewSource(GuiView & lv);
2634 Dialog * createGuiWrap(GuiView & lv);
2637 Dialog * GuiView::build(string const & name)
2639 LASSERT(isValidName(name), return 0);
2641 if (name == "aboutlyx")
2642 return createGuiAbout(*this);
2643 if (name == "bibitem")
2644 return createGuiBibitem(*this);
2645 if (name == "bibtex")
2646 return createGuiBibtex(*this);
2648 return createGuiBox(*this);
2649 if (name == "branch")
2650 return createGuiBranch(*this);
2651 if (name == "changes")
2652 return createGuiChanges(*this);
2653 if (name == "character")
2654 return createGuiCharacter(*this);
2655 if (name == "citation")
2656 return createGuiCitation(*this);
2657 if (name == "document")
2658 return createGuiDocument(*this);
2659 if (name == "errorlist")
2660 return createGuiErrorList(*this);
2662 return createGuiERT(*this);
2663 if (name == "external")
2664 return createGuiExternal(*this);
2666 return createGuiShowFile(*this);
2667 if (name == "findreplace")
2668 return createGuiSearch(*this);
2669 if (name == "findreplaceadv")
2670 return createGuiSearchAdv(*this);
2671 if (name == "float")
2672 return createGuiFloat(*this);
2673 if (name == "graphics")
2674 return createGuiGraphics(*this);
2676 return createGuiHyperlink(*this);
2677 if (name == "include")
2678 return createGuiInclude(*this);
2679 if (name == "index")
2680 return createGuiIndex(*this);
2681 if (name == "index_print")
2682 return createGuiPrintindex(*this);
2684 return createGuiInfo(*this);
2685 if (name == "label")
2686 return createGuiLabel(*this);
2687 if (name == "listings")
2688 return createGuiListings(*this);
2690 return createGuiLog(*this);
2691 if (name == "mathdelimiter")
2692 return createGuiDelimiter(*this);
2693 if (name == "mathspace")
2694 return createGuiMathHSpace(*this);
2695 if (name == "mathmatrix")
2696 return createGuiMathMatrix(*this);
2697 if (name == "nomenclature")
2698 return createGuiNomenclature(*this);
2699 if (name == "nomencl_print")
2700 return createGuiPrintNomencl(*this);
2702 return createGuiNote(*this);
2703 if (name == "paragraph")
2704 return createGuiParagraph(*this);
2705 if (name == "phantom")
2706 return createGuiPhantom(*this);
2707 if (name == "prefs")
2708 return createGuiPreferences(*this);
2709 if (name == "print")
2710 return createGuiPrint(*this);
2712 return createGuiRef(*this);
2713 if (name == "sendto")
2714 return createGuiSendTo(*this);
2715 if (name == "space")
2716 return createGuiTextHSpace(*this);
2717 if (name == "spellchecker")
2718 return createGuiSpellchecker(*this);
2719 if (name == "symbols")
2720 return createGuiSymbols(*this);
2721 if (name == "tabular")
2722 return createGuiTabular(*this);
2723 if (name == "tabularcreate")
2724 return createGuiTabularCreate(*this);
2725 if (name == "texinfo")
2726 return createGuiTexInfo(*this);
2727 if (name == "thesaurus")
2728 return createGuiThesaurus(*this);
2730 return createGuiToc(*this);
2731 if (name == "view-source")
2732 return createGuiViewSource(*this);
2733 if (name == "vspace")
2734 return createGuiVSpace(*this);
2736 return createGuiWrap(*this);
2742 } // namespace frontend
2745 #include "moc_GuiView.cpp"