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());
537 while (GuiWorkArea * wa = currentMainWorkArea()) {
538 Buffer * b = &wa->bufferView().buffer();
540 // This is a child document, just close the tab
541 // after saving but keep the file loaded.
542 if (!closeBuffer(*b, true)) {
544 close_event->ignore();
550 vector<Buffer *> clist = b->getChildren();
551 for (vector<Buffer *>::const_iterator it = clist.begin();
552 it != clist.end(); ++it) {
553 if ((*it)->isClean())
556 // If a child is dirty, do not close
557 // without user intervention
558 if (!closeBuffer(*c, false)) {
560 close_event->ignore();
565 QList<int> const ids = guiApp->viewIds();
566 for (int i = 0; i != ids.size(); ++i) {
569 if (guiApp->view(ids[i]).workArea(*b)) {
570 // FIXME 1: should we put an alert box here that the buffer
571 // is viewed elsewhere?
572 // FIXME 2: should we try to save this buffer in any case?
575 // This buffer is also opened in another view, so
576 // close the associated work area...
578 // ... but don't close the buffer.
583 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
584 if (b && !closeBuffer(*b, true)) {
586 close_event->ignore();
591 // Make sure that nothing will use this close to be closed View.
592 guiApp->unregisterView(this);
594 if (isFullScreen()) {
595 // Switch off fullscreen before closing.
600 // Make sure the timer time out will not trigger a statusbar update.
601 d.statusbar_timer_.stop();
603 // Saving fullscreen requires additional tweaks in the toolbar code.
604 // It wouldn't also work under linux natively.
605 if (lyxrc.allow_geometry_session) {
606 // Save this window geometry and layout.
608 // Then the toolbar private states.
609 ToolbarMap::iterator end = d.toolbars_.end();
610 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
611 it->second->saveSession();
612 // Now take care of all other dialogs:
613 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
614 for (; it!= d.dialogs_.end(); ++it)
615 it->second->saveSession();
618 close_event->accept();
622 void GuiView::dragEnterEvent(QDragEnterEvent * event)
624 if (event->mimeData()->hasUrls())
626 /// \todo Ask lyx-devel is this is enough:
627 /// if (event->mimeData()->hasFormat("text/plain"))
628 /// event->acceptProposedAction();
632 void GuiView::dropEvent(QDropEvent * event)
634 QList<QUrl> files = event->mimeData()->urls();
638 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
639 for (int i = 0; i != files.size(); ++i) {
640 string const file = os::internal_path(fromqstr(
641 files.at(i).toLocalFile()));
643 // Asynchronously post the event. DropEvent usually come
644 // from the BufferView. But reloading a file might close
645 // the BufferView from within its own event handler.
646 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
653 void GuiView::message(docstring const & str)
655 if (ForkedProcess::iAmAChild())
658 statusBar()->showMessage(toqstr(str));
659 d.statusbar_timer_.stop();
660 d.statusbar_timer_.start(3000);
664 void GuiView::smallSizedIcons()
666 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
670 void GuiView::normalSizedIcons()
672 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
676 void GuiView::bigSizedIcons()
678 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
682 void GuiView::clearMessage()
686 theLyXFunc().setLyXView(this);
687 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
688 d.statusbar_timer_.stop();
692 void GuiView::updateWindowTitle(GuiWorkArea * wa)
694 if (wa != d.current_work_area_)
696 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
697 setWindowIconText(wa->windowIconText());
701 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
704 disconnectBufferView();
705 connectBufferView(wa->bufferView());
706 connectBuffer(wa->bufferView().buffer());
707 d.current_work_area_ = wa;
708 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
709 this, SLOT(updateWindowTitle(GuiWorkArea *)));
710 updateWindowTitle(wa);
714 // The document settings needs to be reinitialised.
715 updateDialog("document", "");
717 // Buffer-dependent dialogs must be updated. This is done here because
718 // some dialogs require buffer()->text.
723 void GuiView::on_lastWorkAreaRemoved()
726 // We already are in a close event. Nothing more to do.
729 if (d.splitter_->count() > 1)
730 // We have a splitter so don't close anything.
733 // Reset and updates the dialogs.
734 d.toc_models_.reset(0);
735 updateDialog("document", "");
738 resetWindowTitleAndIconText();
740 if (lyxrc.open_buffers_in_tabs)
741 // Nothing more to do, the window should stay open.
744 if (guiApp->viewIds().size() > 1) {
750 // On Mac we also close the last window because the application stay
751 // resident in memory. On other platforms we don't close the last
752 // window because this would quit the application.
758 void GuiView::updateStatusBar()
760 // let the user see the explicit message
761 if (d.statusbar_timer_.isActive())
764 theLyXFunc().setLyXView(this);
765 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
769 bool GuiView::hasFocus() const
771 return qApp->activeWindow() == this;
775 bool GuiView::event(QEvent * e)
779 // Useful debug code:
780 //case QEvent::ActivationChange:
781 //case QEvent::WindowDeactivate:
782 //case QEvent::Paint:
783 //case QEvent::Enter:
784 //case QEvent::Leave:
785 //case QEvent::HoverEnter:
786 //case QEvent::HoverLeave:
787 //case QEvent::HoverMove:
788 //case QEvent::StatusTip:
789 //case QEvent::DragEnter:
790 //case QEvent::DragLeave:
794 case QEvent::WindowActivate: {
795 if (this == guiApp->currentView()) {
797 return QMainWindow::event(e);
799 guiApp->setCurrentView(this);
800 theLyXFunc().setLyXView(this);
801 if (d.current_work_area_) {
802 BufferView & bv = d.current_work_area_->bufferView();
803 connectBufferView(bv);
804 connectBuffer(bv.buffer());
805 // The document structure, name and dialogs might have
806 // changed in another view.
808 // The document settings needs to be reinitialised.
809 updateDialog("document", "");
812 resetWindowTitleAndIconText();
815 return QMainWindow::event(e);
818 case QEvent::ShortcutOverride: {
822 if (isFullScreen() && menuBar()->isHidden()) {
823 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
824 // FIXME: we should also try to detect special LyX shortcut such as
825 // Alt-P and Alt-M. Right now there is a hack in
826 // GuiWorkArea::processKeySym() that hides again the menubar for
828 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
830 return QMainWindow::event(e);
835 if (d.current_work_area_)
836 // Nothing special to do.
837 return QMainWindow::event(e);
839 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
840 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
842 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
843 || ke->key() == Qt::Key_Backtab)
844 return QMainWindow::event(e);
846 // Allow processing of shortcuts that are allowed even when no Buffer
848 theLyXFunc().setLyXView(this);
850 setKeySymbol(&sym, ke);
851 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
857 return QMainWindow::event(e);
861 void GuiView::resetWindowTitleAndIconText()
863 setWindowTitle(qt_("LyX"));
864 setWindowIconText(qt_("LyX"));
867 bool GuiView::focusNextPrevChild(bool /*next*/)
874 void GuiView::setBusy(bool busy)
876 if (d.current_work_area_) {
877 d.current_work_area_->setUpdatesEnabled(!busy);
879 d.current_work_area_->stopBlinkingCursor();
881 d.current_work_area_->startBlinkingCursor();
885 QApplication::setOverrideCursor(Qt::WaitCursor);
887 QApplication::restoreOverrideCursor();
891 GuiWorkArea * GuiView::workArea(Buffer & buffer)
893 if (currentWorkArea()
894 && ¤tWorkArea()->bufferView().buffer() == &buffer)
895 return (GuiWorkArea *) currentWorkArea();
896 if (TabWorkArea * twa = d.currentTabWorkArea())
897 return twa->workArea(buffer);
902 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
904 // Automatically create a TabWorkArea if there are none yet.
905 TabWorkArea * tab_widget = d.splitter_->count()
906 ? d.currentTabWorkArea() : addTabWorkArea();
907 return tab_widget->addWorkArea(buffer, *this);
911 TabWorkArea * GuiView::addTabWorkArea()
913 TabWorkArea * twa = new TabWorkArea;
914 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
915 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
916 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
917 this, SLOT(on_lastWorkAreaRemoved()));
919 d.splitter_->addWidget(twa);
920 d.stack_widget_->setCurrentWidget(d.splitter_);
925 GuiWorkArea const * GuiView::currentWorkArea() const
927 return d.current_work_area_;
931 GuiWorkArea * GuiView::currentWorkArea()
933 return d.current_work_area_;
937 GuiWorkArea const * GuiView::currentMainWorkArea() const
939 if (d.currentTabWorkArea() == NULL)
941 return d.currentTabWorkArea()->currentWorkArea();
945 GuiWorkArea * GuiView::currentMainWorkArea()
947 if (d.currentTabWorkArea() == NULL)
949 return d.currentTabWorkArea()->currentWorkArea();
953 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
955 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
957 d.current_work_area_ = NULL;
961 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
965 theGuiApp()->setCurrentView(this);
966 d.current_work_area_ = wa;
967 for (int i = 0; i != d.splitter_->count(); ++i) {
968 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
969 //if (d.current_main_work_area_)
970 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
971 d.current_main_work_area_ = wa;
972 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
973 //d.current_main_work_area_->setLineWidth(2);
974 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
978 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
979 on_currentWorkAreaChanged(wa);
980 BufferView & bv = wa->bufferView();
981 bv.cursor().fixIfBroken();
983 wa->setUpdatesEnabled(true);
984 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
988 void GuiView::removeWorkArea(GuiWorkArea * wa)
991 if (wa == d.current_work_area_) {
993 disconnectBufferView();
994 d.current_work_area_ = 0;
995 d.current_main_work_area_ = 0;
998 bool found_twa = false;
999 for (int i = 0; i != d.splitter_->count(); ++i) {
1000 TabWorkArea * twa = d.tabWorkArea(i);
1001 if (twa->removeWorkArea(wa)) {
1002 // Found in this tab group, and deleted the GuiWorkArea.
1004 if (twa->count() != 0) {
1005 if (d.current_work_area_ == 0)
1006 // This means that we are closing the current GuiWorkArea, so
1007 // switch to the next GuiWorkArea in the found TabWorkArea.
1008 setCurrentWorkArea(twa->currentWorkArea());
1010 // No more WorkAreas in this tab group, so delete it.
1017 // It is not a tabbed work area (i.e., the search work area), so it
1018 // should be deleted by other means.
1019 LASSERT(found_twa, /* */);
1021 if (d.current_work_area_ == 0) {
1022 if (d.splitter_->count() != 0) {
1023 TabWorkArea * twa = d.currentTabWorkArea();
1024 setCurrentWorkArea(twa->currentWorkArea());
1026 // No more work areas, switch to the background widget.
1027 setCurrentWorkArea(0);
1033 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1039 void GuiView::updateLayoutList()
1042 d.layout_->updateContents(false);
1046 void GuiView::updateToolbars()
1048 ToolbarMap::iterator end = d.toolbars_.end();
1049 if (d.current_work_area_) {
1051 d.current_work_area_->bufferView().cursor().inMathed();
1053 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1055 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1056 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1057 bool const mathmacrotemplate =
1058 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1060 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1061 it->second->update(math, table, review, mathmacrotemplate);
1063 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1064 it->second->update(false, false, false, false);
1068 Buffer * GuiView::buffer()
1070 if (d.current_work_area_)
1071 return &d.current_work_area_->bufferView().buffer();
1076 Buffer const * GuiView::buffer() const
1078 if (d.current_work_area_)
1079 return &d.current_work_area_->bufferView().buffer();
1084 void GuiView::setBuffer(Buffer * newBuffer)
1086 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1087 LASSERT(newBuffer, return);
1090 GuiWorkArea * wa = workArea(*newBuffer);
1092 newBuffer->masterBuffer()->updateLabels();
1093 wa = addWorkArea(*newBuffer);
1095 //Disconnect the old buffer...there's no new one.
1098 connectBuffer(*newBuffer);
1099 connectBufferView(wa->bufferView());
1100 setCurrentWorkArea(wa);
1106 void GuiView::connectBuffer(Buffer & buf)
1108 buf.setGuiDelegate(this);
1112 void GuiView::disconnectBuffer()
1114 if (d.current_work_area_)
1115 d.current_work_area_->bufferView().setGuiDelegate(0);
1119 void GuiView::connectBufferView(BufferView & bv)
1121 bv.setGuiDelegate(this);
1125 void GuiView::disconnectBufferView()
1127 if (d.current_work_area_)
1128 d.current_work_area_->bufferView().setGuiDelegate(0);
1132 void GuiView::errors(string const & error_type)
1134 ErrorList & el = buffer()->errorList(error_type);
1136 showDialog("errorlist", error_type);
1140 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1142 d.toc_models_.updateItem(toqstr(type), dit);
1146 void GuiView::structureChanged()
1148 d.toc_models_.reset(view());
1149 // Navigator needs more than a simple update in this case. It needs to be
1151 updateDialog("toc", "");
1155 void GuiView::updateDialog(string const & name, string const & data)
1157 if (!isDialogVisible(name))
1160 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1161 if (it == d.dialogs_.end())
1164 Dialog * const dialog = it->second.get();
1165 if (dialog->isVisibleView())
1166 dialog->initialiseParams(data);
1170 BufferView * GuiView::view()
1172 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1176 void GuiView::autoSave()
1178 LYXERR(Debug::INFO, "Running autoSave()");
1181 view()->buffer().autoSave();
1185 void GuiView::resetAutosaveTimers()
1188 d.autosave_timeout_.restart();
1192 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1195 Buffer * buf = buffer();
1197 /* In LyX/Mac, when a dialog is open, the menus of the
1198 application can still be accessed without giving focus to
1199 the main window. In this case, we want to disable the menu
1200 entries that are buffer-related.
1202 Note that this code is not perfect, as bug 1941 attests:
1203 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1205 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1208 if (cmd.origin == FuncRequest::TOC) {
1209 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1211 if (toc->getStatus(view()->cursor(), cmd, fs))
1214 flag.setEnabled(false);
1218 switch(cmd.action) {
1219 case LFUN_BUFFER_WRITE:
1220 enable = buf && (buf->isUnnamed() || !buf->isClean());
1223 case LFUN_BUFFER_WRITE_AS:
1227 case LFUN_SPLIT_VIEW:
1228 if (cmd.getArg(0) == "vertical")
1229 enable = buf && (d.splitter_->count() == 1 ||
1230 d.splitter_->orientation() == Qt::Vertical);
1232 enable = buf && (d.splitter_->count() == 1 ||
1233 d.splitter_->orientation() == Qt::Horizontal);
1236 case LFUN_CLOSE_TAB_GROUP:
1237 enable = d.currentTabWorkArea();
1240 case LFUN_TOOLBAR_TOGGLE:
1241 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1242 flag.setOnOff(t->isVisible());
1245 case LFUN_UI_TOGGLE:
1246 flag.setOnOff(isFullScreen());
1249 case LFUN_DIALOG_TOGGLE:
1250 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1251 // fall through to set "enable"
1252 case LFUN_DIALOG_SHOW: {
1253 string const name = cmd.getArg(0);
1255 enable = name == "aboutlyx"
1256 || name == "file" //FIXME: should be removed.
1258 || name == "texinfo";
1259 else if (name == "print")
1260 enable = buf->isExportable("dvi")
1261 && lyxrc.print_command != "none";
1262 else if (name == "character") {
1266 InsetCode ic = view()->cursor().inset().lyxCode();
1267 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1270 else if (name == "symbols") {
1271 if (!view() || view()->cursor().inMathed())
1274 InsetCode ic = view()->cursor().inset().lyxCode();
1275 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1278 else if (name == "latexlog")
1279 enable = FileName(buf->logName()).isReadableFile();
1280 else if (name == "spellchecker")
1281 #if defined (USE_ASPELL)
1282 enable = !buf->isReadonly();
1286 else if (name == "vclog")
1287 enable = buf->lyxvc().inUse();
1291 case LFUN_DIALOG_UPDATE: {
1292 string const name = cmd.getArg(0);
1294 enable = name == "prefs";
1298 case LFUN_INSET_APPLY: {
1299 string const name = cmd.getArg(0);
1300 Inset * inset = getOpenInset(name);
1302 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1304 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1305 // Every inset is supposed to handle this
1306 LASSERT(false, break);
1310 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1311 flag |= lyx::getStatus(fr);
1313 enable = flag.enabled();
1317 case LFUN_COMPLETION_INLINE:
1318 if (!d.current_work_area_
1319 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1323 case LFUN_COMPLETION_POPUP:
1324 if (!d.current_work_area_
1325 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1329 case LFUN_COMPLETION_COMPLETE:
1330 if (!d.current_work_area_
1331 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1335 case LFUN_COMPLETION_ACCEPT:
1336 if (!d.current_work_area_
1337 || (!d.current_work_area_->completer().popupVisible()
1338 && !d.current_work_area_->completer().inlineVisible()
1339 && !d.current_work_area_->completer().completionAvailable()))
1343 case LFUN_COMPLETION_CANCEL:
1344 if (!d.current_work_area_
1345 || (!d.current_work_area_->completer().popupVisible()
1346 && !d.current_work_area_->completer().inlineVisible()))
1350 case LFUN_BUFFER_ZOOM_OUT:
1351 enable = buf && lyxrc.zoom > 10;
1354 case LFUN_BUFFER_ZOOM_IN:
1363 flag.setEnabled(false);
1369 static FileName selectTemplateFile()
1371 FileDialog dlg(qt_("Select template file"));
1372 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1373 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1375 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1376 QStringList(qt_("LyX Documents (*.lyx)")));
1378 if (result.first == FileDialog::Later)
1380 if (result.second.isEmpty())
1382 return FileName(fromqstr(result.second));
1386 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1390 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1393 message(_("Document not loaded."));
1398 setBuffer(newBuffer);
1400 // scroll to the position when the file was last closed
1401 if (lyxrc.use_lastfilepos) {
1402 LastFilePosSection::FilePos filepos =
1403 theSession().lastFilePos().load(filename);
1404 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1408 theSession().lastFiles().add(filename);
1415 void GuiView::openDocument(string const & fname)
1417 string initpath = lyxrc.document_path;
1420 string const trypath = buffer()->filePath();
1421 // If directory is writeable, use this as default.
1422 if (FileName(trypath).isDirWritable())
1428 if (fname.empty()) {
1429 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1430 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1431 dlg.setButton2(qt_("Examples|#E#e"),
1432 toqstr(addPath(package().system_support().absFilename(), "examples")));
1434 QStringList filter(qt_("LyX Documents (*.lyx)"));
1435 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1436 << qt_("LyX-1.4.x Documents (*.lyx14)")
1437 << qt_("LyX-1.5.x Documents (*.lyx15)")
1438 << qt_("LyX-1.6.x Documents (*.lyx16)");
1439 FileDialog::Result result =
1440 dlg.open(toqstr(initpath), filter);
1442 if (result.first == FileDialog::Later)
1445 filename = fromqstr(result.second);
1447 // check selected filename
1448 if (filename.empty()) {
1449 message(_("Canceled."));
1455 // get absolute path of file and add ".lyx" to the filename if
1457 FileName const fullname =
1458 fileSearch(string(), filename, "lyx", support::may_not_exist);
1459 if (!fullname.empty())
1460 filename = fullname.absFilename();
1462 if (!fullname.onlyPath().isDirectory()) {
1463 Alert::warning(_("Invalid filename"),
1464 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1465 from_utf8(fullname.absFilename())));
1468 // if the file doesn't exist, let the user create one
1469 if (!fullname.exists()) {
1470 // the user specifically chose this name. Believe him.
1471 Buffer * const b = newFile(filename, string(), true);
1477 docstring const disp_fn = makeDisplayPath(filename);
1478 message(bformat(_("Opening document %1$s..."), disp_fn));
1481 Buffer * buf = loadDocument(fullname);
1483 buf->updateLabels();
1485 buf->errors("Parse");
1486 str2 = bformat(_("Document %1$s opened."), disp_fn);
1487 if (buf->lyxvc().inUse())
1488 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1489 " " + _("Version control detected.");
1491 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1496 // FIXME: clean that
1497 static bool import(GuiView * lv, FileName const & filename,
1498 string const & format, ErrorList & errorList)
1500 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1502 string loader_format;
1503 vector<string> loaders = theConverters().loaders();
1504 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1505 for (vector<string>::const_iterator it = loaders.begin();
1506 it != loaders.end(); ++it) {
1507 if (!theConverters().isReachable(format, *it))
1510 string const tofile =
1511 support::changeExtension(filename.absFilename(),
1512 formats.extension(*it));
1513 if (!theConverters().convert(0, filename, FileName(tofile),
1514 filename, format, *it, errorList))
1516 loader_format = *it;
1519 if (loader_format.empty()) {
1520 frontend::Alert::error(_("Couldn't import file"),
1521 bformat(_("No information for importing the format %1$s."),
1522 formats.prettyName(format)));
1526 loader_format = format;
1528 if (loader_format == "lyx") {
1529 Buffer * buf = lv->loadDocument(lyxfile);
1532 buf->updateLabels();
1534 buf->errors("Parse");
1536 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1540 bool as_paragraphs = loader_format == "textparagraph";
1541 string filename2 = (loader_format == format) ? filename.absFilename()
1542 : support::changeExtension(filename.absFilename(),
1543 formats.extension(loader_format));
1544 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1545 theLyXFunc().setLyXView(lv);
1546 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1553 void GuiView::importDocument(string const & argument)
1556 string filename = split(argument, format, ' ');
1558 LYXERR(Debug::INFO, format << " file: " << filename);
1560 // need user interaction
1561 if (filename.empty()) {
1562 string initpath = lyxrc.document_path;
1564 Buffer const * buf = buffer();
1566 string const trypath = buf->filePath();
1567 // If directory is writeable, use this as default.
1568 if (FileName(trypath).isDirWritable())
1572 docstring const text = bformat(_("Select %1$s file to import"),
1573 formats.prettyName(format));
1575 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1576 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1577 dlg.setButton2(qt_("Examples|#E#e"),
1578 toqstr(addPath(package().system_support().absFilename(), "examples")));
1580 docstring filter = formats.prettyName(format);
1583 filter += from_utf8(formats.extension(format));
1586 FileDialog::Result result =
1587 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1589 if (result.first == FileDialog::Later)
1592 filename = fromqstr(result.second);
1594 // check selected filename
1595 if (filename.empty())
1596 message(_("Canceled."));
1599 if (filename.empty())
1602 // get absolute path of file
1603 FileName const fullname(support::makeAbsPath(filename));
1605 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1607 // Check if the document already is open
1608 Buffer * buf = theBufferList().getBuffer(lyxfile);
1611 if (!closeBuffer()) {
1612 message(_("Canceled."));
1617 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1619 // if the file exists already, and we didn't do
1620 // -i lyx thefile.lyx, warn
1621 if (lyxfile.exists() && fullname != lyxfile) {
1623 docstring text = bformat(_("The document %1$s already exists.\n\n"
1624 "Do you want to overwrite that document?"), displaypath);
1625 int const ret = Alert::prompt(_("Overwrite document?"),
1626 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1629 message(_("Canceled."));
1634 message(bformat(_("Importing %1$s..."), displaypath));
1635 ErrorList errorList;
1636 if (import(this, fullname, format, errorList))
1637 message(_("imported."));
1639 message(_("file not imported!"));
1641 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1645 void GuiView::newDocument(string const & filename, bool from_template)
1647 FileName initpath(lyxrc.document_path);
1648 Buffer * buf = buffer();
1650 FileName const trypath(buf->filePath());
1651 // If directory is writeable, use this as default.
1652 if (trypath.isDirWritable())
1656 string templatefile;
1657 if (from_template) {
1658 templatefile = selectTemplateFile().absFilename();
1659 if (templatefile.empty())
1664 if (filename.empty())
1665 b = newUnnamedFile(templatefile, initpath);
1667 b = newFile(filename, templatefile, true);
1672 // If no new document could be created, it is unsure
1673 // whether there is a valid BufferView.
1675 // Ensure the cursor is correctly positioned on screen.
1676 view()->showCursor();
1680 void GuiView::insertLyXFile(docstring const & fname)
1682 BufferView * bv = view();
1687 FileName filename(to_utf8(fname));
1689 if (!filename.empty()) {
1690 bv->insertLyXFile(filename);
1694 // Launch a file browser
1696 string initpath = lyxrc.document_path;
1697 string const trypath = bv->buffer().filePath();
1698 // If directory is writeable, use this as default.
1699 if (FileName(trypath).isDirWritable())
1703 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1704 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1705 dlg.setButton2(qt_("Examples|#E#e"),
1706 toqstr(addPath(package().system_support().absFilename(),
1709 FileDialog::Result result = dlg.open(toqstr(initpath),
1710 QStringList(qt_("LyX Documents (*.lyx)")));
1712 if (result.first == FileDialog::Later)
1716 filename.set(fromqstr(result.second));
1718 // check selected filename
1719 if (filename.empty()) {
1720 // emit message signal.
1721 message(_("Canceled."));
1725 bv->insertLyXFile(filename);
1729 void GuiView::insertPlaintextFile(docstring const & fname,
1732 BufferView * bv = view();
1737 FileName filename(to_utf8(fname));
1739 if (!filename.empty()) {
1740 bv->insertPlaintextFile(filename, asParagraph);
1744 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1745 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1747 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1748 QStringList(qt_("All Files (*)")));
1750 if (result.first == FileDialog::Later)
1754 filename.set(fromqstr(result.second));
1756 // check selected filename
1757 if (filename.empty()) {
1758 // emit message signal.
1759 message(_("Canceled."));
1763 bv->insertPlaintextFile(filename, asParagraph);
1767 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1769 FileName fname = b.fileName();
1770 FileName const oldname = fname;
1772 if (!newname.empty()) {
1774 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1776 // Switch to this Buffer.
1779 // No argument? Ask user through dialog.
1781 FileDialog dlg(qt_("Choose a filename to save document as"),
1782 LFUN_BUFFER_WRITE_AS);
1783 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1784 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1786 if (!isLyXFilename(fname.absFilename()))
1787 fname.changeExtension(".lyx");
1789 FileDialog::Result result =
1790 dlg.save(toqstr(fname.onlyPath().absFilename()),
1791 QStringList(qt_("LyX Documents (*.lyx)")),
1792 toqstr(fname.onlyFileName()));
1794 if (result.first == FileDialog::Later)
1797 fname.set(fromqstr(result.second));
1802 if (!isLyXFilename(fname.absFilename()))
1803 fname.changeExtension(".lyx");
1806 if (FileName(fname).exists()) {
1807 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1808 docstring text = bformat(_("The document %1$s already "
1809 "exists.\n\nDo you want to "
1810 "overwrite that document?"),
1812 int const ret = Alert::prompt(_("Overwrite document?"),
1813 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1816 case 1: return renameBuffer(b, docstring());
1817 case 2: return false;
1821 FileName oldauto = b.getAutosaveFilename();
1823 // Ok, change the name of the buffer
1824 b.setFileName(fname.absFilename());
1826 bool unnamed = b.isUnnamed();
1827 b.setUnnamed(false);
1828 b.saveCheckSum(fname);
1830 // bring the autosave file with us, just in case.
1831 b.moveAutosaveFile(oldauto);
1833 if (!saveBuffer(b)) {
1834 oldauto = b.getAutosaveFilename();
1835 b.setFileName(oldname.absFilename());
1836 b.setUnnamed(unnamed);
1837 b.saveCheckSum(oldname);
1838 b.moveAutosaveFile(oldauto);
1846 bool GuiView::saveBuffer(Buffer & b)
1848 if (workArea(b) && workArea(b)->inDialogMode())
1852 return renameBuffer(b, docstring());
1855 theSession().lastFiles().add(b.fileName());
1859 // Switch to this Buffer.
1862 // FIXME: we don't tell the user *WHY* the save failed !!
1863 docstring const file = makeDisplayPath(b.absFileName(), 30);
1864 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1865 "Do you want to rename the document and "
1866 "try again?"), file);
1867 int const ret = Alert::prompt(_("Rename and save?"),
1868 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1871 if (!renameBuffer(b, docstring()))
1880 return saveBuffer(b);
1884 bool GuiView::closeBuffer()
1886 Buffer * buf = buffer();
1887 return buf && closeBuffer(*buf);
1891 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1893 // goto bookmark to update bookmark pit.
1894 //FIXME: we should update only the bookmarks related to this buffer!
1895 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1896 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1897 theLyXFunc().gotoBookmark(i+1, false, false);
1899 if (buf.isClean() || buf.paragraphs().empty()) {
1900 // save in sessions if requested
1901 // do not save childs if their master
1902 // is opened as well
1904 theSession().lastOpened().add(buf.fileName());
1906 // Don't close child documents.
1907 removeWorkArea(currentMainWorkArea());
1909 theBufferList().release(&buf);
1912 // Switch to this Buffer.
1917 if (buf.isUnnamed())
1918 file = from_utf8(buf.fileName().onlyFileName());
1920 file = buf.fileName().displayName(30);
1922 // Bring this window to top before asking questions.
1926 docstring const text = bformat(_("The document %1$s has unsaved changes."
1927 "\n\nDo you want to save the document or discard the changes?"), file);
1928 int const ret = Alert::prompt(_("Save changed document?"),
1929 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1933 if (!saveBuffer(buf))
1937 // if we crash after this we could
1938 // have no autosave file but I guess
1939 // this is really improbable (Jug)
1940 buf.removeAutosaveFile();
1946 // save file names to .lyx/session
1948 theSession().lastOpened().add(buf.fileName());
1951 // Don't close child documents.
1952 removeWorkArea(currentMainWorkArea());
1954 theBufferList().release(&buf);
1960 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1962 Buffer * const curbuf = buffer();
1963 Buffer * nextbuf = curbuf;
1965 if (np == NEXTBUFFER)
1966 nextbuf = theBufferList().next(nextbuf);
1968 nextbuf = theBufferList().previous(nextbuf);
1969 if (nextbuf == curbuf)
1975 if (workArea(*nextbuf))
1982 bool GuiView::dispatch(FuncRequest const & cmd)
1984 BufferView * bv = view();
1985 // By default we won't need any update.
1987 bv->cursor().updateFlags(Update::None);
1988 bool dispatched = true;
1990 if (cmd.origin == FuncRequest::TOC) {
1991 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1992 toc->doDispatch(bv->cursor(), cmd);
1996 switch(cmd.action) {
1997 case LFUN_BUFFER_IMPORT:
1998 importDocument(to_utf8(cmd.argument()));
2001 case LFUN_BUFFER_SWITCH: {
2003 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2007 bv->cursor().message(_("Document not loaded"));
2011 case LFUN_BUFFER_NEXT:
2012 gotoNextOrPreviousBuffer(NEXTBUFFER);
2015 case LFUN_BUFFER_PREVIOUS:
2016 gotoNextOrPreviousBuffer(PREVBUFFER);
2019 case LFUN_COMMAND_EXECUTE: {
2020 bool const show_it = cmd.argument() != "off";
2021 // FIXME: this is a hack, "minibuffer" should not be
2023 if (GuiToolbar * t = toolbar("minibuffer")) {
2024 t->setVisible(show_it);
2025 if (show_it && t->commandBuffer())
2026 t->commandBuffer()->setFocus();
2030 case LFUN_DROP_LAYOUTS_CHOICE:
2032 d.layout_->showPopup();
2035 case LFUN_MENU_OPEN:
2036 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2037 menu->exec(QCursor::pos());
2040 case LFUN_FILE_INSERT:
2041 insertLyXFile(cmd.argument());
2043 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2044 insertPlaintextFile(cmd.argument(), true);
2047 case LFUN_FILE_INSERT_PLAINTEXT:
2048 insertPlaintextFile(cmd.argument(), false);
2051 case LFUN_BUFFER_WRITE:
2053 saveBuffer(bv->buffer());
2056 case LFUN_BUFFER_WRITE_AS:
2058 renameBuffer(bv->buffer(), cmd.argument());
2061 case LFUN_BUFFER_WRITE_ALL: {
2062 Buffer * first = theBufferList().first();
2065 message(_("Saving all documents..."));
2066 // We cannot use a for loop as the buffer list cycles.
2069 if (!b->isClean()) {
2071 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2073 b = theBufferList().next(b);
2074 } while (b != first);
2075 message(_("All documents saved."));
2079 case LFUN_TOOLBAR_TOGGLE: {
2080 string const name = cmd.getArg(0);
2081 if (GuiToolbar * t = toolbar(name))
2086 case LFUN_DIALOG_UPDATE: {
2087 string const name = to_utf8(cmd.argument());
2088 // Can only update a dialog connected to an existing inset
2089 Inset * inset = getOpenInset(name);
2091 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2092 inset->dispatch(view()->cursor(), fr);
2093 } else if (name == "paragraph") {
2094 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2095 } else if (name == "prefs" || name == "document") {
2096 updateDialog(name, string());
2101 case LFUN_DIALOG_TOGGLE: {
2102 if (isDialogVisible(cmd.getArg(0)))
2103 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2105 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2109 case LFUN_DIALOG_DISCONNECT_INSET:
2110 disconnectDialog(to_utf8(cmd.argument()));
2113 case LFUN_DIALOG_HIDE: {
2114 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2118 case LFUN_DIALOG_SHOW: {
2119 string const name = cmd.getArg(0);
2120 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2122 if (name == "character") {
2123 data = freefont2string();
2125 showDialog("character", data);
2126 } else if (name == "latexlog") {
2127 Buffer::LogType type;
2128 string const logfile = buffer()->logName(&type);
2130 case Buffer::latexlog:
2133 case Buffer::buildlog:
2137 data += Lexer::quoteString(logfile);
2138 showDialog("log", data);
2139 } else if (name == "vclog") {
2140 string const data = "vc " +
2141 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2142 showDialog("log", data);
2143 } else if (name == "symbols") {
2144 data = bv->cursor().getEncoding()->name();
2146 showDialog("symbols", data);
2148 } else if (name == "prefs" && isFullScreen()) {
2149 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2151 showDialog("prefs", data);
2153 showDialog(name, data);
2157 case LFUN_INSET_APPLY: {
2158 string const name = cmd.getArg(0);
2159 Inset * inset = getOpenInset(name);
2161 // put cursor in front of inset.
2162 if (!view()->setCursorFromInset(inset)) {
2163 LASSERT(false, break);
2166 // useful if we are called from a dialog.
2167 view()->cursor().beginUndoGroup();
2168 view()->cursor().recordUndo();
2169 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2170 inset->dispatch(view()->cursor(), fr);
2171 view()->cursor().endUndoGroup();
2173 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2179 case LFUN_UI_TOGGLE:
2181 // Make sure the keyboard focus stays in the work area.
2185 case LFUN_SPLIT_VIEW:
2186 if (Buffer * buf = buffer()) {
2187 string const orientation = cmd.getArg(0);
2188 d.splitter_->setOrientation(orientation == "vertical"
2189 ? Qt::Vertical : Qt::Horizontal);
2190 TabWorkArea * twa = addTabWorkArea();
2191 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2192 setCurrentWorkArea(wa);
2196 case LFUN_CLOSE_TAB_GROUP:
2197 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2199 twa = d.currentTabWorkArea();
2200 // Switch to the next GuiWorkArea in the found TabWorkArea.
2202 // Make sure the work area is up to date.
2203 setCurrentWorkArea(twa->currentWorkArea());
2205 setCurrentWorkArea(0);
2210 case LFUN_COMPLETION_INLINE:
2211 if (d.current_work_area_)
2212 d.current_work_area_->completer().showInline();
2215 case LFUN_COMPLETION_POPUP:
2216 if (d.current_work_area_)
2217 d.current_work_area_->completer().showPopup();
2221 case LFUN_COMPLETION_COMPLETE:
2222 if (d.current_work_area_)
2223 d.current_work_area_->completer().tab();
2226 case LFUN_COMPLETION_CANCEL:
2227 if (d.current_work_area_) {
2228 if (d.current_work_area_->completer().popupVisible())
2229 d.current_work_area_->completer().hidePopup();
2231 d.current_work_area_->completer().hideInline();
2235 case LFUN_COMPLETION_ACCEPT:
2236 if (d.current_work_area_)
2237 d.current_work_area_->completer().activate();
2240 case LFUN_BUFFER_ZOOM_IN:
2241 case LFUN_BUFFER_ZOOM_OUT:
2242 if (cmd.argument().empty()) {
2243 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2248 lyxrc.zoom += convert<int>(cmd.argument());
2250 if (lyxrc.zoom < 10)
2253 // The global QPixmapCache is used in GuiPainter to cache text
2254 // painting so we must reset it.
2255 QPixmapCache::clear();
2256 guiApp->fontLoader().update();
2257 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2265 // Part of automatic menu appearance feature.
2266 if (isFullScreen()) {
2267 if (menuBar()->isVisible())
2269 if (statusBar()->isVisible())
2270 statusBar()->hide();
2277 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2279 string const arg = cmd.getArg(0);
2280 if (arg == "scrollbar") {
2281 // hide() is of no help
2282 if (d.current_work_area_->verticalScrollBarPolicy() ==
2283 Qt::ScrollBarAlwaysOff)
2285 d.current_work_area_->setVerticalScrollBarPolicy(
2286 Qt::ScrollBarAsNeeded);
2288 d.current_work_area_->setVerticalScrollBarPolicy(
2289 Qt::ScrollBarAlwaysOff);
2292 if (arg == "statusbar") {
2293 statusBar()->setVisible(!statusBar()->isVisible());
2296 if (arg == "menubar") {
2297 menuBar()->setVisible(!menuBar()->isVisible());
2300 #if QT_VERSION >= 0x040300
2301 if (arg == "frame") {
2303 getContentsMargins(&l, &t, &r, &b);
2304 //are the frames in default state?
2305 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2307 setContentsMargins(-2, -2, -2, -2);
2309 setContentsMargins(0, 0, 0, 0);
2314 if (arg == "fullscreen") {
2319 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2323 void GuiView::toggleFullScreen()
2325 if (isFullScreen()) {
2326 for (int i = 0; i != d.splitter_->count(); ++i)
2327 d.tabWorkArea(i)->setFullScreen(false);
2328 #if QT_VERSION >= 0x040300
2329 setContentsMargins(0, 0, 0, 0);
2331 setWindowState(windowState() ^ Qt::WindowFullScreen);
2334 statusBar()->show();
2337 hideDialogs("prefs", 0);
2338 for (int i = 0; i != d.splitter_->count(); ++i)
2339 d.tabWorkArea(i)->setFullScreen(true);
2340 #if QT_VERSION >= 0x040300
2341 setContentsMargins(-2, -2, -2, -2);
2344 setWindowState(windowState() ^ Qt::WindowFullScreen);
2345 statusBar()->hide();
2347 if (lyxrc.full_screen_toolbars) {
2348 ToolbarMap::iterator end = d.toolbars_.end();
2349 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2354 // give dialogs like the TOC a chance to adapt
2359 Buffer const * GuiView::updateInset(Inset const * inset)
2361 if (!d.current_work_area_)
2365 d.current_work_area_->scheduleRedraw();
2367 return &d.current_work_area_->bufferView().buffer();
2371 void GuiView::restartCursor()
2373 /* When we move around, or type, it's nice to be able to see
2374 * the cursor immediately after the keypress.
2376 if (d.current_work_area_)
2377 d.current_work_area_->startBlinkingCursor();
2379 // Take this occasion to update the other GUI elements.
2385 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2387 if (d.current_work_area_)
2388 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2393 // This list should be kept in sync with the list of insets in
2394 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2395 // dialog should have the same name as the inset.
2396 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2397 // docs in LyXAction.cpp.
2399 char const * const dialognames[] = {
2400 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2401 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2402 "float", "graphics", "include", "index", "index_print", "info", "nomenclature",
2403 "label", "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2404 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2405 "symbols", "tabular", "tabularcreate",
2407 #ifdef HAVE_LIBAIKSAURUS
2411 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2413 char const * const * const end_dialognames =
2414 dialognames + (sizeof(dialognames) / sizeof(char *));
2418 cmpCStr(char const * name) : name_(name) {}
2419 bool operator()(char const * other) {
2420 return strcmp(other, name_) == 0;
2427 bool isValidName(string const & name)
2429 return find_if(dialognames, end_dialognames,
2430 cmpCStr(name.c_str())) != end_dialognames;
2436 void GuiView::resetDialogs()
2438 // Make sure that no LFUN uses any LyXView.
2439 theLyXFunc().setLyXView(0);
2442 constructToolbars();
2443 guiApp->menus().fillMenuBar(menuBar(), this, false);
2445 d.layout_->updateContents(true);
2446 // Now update controls with current buffer.
2447 theLyXFunc().setLyXView(this);
2453 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2455 if (!isValidName(name))
2458 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2460 if (it != d.dialogs_.end()) {
2462 it->second->hideView();
2463 return it->second.get();
2466 Dialog * dialog = build(name);
2467 d.dialogs_[name].reset(dialog);
2468 if (lyxrc.allow_geometry_session)
2469 dialog->restoreSession();
2476 void GuiView::showDialog(string const & name, string const & data,
2484 Dialog * dialog = findOrBuild(name, false);
2486 dialog->showData(data);
2488 d.open_insets_[name] = inset;
2491 catch (ExceptionMessage const & ex) {
2499 bool GuiView::isDialogVisible(string const & name) const
2501 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2502 if (it == d.dialogs_.end())
2504 return it->second.get()->isVisibleView();
2508 void GuiView::hideDialog(string const & name, Inset * inset)
2510 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2511 if (it == d.dialogs_.end())
2514 if (inset && inset != getOpenInset(name))
2517 Dialog * const dialog = it->second.get();
2518 if (dialog->isVisibleView())
2520 d.open_insets_[name] = 0;
2524 void GuiView::disconnectDialog(string const & name)
2526 if (!isValidName(name))
2529 if (d.open_insets_.find(name) != d.open_insets_.end())
2530 d.open_insets_[name] = 0;
2534 Inset * GuiView::getOpenInset(string const & name) const
2536 if (!isValidName(name))
2539 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2540 return it == d.open_insets_.end() ? 0 : it->second;
2544 void GuiView::hideAll() const
2546 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2547 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2549 for(; it != end; ++it)
2550 it->second->hideView();
2554 void GuiView::updateDialogs()
2556 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2557 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2559 for(; it != end; ++it) {
2560 Dialog * dialog = it->second.get();
2561 if (dialog && dialog->isVisibleView())
2562 dialog->checkStatus();
2569 // will be replaced by a proper factory...
2570 Dialog * createGuiAbout(GuiView & lv);
2571 Dialog * createGuiBibitem(GuiView & lv);
2572 Dialog * createGuiBibtex(GuiView & lv);
2573 Dialog * createGuiBox(GuiView & lv);
2574 Dialog * createGuiBranch(GuiView & lv);
2575 Dialog * createGuiChanges(GuiView & lv);
2576 Dialog * createGuiCharacter(GuiView & lv);
2577 Dialog * createGuiCitation(GuiView & lv);
2578 Dialog * createGuiDelimiter(GuiView & lv);
2579 Dialog * createGuiDocument(GuiView & lv);
2580 Dialog * createGuiErrorList(GuiView & lv);
2581 Dialog * createGuiERT(GuiView & lv);
2582 Dialog * createGuiExternal(GuiView & lv);
2583 Dialog * createGuiFloat(GuiView & lv);
2584 Dialog * createGuiGraphics(GuiView & lv);
2585 Dialog * createGuiInclude(GuiView & lv);
2586 Dialog * createGuiIndex(GuiView & lv);
2587 Dialog * createGuiInfo(GuiView & lv);
2588 Dialog * createGuiLabel(GuiView & lv);
2589 Dialog * createGuiListings(GuiView & lv);
2590 Dialog * createGuiLog(GuiView & lv);
2591 Dialog * createGuiMathHSpace(GuiView & lv);
2592 Dialog * createGuiMathMatrix(GuiView & lv);
2593 Dialog * createGuiNomenclature(GuiView & lv);
2594 Dialog * createGuiNote(GuiView & lv);
2595 Dialog * createGuiParagraph(GuiView & lv);
2596 Dialog * createGuiPhantom(GuiView & lv);
2597 Dialog * createGuiPreferences(GuiView & lv);
2598 Dialog * createGuiPrint(GuiView & lv);
2599 Dialog * createGuiPrintindex(GuiView & lv);
2600 Dialog * createGuiRef(GuiView & lv);
2601 Dialog * createGuiSearch(GuiView & lv);
2602 Dialog * createGuiSearchAdv(GuiView & lv);
2603 Dialog * createGuiSendTo(GuiView & lv);
2604 Dialog * createGuiShowFile(GuiView & lv);
2605 Dialog * createGuiSpellchecker(GuiView & lv);
2606 Dialog * createGuiSymbols(GuiView & lv);
2607 Dialog * createGuiTabularCreate(GuiView & lv);
2608 Dialog * createGuiTabular(GuiView & lv);
2609 Dialog * createGuiTexInfo(GuiView & lv);
2610 Dialog * createGuiTextHSpace(GuiView & lv);
2611 Dialog * createGuiToc(GuiView & lv);
2612 Dialog * createGuiThesaurus(GuiView & lv);
2613 Dialog * createGuiHyperlink(GuiView & lv);
2614 Dialog * createGuiVSpace(GuiView & lv);
2615 Dialog * createGuiViewSource(GuiView & lv);
2616 Dialog * createGuiWrap(GuiView & lv);
2619 Dialog * GuiView::build(string const & name)
2621 LASSERT(isValidName(name), return 0);
2623 if (name == "aboutlyx")
2624 return createGuiAbout(*this);
2625 if (name == "bibitem")
2626 return createGuiBibitem(*this);
2627 if (name == "bibtex")
2628 return createGuiBibtex(*this);
2630 return createGuiBox(*this);
2631 if (name == "branch")
2632 return createGuiBranch(*this);
2633 if (name == "changes")
2634 return createGuiChanges(*this);
2635 if (name == "character")
2636 return createGuiCharacter(*this);
2637 if (name == "citation")
2638 return createGuiCitation(*this);
2639 if (name == "document")
2640 return createGuiDocument(*this);
2641 if (name == "errorlist")
2642 return createGuiErrorList(*this);
2644 return createGuiERT(*this);
2645 if (name == "external")
2646 return createGuiExternal(*this);
2648 return createGuiShowFile(*this);
2649 if (name == "findreplace")
2650 return createGuiSearch(*this);
2651 if (name == "findreplaceadv")
2652 return createGuiSearchAdv(*this);
2653 if (name == "float")
2654 return createGuiFloat(*this);
2655 if (name == "graphics")
2656 return createGuiGraphics(*this);
2657 if (name == "include")
2658 return createGuiInclude(*this);
2659 if (name == "index")
2660 return createGuiIndex(*this);
2662 return createGuiInfo(*this);
2663 if (name == "nomenclature")
2664 return createGuiNomenclature(*this);
2665 if (name == "label")
2666 return createGuiLabel(*this);
2668 return createGuiLog(*this);
2669 if (name == "mathdelimiter")
2670 return createGuiDelimiter(*this);
2671 if (name == "mathspace")
2672 return createGuiMathHSpace(*this);
2673 if (name == "mathmatrix")
2674 return createGuiMathMatrix(*this);
2676 return createGuiNote(*this);
2677 if (name == "paragraph")
2678 return createGuiParagraph(*this);
2679 if (name == "phantom")
2680 return createGuiPhantom(*this);
2681 if (name == "prefs")
2682 return createGuiPreferences(*this);
2683 if (name == "print")
2684 return createGuiPrint(*this);
2686 return createGuiRef(*this);
2687 if (name == "sendto")
2688 return createGuiSendTo(*this);
2689 if (name == "space")
2690 return createGuiTextHSpace(*this);
2691 if (name == "spellchecker")
2692 return createGuiSpellchecker(*this);
2693 if (name == "symbols")
2694 return createGuiSymbols(*this);
2695 if (name == "tabular")
2696 return createGuiTabular(*this);
2697 if (name == "tabularcreate")
2698 return createGuiTabularCreate(*this);
2699 if (name == "texinfo")
2700 return createGuiTexInfo(*this);
2701 if (name == "view-source")
2702 return createGuiViewSource(*this);
2703 #ifdef HAVE_LIBAIKSAURUS
2704 if (name == "thesaurus")
2705 return createGuiThesaurus(*this);
2708 return createGuiHyperlink(*this);
2709 if (name == "index_print")
2710 return createGuiPrintindex(*this);
2711 if (name == "listings")
2712 return createGuiListings(*this);
2714 return createGuiToc(*this);
2715 if (name == "vspace")
2716 return createGuiVSpace(*this);
2718 return createGuiWrap(*this);
2724 } // namespace frontend
2727 #include "moc_GuiView.cpp"