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 setCurrentWorkArea(currentMainWorkArea());
538 int splitter_count = d.splitter_->count();
539 for (; splitter_count; --splitter_count) {
540 TabWorkArea * twa = d.tabWorkArea(0);
542 twa->updateTabTexts();
544 int twa_count = twa->count();
545 for (; twa_count; --twa_count) {
546 twa->setCurrentIndex(0);
548 GuiWorkArea * wa = twa->currentWorkArea();
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)) {
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)) {
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();
1749 FileName filename(to_utf8(fname));
1751 if (!filename.empty()) {
1752 bv->insertPlaintextFile(filename, asParagraph);
1756 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1757 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1759 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1760 QStringList(qt_("All Files (*)")));
1762 if (result.first == FileDialog::Later)
1766 filename.set(fromqstr(result.second));
1768 // check selected filename
1769 if (filename.empty()) {
1770 // emit message signal.
1771 message(_("Canceled."));
1775 bv->insertPlaintextFile(filename, asParagraph);
1779 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1781 FileName fname = b.fileName();
1782 FileName const oldname = fname;
1784 if (!newname.empty()) {
1786 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1788 // Switch to this Buffer.
1791 // No argument? Ask user through dialog.
1793 FileDialog dlg(qt_("Choose a filename to save document as"),
1794 LFUN_BUFFER_WRITE_AS);
1795 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1796 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1798 if (!isLyXFilename(fname.absFilename()))
1799 fname.changeExtension(".lyx");
1801 FileDialog::Result result =
1802 dlg.save(toqstr(fname.onlyPath().absFilename()),
1803 QStringList(qt_("LyX Documents (*.lyx)")),
1804 toqstr(fname.onlyFileName()));
1806 if (result.first == FileDialog::Later)
1809 fname.set(fromqstr(result.second));
1814 if (!isLyXFilename(fname.absFilename()))
1815 fname.changeExtension(".lyx");
1818 if (FileName(fname).exists()) {
1819 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1820 docstring text = bformat(_("The document %1$s already "
1821 "exists.\n\nDo you want to "
1822 "overwrite that document?"),
1824 int const ret = Alert::prompt(_("Overwrite document?"),
1825 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1828 case 1: return renameBuffer(b, docstring());
1829 case 2: return false;
1833 FileName oldauto = b.getAutosaveFilename();
1835 // Ok, change the name of the buffer
1836 b.setFileName(fname.absFilename());
1838 bool unnamed = b.isUnnamed();
1839 b.setUnnamed(false);
1840 b.saveCheckSum(fname);
1842 // bring the autosave file with us, just in case.
1843 b.moveAutosaveFile(oldauto);
1845 if (!saveBuffer(b)) {
1846 oldauto = b.getAutosaveFilename();
1847 b.setFileName(oldname.absFilename());
1848 b.setUnnamed(unnamed);
1849 b.saveCheckSum(oldname);
1850 b.moveAutosaveFile(oldauto);
1858 bool GuiView::saveBuffer(Buffer & b)
1860 if (workArea(b) && workArea(b)->inDialogMode())
1864 return renameBuffer(b, docstring());
1867 theSession().lastFiles().add(b.fileName());
1871 // Switch to this Buffer.
1874 // FIXME: we don't tell the user *WHY* the save failed !!
1875 docstring const file = makeDisplayPath(b.absFileName(), 30);
1876 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1877 "Do you want to rename the document and "
1878 "try again?"), file);
1879 int const ret = Alert::prompt(_("Rename and save?"),
1880 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1883 if (!renameBuffer(b, docstring()))
1892 return saveBuffer(b);
1896 bool GuiView::closeBuffer()
1898 Buffer * buf = buffer();
1899 return buf && closeBuffer(*buf);
1903 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1905 // goto bookmark to update bookmark pit.
1906 //FIXME: we should update only the bookmarks related to this buffer!
1907 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1908 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1909 theLyXFunc().gotoBookmark(i+1, false, false);
1911 if (buf.isClean() || buf.paragraphs().empty()) {
1912 // save in sessions if requested
1913 // do not save childs if their master
1914 // is opened as well
1916 theSession().lastOpened().add(buf.fileName());
1918 // Don't close child documents.
1919 removeWorkArea(currentMainWorkArea());
1921 theBufferList().release(&buf);
1924 // Switch to this Buffer.
1929 if (buf.isUnnamed())
1930 file = from_utf8(buf.fileName().onlyFileName());
1932 file = buf.fileName().displayName(30);
1934 // Bring this window to top before asking questions.
1938 docstring const text = bformat(_("The document %1$s has unsaved changes."
1939 "\n\nDo you want to save the document or discard the changes?"), file);
1940 int const ret = Alert::prompt(_("Save changed document?"),
1941 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1945 if (!saveBuffer(buf))
1949 // if we crash after this we could
1950 // have no autosave file but I guess
1951 // this is really improbable (Jug)
1952 buf.removeAutosaveFile();
1958 // save file names to .lyx/session
1960 theSession().lastOpened().add(buf.fileName());
1963 // Don't close child documents.
1964 removeWorkArea(currentMainWorkArea());
1966 theBufferList().release(&buf);
1972 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1974 Buffer * const curbuf = buffer();
1975 Buffer * nextbuf = curbuf;
1977 if (np == NEXTBUFFER)
1978 nextbuf = theBufferList().next(nextbuf);
1980 nextbuf = theBufferList().previous(nextbuf);
1981 if (nextbuf == curbuf)
1987 if (workArea(*nextbuf))
1994 bool GuiView::dispatch(FuncRequest const & cmd)
1996 BufferView * bv = view();
1997 // By default we won't need any update.
1999 bv->cursor().updateFlags(Update::None);
2000 bool dispatched = true;
2002 if (cmd.origin == FuncRequest::TOC) {
2003 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2004 toc->doDispatch(bv->cursor(), cmd);
2008 switch(cmd.action) {
2009 case LFUN_BUFFER_IMPORT:
2010 importDocument(to_utf8(cmd.argument()));
2013 case LFUN_BUFFER_SWITCH: {
2015 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2019 bv->cursor().message(_("Document not loaded"));
2023 case LFUN_BUFFER_NEXT:
2024 gotoNextOrPreviousBuffer(NEXTBUFFER);
2027 case LFUN_BUFFER_PREVIOUS:
2028 gotoNextOrPreviousBuffer(PREVBUFFER);
2031 case LFUN_COMMAND_EXECUTE: {
2032 bool const show_it = cmd.argument() != "off";
2033 // FIXME: this is a hack, "minibuffer" should not be
2035 if (GuiToolbar * t = toolbar("minibuffer")) {
2036 t->setVisible(show_it);
2037 if (show_it && t->commandBuffer())
2038 t->commandBuffer()->setFocus();
2042 case LFUN_DROP_LAYOUTS_CHOICE:
2044 d.layout_->showPopup();
2047 case LFUN_MENU_OPEN:
2048 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2049 menu->exec(QCursor::pos());
2052 case LFUN_FILE_INSERT:
2053 insertLyXFile(cmd.argument());
2055 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2056 insertPlaintextFile(cmd.argument(), true);
2059 case LFUN_FILE_INSERT_PLAINTEXT:
2060 insertPlaintextFile(cmd.argument(), false);
2063 case LFUN_BUFFER_WRITE:
2065 saveBuffer(bv->buffer());
2068 case LFUN_BUFFER_WRITE_AS:
2070 renameBuffer(bv->buffer(), cmd.argument());
2073 case LFUN_BUFFER_WRITE_ALL: {
2074 Buffer * first = theBufferList().first();
2077 message(_("Saving all documents..."));
2078 // We cannot use a for loop as the buffer list cycles.
2081 if (!b->isClean()) {
2083 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2085 b = theBufferList().next(b);
2086 } while (b != first);
2087 message(_("All documents saved."));
2091 case LFUN_TOOLBAR_TOGGLE: {
2092 string const name = cmd.getArg(0);
2093 if (GuiToolbar * t = toolbar(name))
2098 case LFUN_DIALOG_UPDATE: {
2099 string const name = to_utf8(cmd.argument());
2100 // Can only update a dialog connected to an existing inset
2101 Inset * inset = getOpenInset(name);
2103 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2104 inset->dispatch(view()->cursor(), fr);
2105 } else if (name == "paragraph") {
2106 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2107 } else if (name == "prefs" || name == "document") {
2108 updateDialog(name, string());
2113 case LFUN_DIALOG_TOGGLE: {
2114 if (isDialogVisible(cmd.getArg(0)))
2115 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2117 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2121 case LFUN_DIALOG_DISCONNECT_INSET:
2122 disconnectDialog(to_utf8(cmd.argument()));
2125 case LFUN_DIALOG_HIDE: {
2126 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2130 case LFUN_DIALOG_SHOW: {
2131 string const name = cmd.getArg(0);
2132 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2134 if (name == "character") {
2135 data = freefont2string();
2137 showDialog("character", data);
2138 } else if (name == "latexlog") {
2139 Buffer::LogType type;
2140 string const logfile = buffer()->logName(&type);
2142 case Buffer::latexlog:
2145 case Buffer::buildlog:
2149 data += Lexer::quoteString(logfile);
2150 showDialog("log", data);
2151 } else if (name == "vclog") {
2152 string const data = "vc " +
2153 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2154 showDialog("log", data);
2155 } else if (name == "symbols") {
2156 data = bv->cursor().getEncoding()->name();
2158 showDialog("symbols", data);
2160 } else if (name == "prefs" && isFullScreen()) {
2161 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2163 showDialog("prefs", data);
2165 showDialog(name, data);
2169 case LFUN_INSET_APPLY: {
2170 string const name = cmd.getArg(0);
2171 Inset * inset = getOpenInset(name);
2173 // put cursor in front of inset.
2174 if (!view()->setCursorFromInset(inset)) {
2175 LASSERT(false, break);
2178 // useful if we are called from a dialog.
2179 view()->cursor().beginUndoGroup();
2180 view()->cursor().recordUndo();
2181 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2182 inset->dispatch(view()->cursor(), fr);
2183 view()->cursor().endUndoGroup();
2185 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2191 case LFUN_UI_TOGGLE:
2193 // Make sure the keyboard focus stays in the work area.
2197 case LFUN_SPLIT_VIEW:
2198 if (Buffer * buf = buffer()) {
2199 string const orientation = cmd.getArg(0);
2200 d.splitter_->setOrientation(orientation == "vertical"
2201 ? Qt::Vertical : Qt::Horizontal);
2202 TabWorkArea * twa = addTabWorkArea();
2203 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2204 setCurrentWorkArea(wa);
2208 case LFUN_CLOSE_TAB_GROUP:
2209 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2211 twa = d.currentTabWorkArea();
2212 // Switch to the next GuiWorkArea in the found TabWorkArea.
2214 // Make sure the work area is up to date.
2215 setCurrentWorkArea(twa->currentWorkArea());
2217 setCurrentWorkArea(0);
2222 case LFUN_COMPLETION_INLINE:
2223 if (d.current_work_area_)
2224 d.current_work_area_->completer().showInline();
2227 case LFUN_COMPLETION_POPUP:
2228 if (d.current_work_area_)
2229 d.current_work_area_->completer().showPopup();
2233 case LFUN_COMPLETION_COMPLETE:
2234 if (d.current_work_area_)
2235 d.current_work_area_->completer().tab();
2238 case LFUN_COMPLETION_CANCEL:
2239 if (d.current_work_area_) {
2240 if (d.current_work_area_->completer().popupVisible())
2241 d.current_work_area_->completer().hidePopup();
2243 d.current_work_area_->completer().hideInline();
2247 case LFUN_COMPLETION_ACCEPT:
2248 if (d.current_work_area_)
2249 d.current_work_area_->completer().activate();
2252 case LFUN_BUFFER_ZOOM_IN:
2253 case LFUN_BUFFER_ZOOM_OUT:
2254 if (cmd.argument().empty()) {
2255 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2260 lyxrc.zoom += convert<int>(cmd.argument());
2262 if (lyxrc.zoom < 10)
2265 // The global QPixmapCache is used in GuiPainter to cache text
2266 // painting so we must reset it.
2267 QPixmapCache::clear();
2268 guiApp->fontLoader().update();
2269 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2277 // Part of automatic menu appearance feature.
2278 if (isFullScreen()) {
2279 if (menuBar()->isVisible())
2281 if (statusBar()->isVisible())
2282 statusBar()->hide();
2289 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2291 string const arg = cmd.getArg(0);
2292 if (arg == "scrollbar") {
2293 // hide() is of no help
2294 if (d.current_work_area_->verticalScrollBarPolicy() ==
2295 Qt::ScrollBarAlwaysOff)
2297 d.current_work_area_->setVerticalScrollBarPolicy(
2298 Qt::ScrollBarAsNeeded);
2300 d.current_work_area_->setVerticalScrollBarPolicy(
2301 Qt::ScrollBarAlwaysOff);
2304 if (arg == "statusbar") {
2305 statusBar()->setVisible(!statusBar()->isVisible());
2308 if (arg == "menubar") {
2309 menuBar()->setVisible(!menuBar()->isVisible());
2312 #if QT_VERSION >= 0x040300
2313 if (arg == "frame") {
2315 getContentsMargins(&l, &t, &r, &b);
2316 //are the frames in default state?
2317 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2319 setContentsMargins(-2, -2, -2, -2);
2321 setContentsMargins(0, 0, 0, 0);
2326 if (arg == "fullscreen") {
2331 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2335 void GuiView::toggleFullScreen()
2337 if (isFullScreen()) {
2338 for (int i = 0; i != d.splitter_->count(); ++i)
2339 d.tabWorkArea(i)->setFullScreen(false);
2340 #if QT_VERSION >= 0x040300
2341 setContentsMargins(0, 0, 0, 0);
2343 setWindowState(windowState() ^ Qt::WindowFullScreen);
2346 statusBar()->show();
2349 hideDialogs("prefs", 0);
2350 for (int i = 0; i != d.splitter_->count(); ++i)
2351 d.tabWorkArea(i)->setFullScreen(true);
2352 #if QT_VERSION >= 0x040300
2353 setContentsMargins(-2, -2, -2, -2);
2356 setWindowState(windowState() ^ Qt::WindowFullScreen);
2357 statusBar()->hide();
2359 if (lyxrc.full_screen_toolbars) {
2360 ToolbarMap::iterator end = d.toolbars_.end();
2361 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2366 // give dialogs like the TOC a chance to adapt
2371 Buffer const * GuiView::updateInset(Inset const * inset)
2373 if (!d.current_work_area_)
2377 d.current_work_area_->scheduleRedraw();
2379 return &d.current_work_area_->bufferView().buffer();
2383 void GuiView::restartCursor()
2385 /* When we move around, or type, it's nice to be able to see
2386 * the cursor immediately after the keypress.
2388 if (d.current_work_area_)
2389 d.current_work_area_->startBlinkingCursor();
2391 // Take this occasion to update the other GUI elements.
2397 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2399 if (d.current_work_area_)
2400 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2405 // This list should be kept in sync with the list of insets in
2406 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2407 // dialog should have the same name as the inset.
2408 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2409 // docs in LyXAction.cpp.
2411 char const * const dialognames[] = {
2412 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2413 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2414 "float", "graphics", "include", "index", "index_print", "info", "nomenclature",
2415 "label", "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2416 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2417 "symbols", "tabular", "tabularcreate",
2419 #ifdef HAVE_LIBAIKSAURUS
2423 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2425 char const * const * const end_dialognames =
2426 dialognames + (sizeof(dialognames) / sizeof(char *));
2430 cmpCStr(char const * name) : name_(name) {}
2431 bool operator()(char const * other) {
2432 return strcmp(other, name_) == 0;
2439 bool isValidName(string const & name)
2441 return find_if(dialognames, end_dialognames,
2442 cmpCStr(name.c_str())) != end_dialognames;
2448 void GuiView::resetDialogs()
2450 // Make sure that no LFUN uses any LyXView.
2451 theLyXFunc().setLyXView(0);
2454 constructToolbars();
2455 guiApp->menus().fillMenuBar(menuBar(), this, false);
2457 d.layout_->updateContents(true);
2458 // Now update controls with current buffer.
2459 theLyXFunc().setLyXView(this);
2465 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2467 if (!isValidName(name))
2470 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2472 if (it != d.dialogs_.end()) {
2474 it->second->hideView();
2475 return it->second.get();
2478 Dialog * dialog = build(name);
2479 d.dialogs_[name].reset(dialog);
2480 if (lyxrc.allow_geometry_session)
2481 dialog->restoreSession();
2488 void GuiView::showDialog(string const & name, string const & data,
2496 Dialog * dialog = findOrBuild(name, false);
2498 dialog->showData(data);
2500 d.open_insets_[name] = inset;
2503 catch (ExceptionMessage const & ex) {
2511 bool GuiView::isDialogVisible(string const & name) const
2513 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2514 if (it == d.dialogs_.end())
2516 return it->second.get()->isVisibleView();
2520 void GuiView::hideDialog(string const & name, Inset * inset)
2522 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2523 if (it == d.dialogs_.end())
2526 if (inset && inset != getOpenInset(name))
2529 Dialog * const dialog = it->second.get();
2530 if (dialog->isVisibleView())
2532 d.open_insets_[name] = 0;
2536 void GuiView::disconnectDialog(string const & name)
2538 if (!isValidName(name))
2541 if (d.open_insets_.find(name) != d.open_insets_.end())
2542 d.open_insets_[name] = 0;
2546 Inset * GuiView::getOpenInset(string const & name) const
2548 if (!isValidName(name))
2551 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2552 return it == d.open_insets_.end() ? 0 : it->second;
2556 void GuiView::hideAll() const
2558 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2559 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2561 for(; it != end; ++it)
2562 it->second->hideView();
2566 void GuiView::updateDialogs()
2568 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2569 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2571 for(; it != end; ++it) {
2572 Dialog * dialog = it->second.get();
2573 if (dialog && dialog->isVisibleView())
2574 dialog->checkStatus();
2581 // will be replaced by a proper factory...
2582 Dialog * createGuiAbout(GuiView & lv);
2583 Dialog * createGuiBibitem(GuiView & lv);
2584 Dialog * createGuiBibtex(GuiView & lv);
2585 Dialog * createGuiBox(GuiView & lv);
2586 Dialog * createGuiBranch(GuiView & lv);
2587 Dialog * createGuiChanges(GuiView & lv);
2588 Dialog * createGuiCharacter(GuiView & lv);
2589 Dialog * createGuiCitation(GuiView & lv);
2590 Dialog * createGuiDelimiter(GuiView & lv);
2591 Dialog * createGuiDocument(GuiView & lv);
2592 Dialog * createGuiErrorList(GuiView & lv);
2593 Dialog * createGuiERT(GuiView & lv);
2594 Dialog * createGuiExternal(GuiView & lv);
2595 Dialog * createGuiFloat(GuiView & lv);
2596 Dialog * createGuiGraphics(GuiView & lv);
2597 Dialog * createGuiInclude(GuiView & lv);
2598 Dialog * createGuiIndex(GuiView & lv);
2599 Dialog * createGuiInfo(GuiView & lv);
2600 Dialog * createGuiLabel(GuiView & lv);
2601 Dialog * createGuiListings(GuiView & lv);
2602 Dialog * createGuiLog(GuiView & lv);
2603 Dialog * createGuiMathHSpace(GuiView & lv);
2604 Dialog * createGuiMathMatrix(GuiView & lv);
2605 Dialog * createGuiNomenclature(GuiView & lv);
2606 Dialog * createGuiNote(GuiView & lv);
2607 Dialog * createGuiParagraph(GuiView & lv);
2608 Dialog * createGuiPhantom(GuiView & lv);
2609 Dialog * createGuiPreferences(GuiView & lv);
2610 Dialog * createGuiPrint(GuiView & lv);
2611 Dialog * createGuiPrintindex(GuiView & lv);
2612 Dialog * createGuiRef(GuiView & lv);
2613 Dialog * createGuiSearch(GuiView & lv);
2614 Dialog * createGuiSearchAdv(GuiView & lv);
2615 Dialog * createGuiSendTo(GuiView & lv);
2616 Dialog * createGuiShowFile(GuiView & lv);
2617 Dialog * createGuiSpellchecker(GuiView & lv);
2618 Dialog * createGuiSymbols(GuiView & lv);
2619 Dialog * createGuiTabularCreate(GuiView & lv);
2620 Dialog * createGuiTabular(GuiView & lv);
2621 Dialog * createGuiTexInfo(GuiView & lv);
2622 Dialog * createGuiTextHSpace(GuiView & lv);
2623 Dialog * createGuiToc(GuiView & lv);
2624 Dialog * createGuiThesaurus(GuiView & lv);
2625 Dialog * createGuiHyperlink(GuiView & lv);
2626 Dialog * createGuiVSpace(GuiView & lv);
2627 Dialog * createGuiViewSource(GuiView & lv);
2628 Dialog * createGuiWrap(GuiView & lv);
2631 Dialog * GuiView::build(string const & name)
2633 LASSERT(isValidName(name), return 0);
2635 if (name == "aboutlyx")
2636 return createGuiAbout(*this);
2637 if (name == "bibitem")
2638 return createGuiBibitem(*this);
2639 if (name == "bibtex")
2640 return createGuiBibtex(*this);
2642 return createGuiBox(*this);
2643 if (name == "branch")
2644 return createGuiBranch(*this);
2645 if (name == "changes")
2646 return createGuiChanges(*this);
2647 if (name == "character")
2648 return createGuiCharacter(*this);
2649 if (name == "citation")
2650 return createGuiCitation(*this);
2651 if (name == "document")
2652 return createGuiDocument(*this);
2653 if (name == "errorlist")
2654 return createGuiErrorList(*this);
2656 return createGuiERT(*this);
2657 if (name == "external")
2658 return createGuiExternal(*this);
2660 return createGuiShowFile(*this);
2661 if (name == "findreplace")
2662 return createGuiSearch(*this);
2663 if (name == "findreplaceadv")
2664 return createGuiSearchAdv(*this);
2665 if (name == "float")
2666 return createGuiFloat(*this);
2667 if (name == "graphics")
2668 return createGuiGraphics(*this);
2669 if (name == "include")
2670 return createGuiInclude(*this);
2671 if (name == "index")
2672 return createGuiIndex(*this);
2674 return createGuiInfo(*this);
2675 if (name == "nomenclature")
2676 return createGuiNomenclature(*this);
2677 if (name == "label")
2678 return createGuiLabel(*this);
2680 return createGuiLog(*this);
2681 if (name == "mathdelimiter")
2682 return createGuiDelimiter(*this);
2683 if (name == "mathspace")
2684 return createGuiMathHSpace(*this);
2685 if (name == "mathmatrix")
2686 return createGuiMathMatrix(*this);
2688 return createGuiNote(*this);
2689 if (name == "paragraph")
2690 return createGuiParagraph(*this);
2691 if (name == "phantom")
2692 return createGuiPhantom(*this);
2693 if (name == "prefs")
2694 return createGuiPreferences(*this);
2695 if (name == "print")
2696 return createGuiPrint(*this);
2698 return createGuiRef(*this);
2699 if (name == "sendto")
2700 return createGuiSendTo(*this);
2701 if (name == "space")
2702 return createGuiTextHSpace(*this);
2703 if (name == "spellchecker")
2704 return createGuiSpellchecker(*this);
2705 if (name == "symbols")
2706 return createGuiSymbols(*this);
2707 if (name == "tabular")
2708 return createGuiTabular(*this);
2709 if (name == "tabularcreate")
2710 return createGuiTabularCreate(*this);
2711 if (name == "texinfo")
2712 return createGuiTexInfo(*this);
2713 if (name == "view-source")
2714 return createGuiViewSource(*this);
2715 #ifdef HAVE_LIBAIKSAURUS
2716 if (name == "thesaurus")
2717 return createGuiThesaurus(*this);
2720 return createGuiHyperlink(*this);
2721 if (name == "index_print")
2722 return createGuiPrintindex(*this);
2723 if (name == "listings")
2724 return createGuiListings(*this);
2726 return createGuiToc(*this);
2727 if (name == "vspace")
2728 return createGuiVSpace(*this);
2730 return createGuiWrap(*this);
2736 } // namespace frontend
2739 #include "moc_GuiView.cpp"