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"
26 #include "GuiToolbar.h"
30 #include "qt_helpers.h"
32 #include "frontends/alert.h"
34 #include "buffer_funcs.h"
36 #include "BufferList.h"
37 #include "BufferParams.h"
38 #include "BufferView.h"
39 #include "Converter.h"
42 #include "ErrorList.h"
44 #include "FuncStatus.h"
45 #include "FuncRequest.h"
53 #include "Paragraph.h"
54 #include "TextClass.h"
59 #include "support/convert.h"
60 #include "support/debug.h"
61 #include "support/ExceptionMessage.h"
62 #include "support/FileName.h"
63 #include "support/filetools.h"
64 #include "support/gettext.h"
65 #include "support/ForkedCalls.h"
66 #include "support/lassert.h"
67 #include "support/lstrings.h"
68 #include "support/os.h"
69 #include "support/Package.h"
70 #include "support/Timeout.h"
73 #include <QApplication>
74 #include <QCloseEvent>
76 #include <QDesktopWidget>
77 #include <QDragEnterEvent>
85 #include <QPushButton>
89 #include <QStackedWidget>
96 #include <boost/bind.hpp>
98 #ifdef HAVE_SYS_TIME_H
99 # include <sys/time.h>
106 using namespace lyx::support;
113 class BackgroundWidget : public QWidget
118 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
119 /// The text to be written on top of the pixmap
120 QString const text = lyx_version ?
121 qt_("version ") + lyx_version : qt_("unknown version");
122 splash_ = getPixmap("images/", "banner", "png");
124 QPainter pain(&splash_);
125 pain.setPen(QColor(0, 0, 0));
127 // The font used to display the version info
128 font.setStyleHint(QFont::SansSerif);
129 font.setWeight(QFont::Bold);
130 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
132 pain.drawText(260, 15, text);
135 void paintEvent(QPaintEvent *)
137 int x = (width() - splash_.width()) / 2;
138 int y = (height() - splash_.height()) / 2;
140 pain.drawPixmap(x, y, splash_);
148 /// Toolbar store providing access to individual toolbars by name.
149 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
151 typedef boost::shared_ptr<Dialog> DialogPtr;
156 struct GuiView::GuiViewPrivate
159 : current_work_area_(0), current_main_work_area_(0),
160 layout_(0), autosave_timeout_(5000),
163 // hardcode here the platform specific icon size
164 smallIconSize = 14; // scaling problems
165 normalIconSize = 20; // ok, default
166 bigIconSize = 26; // better for some math icons
168 splitter_ = new QSplitter;
169 bg_widget_ = new BackgroundWidget;
170 stack_widget_ = new QStackedWidget;
171 stack_widget_->addWidget(bg_widget_);
172 stack_widget_->addWidget(splitter_);
180 delete stack_widget_;
183 QMenu * toolBarPopup(GuiView * parent)
185 // FIXME: translation
186 QMenu * menu = new QMenu(parent);
187 QActionGroup * iconSizeGroup = new QActionGroup(parent);
189 QAction * smallIcons = new QAction(iconSizeGroup);
190 smallIcons->setText(qt_("Small-sized icons"));
191 smallIcons->setCheckable(true);
192 QObject::connect(smallIcons, SIGNAL(triggered()),
193 parent, SLOT(smallSizedIcons()));
194 menu->addAction(smallIcons);
196 QAction * normalIcons = new QAction(iconSizeGroup);
197 normalIcons->setText(qt_("Normal-sized icons"));
198 normalIcons->setCheckable(true);
199 QObject::connect(normalIcons, SIGNAL(triggered()),
200 parent, SLOT(normalSizedIcons()));
201 menu->addAction(normalIcons);
203 QAction * bigIcons = new QAction(iconSizeGroup);
204 bigIcons->setText(qt_("Big-sized icons"));
205 bigIcons->setCheckable(true);
206 QObject::connect(bigIcons, SIGNAL(triggered()),
207 parent, SLOT(bigSizedIcons()));
208 menu->addAction(bigIcons);
210 unsigned int cur = parent->iconSize().width();
211 if ( cur == parent->d.smallIconSize)
212 smallIcons->setChecked(true);
213 else if (cur == parent->d.normalIconSize)
214 normalIcons->setChecked(true);
215 else if (cur == parent->d.bigIconSize)
216 bigIcons->setChecked(true);
223 stack_widget_->setCurrentWidget(bg_widget_);
224 bg_widget_->setUpdatesEnabled(true);
227 TabWorkArea * tabWorkArea(int i)
229 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
232 TabWorkArea * currentTabWorkArea()
234 if (splitter_->count() == 1)
235 // The first TabWorkArea is always the first one, if any.
236 return tabWorkArea(0);
238 for (int i = 0; i != splitter_->count(); ++i) {
239 TabWorkArea * twa = tabWorkArea(i);
240 if (current_main_work_area_ == twa->currentWorkArea())
244 // None has the focus so we just take the first one.
245 return tabWorkArea(0);
249 GuiWorkArea * current_work_area_;
250 GuiWorkArea * current_main_work_area_;
251 QSplitter * splitter_;
252 QStackedWidget * stack_widget_;
253 BackgroundWidget * bg_widget_;
255 ToolbarMap toolbars_;
256 /// The main layout box.
258 * \warning Don't Delete! The layout box is actually owned by
259 * whichever toolbar contains it. All the GuiView class needs is a
260 * means of accessing it.
262 * FIXME: replace that with a proper model so that we are not limited
263 * to only one dialog.
265 GuiLayoutBox * layout_;
268 map<string, Inset *> open_insets_;
271 map<string, DialogPtr> dialogs_;
273 unsigned int smallIconSize;
274 unsigned int normalIconSize;
275 unsigned int bigIconSize;
277 QTimer statusbar_timer_;
278 /// auto-saving of buffers
279 Timeout autosave_timeout_;
280 /// flag against a race condition due to multiclicks, see bug #1119
284 TocModels toc_models_;
288 GuiView::GuiView(int id)
289 : d(*new GuiViewPrivate), id_(id), closing_(false)
291 // GuiToolbars *must* be initialised before the menu bar.
292 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
295 // set ourself as the current view. This is needed for the menu bar
296 // filling, at least for the static special menu item on Mac. Otherwise
297 // they are greyed out.
298 theLyXFunc().setLyXView(this);
300 // Fill up the menu bar.
301 guiApp->menus().fillMenuBar(menuBar(), this, true);
303 setCentralWidget(d.stack_widget_);
305 // Start autosave timer
306 if (lyxrc.autosave) {
307 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
308 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
309 d.autosave_timeout_.start();
311 connect(&d.statusbar_timer_, SIGNAL(timeout()),
312 this, SLOT(clearMessage()));
314 // We don't want to keep the window in memory if it is closed.
315 setAttribute(Qt::WA_DeleteOnClose, true);
317 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
318 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
319 // since the icon is provided in the application bundle.
320 setWindowIcon(getPixmap("images/", "lyx", "png"));
324 setAcceptDrops(true);
326 statusBar()->setSizeGripEnabled(true);
328 // Forbid too small unresizable window because it can happen
329 // with some window manager under X11.
330 setMinimumSize(300, 200);
332 if (lyxrc.allow_geometry_session) {
333 // Now take care of session management.
338 // no session handling, default to a sane size.
339 setGeometry(50, 50, 690, 510);
342 // clear session data if any.
344 settings.remove("views");
354 void GuiView::saveLayout() const
357 settings.beginGroup("views");
358 settings.beginGroup(QString::number(id_));
360 settings.setValue("pos", pos());
361 settings.setValue("size", size());
363 settings.setValue("geometry", saveGeometry());
365 settings.setValue("layout", saveState(0));
366 settings.setValue("icon_size", iconSize());
370 bool GuiView::restoreLayout()
373 settings.beginGroup("views");
374 settings.beginGroup(QString::number(id_));
375 QString const icon_key = "icon_size";
376 if (!settings.contains(icon_key))
379 setIconSize(settings.value(icon_key).toSize());
381 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
382 QSize size = settings.value("size", QSize(690, 510)).toSize();
386 if (!restoreGeometry(settings.value("geometry").toByteArray()))
387 setGeometry(50, 50, 690, 510);
389 // Make sure layout is correctly oriented.
390 setLayoutDirection(qApp->layoutDirection());
392 // Allow the toc and view-source dock widget to be restored if needed.
394 if ((d = findOrBuild("toc", true)))
397 if ((d = findOrBuild("view-source", true)))
400 if (!restoreState(settings.value("layout").toByteArray(), 0))
407 GuiToolbar * GuiView::toolbar(string const & name)
409 ToolbarMap::iterator it = d.toolbars_.find(name);
410 if (it != d.toolbars_.end())
413 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
414 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
419 void GuiView::constructToolbars()
421 ToolbarMap::iterator it = d.toolbars_.begin();
422 for (; it != d.toolbars_.end(); ++it)
427 // extracts the toolbars from the backend
428 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
429 Toolbars::Infos::iterator end = guiApp->toolbars().end();
430 for (; cit != end; ++cit)
431 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
435 void GuiView::initToolbars()
437 // extracts the toolbars from the backend
438 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
439 Toolbars::Infos::iterator end = guiApp->toolbars().end();
440 for (; cit != end; ++cit) {
441 GuiToolbar * tb = toolbar(cit->name);
444 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
446 tb->setVisible(false);
447 tb->setVisibility(visibility);
449 if (visibility & Toolbars::TOP) {
451 addToolBarBreak(Qt::TopToolBarArea);
452 addToolBar(Qt::TopToolBarArea, tb);
455 if (visibility & Toolbars::BOTTOM) {
456 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
457 #if (QT_VERSION >= 0x040202)
458 addToolBarBreak(Qt::BottomToolBarArea);
460 addToolBar(Qt::BottomToolBarArea, tb);
463 if (visibility & Toolbars::LEFT) {
464 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
465 #if (QT_VERSION >= 0x040202)
466 addToolBarBreak(Qt::LeftToolBarArea);
468 addToolBar(Qt::LeftToolBarArea, tb);
471 if (visibility & Toolbars::RIGHT) {
472 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
473 #if (QT_VERSION >= 0x040202)
474 addToolBarBreak(Qt::RightToolBarArea);
476 addToolBar(Qt::RightToolBarArea, tb);
479 if (visibility & Toolbars::ON)
480 tb->setVisible(true);
485 TocModels & GuiView::tocModels()
487 return d.toc_models_;
491 void GuiView::setFocus()
493 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
494 // Make sure LyXFunc points to the correct view.
495 guiApp->setCurrentView(this);
496 theLyXFunc().setLyXView(this);
497 QMainWindow::setFocus();
498 if (d.current_work_area_)
499 d.current_work_area_->setFocus();
503 QMenu * GuiView::createPopupMenu()
505 return d.toolBarPopup(this);
509 void GuiView::showEvent(QShowEvent * e)
511 LYXERR(Debug::GUI, "Passed Geometry "
512 << size().height() << "x" << size().width()
513 << "+" << pos().x() << "+" << pos().y());
515 if (d.splitter_->count() == 0)
516 // No work area, switch to the background widget.
519 QMainWindow::showEvent(e);
523 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
524 ** is responsibility of the container (e.g., dialog)
526 void GuiView::closeEvent(QCloseEvent * close_event)
528 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
531 // it can happen that this event arrives without selecting the view,
532 // e.g. when clicking the close button on a background window.
534 setCurrentWorkArea(currentMainWorkArea());
535 while (GuiWorkArea * wa = currentMainWorkArea()) {
536 Buffer * b = &wa->bufferView().buffer();
538 // This is a child document, just close the tab
539 // after saving but keep the file loaded.
540 if (!closeBuffer(*b, true)) {
542 close_event->ignore();
548 vector<Buffer *> clist = b->getChildren();
549 for (vector<Buffer *>::const_iterator it = clist.begin();
550 it != clist.end(); ++it) {
551 if ((*it)->isClean())
554 // If a child is dirty, do not close
555 // without user intervention
556 if (!closeBuffer(*c, false)) {
558 close_event->ignore();
563 QList<int> const ids = guiApp->viewIds();
564 for (int i = 0; i != ids.size(); ++i) {
567 if (guiApp->view(ids[i]).workArea(*b)) {
568 // FIXME 1: should we put an alert box here that the buffer
569 // is viewed elsewhere?
570 // FIXME 2: should we try to save this buffer in any case?
573 // This buffer is also opened in another view, so
574 // close the associated work area...
576 // ... but don't close the buffer.
581 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
582 if (b && !closeBuffer(*b, true)) {
584 close_event->ignore();
589 // Make sure that nothing will use this close to be closed View.
590 guiApp->unregisterView(this);
592 if (isFullScreen()) {
593 // Switch off fullscreen before closing.
598 // Make sure the timer time out will not trigger a statusbar update.
599 d.statusbar_timer_.stop();
601 // Saving fullscreen requires additional tweaks in the toolbar code.
602 // It wouldn't also work under linux natively.
603 if (lyxrc.allow_geometry_session) {
604 // Save this window geometry and layout.
606 // Then the toolbar private states.
607 ToolbarMap::iterator end = d.toolbars_.end();
608 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
609 it->second->saveSession();
610 // Now take care of all other dialogs:
611 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
612 for (; it!= d.dialogs_.end(); ++it)
613 it->second->saveSession();
616 close_event->accept();
620 void GuiView::dragEnterEvent(QDragEnterEvent * event)
622 if (event->mimeData()->hasUrls())
624 /// \todo Ask lyx-devel is this is enough:
625 /// if (event->mimeData()->hasFormat("text/plain"))
626 /// event->acceptProposedAction();
630 void GuiView::dropEvent(QDropEvent * event)
632 QList<QUrl> files = event->mimeData()->urls();
636 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
637 for (int i = 0; i != files.size(); ++i) {
638 string const file = os::internal_path(fromqstr(
639 files.at(i).toLocalFile()));
641 // Asynchronously post the event. DropEvent usually come
642 // from the BufferView. But reloading a file might close
643 // the BufferView from within its own event handler.
644 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
651 void GuiView::message(docstring const & str)
653 if (ForkedProcess::iAmAChild())
656 statusBar()->showMessage(toqstr(str));
657 d.statusbar_timer_.stop();
658 d.statusbar_timer_.start(3000);
662 void GuiView::smallSizedIcons()
664 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
668 void GuiView::normalSizedIcons()
670 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
674 void GuiView::bigSizedIcons()
676 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
680 void GuiView::clearMessage()
684 theLyXFunc().setLyXView(this);
685 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
686 d.statusbar_timer_.stop();
690 void GuiView::updateWindowTitle(GuiWorkArea * wa)
692 if (wa != d.current_work_area_)
694 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
695 setWindowIconText(wa->windowIconText());
699 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
702 disconnectBufferView();
703 connectBufferView(wa->bufferView());
704 connectBuffer(wa->bufferView().buffer());
705 d.current_work_area_ = wa;
706 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
707 this, SLOT(updateWindowTitle(GuiWorkArea *)));
708 updateWindowTitle(wa);
712 // The document settings needs to be reinitialised.
713 updateDialog("document", "");
715 // Buffer-dependent dialogs must be updated. This is done here because
716 // some dialogs require buffer()->text.
721 void GuiView::on_lastWorkAreaRemoved()
724 // We already are in a close event. Nothing more to do.
727 if (d.splitter_->count() > 1)
728 // We have a splitter so don't close anything.
731 // Reset and updates the dialogs.
732 d.toc_models_.reset(0);
733 updateDialog("document", "");
736 resetWindowTitleAndIconText();
738 if (lyxrc.open_buffers_in_tabs)
739 // Nothing more to do, the window should stay open.
742 if (guiApp->viewIds().size() > 1) {
748 // On Mac we also close the last window because the application stay
749 // resident in memory. On other platforms we don't close the last
750 // window because this would quit the application.
756 void GuiView::updateStatusBar()
758 // let the user see the explicit message
759 if (d.statusbar_timer_.isActive())
762 theLyXFunc().setLyXView(this);
763 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
767 bool GuiView::hasFocus() const
769 return qApp->activeWindow() == this;
773 bool GuiView::event(QEvent * e)
777 // Useful debug code:
778 //case QEvent::ActivationChange:
779 //case QEvent::WindowDeactivate:
780 //case QEvent::Paint:
781 //case QEvent::Enter:
782 //case QEvent::Leave:
783 //case QEvent::HoverEnter:
784 //case QEvent::HoverLeave:
785 //case QEvent::HoverMove:
786 //case QEvent::StatusTip:
787 //case QEvent::DragEnter:
788 //case QEvent::DragLeave:
792 case QEvent::WindowActivate: {
793 if (this == guiApp->currentView()) {
795 return QMainWindow::event(e);
797 guiApp->setCurrentView(this);
798 theLyXFunc().setLyXView(this);
799 if (d.current_work_area_) {
800 BufferView & bv = d.current_work_area_->bufferView();
801 connectBufferView(bv);
802 connectBuffer(bv.buffer());
803 // The document structure, name and dialogs might have
804 // changed in another view.
806 // The document settings needs to be reinitialised.
807 updateDialog("document", "");
810 resetWindowTitleAndIconText();
813 return QMainWindow::event(e);
816 case QEvent::ShortcutOverride: {
820 if (isFullScreen() && menuBar()->isHidden()) {
821 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
822 // FIXME: we should also try to detect special LyX shortcut such as
823 // Alt-P and Alt-M. Right now there is a hack in
824 // GuiWorkArea::processKeySym() that hides again the menubar for
826 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
828 return QMainWindow::event(e);
833 if (d.current_work_area_)
834 // Nothing special to do.
835 return QMainWindow::event(e);
837 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
838 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
840 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
841 || ke->key() == Qt::Key_Backtab)
842 return QMainWindow::event(e);
844 // Allow processing of shortcuts that are allowed even when no Buffer
846 theLyXFunc().setLyXView(this);
848 setKeySymbol(&sym, ke);
849 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
855 return QMainWindow::event(e);
859 void GuiView::resetWindowTitleAndIconText()
861 setWindowTitle(qt_("LyX"));
862 setWindowIconText(qt_("LyX"));
865 bool GuiView::focusNextPrevChild(bool /*next*/)
872 void GuiView::setBusy(bool busy)
874 if (d.current_work_area_) {
875 d.current_work_area_->setUpdatesEnabled(!busy);
877 d.current_work_area_->stopBlinkingCursor();
879 d.current_work_area_->startBlinkingCursor();
883 QApplication::setOverrideCursor(Qt::WaitCursor);
885 QApplication::restoreOverrideCursor();
889 GuiWorkArea * GuiView::workArea(Buffer & buffer)
891 if (currentWorkArea()
892 && ¤tWorkArea()->bufferView().buffer() == &buffer)
893 return (GuiWorkArea *) currentWorkArea();
894 if (TabWorkArea * twa = d.currentTabWorkArea())
895 return twa->workArea(buffer);
900 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
902 // Automatically create a TabWorkArea if there are none yet.
903 TabWorkArea * tab_widget = d.splitter_->count()
904 ? d.currentTabWorkArea() : addTabWorkArea();
905 return tab_widget->addWorkArea(buffer, *this);
909 TabWorkArea * GuiView::addTabWorkArea()
911 TabWorkArea * twa = new TabWorkArea;
912 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
913 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
914 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
915 this, SLOT(on_lastWorkAreaRemoved()));
917 d.splitter_->addWidget(twa);
918 d.stack_widget_->setCurrentWidget(d.splitter_);
923 GuiWorkArea const * GuiView::currentWorkArea() const
925 return d.current_work_area_;
929 GuiWorkArea * GuiView::currentWorkArea()
931 return d.current_work_area_;
935 GuiWorkArea const * GuiView::currentMainWorkArea() const
937 if (d.currentTabWorkArea() == NULL)
939 return d.currentTabWorkArea()->currentWorkArea();
943 GuiWorkArea * GuiView::currentMainWorkArea()
945 if (d.currentTabWorkArea() == NULL)
947 return d.currentTabWorkArea()->currentWorkArea();
951 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
953 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
955 d.current_work_area_ = NULL;
959 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
963 theGuiApp()->setCurrentView(this);
964 d.current_work_area_ = wa;
965 for (int i = 0; i != d.splitter_->count(); ++i) {
966 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
967 //if (d.current_main_work_area_)
968 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
969 d.current_main_work_area_ = wa;
970 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
971 //d.current_main_work_area_->setLineWidth(2);
972 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
976 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
977 on_currentWorkAreaChanged(wa);
978 BufferView & bv = wa->bufferView();
979 bv.cursor().fixIfBroken();
981 wa->setUpdatesEnabled(true);
982 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
986 void GuiView::removeWorkArea(GuiWorkArea * wa)
989 if (wa == d.current_work_area_) {
991 disconnectBufferView();
992 d.current_work_area_ = 0;
993 d.current_main_work_area_ = 0;
996 bool found_twa = false;
997 for (int i = 0; i != d.splitter_->count(); ++i) {
998 TabWorkArea * twa = d.tabWorkArea(i);
999 if (twa->removeWorkArea(wa)) {
1000 // Found in this tab group, and deleted the GuiWorkArea.
1002 if (twa->count() != 0) {
1003 if (d.current_work_area_ == 0)
1004 // This means that we are closing the current GuiWorkArea, so
1005 // switch to the next GuiWorkArea in the found TabWorkArea.
1006 setCurrentWorkArea(twa->currentWorkArea());
1008 // No more WorkAreas in this tab group, so delete it.
1015 // It is not a tabbed work area (i.e., the search work area), so it
1016 // should be deleted by other means.
1017 LASSERT(found_twa, /* */);
1019 if (d.current_work_area_ == 0) {
1020 if (d.splitter_->count() != 0) {
1021 TabWorkArea * twa = d.currentTabWorkArea();
1022 setCurrentWorkArea(twa->currentWorkArea());
1024 // No more work areas, switch to the background widget.
1025 setCurrentWorkArea(0);
1031 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1037 void GuiView::updateLayoutList()
1040 d.layout_->updateContents(false);
1044 void GuiView::updateToolbars()
1046 ToolbarMap::iterator end = d.toolbars_.end();
1047 if (d.current_work_area_) {
1049 d.current_work_area_->bufferView().cursor().inMathed();
1051 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1053 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1054 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1055 bool const mathmacrotemplate =
1056 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1058 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1059 it->second->update(math, table, review, mathmacrotemplate);
1061 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1062 it->second->update(false, false, false, false);
1066 Buffer * GuiView::buffer()
1068 if (d.current_work_area_)
1069 return &d.current_work_area_->bufferView().buffer();
1074 Buffer const * GuiView::buffer() const
1076 if (d.current_work_area_)
1077 return &d.current_work_area_->bufferView().buffer();
1082 void GuiView::setBuffer(Buffer * newBuffer)
1084 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1085 LASSERT(newBuffer, return);
1088 GuiWorkArea * wa = workArea(*newBuffer);
1090 newBuffer->masterBuffer()->updateLabels();
1091 wa = addWorkArea(*newBuffer);
1093 //Disconnect the old buffer...there's no new one.
1096 connectBuffer(*newBuffer);
1097 connectBufferView(wa->bufferView());
1098 setCurrentWorkArea(wa);
1104 void GuiView::connectBuffer(Buffer & buf)
1106 buf.setGuiDelegate(this);
1110 void GuiView::disconnectBuffer()
1112 if (d.current_work_area_)
1113 d.current_work_area_->bufferView().setGuiDelegate(0);
1117 void GuiView::connectBufferView(BufferView & bv)
1119 bv.setGuiDelegate(this);
1123 void GuiView::disconnectBufferView()
1125 if (d.current_work_area_)
1126 d.current_work_area_->bufferView().setGuiDelegate(0);
1130 void GuiView::errors(string const & error_type)
1132 ErrorList & el = buffer()->errorList(error_type);
1134 showDialog("errorlist", error_type);
1138 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1140 d.toc_models_.updateItem(toqstr(type), dit);
1144 void GuiView::structureChanged()
1146 d.toc_models_.reset(view());
1147 // Navigator needs more than a simple update in this case. It needs to be
1149 updateDialog("toc", "");
1153 void GuiView::updateDialog(string const & name, string const & data)
1155 if (!isDialogVisible(name))
1158 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1159 if (it == d.dialogs_.end())
1162 Dialog * const dialog = it->second.get();
1163 if (dialog->isVisibleView())
1164 dialog->initialiseParams(data);
1168 BufferView * GuiView::view()
1170 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1174 void GuiView::autoSave()
1176 LYXERR(Debug::INFO, "Running autoSave()");
1179 view()->buffer().autoSave();
1183 void GuiView::resetAutosaveTimers()
1186 d.autosave_timeout_.restart();
1190 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1193 Buffer * buf = buffer();
1195 /* In LyX/Mac, when a dialog is open, the menus of the
1196 application can still be accessed without giving focus to
1197 the main window. In this case, we want to disable the menu
1198 entries that are buffer-related.
1200 Note that this code is not perfect, as bug 1941 attests:
1201 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1203 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1206 switch(cmd.action) {
1207 case LFUN_BUFFER_WRITE:
1208 enable = buf && (buf->isUnnamed() || !buf->isClean());
1211 case LFUN_BUFFER_WRITE_AS:
1215 case LFUN_SPLIT_VIEW:
1216 if (cmd.getArg(0) == "vertical")
1217 enable = buf && (d.splitter_->count() == 1 ||
1218 d.splitter_->orientation() == Qt::Vertical);
1220 enable = buf && (d.splitter_->count() == 1 ||
1221 d.splitter_->orientation() == Qt::Horizontal);
1224 case LFUN_CLOSE_TAB_GROUP:
1225 enable = d.currentTabWorkArea();
1228 case LFUN_TOOLBAR_TOGGLE:
1229 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1230 flag.setOnOff(t->isVisible());
1233 case LFUN_UI_TOGGLE:
1234 flag.setOnOff(isFullScreen());
1237 case LFUN_DIALOG_TOGGLE:
1238 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1239 // fall through to set "enable"
1240 case LFUN_DIALOG_SHOW: {
1241 string const name = cmd.getArg(0);
1243 enable = name == "aboutlyx"
1244 || name == "file" //FIXME: should be removed.
1246 || name == "texinfo";
1247 else if (name == "print")
1248 enable = buf->isExportable("dvi")
1249 && lyxrc.print_command != "none";
1250 else if (name == "character") {
1254 InsetCode ic = view()->cursor().inset().lyxCode();
1255 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1258 else if (name == "symbols") {
1259 if (!view() || view()->cursor().inMathed())
1262 InsetCode ic = view()->cursor().inset().lyxCode();
1263 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1266 else if (name == "latexlog")
1267 enable = FileName(buf->logName()).isReadableFile();
1268 else if (name == "spellchecker")
1269 #if defined (USE_ASPELL)
1270 enable = !buf->isReadonly();
1274 else if (name == "vclog")
1275 enable = buf->lyxvc().inUse();
1279 case LFUN_DIALOG_UPDATE: {
1280 string const name = cmd.getArg(0);
1282 enable = name == "prefs";
1286 case LFUN_INSET_APPLY: {
1287 string const name = cmd.getArg(0);
1288 Inset * inset = getOpenInset(name);
1290 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1292 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1293 // Every inset is supposed to handle this
1294 LASSERT(false, break);
1298 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1299 flag |= lyx::getStatus(fr);
1301 enable = flag.enabled();
1305 case LFUN_COMPLETION_INLINE:
1306 if (!d.current_work_area_
1307 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1311 case LFUN_COMPLETION_POPUP:
1312 if (!d.current_work_area_
1313 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1317 case LFUN_COMPLETION_COMPLETE:
1318 if (!d.current_work_area_
1319 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1323 case LFUN_COMPLETION_ACCEPT:
1324 if (!d.current_work_area_
1325 || (!d.current_work_area_->completer().popupVisible()
1326 && !d.current_work_area_->completer().inlineVisible()
1327 && !d.current_work_area_->completer().completionAvailable()))
1331 case LFUN_COMPLETION_CANCEL:
1332 if (!d.current_work_area_
1333 || (!d.current_work_area_->completer().popupVisible()
1334 && !d.current_work_area_->completer().inlineVisible()))
1338 case LFUN_BUFFER_ZOOM_OUT:
1339 enable = buf && lyxrc.zoom > 10;
1342 case LFUN_BUFFER_ZOOM_IN:
1351 flag.setEnabled(false);
1357 static FileName selectTemplateFile()
1359 FileDialog dlg(qt_("Select template file"));
1360 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1361 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1363 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1364 QStringList(qt_("LyX Documents (*.lyx)")));
1366 if (result.first == FileDialog::Later)
1368 if (result.second.isEmpty())
1370 return FileName(fromqstr(result.second));
1374 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1378 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1381 message(_("Document not loaded."));
1386 setBuffer(newBuffer);
1388 // scroll to the position when the file was last closed
1389 if (lyxrc.use_lastfilepos) {
1390 LastFilePosSection::FilePos filepos =
1391 theSession().lastFilePos().load(filename);
1392 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1396 theSession().lastFiles().add(filename);
1403 void GuiView::openDocument(string const & fname)
1405 string initpath = lyxrc.document_path;
1408 string const trypath = buffer()->filePath();
1409 // If directory is writeable, use this as default.
1410 if (FileName(trypath).isDirWritable())
1416 if (fname.empty()) {
1417 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1418 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1419 dlg.setButton2(qt_("Examples|#E#e"),
1420 toqstr(addPath(package().system_support().absFilename(), "examples")));
1422 QStringList filter(qt_("LyX Documents (*.lyx)"));
1423 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1424 << qt_("LyX-1.4.x Documents (*.lyx14)")
1425 << qt_("LyX-1.5.x Documents (*.lyx15)")
1426 << qt_("LyX-1.6.x Documents (*.lyx16)");
1427 FileDialog::Result result =
1428 dlg.open(toqstr(initpath), filter);
1430 if (result.first == FileDialog::Later)
1433 filename = fromqstr(result.second);
1435 // check selected filename
1436 if (filename.empty()) {
1437 message(_("Canceled."));
1443 // get absolute path of file and add ".lyx" to the filename if
1445 FileName const fullname =
1446 fileSearch(string(), filename, "lyx", support::may_not_exist);
1447 if (!fullname.empty())
1448 filename = fullname.absFilename();
1450 if (!fullname.onlyPath().isDirectory()) {
1451 Alert::warning(_("Invalid filename"),
1452 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1453 from_utf8(fullname.absFilename())));
1456 // if the file doesn't exist, let the user create one
1457 if (!fullname.exists()) {
1458 // the user specifically chose this name. Believe him.
1459 Buffer * const b = newFile(filename, string(), true);
1465 docstring const disp_fn = makeDisplayPath(filename);
1466 message(bformat(_("Opening document %1$s..."), disp_fn));
1469 Buffer * buf = loadDocument(fullname);
1471 buf->updateLabels();
1473 buf->errors("Parse");
1474 str2 = bformat(_("Document %1$s opened."), disp_fn);
1475 if (buf->lyxvc().inUse())
1476 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1477 " " + _("Version control detected.");
1479 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1484 // FIXME: clean that
1485 static bool import(GuiView * lv, FileName const & filename,
1486 string const & format, ErrorList & errorList)
1488 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1490 string loader_format;
1491 vector<string> loaders = theConverters().loaders();
1492 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1493 for (vector<string>::const_iterator it = loaders.begin();
1494 it != loaders.end(); ++it) {
1495 if (!theConverters().isReachable(format, *it))
1498 string const tofile =
1499 support::changeExtension(filename.absFilename(),
1500 formats.extension(*it));
1501 if (!theConverters().convert(0, filename, FileName(tofile),
1502 filename, format, *it, errorList))
1504 loader_format = *it;
1507 if (loader_format.empty()) {
1508 frontend::Alert::error(_("Couldn't import file"),
1509 bformat(_("No information for importing the format %1$s."),
1510 formats.prettyName(format)));
1514 loader_format = format;
1516 if (loader_format == "lyx") {
1517 Buffer * buf = lv->loadDocument(lyxfile);
1520 buf->updateLabels();
1522 buf->errors("Parse");
1524 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1528 bool as_paragraphs = loader_format == "textparagraph";
1529 string filename2 = (loader_format == format) ? filename.absFilename()
1530 : support::changeExtension(filename.absFilename(),
1531 formats.extension(loader_format));
1532 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1533 theLyXFunc().setLyXView(lv);
1534 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1541 void GuiView::importDocument(string const & argument)
1544 string filename = split(argument, format, ' ');
1546 LYXERR(Debug::INFO, format << " file: " << filename);
1548 // need user interaction
1549 if (filename.empty()) {
1550 string initpath = lyxrc.document_path;
1552 Buffer const * buf = buffer();
1554 string const trypath = buf->filePath();
1555 // If directory is writeable, use this as default.
1556 if (FileName(trypath).isDirWritable())
1560 docstring const text = bformat(_("Select %1$s file to import"),
1561 formats.prettyName(format));
1563 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1564 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1565 dlg.setButton2(qt_("Examples|#E#e"),
1566 toqstr(addPath(package().system_support().absFilename(), "examples")));
1568 docstring filter = formats.prettyName(format);
1571 filter += from_utf8(formats.extension(format));
1574 FileDialog::Result result =
1575 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1577 if (result.first == FileDialog::Later)
1580 filename = fromqstr(result.second);
1582 // check selected filename
1583 if (filename.empty())
1584 message(_("Canceled."));
1587 if (filename.empty())
1590 // get absolute path of file
1591 FileName const fullname(support::makeAbsPath(filename));
1593 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1595 // Check if the document already is open
1596 Buffer * buf = theBufferList().getBuffer(lyxfile);
1599 if (!closeBuffer()) {
1600 message(_("Canceled."));
1605 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1607 // if the file exists already, and we didn't do
1608 // -i lyx thefile.lyx, warn
1609 if (lyxfile.exists() && fullname != lyxfile) {
1611 docstring text = bformat(_("The document %1$s already exists.\n\n"
1612 "Do you want to overwrite that document?"), displaypath);
1613 int const ret = Alert::prompt(_("Overwrite document?"),
1614 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1617 message(_("Canceled."));
1622 message(bformat(_("Importing %1$s..."), displaypath));
1623 ErrorList errorList;
1624 if (import(this, fullname, format, errorList))
1625 message(_("imported."));
1627 message(_("file not imported!"));
1629 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1633 void GuiView::newDocument(string const & filename, bool from_template)
1635 FileName initpath(lyxrc.document_path);
1636 Buffer * buf = buffer();
1638 FileName const trypath(buf->filePath());
1639 // If directory is writeable, use this as default.
1640 if (trypath.isDirWritable())
1644 string templatefile;
1645 if (from_template) {
1646 templatefile = selectTemplateFile().absFilename();
1647 if (templatefile.empty())
1652 if (filename.empty())
1653 b = newUnnamedFile(templatefile, initpath);
1655 b = newFile(filename, templatefile, true);
1660 // If no new document could be created, it is unsure
1661 // whether there is a valid BufferView.
1663 // Ensure the cursor is correctly positioned on screen.
1664 view()->showCursor();
1668 void GuiView::insertLyXFile(docstring const & fname)
1670 BufferView * bv = view();
1675 FileName filename(to_utf8(fname));
1677 if (!filename.empty()) {
1678 bv->insertLyXFile(filename);
1682 // Launch a file browser
1684 string initpath = lyxrc.document_path;
1685 string const trypath = bv->buffer().filePath();
1686 // If directory is writeable, use this as default.
1687 if (FileName(trypath).isDirWritable())
1691 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1692 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1693 dlg.setButton2(qt_("Examples|#E#e"),
1694 toqstr(addPath(package().system_support().absFilename(),
1697 FileDialog::Result result = dlg.open(toqstr(initpath),
1698 QStringList(qt_("LyX Documents (*.lyx)")));
1700 if (result.first == FileDialog::Later)
1704 filename.set(fromqstr(result.second));
1706 // check selected filename
1707 if (filename.empty()) {
1708 // emit message signal.
1709 message(_("Canceled."));
1713 bv->insertLyXFile(filename);
1717 void GuiView::insertPlaintextFile(docstring const & fname,
1720 BufferView * bv = view();
1725 FileName filename(to_utf8(fname));
1727 if (!filename.empty()) {
1728 bv->insertPlaintextFile(filename, asParagraph);
1732 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1733 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1735 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1736 QStringList(qt_("All Files (*)")));
1738 if (result.first == FileDialog::Later)
1742 filename.set(fromqstr(result.second));
1744 // check selected filename
1745 if (filename.empty()) {
1746 // emit message signal.
1747 message(_("Canceled."));
1751 bv->insertPlaintextFile(filename, asParagraph);
1755 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1757 FileName fname = b.fileName();
1758 FileName const oldname = fname;
1760 if (!newname.empty()) {
1762 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1764 // Switch to this Buffer.
1767 // No argument? Ask user through dialog.
1769 FileDialog dlg(qt_("Choose a filename to save document as"),
1770 LFUN_BUFFER_WRITE_AS);
1771 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1772 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1774 if (!isLyXFilename(fname.absFilename()))
1775 fname.changeExtension(".lyx");
1777 FileDialog::Result result =
1778 dlg.save(toqstr(fname.onlyPath().absFilename()),
1779 QStringList(qt_("LyX Documents (*.lyx)")),
1780 toqstr(fname.onlyFileName()));
1782 if (result.first == FileDialog::Later)
1785 fname.set(fromqstr(result.second));
1790 if (!isLyXFilename(fname.absFilename()))
1791 fname.changeExtension(".lyx");
1794 if (FileName(fname).exists()) {
1795 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1796 docstring text = bformat(_("The document %1$s already "
1797 "exists.\n\nDo you want to "
1798 "overwrite that document?"),
1800 int const ret = Alert::prompt(_("Overwrite document?"),
1801 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1804 case 1: return renameBuffer(b, docstring());
1805 case 2: return false;
1809 // Ok, change the name of the buffer
1810 b.setFileName(fname.absFilename());
1812 bool unnamed = b.isUnnamed();
1813 b.setUnnamed(false);
1814 b.saveCheckSum(fname);
1816 if (!saveBuffer(b)) {
1817 b.setFileName(oldname.absFilename());
1818 b.setUnnamed(unnamed);
1819 b.saveCheckSum(oldname);
1827 bool GuiView::saveBuffer(Buffer & b)
1829 if (workArea(b) && workArea(b)->inDialogMode())
1833 return renameBuffer(b, docstring());
1836 theSession().lastFiles().add(b.fileName());
1840 // Switch to this Buffer.
1843 // FIXME: we don't tell the user *WHY* the save failed !!
1844 docstring const file = makeDisplayPath(b.absFileName(), 30);
1845 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1846 "Do you want to rename the document and "
1847 "try again?"), file);
1848 int const ret = Alert::prompt(_("Rename and save?"),
1849 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1852 if (!renameBuffer(b, docstring()))
1861 return saveBuffer(b);
1865 bool GuiView::closeBuffer()
1867 Buffer * buf = buffer();
1868 return buf && closeBuffer(*buf);
1872 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1874 // goto bookmark to update bookmark pit.
1875 //FIXME: we should update only the bookmarks related to this buffer!
1876 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1877 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1878 theLyXFunc().gotoBookmark(i+1, false, false);
1880 if (buf.isClean() || buf.paragraphs().empty()) {
1881 // save in sessions if requested
1882 // do not save childs if their master
1883 // is opened as well
1885 theSession().lastOpened().add(buf.fileName());
1887 // Don't close child documents.
1888 removeWorkArea(currentMainWorkArea());
1890 theBufferList().release(&buf);
1893 // Switch to this Buffer.
1898 if (buf.isUnnamed())
1899 file = from_utf8(buf.fileName().onlyFileName());
1901 file = buf.fileName().displayName(30);
1903 // Bring this window to top before asking questions.
1907 docstring const text = bformat(_("The document %1$s has unsaved changes."
1908 "\n\nDo you want to save the document or discard the changes?"), file);
1909 int const ret = Alert::prompt(_("Save changed document?"),
1910 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1914 if (!saveBuffer(buf))
1918 // if we crash after this we could
1919 // have no autosave file but I guess
1920 // this is really improbable (Jug)
1921 buf.removeAutosaveFile();
1927 // save file names to .lyx/session
1929 theSession().lastOpened().add(buf.fileName());
1932 // Don't close child documents.
1933 removeWorkArea(currentMainWorkArea());
1935 theBufferList().release(&buf);
1941 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1943 Buffer * const curbuf = buffer();
1944 Buffer * nextbuf = curbuf;
1946 if (np == NEXTBUFFER)
1947 nextbuf = theBufferList().next(nextbuf);
1949 nextbuf = theBufferList().previous(nextbuf);
1950 if (nextbuf == curbuf)
1956 if (workArea(*nextbuf))
1963 bool GuiView::dispatch(FuncRequest const & cmd)
1965 BufferView * bv = view();
1966 // By default we won't need any update.
1968 bv->cursor().updateFlags(Update::None);
1969 bool dispatched = true;
1971 switch(cmd.action) {
1972 case LFUN_BUFFER_IMPORT:
1973 importDocument(to_utf8(cmd.argument()));
1976 case LFUN_BUFFER_SWITCH: {
1978 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
1982 bv->cursor().message(_("Document not loaded"));
1986 case LFUN_BUFFER_NEXT:
1987 gotoNextOrPreviousBuffer(NEXTBUFFER);
1990 case LFUN_BUFFER_PREVIOUS:
1991 gotoNextOrPreviousBuffer(PREVBUFFER);
1994 case LFUN_COMMAND_EXECUTE: {
1995 bool const show_it = cmd.argument() != "off";
1996 // FIXME: this is a hack, "minibuffer" should not be
1998 if (GuiToolbar * t = toolbar("minibuffer")) {
1999 t->setVisible(show_it);
2000 if (show_it && t->commandBuffer())
2001 t->commandBuffer()->setFocus();
2005 case LFUN_DROP_LAYOUTS_CHOICE:
2007 d.layout_->showPopup();
2010 case LFUN_MENU_OPEN:
2011 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2012 menu->exec(QCursor::pos());
2015 case LFUN_FILE_INSERT:
2016 insertLyXFile(cmd.argument());
2018 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2019 insertPlaintextFile(cmd.argument(), true);
2022 case LFUN_FILE_INSERT_PLAINTEXT:
2023 insertPlaintextFile(cmd.argument(), false);
2026 case LFUN_BUFFER_WRITE:
2028 saveBuffer(bv->buffer());
2031 case LFUN_BUFFER_WRITE_AS:
2033 renameBuffer(bv->buffer(), cmd.argument());
2036 case LFUN_BUFFER_WRITE_ALL: {
2037 Buffer * first = theBufferList().first();
2040 message(_("Saving all documents..."));
2041 // We cannot use a for loop as the buffer list cycles.
2044 if (!b->isClean()) {
2046 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2048 b = theBufferList().next(b);
2049 } while (b != first);
2050 message(_("All documents saved."));
2054 case LFUN_TOOLBAR_TOGGLE: {
2055 string const name = cmd.getArg(0);
2056 if (GuiToolbar * t = toolbar(name))
2061 case LFUN_DIALOG_UPDATE: {
2062 string const name = to_utf8(cmd.argument());
2063 // Can only update a dialog connected to an existing inset
2064 Inset * inset = getOpenInset(name);
2066 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2067 inset->dispatch(view()->cursor(), fr);
2068 } else if (name == "paragraph") {
2069 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2070 } else if (name == "prefs" || name == "document") {
2071 updateDialog(name, string());
2076 case LFUN_DIALOG_TOGGLE: {
2077 if (isDialogVisible(cmd.getArg(0)))
2078 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2080 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2084 case LFUN_DIALOG_DISCONNECT_INSET:
2085 disconnectDialog(to_utf8(cmd.argument()));
2088 case LFUN_DIALOG_HIDE: {
2089 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2093 case LFUN_DIALOG_SHOW: {
2094 string const name = cmd.getArg(0);
2095 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2097 if (name == "character") {
2098 data = freefont2string();
2100 showDialog("character", data);
2101 } else if (name == "latexlog") {
2102 Buffer::LogType type;
2103 string const logfile = buffer()->logName(&type);
2105 case Buffer::latexlog:
2108 case Buffer::buildlog:
2112 data += Lexer::quoteString(logfile);
2113 showDialog("log", data);
2114 } else if (name == "vclog") {
2115 string const data = "vc " +
2116 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2117 showDialog("log", data);
2118 } else if (name == "symbols") {
2119 data = bv->cursor().getEncoding()->name();
2121 showDialog("symbols", data);
2123 } else if (name == "prefs" && isFullScreen()) {
2124 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2126 showDialog("prefs", data);
2128 showDialog(name, data);
2132 case LFUN_INSET_APPLY: {
2133 string const name = cmd.getArg(0);
2134 Inset * inset = getOpenInset(name);
2136 // put cursor in front of inset.
2137 if (!view()->setCursorFromInset(inset)) {
2138 LASSERT(false, break);
2141 // useful if we are called from a dialog.
2142 view()->cursor().beginUndoGroup();
2143 view()->cursor().recordUndo();
2144 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2145 inset->dispatch(view()->cursor(), fr);
2146 view()->cursor().endUndoGroup();
2148 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2154 case LFUN_UI_TOGGLE:
2156 // Make sure the keyboard focus stays in the work area.
2160 case LFUN_SPLIT_VIEW:
2161 if (Buffer * buf = buffer()) {
2162 string const orientation = cmd.getArg(0);
2163 d.splitter_->setOrientation(orientation == "vertical"
2164 ? Qt::Vertical : Qt::Horizontal);
2165 TabWorkArea * twa = addTabWorkArea();
2166 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2167 setCurrentWorkArea(wa);
2171 case LFUN_CLOSE_TAB_GROUP:
2172 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2174 twa = d.currentTabWorkArea();
2175 // Switch to the next GuiWorkArea in the found TabWorkArea.
2177 // Make sure the work area is up to date.
2178 setCurrentWorkArea(twa->currentWorkArea());
2180 setCurrentWorkArea(0);
2185 case LFUN_COMPLETION_INLINE:
2186 if (d.current_work_area_)
2187 d.current_work_area_->completer().showInline();
2190 case LFUN_COMPLETION_POPUP:
2191 if (d.current_work_area_)
2192 d.current_work_area_->completer().showPopup();
2196 case LFUN_COMPLETION_COMPLETE:
2197 if (d.current_work_area_)
2198 d.current_work_area_->completer().tab();
2201 case LFUN_COMPLETION_CANCEL:
2202 if (d.current_work_area_) {
2203 if (d.current_work_area_->completer().popupVisible())
2204 d.current_work_area_->completer().hidePopup();
2206 d.current_work_area_->completer().hideInline();
2210 case LFUN_COMPLETION_ACCEPT:
2211 if (d.current_work_area_)
2212 d.current_work_area_->completer().activate();
2215 case LFUN_BUFFER_ZOOM_IN:
2216 case LFUN_BUFFER_ZOOM_OUT:
2217 if (cmd.argument().empty()) {
2218 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2223 lyxrc.zoom += convert<int>(cmd.argument());
2225 if (lyxrc.zoom < 10)
2228 // The global QPixmapCache is used in GuiPainter to cache text
2229 // painting so we must reset it.
2230 QPixmapCache::clear();
2231 guiApp->fontLoader().update();
2232 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2240 // Part of automatic menu appearance feature.
2241 if (isFullScreen()) {
2242 if (menuBar()->isVisible())
2244 if (statusBar()->isVisible())
2245 statusBar()->hide();
2252 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2254 string const arg = cmd.getArg(0);
2255 if (arg == "scrollbar") {
2256 // hide() is of no help
2257 if (d.current_work_area_->verticalScrollBarPolicy() ==
2258 Qt::ScrollBarAlwaysOff)
2260 d.current_work_area_->setVerticalScrollBarPolicy(
2261 Qt::ScrollBarAsNeeded);
2263 d.current_work_area_->setVerticalScrollBarPolicy(
2264 Qt::ScrollBarAlwaysOff);
2267 if (arg == "statusbar") {
2268 statusBar()->setVisible(!statusBar()->isVisible());
2271 if (arg == "menubar") {
2272 menuBar()->setVisible(!menuBar()->isVisible());
2275 #if QT_VERSION >= 0x040300
2276 if (arg == "frame") {
2278 getContentsMargins(&l, &t, &r, &b);
2279 //are the frames in default state?
2280 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2282 setContentsMargins(-2, -2, -2, -2);
2284 setContentsMargins(0, 0, 0, 0);
2289 if (arg == "fullscreen") {
2294 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2298 void GuiView::toggleFullScreen()
2300 if (isFullScreen()) {
2301 for (int i = 0; i != d.splitter_->count(); ++i)
2302 d.tabWorkArea(i)->setFullScreen(false);
2303 #if QT_VERSION >= 0x040300
2304 setContentsMargins(0, 0, 0, 0);
2306 setWindowState(windowState() ^ Qt::WindowFullScreen);
2309 statusBar()->show();
2312 hideDialogs("prefs", 0);
2313 for (int i = 0; i != d.splitter_->count(); ++i)
2314 d.tabWorkArea(i)->setFullScreen(true);
2315 #if QT_VERSION >= 0x040300
2316 setContentsMargins(-2, -2, -2, -2);
2319 setWindowState(windowState() ^ Qt::WindowFullScreen);
2320 statusBar()->hide();
2322 if (lyxrc.full_screen_toolbars) {
2323 ToolbarMap::iterator end = d.toolbars_.end();
2324 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2329 // give dialogs like the TOC a chance to adapt
2334 Buffer const * GuiView::updateInset(Inset const * inset)
2336 if (!d.current_work_area_)
2340 d.current_work_area_->scheduleRedraw();
2342 return &d.current_work_area_->bufferView().buffer();
2346 void GuiView::restartCursor()
2348 /* When we move around, or type, it's nice to be able to see
2349 * the cursor immediately after the keypress.
2351 if (d.current_work_area_)
2352 d.current_work_area_->startBlinkingCursor();
2354 // Take this occasion to update the other GUI elements.
2360 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2362 if (d.current_work_area_)
2363 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2368 // This list should be kept in sync with the list of insets in
2369 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2370 // dialog should have the same name as the inset.
2371 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2372 // docs in LyXAction.cpp.
2374 char const * const dialognames[] = {
2375 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2376 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2377 "float", "graphics", "include", "index", "info", "nomenclature", "label",
2378 "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2379 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2380 "symbols", "tabular", "tabularcreate",
2382 #ifdef HAVE_LIBAIKSAURUS
2386 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2388 char const * const * const end_dialognames =
2389 dialognames + (sizeof(dialognames) / sizeof(char *));
2393 cmpCStr(char const * name) : name_(name) {}
2394 bool operator()(char const * other) {
2395 return strcmp(other, name_) == 0;
2402 bool isValidName(string const & name)
2404 return find_if(dialognames, end_dialognames,
2405 cmpCStr(name.c_str())) != end_dialognames;
2411 void GuiView::resetDialogs()
2413 // Make sure that no LFUN uses any LyXView.
2414 theLyXFunc().setLyXView(0);
2417 constructToolbars();
2418 guiApp->menus().fillMenuBar(menuBar(), this, false);
2420 d.layout_->updateContents(true);
2421 // Now update controls with current buffer.
2422 theLyXFunc().setLyXView(this);
2428 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2430 if (!isValidName(name))
2433 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2435 if (it != d.dialogs_.end()) {
2437 it->second->hideView();
2438 return it->second.get();
2441 Dialog * dialog = build(name);
2442 d.dialogs_[name].reset(dialog);
2443 if (lyxrc.allow_geometry_session)
2444 dialog->restoreSession();
2451 void GuiView::showDialog(string const & name, string const & data,
2459 Dialog * dialog = findOrBuild(name, false);
2461 dialog->showData(data);
2463 d.open_insets_[name] = inset;
2466 catch (ExceptionMessage const & ex) {
2474 bool GuiView::isDialogVisible(string const & name) const
2476 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2477 if (it == d.dialogs_.end())
2479 return it->second.get()->isVisibleView();
2483 void GuiView::hideDialog(string const & name, Inset * inset)
2485 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2486 if (it == d.dialogs_.end())
2489 if (inset && inset != getOpenInset(name))
2492 Dialog * const dialog = it->second.get();
2493 if (dialog->isVisibleView())
2495 d.open_insets_[name] = 0;
2499 void GuiView::disconnectDialog(string const & name)
2501 if (!isValidName(name))
2504 if (d.open_insets_.find(name) != d.open_insets_.end())
2505 d.open_insets_[name] = 0;
2509 Inset * GuiView::getOpenInset(string const & name) const
2511 if (!isValidName(name))
2514 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2515 return it == d.open_insets_.end() ? 0 : it->second;
2519 void GuiView::hideAll() const
2521 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2522 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2524 for(; it != end; ++it)
2525 it->second->hideView();
2529 void GuiView::updateDialogs()
2531 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2532 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2534 for(; it != end; ++it) {
2535 Dialog * dialog = it->second.get();
2536 if (dialog && dialog->isVisibleView())
2537 dialog->checkStatus();
2544 // will be replaced by a proper factory...
2545 Dialog * createGuiAbout(GuiView & lv);
2546 Dialog * createGuiBibitem(GuiView & lv);
2547 Dialog * createGuiBibtex(GuiView & lv);
2548 Dialog * createGuiBox(GuiView & lv);
2549 Dialog * createGuiBranch(GuiView & lv);
2550 Dialog * createGuiChanges(GuiView & lv);
2551 Dialog * createGuiCharacter(GuiView & lv);
2552 Dialog * createGuiCitation(GuiView & lv);
2553 Dialog * createGuiDelimiter(GuiView & lv);
2554 Dialog * createGuiDocument(GuiView & lv);
2555 Dialog * createGuiErrorList(GuiView & lv);
2556 Dialog * createGuiERT(GuiView & lv);
2557 Dialog * createGuiExternal(GuiView & lv);
2558 Dialog * createGuiFloat(GuiView & lv);
2559 Dialog * createGuiGraphics(GuiView & lv);
2560 Dialog * createGuiInclude(GuiView & lv);
2561 Dialog * createGuiInfo(GuiView & lv);
2562 Dialog * createGuiLabel(GuiView & lv);
2563 Dialog * createGuiListings(GuiView & lv);
2564 Dialog * createGuiLog(GuiView & lv);
2565 Dialog * createGuiMathHSpace(GuiView & lv);
2566 Dialog * createGuiMathMatrix(GuiView & lv);
2567 Dialog * createGuiNomenclature(GuiView & lv);
2568 Dialog * createGuiNote(GuiView & lv);
2569 Dialog * createGuiParagraph(GuiView & lv);
2570 Dialog * createGuiPhantom(GuiView & lv);
2571 Dialog * createGuiPreferences(GuiView & lv);
2572 Dialog * createGuiPrint(GuiView & lv);
2573 Dialog * createGuiRef(GuiView & lv);
2574 Dialog * createGuiSearch(GuiView & lv);
2575 Dialog * createGuiSearchAdv(GuiView & lv);
2576 Dialog * createGuiSendTo(GuiView & lv);
2577 Dialog * createGuiShowFile(GuiView & lv);
2578 Dialog * createGuiSpellchecker(GuiView & lv);
2579 Dialog * createGuiSymbols(GuiView & lv);
2580 Dialog * createGuiTabularCreate(GuiView & lv);
2581 Dialog * createGuiTabular(GuiView & lv);
2582 Dialog * createGuiTexInfo(GuiView & lv);
2583 Dialog * createGuiTextHSpace(GuiView & lv);
2584 Dialog * createGuiToc(GuiView & lv);
2585 Dialog * createGuiThesaurus(GuiView & lv);
2586 Dialog * createGuiHyperlink(GuiView & lv);
2587 Dialog * createGuiVSpace(GuiView & lv);
2588 Dialog * createGuiViewSource(GuiView & lv);
2589 Dialog * createGuiWrap(GuiView & lv);
2592 Dialog * GuiView::build(string const & name)
2594 LASSERT(isValidName(name), return 0);
2596 if (name == "aboutlyx")
2597 return createGuiAbout(*this);
2598 if (name == "bibitem")
2599 return createGuiBibitem(*this);
2600 if (name == "bibtex")
2601 return createGuiBibtex(*this);
2603 return createGuiBox(*this);
2604 if (name == "branch")
2605 return createGuiBranch(*this);
2606 if (name == "changes")
2607 return createGuiChanges(*this);
2608 if (name == "character")
2609 return createGuiCharacter(*this);
2610 if (name == "citation")
2611 return createGuiCitation(*this);
2612 if (name == "document")
2613 return createGuiDocument(*this);
2614 if (name == "errorlist")
2615 return createGuiErrorList(*this);
2617 return createGuiERT(*this);
2618 if (name == "external")
2619 return createGuiExternal(*this);
2621 return createGuiShowFile(*this);
2622 if (name == "findreplace")
2623 return createGuiSearch(*this);
2624 if (name == "findreplaceadv")
2625 return createGuiSearchAdv(*this);
2626 if (name == "float")
2627 return createGuiFloat(*this);
2628 if (name == "graphics")
2629 return createGuiGraphics(*this);
2630 if (name == "include")
2631 return createGuiInclude(*this);
2633 return createGuiInfo(*this);
2634 if (name == "nomenclature")
2635 return createGuiNomenclature(*this);
2636 if (name == "label")
2637 return createGuiLabel(*this);
2639 return createGuiLog(*this);
2640 if (name == "mathdelimiter")
2641 return createGuiDelimiter(*this);
2642 if (name == "mathspace")
2643 return createGuiMathHSpace(*this);
2644 if (name == "mathmatrix")
2645 return createGuiMathMatrix(*this);
2647 return createGuiNote(*this);
2648 if (name == "paragraph")
2649 return createGuiParagraph(*this);
2650 if (name == "phantom")
2651 return createGuiPhantom(*this);
2652 if (name == "prefs")
2653 return createGuiPreferences(*this);
2654 if (name == "print")
2655 return createGuiPrint(*this);
2657 return createGuiRef(*this);
2658 if (name == "sendto")
2659 return createGuiSendTo(*this);
2660 if (name == "space")
2661 return createGuiTextHSpace(*this);
2662 if (name == "spellchecker")
2663 return createGuiSpellchecker(*this);
2664 if (name == "symbols")
2665 return createGuiSymbols(*this);
2666 if (name == "tabular")
2667 return createGuiTabular(*this);
2668 if (name == "tabularcreate")
2669 return createGuiTabularCreate(*this);
2670 if (name == "texinfo")
2671 return createGuiTexInfo(*this);
2672 if (name == "view-source")
2673 return createGuiViewSource(*this);
2674 #ifdef HAVE_LIBAIKSAURUS
2675 if (name == "thesaurus")
2676 return createGuiThesaurus(*this);
2679 return createGuiHyperlink(*this);
2680 if (name == "listings")
2681 return createGuiListings(*this);
2683 return createGuiToc(*this);
2684 if (name == "vspace")
2685 return createGuiVSpace(*this);
2687 return createGuiWrap(*this);
2693 } // namespace frontend
2696 #include "moc_GuiView.cpp"