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 setIconSize(settings.value(icon_key).toSize());
383 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
384 QSize size = settings.value("size", QSize(690, 510)).toSize();
388 if (!restoreGeometry(settings.value("geometry").toByteArray()))
389 setGeometry(50, 50, 690, 510);
391 // Make sure layout is correctly oriented.
392 setLayoutDirection(qApp->layoutDirection());
394 // Allow the toc and view-source dock widget to be restored if needed.
396 if ((d = findOrBuild("toc", true)))
399 if ((d = findOrBuild("view-source", true)))
402 if (!restoreState(settings.value("layout").toByteArray(), 0))
409 GuiToolbar * GuiView::toolbar(string const & name)
411 ToolbarMap::iterator it = d.toolbars_.find(name);
412 if (it != d.toolbars_.end())
415 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
416 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
421 void GuiView::constructToolbars()
423 ToolbarMap::iterator it = d.toolbars_.begin();
424 for (; it != d.toolbars_.end(); ++it)
429 // extracts the toolbars from the backend
430 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
431 Toolbars::Infos::iterator end = guiApp->toolbars().end();
432 for (; cit != end; ++cit)
433 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
437 void GuiView::initToolbars()
439 // extracts the toolbars from the backend
440 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
441 Toolbars::Infos::iterator end = guiApp->toolbars().end();
442 for (; cit != end; ++cit) {
443 GuiToolbar * tb = toolbar(cit->name);
446 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
448 tb->setVisible(false);
449 tb->setVisibility(visibility);
451 if (visibility & Toolbars::TOP) {
453 addToolBarBreak(Qt::TopToolBarArea);
454 addToolBar(Qt::TopToolBarArea, tb);
457 if (visibility & Toolbars::BOTTOM) {
458 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
459 #if (QT_VERSION >= 0x040202)
460 addToolBarBreak(Qt::BottomToolBarArea);
462 addToolBar(Qt::BottomToolBarArea, tb);
465 if (visibility & Toolbars::LEFT) {
466 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
467 #if (QT_VERSION >= 0x040202)
468 addToolBarBreak(Qt::LeftToolBarArea);
470 addToolBar(Qt::LeftToolBarArea, tb);
473 if (visibility & Toolbars::RIGHT) {
474 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
475 #if (QT_VERSION >= 0x040202)
476 addToolBarBreak(Qt::RightToolBarArea);
478 addToolBar(Qt::RightToolBarArea, tb);
481 if (visibility & Toolbars::ON)
482 tb->setVisible(true);
487 TocModels & GuiView::tocModels()
489 return d.toc_models_;
493 void GuiView::setFocus()
495 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
496 // Make sure LyXFunc points to the correct view.
497 guiApp->setCurrentView(this);
498 theLyXFunc().setLyXView(this);
499 QMainWindow::setFocus();
500 if (d.current_work_area_)
501 d.current_work_area_->setFocus();
505 QMenu * GuiView::createPopupMenu()
507 return d.toolBarPopup(this);
511 void GuiView::showEvent(QShowEvent * e)
513 LYXERR(Debug::GUI, "Passed Geometry "
514 << size().height() << "x" << size().width()
515 << "+" << pos().x() << "+" << pos().y());
517 if (d.splitter_->count() == 0)
518 // No work area, switch to the background widget.
521 QMainWindow::showEvent(e);
525 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
526 ** is responsibility of the container (e.g., dialog)
528 void GuiView::closeEvent(QCloseEvent * close_event)
530 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
533 // it can happen that this event arrives without selecting the view,
534 // e.g. when clicking the close button on a background window.
536 GuiWorkArea * active_wa = currentMainWorkArea();
537 setCurrentWorkArea(active_wa);
539 // When a view/window was closed before without quitting LyX, there
540 // are already entries in the lastOpened list.
541 theSession().lastOpened().clear();
543 int splitter_count = d.splitter_->count();
544 for (; splitter_count; --splitter_count) {
545 TabWorkArea * twa = d.tabWorkArea(0);
547 int twa_count = twa->count();
548 for (; twa_count; --twa_count) {
549 twa->setCurrentIndex(twa_count-1);
551 GuiWorkArea * wa = twa->currentWorkArea();
552 bool const is_active_wa = active_wa == wa;
553 Buffer * b = &wa->bufferView().buffer();
555 // This is a child document, just close the tab
556 // after saving but keep the file loaded.
557 if (!closeBuffer(*b, true, is_active_wa)) {
559 close_event->ignore();
565 vector<Buffer *> clist = b->getChildren();
566 for (vector<Buffer *>::const_iterator it = clist.begin();
567 it != clist.end(); ++it) {
568 if ((*it)->isClean())
571 // If a child is dirty, do not close
572 // without user intervention
573 if (!closeBuffer(*c, false)) {
575 close_event->ignore();
580 QList<int> const ids = guiApp->viewIds();
581 for (int i = 0; i != ids.size(); ++i) {
584 if (guiApp->view(ids[i]).workArea(*b)) {
585 // FIXME 1: should we put an alert box here
586 // that the buffer is viewed elsewhere?
587 // FIXME 2: should we try to save this buffer in any case?
590 // This buffer is also opened in another view, so
591 // close the associated work area...
593 // ... but don't close the buffer.
598 // closeBuffer() needs buffer workArea still alive and
599 // set as currrent one, and destroys it
600 if (b && !closeBuffer(*b, true, is_active_wa)) {
602 close_event->ignore();
607 // Make sure that nothing will use this close to be closed View.
608 guiApp->unregisterView(this);
610 if (isFullScreen()) {
611 // Switch off fullscreen before closing.
616 // Make sure the timer time out will not trigger a statusbar update.
617 d.statusbar_timer_.stop();
619 // Saving fullscreen requires additional tweaks in the toolbar code.
620 // It wouldn't also work under linux natively.
621 if (lyxrc.allow_geometry_session) {
622 // Save this window geometry and layout.
624 // Then the toolbar private states.
625 ToolbarMap::iterator end = d.toolbars_.end();
626 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
627 it->second->saveSession();
628 // Now take care of all other dialogs:
629 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
630 for (; it!= d.dialogs_.end(); ++it)
631 it->second->saveSession();
634 close_event->accept();
638 void GuiView::dragEnterEvent(QDragEnterEvent * event)
640 if (event->mimeData()->hasUrls())
642 /// \todo Ask lyx-devel is this is enough:
643 /// if (event->mimeData()->hasFormat("text/plain"))
644 /// event->acceptProposedAction();
648 void GuiView::dropEvent(QDropEvent * event)
650 QList<QUrl> files = event->mimeData()->urls();
654 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
655 for (int i = 0; i != files.size(); ++i) {
656 string const file = os::internal_path(fromqstr(
657 files.at(i).toLocalFile()));
659 // Asynchronously post the event. DropEvent usually come
660 // from the BufferView. But reloading a file might close
661 // the BufferView from within its own event handler.
662 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
669 void GuiView::message(docstring const & str)
671 if (ForkedProcess::iAmAChild())
674 statusBar()->showMessage(toqstr(str));
675 d.statusbar_timer_.stop();
676 d.statusbar_timer_.start(3000);
680 void GuiView::smallSizedIcons()
682 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
686 void GuiView::normalSizedIcons()
688 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
692 void GuiView::bigSizedIcons()
694 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
698 void GuiView::clearMessage()
702 theLyXFunc().setLyXView(this);
703 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
704 d.statusbar_timer_.stop();
708 void GuiView::updateWindowTitle(GuiWorkArea * wa)
710 if (wa != d.current_work_area_)
712 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
713 setWindowIconText(wa->windowIconText());
717 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
720 disconnectBufferView();
721 connectBufferView(wa->bufferView());
722 connectBuffer(wa->bufferView().buffer());
723 d.current_work_area_ = wa;
724 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
725 this, SLOT(updateWindowTitle(GuiWorkArea *)));
726 updateWindowTitle(wa);
730 // The document settings needs to be reinitialised.
731 updateDialog("document", "");
733 // Buffer-dependent dialogs must be updated. This is done here because
734 // some dialogs require buffer()->text.
739 void GuiView::on_lastWorkAreaRemoved()
742 // We already are in a close event. Nothing more to do.
745 if (d.splitter_->count() > 1)
746 // We have a splitter so don't close anything.
749 // Reset and updates the dialogs.
750 d.toc_models_.reset(0);
751 updateDialog("document", "");
754 resetWindowTitleAndIconText();
756 if (lyxrc.open_buffers_in_tabs)
757 // Nothing more to do, the window should stay open.
760 if (guiApp->viewIds().size() > 1) {
766 // On Mac we also close the last window because the application stay
767 // resident in memory. On other platforms we don't close the last
768 // window because this would quit the application.
774 void GuiView::updateStatusBar()
776 // let the user see the explicit message
777 if (d.statusbar_timer_.isActive())
780 theLyXFunc().setLyXView(this);
781 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
785 bool GuiView::hasFocus() const
787 return qApp->activeWindow() == this;
791 bool GuiView::event(QEvent * e)
795 // Useful debug code:
796 //case QEvent::ActivationChange:
797 //case QEvent::WindowDeactivate:
798 //case QEvent::Paint:
799 //case QEvent::Enter:
800 //case QEvent::Leave:
801 //case QEvent::HoverEnter:
802 //case QEvent::HoverLeave:
803 //case QEvent::HoverMove:
804 //case QEvent::StatusTip:
805 //case QEvent::DragEnter:
806 //case QEvent::DragLeave:
810 case QEvent::WindowActivate: {
811 if (this == guiApp->currentView()) {
813 return QMainWindow::event(e);
815 guiApp->setCurrentView(this);
816 theLyXFunc().setLyXView(this);
817 if (d.current_work_area_) {
818 BufferView & bv = d.current_work_area_->bufferView();
819 connectBufferView(bv);
820 connectBuffer(bv.buffer());
821 // The document structure, name and dialogs might have
822 // changed in another view.
824 // The document settings needs to be reinitialised.
825 updateDialog("document", "");
828 resetWindowTitleAndIconText();
831 return QMainWindow::event(e);
834 case QEvent::ShortcutOverride: {
837 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
838 if (isFullScreen() && menuBar()->isHidden()) {
839 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
840 // FIXME: we should also try to detect special LyX shortcut such as
841 // Alt-P and Alt-M. Right now there is a hack in
842 // GuiWorkArea::processKeySym() that hides again the menubar for
844 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
846 return QMainWindow::event(e);
851 if (d.current_work_area_)
852 // Nothing special to do.
853 return QMainWindow::event(e);
855 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
856 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
858 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
859 || ke->key() == Qt::Key_Backtab)
860 return QMainWindow::event(e);
862 // Allow processing of shortcuts that are allowed even when no Buffer
864 theLyXFunc().setLyXView(this);
866 setKeySymbol(&sym, ke);
867 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
873 return QMainWindow::event(e);
877 void GuiView::resetWindowTitleAndIconText()
879 setWindowTitle(qt_("LyX"));
880 setWindowIconText(qt_("LyX"));
883 bool GuiView::focusNextPrevChild(bool /*next*/)
890 void GuiView::setBusy(bool busy)
892 if (d.current_work_area_) {
893 d.current_work_area_->setUpdatesEnabled(!busy);
895 d.current_work_area_->stopBlinkingCursor();
897 d.current_work_area_->startBlinkingCursor();
901 QApplication::setOverrideCursor(Qt::WaitCursor);
903 QApplication::restoreOverrideCursor();
907 GuiWorkArea * GuiView::workArea(Buffer & buffer)
909 if (currentWorkArea()
910 && ¤tWorkArea()->bufferView().buffer() == &buffer)
911 return (GuiWorkArea *) currentWorkArea();
912 if (TabWorkArea * twa = d.currentTabWorkArea())
913 return twa->workArea(buffer);
918 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
920 // Automatically create a TabWorkArea if there are none yet.
921 TabWorkArea * tab_widget = d.splitter_->count()
922 ? d.currentTabWorkArea() : addTabWorkArea();
923 return tab_widget->addWorkArea(buffer, *this);
927 TabWorkArea * GuiView::addTabWorkArea()
929 TabWorkArea * twa = new TabWorkArea;
930 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
931 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
932 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
933 this, SLOT(on_lastWorkAreaRemoved()));
935 d.splitter_->addWidget(twa);
936 d.stack_widget_->setCurrentWidget(d.splitter_);
941 GuiWorkArea const * GuiView::currentWorkArea() const
943 return d.current_work_area_;
947 GuiWorkArea * GuiView::currentWorkArea()
949 return d.current_work_area_;
953 GuiWorkArea const * GuiView::currentMainWorkArea() const
955 if (d.currentTabWorkArea() == NULL)
957 return d.currentTabWorkArea()->currentWorkArea();
961 GuiWorkArea * GuiView::currentMainWorkArea()
963 if (d.currentTabWorkArea() == NULL)
965 return d.currentTabWorkArea()->currentWorkArea();
969 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
971 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
973 d.current_work_area_ = NULL;
977 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
981 theGuiApp()->setCurrentView(this);
982 d.current_work_area_ = wa;
983 for (int i = 0; i != d.splitter_->count(); ++i) {
984 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
985 //if (d.current_main_work_area_)
986 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
987 d.current_main_work_area_ = wa;
988 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
989 //d.current_main_work_area_->setLineWidth(2);
990 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
994 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
995 on_currentWorkAreaChanged(wa);
996 BufferView & bv = wa->bufferView();
997 bv.cursor().fixIfBroken();
999 wa->setUpdatesEnabled(true);
1000 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1004 void GuiView::removeWorkArea(GuiWorkArea * wa)
1006 LASSERT(wa, return);
1007 if (wa == d.current_work_area_) {
1009 disconnectBufferView();
1010 d.current_work_area_ = 0;
1011 d.current_main_work_area_ = 0;
1014 bool found_twa = false;
1015 for (int i = 0; i != d.splitter_->count(); ++i) {
1016 TabWorkArea * twa = d.tabWorkArea(i);
1017 if (twa->removeWorkArea(wa)) {
1018 // Found in this tab group, and deleted the GuiWorkArea.
1020 if (twa->count() != 0) {
1021 if (d.current_work_area_ == 0)
1022 // This means that we are closing the current GuiWorkArea, so
1023 // switch to the next GuiWorkArea in the found TabWorkArea.
1024 setCurrentWorkArea(twa->currentWorkArea());
1026 // No more WorkAreas in this tab group, so delete it.
1033 // It is not a tabbed work area (i.e., the search work area), so it
1034 // should be deleted by other means.
1035 LASSERT(found_twa, /* */);
1037 if (d.current_work_area_ == 0) {
1038 if (d.splitter_->count() != 0) {
1039 TabWorkArea * twa = d.currentTabWorkArea();
1040 setCurrentWorkArea(twa->currentWorkArea());
1042 // No more work areas, switch to the background widget.
1043 setCurrentWorkArea(0);
1049 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1055 void GuiView::updateLayoutList()
1058 d.layout_->updateContents(false);
1062 void GuiView::updateToolbars()
1064 ToolbarMap::iterator end = d.toolbars_.end();
1065 if (d.current_work_area_) {
1067 d.current_work_area_->bufferView().cursor().inMathed();
1069 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1071 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1072 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1073 bool const mathmacrotemplate =
1074 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1076 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1077 it->second->update(math, table, review, mathmacrotemplate);
1079 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1080 it->second->update(false, false, false, false);
1084 Buffer * GuiView::buffer()
1086 if (d.current_work_area_)
1087 return &d.current_work_area_->bufferView().buffer();
1092 Buffer const * GuiView::buffer() const
1094 if (d.current_work_area_)
1095 return &d.current_work_area_->bufferView().buffer();
1100 void GuiView::setBuffer(Buffer * newBuffer)
1102 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1103 LASSERT(newBuffer, return);
1106 GuiWorkArea * wa = workArea(*newBuffer);
1108 newBuffer->masterBuffer()->updateLabels();
1109 wa = addWorkArea(*newBuffer);
1111 //Disconnect the old buffer...there's no new one.
1114 connectBuffer(*newBuffer);
1115 connectBufferView(wa->bufferView());
1116 setCurrentWorkArea(wa);
1122 void GuiView::connectBuffer(Buffer & buf)
1124 buf.setGuiDelegate(this);
1128 void GuiView::disconnectBuffer()
1130 if (d.current_work_area_)
1131 d.current_work_area_->bufferView().setGuiDelegate(0);
1135 void GuiView::connectBufferView(BufferView & bv)
1137 bv.setGuiDelegate(this);
1141 void GuiView::disconnectBufferView()
1143 if (d.current_work_area_)
1144 d.current_work_area_->bufferView().setGuiDelegate(0);
1148 void GuiView::errors(string const & error_type)
1150 ErrorList & el = buffer()->errorList(error_type);
1152 showDialog("errorlist", error_type);
1156 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1158 d.toc_models_.updateItem(toqstr(type), dit);
1162 void GuiView::structureChanged()
1164 d.toc_models_.reset(view());
1165 // Navigator needs more than a simple update in this case. It needs to be
1167 updateDialog("toc", "");
1171 void GuiView::updateDialog(string const & name, string const & data)
1173 if (!isDialogVisible(name))
1176 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1177 if (it == d.dialogs_.end())
1180 Dialog * const dialog = it->second.get();
1181 if (dialog->isVisibleView())
1182 dialog->initialiseParams(data);
1186 BufferView * GuiView::view()
1188 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1192 void GuiView::autoSave()
1194 LYXERR(Debug::INFO, "Running autoSave()");
1197 view()->buffer().autoSave();
1201 void GuiView::resetAutosaveTimers()
1204 d.autosave_timeout_.restart();
1208 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1211 Buffer * buf = buffer();
1213 /* In LyX/Mac, when a dialog is open, the menus of the
1214 application can still be accessed without giving focus to
1215 the main window. In this case, we want to disable the menu
1216 entries that are buffer-related.
1218 Note that this code is not perfect, as bug 1941 attests:
1219 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1221 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1224 if (cmd.origin == FuncRequest::TOC) {
1225 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1227 if (toc->getStatus(view()->cursor(), cmd, fs))
1230 flag.setEnabled(false);
1234 switch(cmd.action) {
1235 case LFUN_BUFFER_WRITE:
1236 enable = buf && (buf->isUnnamed() || !buf->isClean());
1239 case LFUN_BUFFER_WRITE_AS:
1243 case LFUN_SPLIT_VIEW:
1244 if (cmd.getArg(0) == "vertical")
1245 enable = buf && (d.splitter_->count() == 1 ||
1246 d.splitter_->orientation() == Qt::Vertical);
1248 enable = buf && (d.splitter_->count() == 1 ||
1249 d.splitter_->orientation() == Qt::Horizontal);
1252 case LFUN_CLOSE_TAB_GROUP:
1253 enable = d.currentTabWorkArea();
1256 case LFUN_TOOLBAR_TOGGLE:
1257 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1258 flag.setOnOff(t->isVisible());
1261 case LFUN_UI_TOGGLE:
1262 flag.setOnOff(isFullScreen());
1265 case LFUN_DIALOG_TOGGLE:
1266 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1267 // fall through to set "enable"
1268 case LFUN_DIALOG_SHOW: {
1269 string const name = cmd.getArg(0);
1271 enable = name == "aboutlyx"
1272 || name == "file" //FIXME: should be removed.
1274 || name == "texinfo";
1275 else if (name == "print")
1276 enable = buf->isExportable("dvi")
1277 && lyxrc.print_command != "none";
1278 else if (name == "character") {
1282 InsetCode ic = view()->cursor().inset().lyxCode();
1283 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1286 else if (name == "symbols") {
1287 if (!view() || view()->cursor().inMathed())
1290 InsetCode ic = view()->cursor().inset().lyxCode();
1291 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1294 else if (name == "latexlog")
1295 enable = FileName(buf->logName()).isReadableFile();
1296 else if (name == "spellchecker")
1297 #if defined (USE_ASPELL)
1298 enable = !buf->isReadonly();
1302 else if (name == "vclog")
1303 enable = buf->lyxvc().inUse();
1307 case LFUN_DIALOG_UPDATE: {
1308 string const name = cmd.getArg(0);
1310 enable = name == "prefs";
1314 case LFUN_INSET_APPLY: {
1315 string const name = cmd.getArg(0);
1316 Inset * inset = getOpenInset(name);
1318 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1320 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1321 // Every inset is supposed to handle this
1322 LASSERT(false, break);
1326 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1327 flag |= lyx::getStatus(fr);
1329 enable = flag.enabled();
1333 case LFUN_COMPLETION_INLINE:
1334 if (!d.current_work_area_
1335 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1339 case LFUN_COMPLETION_POPUP:
1340 if (!d.current_work_area_
1341 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1345 case LFUN_COMPLETION_COMPLETE:
1346 if (!d.current_work_area_
1347 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1351 case LFUN_COMPLETION_ACCEPT:
1352 if (!d.current_work_area_
1353 || (!d.current_work_area_->completer().popupVisible()
1354 && !d.current_work_area_->completer().inlineVisible()
1355 && !d.current_work_area_->completer().completionAvailable()))
1359 case LFUN_COMPLETION_CANCEL:
1360 if (!d.current_work_area_
1361 || (!d.current_work_area_->completer().popupVisible()
1362 && !d.current_work_area_->completer().inlineVisible()))
1366 case LFUN_BUFFER_ZOOM_OUT:
1367 enable = buf && lyxrc.zoom > 10;
1370 case LFUN_BUFFER_ZOOM_IN:
1379 flag.setEnabled(false);
1385 static FileName selectTemplateFile()
1387 FileDialog dlg(qt_("Select template file"));
1388 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1389 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1391 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1392 QStringList(qt_("LyX Documents (*.lyx)")));
1394 if (result.first == FileDialog::Later)
1396 if (result.second.isEmpty())
1398 return FileName(fromqstr(result.second));
1402 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1406 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1409 message(_("Document not loaded."));
1414 setBuffer(newBuffer);
1416 // scroll to the position when the file was last closed
1417 if (lyxrc.use_lastfilepos) {
1418 LastFilePosSection::FilePos filepos =
1419 theSession().lastFilePos().load(filename);
1420 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1424 theSession().lastFiles().add(filename);
1431 void GuiView::openDocument(string const & fname)
1433 string initpath = lyxrc.document_path;
1436 string const trypath = buffer()->filePath();
1437 // If directory is writeable, use this as default.
1438 if (FileName(trypath).isDirWritable())
1444 if (fname.empty()) {
1445 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1446 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1447 dlg.setButton2(qt_("Examples|#E#e"),
1448 toqstr(addPath(package().system_support().absFilename(), "examples")));
1450 QStringList filter(qt_("LyX Documents (*.lyx)"));
1451 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1452 << qt_("LyX-1.4.x Documents (*.lyx14)")
1453 << qt_("LyX-1.5.x Documents (*.lyx15)")
1454 << qt_("LyX-1.6.x Documents (*.lyx16)");
1455 FileDialog::Result result =
1456 dlg.open(toqstr(initpath), filter);
1458 if (result.first == FileDialog::Later)
1461 filename = fromqstr(result.second);
1463 // check selected filename
1464 if (filename.empty()) {
1465 message(_("Canceled."));
1471 // get absolute path of file and add ".lyx" to the filename if
1473 FileName const fullname =
1474 fileSearch(string(), filename, "lyx", support::may_not_exist);
1475 if (!fullname.empty())
1476 filename = fullname.absFilename();
1478 if (!fullname.onlyPath().isDirectory()) {
1479 Alert::warning(_("Invalid filename"),
1480 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1481 from_utf8(fullname.absFilename())));
1484 // if the file doesn't exist, let the user create one
1485 if (!fullname.exists()) {
1486 // the user specifically chose this name. Believe him.
1487 Buffer * const b = newFile(filename, string(), true);
1493 docstring const disp_fn = makeDisplayPath(filename);
1494 message(bformat(_("Opening document %1$s..."), disp_fn));
1497 Buffer * buf = loadDocument(fullname);
1499 buf->updateLabels();
1501 buf->errors("Parse");
1502 str2 = bformat(_("Document %1$s opened."), disp_fn);
1503 if (buf->lyxvc().inUse())
1504 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1505 " " + _("Version control detected.");
1507 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1512 // FIXME: clean that
1513 static bool import(GuiView * lv, FileName const & filename,
1514 string const & format, ErrorList & errorList)
1516 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1518 string loader_format;
1519 vector<string> loaders = theConverters().loaders();
1520 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1521 for (vector<string>::const_iterator it = loaders.begin();
1522 it != loaders.end(); ++it) {
1523 if (!theConverters().isReachable(format, *it))
1526 string const tofile =
1527 support::changeExtension(filename.absFilename(),
1528 formats.extension(*it));
1529 if (!theConverters().convert(0, filename, FileName(tofile),
1530 filename, format, *it, errorList))
1532 loader_format = *it;
1535 if (loader_format.empty()) {
1536 frontend::Alert::error(_("Couldn't import file"),
1537 bformat(_("No information for importing the format %1$s."),
1538 formats.prettyName(format)));
1542 loader_format = format;
1544 if (loader_format == "lyx") {
1545 Buffer * buf = lv->loadDocument(lyxfile);
1548 buf->updateLabels();
1550 buf->errors("Parse");
1552 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1556 bool as_paragraphs = loader_format == "textparagraph";
1557 string filename2 = (loader_format == format) ? filename.absFilename()
1558 : support::changeExtension(filename.absFilename(),
1559 formats.extension(loader_format));
1560 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1561 theLyXFunc().setLyXView(lv);
1562 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1569 void GuiView::importDocument(string const & argument)
1572 string filename = split(argument, format, ' ');
1574 LYXERR(Debug::INFO, format << " file: " << filename);
1576 // need user interaction
1577 if (filename.empty()) {
1578 string initpath = lyxrc.document_path;
1580 Buffer const * buf = buffer();
1582 string const trypath = buf->filePath();
1583 // If directory is writeable, use this as default.
1584 if (FileName(trypath).isDirWritable())
1588 docstring const text = bformat(_("Select %1$s file to import"),
1589 formats.prettyName(format));
1591 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1592 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1593 dlg.setButton2(qt_("Examples|#E#e"),
1594 toqstr(addPath(package().system_support().absFilename(), "examples")));
1596 docstring filter = formats.prettyName(format);
1599 filter += from_utf8(formats.extension(format));
1602 FileDialog::Result result =
1603 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1605 if (result.first == FileDialog::Later)
1608 filename = fromqstr(result.second);
1610 // check selected filename
1611 if (filename.empty())
1612 message(_("Canceled."));
1615 if (filename.empty())
1618 // get absolute path of file
1619 FileName const fullname(support::makeAbsPath(filename));
1621 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1623 // Check if the document already is open
1624 Buffer * buf = theBufferList().getBuffer(lyxfile);
1627 if (!closeBuffer()) {
1628 message(_("Canceled."));
1633 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1635 // if the file exists already, and we didn't do
1636 // -i lyx thefile.lyx, warn
1637 if (lyxfile.exists() && fullname != lyxfile) {
1639 docstring text = bformat(_("The document %1$s already exists.\n\n"
1640 "Do you want to overwrite that document?"), displaypath);
1641 int const ret = Alert::prompt(_("Overwrite document?"),
1642 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1645 message(_("Canceled."));
1650 message(bformat(_("Importing %1$s..."), displaypath));
1651 ErrorList errorList;
1652 if (import(this, fullname, format, errorList))
1653 message(_("imported."));
1655 message(_("file not imported!"));
1657 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1661 void GuiView::newDocument(string const & filename, bool from_template)
1663 FileName initpath(lyxrc.document_path);
1664 Buffer * buf = buffer();
1666 FileName const trypath(buf->filePath());
1667 // If directory is writeable, use this as default.
1668 if (trypath.isDirWritable())
1672 string templatefile;
1673 if (from_template) {
1674 templatefile = selectTemplateFile().absFilename();
1675 if (templatefile.empty())
1680 if (filename.empty())
1681 b = newUnnamedFile(templatefile, initpath);
1683 b = newFile(filename, templatefile, true);
1688 // If no new document could be created, it is unsure
1689 // whether there is a valid BufferView.
1691 // Ensure the cursor is correctly positioned on screen.
1692 view()->showCursor();
1696 void GuiView::insertLyXFile(docstring const & fname)
1698 BufferView * bv = view();
1703 FileName filename(to_utf8(fname));
1705 if (!filename.empty()) {
1706 bv->insertLyXFile(filename);
1710 // Launch a file browser
1712 string initpath = lyxrc.document_path;
1713 string const trypath = bv->buffer().filePath();
1714 // If directory is writeable, use this as default.
1715 if (FileName(trypath).isDirWritable())
1719 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1720 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1721 dlg.setButton2(qt_("Examples|#E#e"),
1722 toqstr(addPath(package().system_support().absFilename(),
1725 FileDialog::Result result = dlg.open(toqstr(initpath),
1726 QStringList(qt_("LyX Documents (*.lyx)")));
1728 if (result.first == FileDialog::Later)
1732 filename.set(fromqstr(result.second));
1734 // check selected filename
1735 if (filename.empty()) {
1736 // emit message signal.
1737 message(_("Canceled."));
1741 bv->insertLyXFile(filename);
1745 void GuiView::insertPlaintextFile(docstring const & fname,
1748 BufferView * bv = view();
1753 FileName filename(to_utf8(fname));
1755 if (!filename.empty()) {
1756 bv->insertPlaintextFile(filename, asParagraph);
1760 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1761 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1763 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1764 QStringList(qt_("All Files (*)")));
1766 if (result.first == FileDialog::Later)
1770 filename.set(fromqstr(result.second));
1772 // check selected filename
1773 if (filename.empty()) {
1774 // emit message signal.
1775 message(_("Canceled."));
1779 bv->insertPlaintextFile(filename, asParagraph);
1783 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1785 FileName fname = b.fileName();
1786 FileName const oldname = fname;
1788 if (!newname.empty()) {
1790 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1792 // Switch to this Buffer.
1795 // No argument? Ask user through dialog.
1797 FileDialog dlg(qt_("Choose a filename to save document as"),
1798 LFUN_BUFFER_WRITE_AS);
1799 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1800 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1802 if (!isLyXFilename(fname.absFilename()))
1803 fname.changeExtension(".lyx");
1805 FileDialog::Result result =
1806 dlg.save(toqstr(fname.onlyPath().absFilename()),
1807 QStringList(qt_("LyX Documents (*.lyx)")),
1808 toqstr(fname.onlyFileName()));
1810 if (result.first == FileDialog::Later)
1813 fname.set(fromqstr(result.second));
1818 if (!isLyXFilename(fname.absFilename()))
1819 fname.changeExtension(".lyx");
1822 if (FileName(fname).exists()) {
1823 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1824 docstring text = bformat(_("The document %1$s already "
1825 "exists.\n\nDo you want to "
1826 "overwrite that document?"),
1828 int const ret = Alert::prompt(_("Overwrite document?"),
1829 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1832 case 1: return renameBuffer(b, docstring());
1833 case 2: return false;
1837 FileName oldauto = b.getAutosaveFilename();
1839 // Ok, change the name of the buffer
1840 b.setFileName(fname.absFilename());
1842 bool unnamed = b.isUnnamed();
1843 b.setUnnamed(false);
1844 b.saveCheckSum(fname);
1846 // bring the autosave file with us, just in case.
1847 b.moveAutosaveFile(oldauto);
1849 if (!saveBuffer(b)) {
1850 oldauto = b.getAutosaveFilename();
1851 b.setFileName(oldname.absFilename());
1852 b.setUnnamed(unnamed);
1853 b.saveCheckSum(oldname);
1854 b.moveAutosaveFile(oldauto);
1862 bool GuiView::saveBuffer(Buffer & b)
1864 if (workArea(b) && workArea(b)->inDialogMode())
1868 return renameBuffer(b, docstring());
1871 theSession().lastFiles().add(b.fileName());
1875 // Switch to this Buffer.
1878 // FIXME: we don't tell the user *WHY* the save failed !!
1879 docstring const file = makeDisplayPath(b.absFileName(), 30);
1880 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1881 "Do you want to rename the document and "
1882 "try again?"), file);
1883 int const ret = Alert::prompt(_("Rename and save?"),
1884 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1887 if (!renameBuffer(b, docstring()))
1896 return saveBuffer(b);
1900 bool GuiView::closeBuffer()
1902 Buffer * buf = buffer();
1903 return buf && closeBuffer(*buf);
1907 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
1909 // goto bookmark to update bookmark pit.
1910 //FIXME: we should update only the bookmarks related to this buffer!
1911 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1912 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1913 theLyXFunc().gotoBookmark(i+1, false, false);
1915 if (buf.isClean() || buf.paragraphs().empty()) {
1916 // save in sessions if requested
1917 // do not save childs if their master
1918 // is opened as well
1920 theSession().lastOpened().add(buf.fileName(), mark_active);
1922 // Don't close child documents.
1923 removeWorkArea(currentMainWorkArea());
1925 theBufferList().release(&buf);
1928 // Switch to this Buffer.
1933 if (buf.isUnnamed())
1934 file = from_utf8(buf.fileName().onlyFileName());
1936 file = buf.fileName().displayName(30);
1938 // Bring this window to top before asking questions.
1942 docstring const text = bformat(_("The document %1$s has unsaved changes."
1943 "\n\nDo you want to save the document or discard the changes?"), file);
1944 int const ret = Alert::prompt(_("Save changed document?"),
1945 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1949 if (!saveBuffer(buf))
1953 // if we crash after this we could
1954 // have no autosave file but I guess
1955 // this is really improbable (Jug)
1956 buf.removeAutosaveFile();
1962 // save file names to .lyx/session
1964 theSession().lastOpened().add(buf.fileName(), mark_active);
1967 // Don't close child documents.
1968 removeWorkArea(currentMainWorkArea());
1970 theBufferList().release(&buf);
1976 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1978 Buffer * const curbuf = buffer();
1979 Buffer * nextbuf = curbuf;
1981 if (np == NEXTBUFFER)
1982 nextbuf = theBufferList().next(nextbuf);
1984 nextbuf = theBufferList().previous(nextbuf);
1985 if (nextbuf == curbuf)
1991 if (workArea(*nextbuf))
1998 bool GuiView::dispatch(FuncRequest const & cmd)
2000 BufferView * bv = view();
2001 // By default we won't need any update.
2003 bv->cursor().updateFlags(Update::None);
2004 bool dispatched = true;
2006 if (cmd.origin == FuncRequest::TOC) {
2007 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2008 toc->doDispatch(bv->cursor(), cmd);
2012 switch(cmd.action) {
2013 case LFUN_BUFFER_IMPORT:
2014 importDocument(to_utf8(cmd.argument()));
2017 case LFUN_BUFFER_SWITCH: {
2019 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2023 bv->cursor().message(_("Document not loaded"));
2027 case LFUN_BUFFER_NEXT:
2028 gotoNextOrPreviousBuffer(NEXTBUFFER);
2031 case LFUN_BUFFER_PREVIOUS:
2032 gotoNextOrPreviousBuffer(PREVBUFFER);
2035 case LFUN_COMMAND_EXECUTE: {
2036 bool const show_it = cmd.argument() != "off";
2037 // FIXME: this is a hack, "minibuffer" should not be
2039 if (GuiToolbar * t = toolbar("minibuffer")) {
2040 t->setVisible(show_it);
2041 if (show_it && t->commandBuffer())
2042 t->commandBuffer()->setFocus();
2046 case LFUN_DROP_LAYOUTS_CHOICE:
2048 d.layout_->showPopup();
2051 case LFUN_MENU_OPEN:
2052 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2053 menu->exec(QCursor::pos());
2056 case LFUN_FILE_INSERT:
2057 insertLyXFile(cmd.argument());
2059 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2060 insertPlaintextFile(cmd.argument(), true);
2063 case LFUN_FILE_INSERT_PLAINTEXT:
2064 insertPlaintextFile(cmd.argument(), false);
2067 case LFUN_BUFFER_WRITE:
2069 saveBuffer(bv->buffer());
2072 case LFUN_BUFFER_WRITE_AS:
2074 renameBuffer(bv->buffer(), cmd.argument());
2077 case LFUN_BUFFER_WRITE_ALL: {
2078 Buffer * first = theBufferList().first();
2081 message(_("Saving all documents..."));
2082 // We cannot use a for loop as the buffer list cycles.
2085 if (!b->isClean()) {
2087 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2089 b = theBufferList().next(b);
2090 } while (b != first);
2091 message(_("All documents saved."));
2095 case LFUN_TOOLBAR_TOGGLE: {
2096 string const name = cmd.getArg(0);
2097 if (GuiToolbar * t = toolbar(name))
2102 case LFUN_DIALOG_UPDATE: {
2103 string const name = to_utf8(cmd.argument());
2104 // Can only update a dialog connected to an existing inset
2105 Inset * inset = getOpenInset(name);
2107 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2108 inset->dispatch(view()->cursor(), fr);
2109 } else if (name == "paragraph") {
2110 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2111 } else if (name == "prefs" || name == "document") {
2112 updateDialog(name, string());
2117 case LFUN_DIALOG_TOGGLE: {
2118 if (isDialogVisible(cmd.getArg(0)))
2119 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2121 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2125 case LFUN_DIALOG_DISCONNECT_INSET:
2126 disconnectDialog(to_utf8(cmd.argument()));
2129 case LFUN_DIALOG_HIDE: {
2130 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2134 case LFUN_DIALOG_SHOW: {
2135 string const name = cmd.getArg(0);
2136 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2138 if (name == "character") {
2139 data = freefont2string();
2141 showDialog("character", data);
2142 } else if (name == "latexlog") {
2143 Buffer::LogType type;
2144 string const logfile = buffer()->logName(&type);
2146 case Buffer::latexlog:
2149 case Buffer::buildlog:
2153 data += Lexer::quoteString(logfile);
2154 showDialog("log", data);
2155 } else if (name == "vclog") {
2156 string const data = "vc " +
2157 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2158 showDialog("log", data);
2159 } else if (name == "symbols") {
2160 data = bv->cursor().getEncoding()->name();
2162 showDialog("symbols", data);
2164 } else if (name == "prefs" && isFullScreen()) {
2165 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2167 showDialog("prefs", data);
2169 showDialog(name, data);
2173 case LFUN_INSET_APPLY: {
2174 string const name = cmd.getArg(0);
2175 Inset * inset = getOpenInset(name);
2177 // put cursor in front of inset.
2178 if (!view()->setCursorFromInset(inset)) {
2179 LASSERT(false, break);
2182 // useful if we are called from a dialog.
2183 view()->cursor().beginUndoGroup();
2184 view()->cursor().recordUndo();
2185 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2186 inset->dispatch(view()->cursor(), fr);
2187 view()->cursor().endUndoGroup();
2189 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2195 case LFUN_UI_TOGGLE:
2197 // Make sure the keyboard focus stays in the work area.
2201 case LFUN_SPLIT_VIEW:
2202 if (Buffer * buf = buffer()) {
2203 string const orientation = cmd.getArg(0);
2204 d.splitter_->setOrientation(orientation == "vertical"
2205 ? Qt::Vertical : Qt::Horizontal);
2206 TabWorkArea * twa = addTabWorkArea();
2207 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2208 setCurrentWorkArea(wa);
2212 case LFUN_CLOSE_TAB_GROUP:
2213 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2215 twa = d.currentTabWorkArea();
2216 // Switch to the next GuiWorkArea in the found TabWorkArea.
2218 // Make sure the work area is up to date.
2219 setCurrentWorkArea(twa->currentWorkArea());
2221 setCurrentWorkArea(0);
2226 case LFUN_COMPLETION_INLINE:
2227 if (d.current_work_area_)
2228 d.current_work_area_->completer().showInline();
2231 case LFUN_COMPLETION_POPUP:
2232 if (d.current_work_area_)
2233 d.current_work_area_->completer().showPopup();
2237 case LFUN_COMPLETION_COMPLETE:
2238 if (d.current_work_area_)
2239 d.current_work_area_->completer().tab();
2242 case LFUN_COMPLETION_CANCEL:
2243 if (d.current_work_area_) {
2244 if (d.current_work_area_->completer().popupVisible())
2245 d.current_work_area_->completer().hidePopup();
2247 d.current_work_area_->completer().hideInline();
2251 case LFUN_COMPLETION_ACCEPT:
2252 if (d.current_work_area_)
2253 d.current_work_area_->completer().activate();
2256 case LFUN_BUFFER_ZOOM_IN:
2257 case LFUN_BUFFER_ZOOM_OUT:
2258 if (cmd.argument().empty()) {
2259 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2264 lyxrc.zoom += convert<int>(cmd.argument());
2266 if (lyxrc.zoom < 10)
2269 // The global QPixmapCache is used in GuiPainter to cache text
2270 // painting so we must reset it.
2271 QPixmapCache::clear();
2272 guiApp->fontLoader().update();
2273 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2281 // Part of automatic menu appearance feature.
2282 if (isFullScreen()) {
2283 if (menuBar()->isVisible())
2285 if (statusBar()->isVisible())
2286 statusBar()->hide();
2293 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2295 string const arg = cmd.getArg(0);
2296 if (arg == "scrollbar") {
2297 // hide() is of no help
2298 if (d.current_work_area_->verticalScrollBarPolicy() ==
2299 Qt::ScrollBarAlwaysOff)
2301 d.current_work_area_->setVerticalScrollBarPolicy(
2302 Qt::ScrollBarAsNeeded);
2304 d.current_work_area_->setVerticalScrollBarPolicy(
2305 Qt::ScrollBarAlwaysOff);
2308 if (arg == "statusbar") {
2309 statusBar()->setVisible(!statusBar()->isVisible());
2312 if (arg == "menubar") {
2313 menuBar()->setVisible(!menuBar()->isVisible());
2316 #if QT_VERSION >= 0x040300
2317 if (arg == "frame") {
2319 getContentsMargins(&l, &t, &r, &b);
2320 //are the frames in default state?
2321 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2323 setContentsMargins(-2, -2, -2, -2);
2325 setContentsMargins(0, 0, 0, 0);
2330 if (arg == "fullscreen") {
2335 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2339 void GuiView::toggleFullScreen()
2341 if (isFullScreen()) {
2342 for (int i = 0; i != d.splitter_->count(); ++i)
2343 d.tabWorkArea(i)->setFullScreen(false);
2344 #if QT_VERSION >= 0x040300
2345 setContentsMargins(0, 0, 0, 0);
2347 setWindowState(windowState() ^ Qt::WindowFullScreen);
2350 statusBar()->show();
2353 hideDialogs("prefs", 0);
2354 for (int i = 0; i != d.splitter_->count(); ++i)
2355 d.tabWorkArea(i)->setFullScreen(true);
2356 #if QT_VERSION >= 0x040300
2357 setContentsMargins(-2, -2, -2, -2);
2360 setWindowState(windowState() ^ Qt::WindowFullScreen);
2361 statusBar()->hide();
2363 if (lyxrc.full_screen_toolbars) {
2364 ToolbarMap::iterator end = d.toolbars_.end();
2365 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2370 // give dialogs like the TOC a chance to adapt
2375 Buffer const * GuiView::updateInset(Inset const * inset)
2377 if (!d.current_work_area_)
2381 d.current_work_area_->scheduleRedraw();
2383 return &d.current_work_area_->bufferView().buffer();
2387 void GuiView::restartCursor()
2389 /* When we move around, or type, it's nice to be able to see
2390 * the cursor immediately after the keypress.
2392 if (d.current_work_area_)
2393 d.current_work_area_->startBlinkingCursor();
2395 // Take this occasion to update the other GUI elements.
2401 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2403 if (d.current_work_area_)
2404 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2409 // This list should be kept in sync with the list of insets in
2410 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2411 // dialog should have the same name as the inset.
2412 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2413 // docs in LyXAction.cpp.
2415 char const * const dialognames[] = {
2416 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2417 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2418 "float", "graphics", "include", "index", "index_print", "info", "nomenclature",
2419 "label", "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2420 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2421 "symbols", "tabular", "tabularcreate", "thesaurus", "texinfo", "toc", "href",
2422 "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2424 char const * const * const end_dialognames =
2425 dialognames + (sizeof(dialognames) / sizeof(char *));
2429 cmpCStr(char const * name) : name_(name) {}
2430 bool operator()(char const * other) {
2431 return strcmp(other, name_) == 0;
2438 bool isValidName(string const & name)
2440 return find_if(dialognames, end_dialognames,
2441 cmpCStr(name.c_str())) != end_dialognames;
2447 void GuiView::resetDialogs()
2449 // Make sure that no LFUN uses any LyXView.
2450 theLyXFunc().setLyXView(0);
2453 constructToolbars();
2454 guiApp->menus().fillMenuBar(menuBar(), this, false);
2456 d.layout_->updateContents(true);
2457 // Now update controls with current buffer.
2458 theLyXFunc().setLyXView(this);
2464 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2466 if (!isValidName(name))
2469 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2471 if (it != d.dialogs_.end()) {
2473 it->second->hideView();
2474 return it->second.get();
2477 Dialog * dialog = build(name);
2478 d.dialogs_[name].reset(dialog);
2479 if (lyxrc.allow_geometry_session)
2480 dialog->restoreSession();
2487 void GuiView::showDialog(string const & name, string const & data,
2495 Dialog * dialog = findOrBuild(name, false);
2497 dialog->showData(data);
2499 d.open_insets_[name] = inset;
2502 catch (ExceptionMessage const & ex) {
2510 bool GuiView::isDialogVisible(string const & name) const
2512 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2513 if (it == d.dialogs_.end())
2515 return it->second.get()->isVisibleView();
2519 void GuiView::hideDialog(string const & name, Inset * inset)
2521 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2522 if (it == d.dialogs_.end())
2525 if (inset && inset != getOpenInset(name))
2528 Dialog * const dialog = it->second.get();
2529 if (dialog->isVisibleView())
2531 d.open_insets_[name] = 0;
2535 void GuiView::disconnectDialog(string const & name)
2537 if (!isValidName(name))
2540 if (d.open_insets_.find(name) != d.open_insets_.end())
2541 d.open_insets_[name] = 0;
2545 Inset * GuiView::getOpenInset(string const & name) const
2547 if (!isValidName(name))
2550 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2551 return it == d.open_insets_.end() ? 0 : it->second;
2555 void GuiView::hideAll() const
2557 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2558 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2560 for(; it != end; ++it)
2561 it->second->hideView();
2565 void GuiView::updateDialogs()
2567 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2568 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2570 for(; it != end; ++it) {
2571 Dialog * dialog = it->second.get();
2572 if (dialog && dialog->isVisibleView())
2573 dialog->checkStatus();
2580 // will be replaced by a proper factory...
2581 Dialog * createGuiAbout(GuiView & lv);
2582 Dialog * createGuiBibitem(GuiView & lv);
2583 Dialog * createGuiBibtex(GuiView & lv);
2584 Dialog * createGuiBox(GuiView & lv);
2585 Dialog * createGuiBranch(GuiView & lv);
2586 Dialog * createGuiChanges(GuiView & lv);
2587 Dialog * createGuiCharacter(GuiView & lv);
2588 Dialog * createGuiCitation(GuiView & lv);
2589 Dialog * createGuiDelimiter(GuiView & lv);
2590 Dialog * createGuiDocument(GuiView & lv);
2591 Dialog * createGuiErrorList(GuiView & lv);
2592 Dialog * createGuiERT(GuiView & lv);
2593 Dialog * createGuiExternal(GuiView & lv);
2594 Dialog * createGuiFloat(GuiView & lv);
2595 Dialog * createGuiGraphics(GuiView & lv);
2596 Dialog * createGuiInclude(GuiView & lv);
2597 Dialog * createGuiIndex(GuiView & lv);
2598 Dialog * createGuiInfo(GuiView & lv);
2599 Dialog * createGuiLabel(GuiView & lv);
2600 Dialog * createGuiListings(GuiView & lv);
2601 Dialog * createGuiLog(GuiView & lv);
2602 Dialog * createGuiMathHSpace(GuiView & lv);
2603 Dialog * createGuiMathMatrix(GuiView & lv);
2604 Dialog * createGuiNomenclature(GuiView & lv);
2605 Dialog * createGuiNote(GuiView & lv);
2606 Dialog * createGuiParagraph(GuiView & lv);
2607 Dialog * createGuiPhantom(GuiView & lv);
2608 Dialog * createGuiPreferences(GuiView & lv);
2609 Dialog * createGuiPrint(GuiView & lv);
2610 Dialog * createGuiPrintindex(GuiView & lv);
2611 Dialog * createGuiRef(GuiView & lv);
2612 Dialog * createGuiSearch(GuiView & lv);
2613 Dialog * createGuiSearchAdv(GuiView & lv);
2614 Dialog * createGuiSendTo(GuiView & lv);
2615 Dialog * createGuiShowFile(GuiView & lv);
2616 Dialog * createGuiSpellchecker(GuiView & lv);
2617 Dialog * createGuiSymbols(GuiView & lv);
2618 Dialog * createGuiTabularCreate(GuiView & lv);
2619 Dialog * createGuiTabular(GuiView & lv);
2620 Dialog * createGuiTexInfo(GuiView & lv);
2621 Dialog * createGuiTextHSpace(GuiView & lv);
2622 Dialog * createGuiToc(GuiView & lv);
2623 Dialog * createGuiThesaurus(GuiView & lv);
2624 Dialog * createGuiHyperlink(GuiView & lv);
2625 Dialog * createGuiVSpace(GuiView & lv);
2626 Dialog * createGuiViewSource(GuiView & lv);
2627 Dialog * createGuiWrap(GuiView & lv);
2630 Dialog * GuiView::build(string const & name)
2632 LASSERT(isValidName(name), return 0);
2634 if (name == "aboutlyx")
2635 return createGuiAbout(*this);
2636 if (name == "bibitem")
2637 return createGuiBibitem(*this);
2638 if (name == "bibtex")
2639 return createGuiBibtex(*this);
2641 return createGuiBox(*this);
2642 if (name == "branch")
2643 return createGuiBranch(*this);
2644 if (name == "changes")
2645 return createGuiChanges(*this);
2646 if (name == "character")
2647 return createGuiCharacter(*this);
2648 if (name == "citation")
2649 return createGuiCitation(*this);
2650 if (name == "document")
2651 return createGuiDocument(*this);
2652 if (name == "errorlist")
2653 return createGuiErrorList(*this);
2655 return createGuiERT(*this);
2656 if (name == "external")
2657 return createGuiExternal(*this);
2659 return createGuiShowFile(*this);
2660 if (name == "findreplace")
2661 return createGuiSearch(*this);
2662 if (name == "findreplaceadv")
2663 return createGuiSearchAdv(*this);
2664 if (name == "float")
2665 return createGuiFloat(*this);
2666 if (name == "graphics")
2667 return createGuiGraphics(*this);
2668 if (name == "include")
2669 return createGuiInclude(*this);
2670 if (name == "index")
2671 return createGuiIndex(*this);
2673 return createGuiInfo(*this);
2674 if (name == "nomenclature")
2675 return createGuiNomenclature(*this);
2676 if (name == "label")
2677 return createGuiLabel(*this);
2679 return createGuiLog(*this);
2680 if (name == "mathdelimiter")
2681 return createGuiDelimiter(*this);
2682 if (name == "mathspace")
2683 return createGuiMathHSpace(*this);
2684 if (name == "mathmatrix")
2685 return createGuiMathMatrix(*this);
2687 return createGuiNote(*this);
2688 if (name == "paragraph")
2689 return createGuiParagraph(*this);
2690 if (name == "phantom")
2691 return createGuiPhantom(*this);
2692 if (name == "prefs")
2693 return createGuiPreferences(*this);
2694 if (name == "print")
2695 return createGuiPrint(*this);
2697 return createGuiRef(*this);
2698 if (name == "sendto")
2699 return createGuiSendTo(*this);
2700 if (name == "space")
2701 return createGuiTextHSpace(*this);
2702 if (name == "spellchecker")
2703 return createGuiSpellchecker(*this);
2704 if (name == "symbols")
2705 return createGuiSymbols(*this);
2706 if (name == "tabular")
2707 return createGuiTabular(*this);
2708 if (name == "tabularcreate")
2709 return createGuiTabularCreate(*this);
2710 if (name == "texinfo")
2711 return createGuiTexInfo(*this);
2712 if (name == "view-source")
2713 return createGuiViewSource(*this);
2714 if (name == "thesaurus")
2715 return createGuiThesaurus(*this);
2717 return createGuiHyperlink(*this);
2718 if (name == "index_print")
2719 return createGuiPrintindex(*this);
2720 if (name == "listings")
2721 return createGuiListings(*this);
2723 return createGuiToc(*this);
2724 if (name == "vspace")
2725 return createGuiVSpace(*this);
2727 return createGuiWrap(*this);
2733 } // namespace frontend
2736 #include "moc_GuiView.cpp"