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 int splitter_count = d.splitter_->count();
540 for (; splitter_count; --splitter_count) {
541 TabWorkArea * twa = d.tabWorkArea(0);
543 int twa_count = twa->count();
544 for (; twa_count; --twa_count) {
545 twa->setCurrentIndex(twa_count-1);
547 GuiWorkArea * wa = twa->currentWorkArea();
548 bool const is_active_wa = active_wa == wa;
549 Buffer * b = &wa->bufferView().buffer();
551 // This is a child document, just close the tab
552 // after saving but keep the file loaded.
553 if (!closeBuffer(*b, true, is_active_wa)) {
555 close_event->ignore();
561 vector<Buffer *> clist = b->getChildren();
562 for (vector<Buffer *>::const_iterator it = clist.begin();
563 it != clist.end(); ++it) {
564 if ((*it)->isClean())
567 // If a child is dirty, do not close
568 // without user intervention
569 if (!closeBuffer(*c, false)) {
571 close_event->ignore();
576 QList<int> const ids = guiApp->viewIds();
577 for (int i = 0; i != ids.size(); ++i) {
580 if (guiApp->view(ids[i]).workArea(*b)) {
581 // FIXME 1: should we put an alert box here
582 // that the buffer is viewed elsewhere?
583 // FIXME 2: should we try to save this buffer in any case?
586 // This buffer is also opened in another view, so
587 // close the associated work area...
589 // ... but don't close the buffer.
594 // closeBuffer() needs buffer workArea still alive and
595 // set as currrent one, and destroys it
596 if (b && !closeBuffer(*b, true, is_active_wa)) {
598 close_event->ignore();
603 // Make sure that nothing will use this close to be closed View.
604 guiApp->unregisterView(this);
606 if (isFullScreen()) {
607 // Switch off fullscreen before closing.
612 // Make sure the timer time out will not trigger a statusbar update.
613 d.statusbar_timer_.stop();
615 // Saving fullscreen requires additional tweaks in the toolbar code.
616 // It wouldn't also work under linux natively.
617 if (lyxrc.allow_geometry_session) {
618 // Save this window geometry and layout.
620 // Then the toolbar private states.
621 ToolbarMap::iterator end = d.toolbars_.end();
622 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
623 it->second->saveSession();
624 // Now take care of all other dialogs:
625 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
626 for (; it!= d.dialogs_.end(); ++it)
627 it->second->saveSession();
630 close_event->accept();
634 void GuiView::dragEnterEvent(QDragEnterEvent * event)
636 if (event->mimeData()->hasUrls())
638 /// \todo Ask lyx-devel is this is enough:
639 /// if (event->mimeData()->hasFormat("text/plain"))
640 /// event->acceptProposedAction();
644 void GuiView::dropEvent(QDropEvent * event)
646 QList<QUrl> files = event->mimeData()->urls();
650 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
651 for (int i = 0; i != files.size(); ++i) {
652 string const file = os::internal_path(fromqstr(
653 files.at(i).toLocalFile()));
655 // Asynchronously post the event. DropEvent usually come
656 // from the BufferView. But reloading a file might close
657 // the BufferView from within its own event handler.
658 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
665 void GuiView::message(docstring const & str)
667 if (ForkedProcess::iAmAChild())
670 statusBar()->showMessage(toqstr(str));
671 d.statusbar_timer_.stop();
672 d.statusbar_timer_.start(3000);
676 void GuiView::smallSizedIcons()
678 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
682 void GuiView::normalSizedIcons()
684 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
688 void GuiView::bigSizedIcons()
690 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
694 void GuiView::clearMessage()
698 theLyXFunc().setLyXView(this);
699 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
700 d.statusbar_timer_.stop();
704 void GuiView::updateWindowTitle(GuiWorkArea * wa)
706 if (wa != d.current_work_area_)
708 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
709 setWindowIconText(wa->windowIconText());
713 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
716 disconnectBufferView();
717 connectBufferView(wa->bufferView());
718 connectBuffer(wa->bufferView().buffer());
719 d.current_work_area_ = wa;
720 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
721 this, SLOT(updateWindowTitle(GuiWorkArea *)));
722 updateWindowTitle(wa);
726 // The document settings needs to be reinitialised.
727 updateDialog("document", "");
729 // Buffer-dependent dialogs must be updated. This is done here because
730 // some dialogs require buffer()->text.
735 void GuiView::on_lastWorkAreaRemoved()
738 // We already are in a close event. Nothing more to do.
741 if (d.splitter_->count() > 1)
742 // We have a splitter so don't close anything.
745 // Reset and updates the dialogs.
746 d.toc_models_.reset(0);
747 updateDialog("document", "");
750 resetWindowTitleAndIconText();
752 if (lyxrc.open_buffers_in_tabs)
753 // Nothing more to do, the window should stay open.
756 if (guiApp->viewIds().size() > 1) {
762 // On Mac we also close the last window because the application stay
763 // resident in memory. On other platforms we don't close the last
764 // window because this would quit the application.
770 void GuiView::updateStatusBar()
772 // let the user see the explicit message
773 if (d.statusbar_timer_.isActive())
776 theLyXFunc().setLyXView(this);
777 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
781 bool GuiView::hasFocus() const
783 return qApp->activeWindow() == this;
787 bool GuiView::event(QEvent * e)
791 // Useful debug code:
792 //case QEvent::ActivationChange:
793 //case QEvent::WindowDeactivate:
794 //case QEvent::Paint:
795 //case QEvent::Enter:
796 //case QEvent::Leave:
797 //case QEvent::HoverEnter:
798 //case QEvent::HoverLeave:
799 //case QEvent::HoverMove:
800 //case QEvent::StatusTip:
801 //case QEvent::DragEnter:
802 //case QEvent::DragLeave:
806 case QEvent::WindowActivate: {
807 if (this == guiApp->currentView()) {
809 return QMainWindow::event(e);
811 guiApp->setCurrentView(this);
812 theLyXFunc().setLyXView(this);
813 if (d.current_work_area_) {
814 BufferView & bv = d.current_work_area_->bufferView();
815 connectBufferView(bv);
816 connectBuffer(bv.buffer());
817 // The document structure, name and dialogs might have
818 // changed in another view.
820 // The document settings needs to be reinitialised.
821 updateDialog("document", "");
824 resetWindowTitleAndIconText();
827 return QMainWindow::event(e);
830 case QEvent::ShortcutOverride: {
833 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
834 if (isFullScreen() && menuBar()->isHidden()) {
835 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
836 // FIXME: we should also try to detect special LyX shortcut such as
837 // Alt-P and Alt-M. Right now there is a hack in
838 // GuiWorkArea::processKeySym() that hides again the menubar for
840 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
842 return QMainWindow::event(e);
847 if (d.current_work_area_)
848 // Nothing special to do.
849 return QMainWindow::event(e);
851 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
852 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
854 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
855 || ke->key() == Qt::Key_Backtab)
856 return QMainWindow::event(e);
858 // Allow processing of shortcuts that are allowed even when no Buffer
860 theLyXFunc().setLyXView(this);
862 setKeySymbol(&sym, ke);
863 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
869 return QMainWindow::event(e);
873 void GuiView::resetWindowTitleAndIconText()
875 setWindowTitle(qt_("LyX"));
876 setWindowIconText(qt_("LyX"));
879 bool GuiView::focusNextPrevChild(bool /*next*/)
886 void GuiView::setBusy(bool busy)
888 if (d.current_work_area_) {
889 d.current_work_area_->setUpdatesEnabled(!busy);
891 d.current_work_area_->stopBlinkingCursor();
893 d.current_work_area_->startBlinkingCursor();
897 QApplication::setOverrideCursor(Qt::WaitCursor);
899 QApplication::restoreOverrideCursor();
903 GuiWorkArea * GuiView::workArea(Buffer & buffer)
905 if (currentWorkArea()
906 && ¤tWorkArea()->bufferView().buffer() == &buffer)
907 return (GuiWorkArea *) currentWorkArea();
908 if (TabWorkArea * twa = d.currentTabWorkArea())
909 return twa->workArea(buffer);
914 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
916 // Automatically create a TabWorkArea if there are none yet.
917 TabWorkArea * tab_widget = d.splitter_->count()
918 ? d.currentTabWorkArea() : addTabWorkArea();
919 return tab_widget->addWorkArea(buffer, *this);
923 TabWorkArea * GuiView::addTabWorkArea()
925 TabWorkArea * twa = new TabWorkArea;
926 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
927 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
928 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
929 this, SLOT(on_lastWorkAreaRemoved()));
931 d.splitter_->addWidget(twa);
932 d.stack_widget_->setCurrentWidget(d.splitter_);
937 GuiWorkArea const * GuiView::currentWorkArea() const
939 return d.current_work_area_;
943 GuiWorkArea * GuiView::currentWorkArea()
945 return d.current_work_area_;
949 GuiWorkArea const * GuiView::currentMainWorkArea() const
951 if (d.currentTabWorkArea() == NULL)
953 return d.currentTabWorkArea()->currentWorkArea();
957 GuiWorkArea * GuiView::currentMainWorkArea()
959 if (d.currentTabWorkArea() == NULL)
961 return d.currentTabWorkArea()->currentWorkArea();
965 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
967 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
969 d.current_work_area_ = NULL;
973 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
977 theGuiApp()->setCurrentView(this);
978 d.current_work_area_ = wa;
979 for (int i = 0; i != d.splitter_->count(); ++i) {
980 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
981 //if (d.current_main_work_area_)
982 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
983 d.current_main_work_area_ = wa;
984 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
985 //d.current_main_work_area_->setLineWidth(2);
986 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
990 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
991 on_currentWorkAreaChanged(wa);
992 BufferView & bv = wa->bufferView();
993 bv.cursor().fixIfBroken();
995 wa->setUpdatesEnabled(true);
996 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1000 void GuiView::removeWorkArea(GuiWorkArea * wa)
1002 LASSERT(wa, return);
1003 if (wa == d.current_work_area_) {
1005 disconnectBufferView();
1006 d.current_work_area_ = 0;
1007 d.current_main_work_area_ = 0;
1010 bool found_twa = false;
1011 for (int i = 0; i != d.splitter_->count(); ++i) {
1012 TabWorkArea * twa = d.tabWorkArea(i);
1013 if (twa->removeWorkArea(wa)) {
1014 // Found in this tab group, and deleted the GuiWorkArea.
1016 if (twa->count() != 0) {
1017 if (d.current_work_area_ == 0)
1018 // This means that we are closing the current GuiWorkArea, so
1019 // switch to the next GuiWorkArea in the found TabWorkArea.
1020 setCurrentWorkArea(twa->currentWorkArea());
1022 // No more WorkAreas in this tab group, so delete it.
1029 // It is not a tabbed work area (i.e., the search work area), so it
1030 // should be deleted by other means.
1031 LASSERT(found_twa, /* */);
1033 if (d.current_work_area_ == 0) {
1034 if (d.splitter_->count() != 0) {
1035 TabWorkArea * twa = d.currentTabWorkArea();
1036 setCurrentWorkArea(twa->currentWorkArea());
1038 // No more work areas, switch to the background widget.
1039 setCurrentWorkArea(0);
1045 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1051 void GuiView::updateLayoutList()
1054 d.layout_->updateContents(false);
1058 void GuiView::updateToolbars()
1060 ToolbarMap::iterator end = d.toolbars_.end();
1061 if (d.current_work_area_) {
1063 d.current_work_area_->bufferView().cursor().inMathed();
1065 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1067 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1068 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1069 bool const mathmacrotemplate =
1070 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1072 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1073 it->second->update(math, table, review, mathmacrotemplate);
1075 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1076 it->second->update(false, false, false, false);
1080 Buffer * GuiView::buffer()
1082 if (d.current_work_area_)
1083 return &d.current_work_area_->bufferView().buffer();
1088 Buffer const * GuiView::buffer() const
1090 if (d.current_work_area_)
1091 return &d.current_work_area_->bufferView().buffer();
1096 void GuiView::setBuffer(Buffer * newBuffer)
1098 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1099 LASSERT(newBuffer, return);
1102 GuiWorkArea * wa = workArea(*newBuffer);
1104 newBuffer->masterBuffer()->updateLabels();
1105 wa = addWorkArea(*newBuffer);
1107 //Disconnect the old buffer...there's no new one.
1110 connectBuffer(*newBuffer);
1111 connectBufferView(wa->bufferView());
1112 setCurrentWorkArea(wa);
1118 void GuiView::connectBuffer(Buffer & buf)
1120 buf.setGuiDelegate(this);
1124 void GuiView::disconnectBuffer()
1126 if (d.current_work_area_)
1127 d.current_work_area_->bufferView().setGuiDelegate(0);
1131 void GuiView::connectBufferView(BufferView & bv)
1133 bv.setGuiDelegate(this);
1137 void GuiView::disconnectBufferView()
1139 if (d.current_work_area_)
1140 d.current_work_area_->bufferView().setGuiDelegate(0);
1144 void GuiView::errors(string const & error_type)
1146 ErrorList & el = buffer()->errorList(error_type);
1148 showDialog("errorlist", error_type);
1152 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1154 d.toc_models_.updateItem(toqstr(type), dit);
1158 void GuiView::structureChanged()
1160 d.toc_models_.reset(view());
1161 // Navigator needs more than a simple update in this case. It needs to be
1163 updateDialog("toc", "");
1167 void GuiView::updateDialog(string const & name, string const & data)
1169 if (!isDialogVisible(name))
1172 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1173 if (it == d.dialogs_.end())
1176 Dialog * const dialog = it->second.get();
1177 if (dialog->isVisibleView())
1178 dialog->initialiseParams(data);
1182 BufferView * GuiView::view()
1184 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1188 void GuiView::autoSave()
1190 LYXERR(Debug::INFO, "Running autoSave()");
1193 view()->buffer().autoSave();
1197 void GuiView::resetAutosaveTimers()
1200 d.autosave_timeout_.restart();
1204 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1207 Buffer * buf = buffer();
1209 /* In LyX/Mac, when a dialog is open, the menus of the
1210 application can still be accessed without giving focus to
1211 the main window. In this case, we want to disable the menu
1212 entries that are buffer-related.
1214 Note that this code is not perfect, as bug 1941 attests:
1215 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1217 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1220 if (cmd.origin == FuncRequest::TOC) {
1221 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1223 if (toc->getStatus(view()->cursor(), cmd, fs))
1226 flag.setEnabled(false);
1230 switch(cmd.action) {
1231 case LFUN_BUFFER_WRITE:
1232 enable = buf && (buf->isUnnamed() || !buf->isClean());
1235 case LFUN_BUFFER_WRITE_AS:
1239 case LFUN_SPLIT_VIEW:
1240 if (cmd.getArg(0) == "vertical")
1241 enable = buf && (d.splitter_->count() == 1 ||
1242 d.splitter_->orientation() == Qt::Vertical);
1244 enable = buf && (d.splitter_->count() == 1 ||
1245 d.splitter_->orientation() == Qt::Horizontal);
1248 case LFUN_CLOSE_TAB_GROUP:
1249 enable = d.currentTabWorkArea();
1252 case LFUN_TOOLBAR_TOGGLE:
1253 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1254 flag.setOnOff(t->isVisible());
1257 case LFUN_UI_TOGGLE:
1258 flag.setOnOff(isFullScreen());
1261 case LFUN_DIALOG_TOGGLE:
1262 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1263 // fall through to set "enable"
1264 case LFUN_DIALOG_SHOW: {
1265 string const name = cmd.getArg(0);
1267 enable = name == "aboutlyx"
1268 || name == "file" //FIXME: should be removed.
1270 || name == "texinfo";
1271 else if (name == "print")
1272 enable = buf->isExportable("dvi")
1273 && lyxrc.print_command != "none";
1274 else if (name == "character") {
1278 InsetCode ic = view()->cursor().inset().lyxCode();
1279 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1282 else if (name == "symbols") {
1283 if (!view() || view()->cursor().inMathed())
1286 InsetCode ic = view()->cursor().inset().lyxCode();
1287 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1290 else if (name == "latexlog")
1291 enable = FileName(buf->logName()).isReadableFile();
1292 else if (name == "spellchecker")
1293 #if defined (USE_ASPELL)
1294 enable = !buf->isReadonly();
1298 else if (name == "vclog")
1299 enable = buf->lyxvc().inUse();
1303 case LFUN_DIALOG_UPDATE: {
1304 string const name = cmd.getArg(0);
1306 enable = name == "prefs";
1310 case LFUN_INSET_APPLY: {
1311 string const name = cmd.getArg(0);
1312 Inset * inset = getOpenInset(name);
1314 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1316 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1317 // Every inset is supposed to handle this
1318 LASSERT(false, break);
1322 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1323 flag |= lyx::getStatus(fr);
1325 enable = flag.enabled();
1329 case LFUN_COMPLETION_INLINE:
1330 if (!d.current_work_area_
1331 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1335 case LFUN_COMPLETION_POPUP:
1336 if (!d.current_work_area_
1337 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1341 case LFUN_COMPLETION_COMPLETE:
1342 if (!d.current_work_area_
1343 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1347 case LFUN_COMPLETION_ACCEPT:
1348 if (!d.current_work_area_
1349 || (!d.current_work_area_->completer().popupVisible()
1350 && !d.current_work_area_->completer().inlineVisible()
1351 && !d.current_work_area_->completer().completionAvailable()))
1355 case LFUN_COMPLETION_CANCEL:
1356 if (!d.current_work_area_
1357 || (!d.current_work_area_->completer().popupVisible()
1358 && !d.current_work_area_->completer().inlineVisible()))
1362 case LFUN_BUFFER_ZOOM_OUT:
1363 enable = buf && lyxrc.zoom > 10;
1366 case LFUN_BUFFER_ZOOM_IN:
1375 flag.setEnabled(false);
1381 static FileName selectTemplateFile()
1383 FileDialog dlg(qt_("Select template file"));
1384 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1385 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1387 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1388 QStringList(qt_("LyX Documents (*.lyx)")));
1390 if (result.first == FileDialog::Later)
1392 if (result.second.isEmpty())
1394 return FileName(fromqstr(result.second));
1398 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1402 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1405 message(_("Document not loaded."));
1410 setBuffer(newBuffer);
1412 // scroll to the position when the file was last closed
1413 if (lyxrc.use_lastfilepos) {
1414 LastFilePosSection::FilePos filepos =
1415 theSession().lastFilePos().load(filename);
1416 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1420 theSession().lastFiles().add(filename);
1427 void GuiView::openDocument(string const & fname)
1429 string initpath = lyxrc.document_path;
1432 string const trypath = buffer()->filePath();
1433 // If directory is writeable, use this as default.
1434 if (FileName(trypath).isDirWritable())
1440 if (fname.empty()) {
1441 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1442 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1443 dlg.setButton2(qt_("Examples|#E#e"),
1444 toqstr(addPath(package().system_support().absFilename(), "examples")));
1446 QStringList filter(qt_("LyX Documents (*.lyx)"));
1447 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1448 << qt_("LyX-1.4.x Documents (*.lyx14)")
1449 << qt_("LyX-1.5.x Documents (*.lyx15)")
1450 << qt_("LyX-1.6.x Documents (*.lyx16)");
1451 FileDialog::Result result =
1452 dlg.open(toqstr(initpath), filter);
1454 if (result.first == FileDialog::Later)
1457 filename = fromqstr(result.second);
1459 // check selected filename
1460 if (filename.empty()) {
1461 message(_("Canceled."));
1467 // get absolute path of file and add ".lyx" to the filename if
1469 FileName const fullname =
1470 fileSearch(string(), filename, "lyx", support::may_not_exist);
1471 if (!fullname.empty())
1472 filename = fullname.absFilename();
1474 if (!fullname.onlyPath().isDirectory()) {
1475 Alert::warning(_("Invalid filename"),
1476 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1477 from_utf8(fullname.absFilename())));
1480 // if the file doesn't exist, let the user create one
1481 if (!fullname.exists()) {
1482 // the user specifically chose this name. Believe him.
1483 Buffer * const b = newFile(filename, string(), true);
1489 docstring const disp_fn = makeDisplayPath(filename);
1490 message(bformat(_("Opening document %1$s..."), disp_fn));
1493 Buffer * buf = loadDocument(fullname);
1495 buf->updateLabels();
1497 buf->errors("Parse");
1498 str2 = bformat(_("Document %1$s opened."), disp_fn);
1499 if (buf->lyxvc().inUse())
1500 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1501 " " + _("Version control detected.");
1503 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1508 // FIXME: clean that
1509 static bool import(GuiView * lv, FileName const & filename,
1510 string const & format, ErrorList & errorList)
1512 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1514 string loader_format;
1515 vector<string> loaders = theConverters().loaders();
1516 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1517 for (vector<string>::const_iterator it = loaders.begin();
1518 it != loaders.end(); ++it) {
1519 if (!theConverters().isReachable(format, *it))
1522 string const tofile =
1523 support::changeExtension(filename.absFilename(),
1524 formats.extension(*it));
1525 if (!theConverters().convert(0, filename, FileName(tofile),
1526 filename, format, *it, errorList))
1528 loader_format = *it;
1531 if (loader_format.empty()) {
1532 frontend::Alert::error(_("Couldn't import file"),
1533 bformat(_("No information for importing the format %1$s."),
1534 formats.prettyName(format)));
1538 loader_format = format;
1540 if (loader_format == "lyx") {
1541 Buffer * buf = lv->loadDocument(lyxfile);
1544 buf->updateLabels();
1546 buf->errors("Parse");
1548 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1552 bool as_paragraphs = loader_format == "textparagraph";
1553 string filename2 = (loader_format == format) ? filename.absFilename()
1554 : support::changeExtension(filename.absFilename(),
1555 formats.extension(loader_format));
1556 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1557 theLyXFunc().setLyXView(lv);
1558 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1565 void GuiView::importDocument(string const & argument)
1568 string filename = split(argument, format, ' ');
1570 LYXERR(Debug::INFO, format << " file: " << filename);
1572 // need user interaction
1573 if (filename.empty()) {
1574 string initpath = lyxrc.document_path;
1576 Buffer const * buf = buffer();
1578 string const trypath = buf->filePath();
1579 // If directory is writeable, use this as default.
1580 if (FileName(trypath).isDirWritable())
1584 docstring const text = bformat(_("Select %1$s file to import"),
1585 formats.prettyName(format));
1587 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1588 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1589 dlg.setButton2(qt_("Examples|#E#e"),
1590 toqstr(addPath(package().system_support().absFilename(), "examples")));
1592 docstring filter = formats.prettyName(format);
1595 filter += from_utf8(formats.extension(format));
1598 FileDialog::Result result =
1599 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1601 if (result.first == FileDialog::Later)
1604 filename = fromqstr(result.second);
1606 // check selected filename
1607 if (filename.empty())
1608 message(_("Canceled."));
1611 if (filename.empty())
1614 // get absolute path of file
1615 FileName const fullname(support::makeAbsPath(filename));
1617 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1619 // Check if the document already is open
1620 Buffer * buf = theBufferList().getBuffer(lyxfile);
1623 if (!closeBuffer()) {
1624 message(_("Canceled."));
1629 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1631 // if the file exists already, and we didn't do
1632 // -i lyx thefile.lyx, warn
1633 if (lyxfile.exists() && fullname != lyxfile) {
1635 docstring text = bformat(_("The document %1$s already exists.\n\n"
1636 "Do you want to overwrite that document?"), displaypath);
1637 int const ret = Alert::prompt(_("Overwrite document?"),
1638 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1641 message(_("Canceled."));
1646 message(bformat(_("Importing %1$s..."), displaypath));
1647 ErrorList errorList;
1648 if (import(this, fullname, format, errorList))
1649 message(_("imported."));
1651 message(_("file not imported!"));
1653 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1657 void GuiView::newDocument(string const & filename, bool from_template)
1659 FileName initpath(lyxrc.document_path);
1660 Buffer * buf = buffer();
1662 FileName const trypath(buf->filePath());
1663 // If directory is writeable, use this as default.
1664 if (trypath.isDirWritable())
1668 string templatefile;
1669 if (from_template) {
1670 templatefile = selectTemplateFile().absFilename();
1671 if (templatefile.empty())
1676 if (filename.empty())
1677 b = newUnnamedFile(templatefile, initpath);
1679 b = newFile(filename, templatefile, true);
1684 // If no new document could be created, it is unsure
1685 // whether there is a valid BufferView.
1687 // Ensure the cursor is correctly positioned on screen.
1688 view()->showCursor();
1692 void GuiView::insertLyXFile(docstring const & fname)
1694 BufferView * bv = view();
1699 FileName filename(to_utf8(fname));
1701 if (!filename.empty()) {
1702 bv->insertLyXFile(filename);
1706 // Launch a file browser
1708 string initpath = lyxrc.document_path;
1709 string const trypath = bv->buffer().filePath();
1710 // If directory is writeable, use this as default.
1711 if (FileName(trypath).isDirWritable())
1715 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1716 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1717 dlg.setButton2(qt_("Examples|#E#e"),
1718 toqstr(addPath(package().system_support().absFilename(),
1721 FileDialog::Result result = dlg.open(toqstr(initpath),
1722 QStringList(qt_("LyX Documents (*.lyx)")));
1724 if (result.first == FileDialog::Later)
1728 filename.set(fromqstr(result.second));
1730 // check selected filename
1731 if (filename.empty()) {
1732 // emit message signal.
1733 message(_("Canceled."));
1737 bv->insertLyXFile(filename);
1741 void GuiView::insertPlaintextFile(docstring const & fname,
1744 BufferView * bv = view();
1748 if (!fname.empty() && !FileName::isAbsolute(to_utf8(fname))) {
1749 message(_("Absolute filename expected."));
1754 FileName filename(to_utf8(fname));
1756 if (!filename.empty()) {
1757 bv->insertPlaintextFile(filename, asParagraph);
1761 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1762 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1764 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1765 QStringList(qt_("All Files (*)")));
1767 if (result.first == FileDialog::Later)
1771 filename.set(fromqstr(result.second));
1773 // check selected filename
1774 if (filename.empty()) {
1775 // emit message signal.
1776 message(_("Canceled."));
1780 bv->insertPlaintextFile(filename, asParagraph);
1784 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1786 FileName fname = b.fileName();
1787 FileName const oldname = fname;
1789 if (!newname.empty()) {
1791 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1793 // Switch to this Buffer.
1796 // No argument? Ask user through dialog.
1798 FileDialog dlg(qt_("Choose a filename to save document as"),
1799 LFUN_BUFFER_WRITE_AS);
1800 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1801 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1803 if (!isLyXFilename(fname.absFilename()))
1804 fname.changeExtension(".lyx");
1806 FileDialog::Result result =
1807 dlg.save(toqstr(fname.onlyPath().absFilename()),
1808 QStringList(qt_("LyX Documents (*.lyx)")),
1809 toqstr(fname.onlyFileName()));
1811 if (result.first == FileDialog::Later)
1814 fname.set(fromqstr(result.second));
1819 if (!isLyXFilename(fname.absFilename()))
1820 fname.changeExtension(".lyx");
1823 if (FileName(fname).exists()) {
1824 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1825 docstring text = bformat(_("The document %1$s already "
1826 "exists.\n\nDo you want to "
1827 "overwrite that document?"),
1829 int const ret = Alert::prompt(_("Overwrite document?"),
1830 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1833 case 1: return renameBuffer(b, docstring());
1834 case 2: return false;
1838 FileName oldauto = b.getAutosaveFilename();
1840 // Ok, change the name of the buffer
1841 b.setFileName(fname.absFilename());
1843 bool unnamed = b.isUnnamed();
1844 b.setUnnamed(false);
1845 b.saveCheckSum(fname);
1847 // bring the autosave file with us, just in case.
1848 b.moveAutosaveFile(oldauto);
1850 if (!saveBuffer(b)) {
1851 oldauto = b.getAutosaveFilename();
1852 b.setFileName(oldname.absFilename());
1853 b.setUnnamed(unnamed);
1854 b.saveCheckSum(oldname);
1855 b.moveAutosaveFile(oldauto);
1863 bool GuiView::saveBuffer(Buffer & b)
1865 if (workArea(b) && workArea(b)->inDialogMode())
1869 return renameBuffer(b, docstring());
1872 theSession().lastFiles().add(b.fileName());
1876 // Switch to this Buffer.
1879 // FIXME: we don't tell the user *WHY* the save failed !!
1880 docstring const file = makeDisplayPath(b.absFileName(), 30);
1881 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1882 "Do you want to rename the document and "
1883 "try again?"), file);
1884 int const ret = Alert::prompt(_("Rename and save?"),
1885 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1888 if (!renameBuffer(b, docstring()))
1897 return saveBuffer(b);
1901 bool GuiView::closeBuffer()
1903 Buffer * buf = buffer();
1904 return buf && closeBuffer(*buf);
1908 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
1910 // goto bookmark to update bookmark pit.
1911 //FIXME: we should update only the bookmarks related to this buffer!
1912 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1913 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1914 theLyXFunc().gotoBookmark(i+1, false, false);
1916 if (buf.isClean() || buf.paragraphs().empty()) {
1917 // save in sessions if requested
1918 // do not save childs if their master
1919 // is opened as well
1921 theSession().lastOpened().add(buf.fileName(), mark_active);
1923 // Don't close child documents.
1924 removeWorkArea(currentMainWorkArea());
1926 theBufferList().release(&buf);
1929 // Switch to this Buffer.
1934 if (buf.isUnnamed())
1935 file = from_utf8(buf.fileName().onlyFileName());
1937 file = buf.fileName().displayName(30);
1939 // Bring this window to top before asking questions.
1943 docstring const text = bformat(_("The document %1$s has unsaved changes."
1944 "\n\nDo you want to save the document or discard the changes?"), file);
1945 int const ret = Alert::prompt(_("Save changed document?"),
1946 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1950 if (!saveBuffer(buf))
1954 // if we crash after this we could
1955 // have no autosave file but I guess
1956 // this is really improbable (Jug)
1957 buf.removeAutosaveFile();
1963 // save file names to .lyx/session
1965 theSession().lastOpened().add(buf.fileName(), mark_active);
1968 // Don't close child documents.
1969 removeWorkArea(currentMainWorkArea());
1971 theBufferList().release(&buf);
1977 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1979 Buffer * const curbuf = buffer();
1980 Buffer * nextbuf = curbuf;
1982 if (np == NEXTBUFFER)
1983 nextbuf = theBufferList().next(nextbuf);
1985 nextbuf = theBufferList().previous(nextbuf);
1986 if (nextbuf == curbuf)
1992 if (workArea(*nextbuf))
1999 bool GuiView::dispatch(FuncRequest const & cmd)
2001 BufferView * bv = view();
2002 // By default we won't need any update.
2004 bv->cursor().updateFlags(Update::None);
2005 bool dispatched = true;
2007 if (cmd.origin == FuncRequest::TOC) {
2008 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2009 toc->doDispatch(bv->cursor(), cmd);
2013 switch(cmd.action) {
2014 case LFUN_BUFFER_IMPORT:
2015 importDocument(to_utf8(cmd.argument()));
2018 case LFUN_BUFFER_SWITCH:
2019 if (FileName::isAbsolute(to_utf8(cmd.argument()))) {
2021 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2025 bv->cursor().message(_("Document not loaded"));
2029 case LFUN_BUFFER_NEXT:
2030 gotoNextOrPreviousBuffer(NEXTBUFFER);
2033 case LFUN_BUFFER_PREVIOUS:
2034 gotoNextOrPreviousBuffer(PREVBUFFER);
2037 case LFUN_COMMAND_EXECUTE: {
2038 bool const show_it = cmd.argument() != "off";
2039 // FIXME: this is a hack, "minibuffer" should not be
2041 if (GuiToolbar * t = toolbar("minibuffer")) {
2042 t->setVisible(show_it);
2043 if (show_it && t->commandBuffer())
2044 t->commandBuffer()->setFocus();
2048 case LFUN_DROP_LAYOUTS_CHOICE:
2050 d.layout_->showPopup();
2053 case LFUN_MENU_OPEN:
2054 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2055 menu->exec(QCursor::pos());
2058 case LFUN_FILE_INSERT:
2059 insertLyXFile(cmd.argument());
2061 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2062 insertPlaintextFile(cmd.argument(), true);
2065 case LFUN_FILE_INSERT_PLAINTEXT:
2066 insertPlaintextFile(cmd.argument(), false);
2069 case LFUN_BUFFER_WRITE:
2071 saveBuffer(bv->buffer());
2074 case LFUN_BUFFER_WRITE_AS:
2076 renameBuffer(bv->buffer(), cmd.argument());
2079 case LFUN_BUFFER_WRITE_ALL: {
2080 Buffer * first = theBufferList().first();
2083 message(_("Saving all documents..."));
2084 // We cannot use a for loop as the buffer list cycles.
2087 if (!b->isClean()) {
2089 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2091 b = theBufferList().next(b);
2092 } while (b != first);
2093 message(_("All documents saved."));
2097 case LFUN_TOOLBAR_TOGGLE: {
2098 string const name = cmd.getArg(0);
2099 if (GuiToolbar * t = toolbar(name))
2104 case LFUN_DIALOG_UPDATE: {
2105 string const name = to_utf8(cmd.argument());
2106 // Can only update a dialog connected to an existing inset
2107 Inset * inset = getOpenInset(name);
2109 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2110 inset->dispatch(view()->cursor(), fr);
2111 } else if (name == "paragraph") {
2112 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2113 } else if (name == "prefs" || name == "document") {
2114 updateDialog(name, string());
2119 case LFUN_DIALOG_TOGGLE: {
2120 if (isDialogVisible(cmd.getArg(0)))
2121 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2123 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2127 case LFUN_DIALOG_DISCONNECT_INSET:
2128 disconnectDialog(to_utf8(cmd.argument()));
2131 case LFUN_DIALOG_HIDE: {
2132 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2136 case LFUN_DIALOG_SHOW: {
2137 string const name = cmd.getArg(0);
2138 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2140 if (name == "character") {
2141 data = freefont2string();
2143 showDialog("character", data);
2144 } else if (name == "latexlog") {
2145 Buffer::LogType type;
2146 string const logfile = buffer()->logName(&type);
2148 case Buffer::latexlog:
2151 case Buffer::buildlog:
2155 data += Lexer::quoteString(logfile);
2156 showDialog("log", data);
2157 } else if (name == "vclog") {
2158 string const data = "vc " +
2159 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2160 showDialog("log", data);
2161 } else if (name == "symbols") {
2162 data = bv->cursor().getEncoding()->name();
2164 showDialog("symbols", data);
2166 } else if (name == "prefs" && isFullScreen()) {
2167 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2169 showDialog("prefs", data);
2171 showDialog(name, data);
2175 case LFUN_INSET_APPLY: {
2176 string const name = cmd.getArg(0);
2177 Inset * inset = getOpenInset(name);
2179 // put cursor in front of inset.
2180 if (!view()->setCursorFromInset(inset)) {
2181 LASSERT(false, break);
2184 // useful if we are called from a dialog.
2185 view()->cursor().beginUndoGroup();
2186 view()->cursor().recordUndo();
2187 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2188 inset->dispatch(view()->cursor(), fr);
2189 view()->cursor().endUndoGroup();
2191 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2197 case LFUN_UI_TOGGLE:
2199 // Make sure the keyboard focus stays in the work area.
2203 case LFUN_SPLIT_VIEW:
2204 if (Buffer * buf = buffer()) {
2205 string const orientation = cmd.getArg(0);
2206 d.splitter_->setOrientation(orientation == "vertical"
2207 ? Qt::Vertical : Qt::Horizontal);
2208 TabWorkArea * twa = addTabWorkArea();
2209 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2210 setCurrentWorkArea(wa);
2214 case LFUN_CLOSE_TAB_GROUP:
2215 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2217 twa = d.currentTabWorkArea();
2218 // Switch to the next GuiWorkArea in the found TabWorkArea.
2220 // Make sure the work area is up to date.
2221 setCurrentWorkArea(twa->currentWorkArea());
2223 setCurrentWorkArea(0);
2228 case LFUN_COMPLETION_INLINE:
2229 if (d.current_work_area_)
2230 d.current_work_area_->completer().showInline();
2233 case LFUN_COMPLETION_POPUP:
2234 if (d.current_work_area_)
2235 d.current_work_area_->completer().showPopup();
2239 case LFUN_COMPLETION_COMPLETE:
2240 if (d.current_work_area_)
2241 d.current_work_area_->completer().tab();
2244 case LFUN_COMPLETION_CANCEL:
2245 if (d.current_work_area_) {
2246 if (d.current_work_area_->completer().popupVisible())
2247 d.current_work_area_->completer().hidePopup();
2249 d.current_work_area_->completer().hideInline();
2253 case LFUN_COMPLETION_ACCEPT:
2254 if (d.current_work_area_)
2255 d.current_work_area_->completer().activate();
2258 case LFUN_BUFFER_ZOOM_IN:
2259 case LFUN_BUFFER_ZOOM_OUT:
2260 if (cmd.argument().empty()) {
2261 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2266 lyxrc.zoom += convert<int>(cmd.argument());
2268 if (lyxrc.zoom < 10)
2271 // The global QPixmapCache is used in GuiPainter to cache text
2272 // painting so we must reset it.
2273 QPixmapCache::clear();
2274 guiApp->fontLoader().update();
2275 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2283 // Part of automatic menu appearance feature.
2284 if (isFullScreen()) {
2285 if (menuBar()->isVisible())
2287 if (statusBar()->isVisible())
2288 statusBar()->hide();
2295 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2297 string const arg = cmd.getArg(0);
2298 if (arg == "scrollbar") {
2299 // hide() is of no help
2300 if (d.current_work_area_->verticalScrollBarPolicy() ==
2301 Qt::ScrollBarAlwaysOff)
2303 d.current_work_area_->setVerticalScrollBarPolicy(
2304 Qt::ScrollBarAsNeeded);
2306 d.current_work_area_->setVerticalScrollBarPolicy(
2307 Qt::ScrollBarAlwaysOff);
2310 if (arg == "statusbar") {
2311 statusBar()->setVisible(!statusBar()->isVisible());
2314 if (arg == "menubar") {
2315 menuBar()->setVisible(!menuBar()->isVisible());
2318 #if QT_VERSION >= 0x040300
2319 if (arg == "frame") {
2321 getContentsMargins(&l, &t, &r, &b);
2322 //are the frames in default state?
2323 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2325 setContentsMargins(-2, -2, -2, -2);
2327 setContentsMargins(0, 0, 0, 0);
2332 if (arg == "fullscreen") {
2337 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2341 void GuiView::toggleFullScreen()
2343 if (isFullScreen()) {
2344 for (int i = 0; i != d.splitter_->count(); ++i)
2345 d.tabWorkArea(i)->setFullScreen(false);
2346 #if QT_VERSION >= 0x040300
2347 setContentsMargins(0, 0, 0, 0);
2349 setWindowState(windowState() ^ Qt::WindowFullScreen);
2352 statusBar()->show();
2355 hideDialogs("prefs", 0);
2356 for (int i = 0; i != d.splitter_->count(); ++i)
2357 d.tabWorkArea(i)->setFullScreen(true);
2358 #if QT_VERSION >= 0x040300
2359 setContentsMargins(-2, -2, -2, -2);
2362 setWindowState(windowState() ^ Qt::WindowFullScreen);
2363 statusBar()->hide();
2365 if (lyxrc.full_screen_toolbars) {
2366 ToolbarMap::iterator end = d.toolbars_.end();
2367 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2372 // give dialogs like the TOC a chance to adapt
2377 Buffer const * GuiView::updateInset(Inset const * inset)
2379 if (!d.current_work_area_)
2383 d.current_work_area_->scheduleRedraw();
2385 return &d.current_work_area_->bufferView().buffer();
2389 void GuiView::restartCursor()
2391 /* When we move around, or type, it's nice to be able to see
2392 * the cursor immediately after the keypress.
2394 if (d.current_work_area_)
2395 d.current_work_area_->startBlinkingCursor();
2397 // Take this occasion to update the other GUI elements.
2403 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2405 if (d.current_work_area_)
2406 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2411 // This list should be kept in sync with the list of insets in
2412 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2413 // dialog should have the same name as the inset.
2414 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2415 // docs in LyXAction.cpp.
2417 char const * const dialognames[] = {
2418 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2419 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2420 "findreplaceadv", "float", "graphics", "href", "include", "index",
2421 "index_print", "info", "listings", "label", "log", "mathdelimiter",
2422 "mathmatrix", "mathspace", "nomenclature", "note", "paragraph", "phantom",
2423 "prefs", "print", "nomencl_print", "ref", "sendto", "space", "spellchecker",
2424 "symbols", "tabular", "tabularcreate", "thesaurus", "texinfo", "toc",
2425 "view-source", "vspace", "wrap" };
2427 char const * const * const end_dialognames =
2428 dialognames + (sizeof(dialognames) / sizeof(char *));
2432 cmpCStr(char const * name) : name_(name) {}
2433 bool operator()(char const * other) {
2434 return strcmp(other, name_) == 0;
2441 bool isValidName(string const & name)
2443 return find_if(dialognames, end_dialognames,
2444 cmpCStr(name.c_str())) != end_dialognames;
2450 void GuiView::resetDialogs()
2452 // Make sure that no LFUN uses any LyXView.
2453 theLyXFunc().setLyXView(0);
2456 constructToolbars();
2457 guiApp->menus().fillMenuBar(menuBar(), this, false);
2459 d.layout_->updateContents(true);
2460 // Now update controls with current buffer.
2461 theLyXFunc().setLyXView(this);
2467 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2469 if (!isValidName(name))
2472 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2474 if (it != d.dialogs_.end()) {
2476 it->second->hideView();
2477 return it->second.get();
2480 Dialog * dialog = build(name);
2481 d.dialogs_[name].reset(dialog);
2482 if (lyxrc.allow_geometry_session)
2483 dialog->restoreSession();
2490 void GuiView::showDialog(string const & name, string const & data,
2498 Dialog * dialog = findOrBuild(name, false);
2500 dialog->showData(data);
2502 d.open_insets_[name] = inset;
2505 catch (ExceptionMessage const & ex) {
2513 bool GuiView::isDialogVisible(string const & name) const
2515 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2516 if (it == d.dialogs_.end())
2518 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
2522 void GuiView::hideDialog(string const & name, Inset * inset)
2524 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2525 if (it == d.dialogs_.end())
2528 if (inset && inset != getOpenInset(name))
2531 Dialog * const dialog = it->second.get();
2532 if (dialog->isVisibleView())
2534 d.open_insets_[name] = 0;
2538 void GuiView::disconnectDialog(string const & name)
2540 if (!isValidName(name))
2543 if (d.open_insets_.find(name) != d.open_insets_.end())
2544 d.open_insets_[name] = 0;
2548 Inset * GuiView::getOpenInset(string const & name) const
2550 if (!isValidName(name))
2553 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2554 return it == d.open_insets_.end() ? 0 : it->second;
2558 void GuiView::hideAll() const
2560 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2561 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2563 for(; it != end; ++it)
2564 it->second->hideView();
2568 void GuiView::updateDialogs()
2570 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2571 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2573 for(; it != end; ++it) {
2574 Dialog * dialog = it->second.get();
2575 if (dialog && dialog->isVisibleView())
2576 dialog->checkStatus();
2583 // will be replaced by a proper factory...
2584 Dialog * createGuiAbout(GuiView & lv);
2585 Dialog * createGuiBibitem(GuiView & lv);
2586 Dialog * createGuiBibtex(GuiView & lv);
2587 Dialog * createGuiBox(GuiView & lv);
2588 Dialog * createGuiBranch(GuiView & lv);
2589 Dialog * createGuiChanges(GuiView & lv);
2590 Dialog * createGuiCharacter(GuiView & lv);
2591 Dialog * createGuiCitation(GuiView & lv);
2592 Dialog * createGuiDelimiter(GuiView & lv);
2593 Dialog * createGuiDocument(GuiView & lv);
2594 Dialog * createGuiErrorList(GuiView & lv);
2595 Dialog * createGuiERT(GuiView & lv);
2596 Dialog * createGuiExternal(GuiView & lv);
2597 Dialog * createGuiFloat(GuiView & lv);
2598 Dialog * createGuiGraphics(GuiView & lv);
2599 Dialog * createGuiInclude(GuiView & lv);
2600 Dialog * createGuiIndex(GuiView & lv);
2601 Dialog * createGuiInfo(GuiView & lv);
2602 Dialog * createGuiLabel(GuiView & lv);
2603 Dialog * createGuiListings(GuiView & lv);
2604 Dialog * createGuiLog(GuiView & lv);
2605 Dialog * createGuiMathHSpace(GuiView & lv);
2606 Dialog * createGuiMathMatrix(GuiView & lv);
2607 Dialog * createGuiNomenclature(GuiView & lv);
2608 Dialog * createGuiNote(GuiView & lv);
2609 Dialog * createGuiParagraph(GuiView & lv);
2610 Dialog * createGuiPhantom(GuiView & lv);
2611 Dialog * createGuiPreferences(GuiView & lv);
2612 Dialog * createGuiPrint(GuiView & lv);
2613 Dialog * createGuiPrintindex(GuiView & lv);
2614 Dialog * createGuiPrintNomencl(GuiView & lv);
2615 Dialog * createGuiRef(GuiView & lv);
2616 Dialog * createGuiSearch(GuiView & lv);
2617 Dialog * createGuiSearchAdv(GuiView & lv);
2618 Dialog * createGuiSendTo(GuiView & lv);
2619 Dialog * createGuiShowFile(GuiView & lv);
2620 Dialog * createGuiSpellchecker(GuiView & lv);
2621 Dialog * createGuiSymbols(GuiView & lv);
2622 Dialog * createGuiTabularCreate(GuiView & lv);
2623 Dialog * createGuiTabular(GuiView & lv);
2624 Dialog * createGuiTexInfo(GuiView & lv);
2625 Dialog * createGuiTextHSpace(GuiView & lv);
2626 Dialog * createGuiToc(GuiView & lv);
2627 Dialog * createGuiThesaurus(GuiView & lv);
2628 Dialog * createGuiHyperlink(GuiView & lv);
2629 Dialog * createGuiVSpace(GuiView & lv);
2630 Dialog * createGuiViewSource(GuiView & lv);
2631 Dialog * createGuiWrap(GuiView & lv);
2634 Dialog * GuiView::build(string const & name)
2636 LASSERT(isValidName(name), return 0);
2638 if (name == "aboutlyx")
2639 return createGuiAbout(*this);
2640 if (name == "bibitem")
2641 return createGuiBibitem(*this);
2642 if (name == "bibtex")
2643 return createGuiBibtex(*this);
2645 return createGuiBox(*this);
2646 if (name == "branch")
2647 return createGuiBranch(*this);
2648 if (name == "changes")
2649 return createGuiChanges(*this);
2650 if (name == "character")
2651 return createGuiCharacter(*this);
2652 if (name == "citation")
2653 return createGuiCitation(*this);
2654 if (name == "document")
2655 return createGuiDocument(*this);
2656 if (name == "errorlist")
2657 return createGuiErrorList(*this);
2659 return createGuiERT(*this);
2660 if (name == "external")
2661 return createGuiExternal(*this);
2663 return createGuiShowFile(*this);
2664 if (name == "findreplace")
2665 return createGuiSearch(*this);
2666 if (name == "findreplaceadv")
2667 return createGuiSearchAdv(*this);
2668 if (name == "float")
2669 return createGuiFloat(*this);
2670 if (name == "graphics")
2671 return createGuiGraphics(*this);
2672 if (name == "include")
2673 return createGuiInclude(*this);
2674 if (name == "index")
2675 return createGuiIndex(*this);
2677 return createGuiInfo(*this);
2678 if (name == "nomenclature")
2679 return createGuiNomenclature(*this);
2680 if (name == "label")
2681 return createGuiLabel(*this);
2683 return createGuiLog(*this);
2684 if (name == "mathdelimiter")
2685 return createGuiDelimiter(*this);
2686 if (name == "mathspace")
2687 return createGuiMathHSpace(*this);
2688 if (name == "mathmatrix")
2689 return createGuiMathMatrix(*this);
2691 return createGuiNote(*this);
2692 if (name == "paragraph")
2693 return createGuiParagraph(*this);
2694 if (name == "phantom")
2695 return createGuiPhantom(*this);
2696 if (name == "prefs")
2697 return createGuiPreferences(*this);
2698 if (name == "print")
2699 return createGuiPrint(*this);
2700 if (name == "nomencl_print")
2701 return createGuiPrintNomencl(*this);
2703 return createGuiRef(*this);
2704 if (name == "sendto")
2705 return createGuiSendTo(*this);
2706 if (name == "space")
2707 return createGuiTextHSpace(*this);
2708 if (name == "spellchecker")
2709 return createGuiSpellchecker(*this);
2710 if (name == "symbols")
2711 return createGuiSymbols(*this);
2712 if (name == "tabular")
2713 return createGuiTabular(*this);
2714 if (name == "tabularcreate")
2715 return createGuiTabularCreate(*this);
2716 if (name == "texinfo")
2717 return createGuiTexInfo(*this);
2718 if (name == "view-source")
2719 return createGuiViewSource(*this);
2720 if (name == "thesaurus")
2721 return createGuiThesaurus(*this);
2723 return createGuiHyperlink(*this);
2724 if (name == "index_print")
2725 return createGuiPrintindex(*this);
2726 if (name == "listings")
2727 return createGuiListings(*this);
2729 return createGuiToc(*this);
2730 if (name == "vspace")
2731 return createGuiVSpace(*this);
2733 return createGuiWrap(*this);
2739 } // namespace frontend
2742 #include "moc_GuiView.cpp"