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 "GuiApplication.h"
21 #include "GuiCommandBuffer.h"
22 #include "GuiCompleter.h"
23 #include "GuiWorkArea.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToolbar.h"
29 #include "qt_helpers.h"
31 #include "frontends/alert.h"
33 #include "buffer_funcs.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
41 #include "ErrorList.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
58 #include "support/lassert.h"
59 #include "support/debug.h"
60 #include "support/ExceptionMessage.h"
61 #include "support/FileName.h"
62 #include "support/filetools.h"
63 #include "support/gettext.h"
64 #include "support/ForkedCalls.h"
65 #include "support/lstrings.h"
66 #include "support/os.h"
67 #include "support/Package.h"
68 #include "support/Timeout.h"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
116 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
117 /// The text to be written on top of the pixmap
118 QString const text = lyx_version ?
119 qt_("version ") + lyx_version : qt_("unknown version");
120 splash_ = QPixmap(":/images/banner.png");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
125 // The font used to display the version info
126 font.setStyleHint(QFont::SansSerif);
127 font.setWeight(QFont::Bold);
128 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
130 pain.drawText(260, 15, text);
133 void paintEvent(QPaintEvent *)
135 int x = (width() - splash_.width()) / 2;
136 int y = (height() - splash_.height()) / 2;
138 pain.drawPixmap(x, y, splash_);
146 /// Toolbar store providing access to individual toolbars by name.
147 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
149 typedef boost::shared_ptr<Dialog> DialogPtr;
154 struct GuiView::GuiViewPrivate
157 : current_work_area_(0), current_main_work_area_(0),
158 layout_(0), autosave_timeout_(5000),
161 // hardcode here the platform specific icon size
162 smallIconSize = 14; // scaling problems
163 normalIconSize = 20; // ok, default
164 bigIconSize = 26; // better for some math icons
166 splitter_ = new QSplitter;
167 bg_widget_ = new BackgroundWidget;
168 stack_widget_ = new QStackedWidget;
169 stack_widget_->addWidget(bg_widget_);
170 stack_widget_->addWidget(splitter_);
178 delete stack_widget_;
181 QMenu * toolBarPopup(GuiView * parent)
183 // FIXME: translation
184 QMenu * menu = new QMenu(parent);
185 QActionGroup * iconSizeGroup = new QActionGroup(parent);
187 QAction * smallIcons = new QAction(iconSizeGroup);
188 smallIcons->setText(qt_("Small-sized icons"));
189 smallIcons->setCheckable(true);
190 QObject::connect(smallIcons, SIGNAL(triggered()),
191 parent, SLOT(smallSizedIcons()));
192 menu->addAction(smallIcons);
194 QAction * normalIcons = new QAction(iconSizeGroup);
195 normalIcons->setText(qt_("Normal-sized icons"));
196 normalIcons->setCheckable(true);
197 QObject::connect(normalIcons, SIGNAL(triggered()),
198 parent, SLOT(normalSizedIcons()));
199 menu->addAction(normalIcons);
201 QAction * bigIcons = new QAction(iconSizeGroup);
202 bigIcons->setText(qt_("Big-sized icons"));
203 bigIcons->setCheckable(true);
204 QObject::connect(bigIcons, SIGNAL(triggered()),
205 parent, SLOT(bigSizedIcons()));
206 menu->addAction(bigIcons);
208 unsigned int cur = parent->iconSize().width();
209 if ( cur == parent->d.smallIconSize)
210 smallIcons->setChecked(true);
211 else if (cur == parent->d.normalIconSize)
212 normalIcons->setChecked(true);
213 else if (cur == parent->d.bigIconSize)
214 bigIcons->setChecked(true);
221 stack_widget_->setCurrentWidget(bg_widget_);
222 bg_widget_->setUpdatesEnabled(true);
225 TabWorkArea * tabWorkArea(int i)
227 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
230 TabWorkArea * currentTabWorkArea()
232 if (splitter_->count() == 1)
233 // The first TabWorkArea is always the first one, if any.
234 return tabWorkArea(0);
236 for (int i = 0; i != splitter_->count(); ++i) {
237 TabWorkArea * twa = tabWorkArea(i);
238 if (current_main_work_area_ == twa->currentWorkArea())
242 // None has the focus so we just take the first one.
243 return tabWorkArea(0);
247 GuiWorkArea * current_work_area_;
248 GuiWorkArea * current_main_work_area_;
249 QSplitter * splitter_;
250 QStackedWidget * stack_widget_;
251 BackgroundWidget * bg_widget_;
253 ToolbarMap toolbars_;
254 /// The main layout box.
256 * \warning Don't Delete! The layout box is actually owned by
257 * whichever toolbar contains it. All the GuiView class needs is a
258 * means of accessing it.
260 * FIXME: replace that with a proper model so that we are not limited
261 * to only one dialog.
263 GuiLayoutBox * layout_;
266 map<string, Inset *> open_insets_;
269 map<string, DialogPtr> dialogs_;
271 unsigned int smallIconSize;
272 unsigned int normalIconSize;
273 unsigned int bigIconSize;
275 QTimer statusbar_timer_;
276 /// auto-saving of buffers
277 Timeout autosave_timeout_;
278 /// flag against a race condition due to multiclicks, see bug #1119
282 TocModels toc_models_;
286 GuiView::GuiView(int id)
287 : d(*new GuiViewPrivate), id_(id), closing_(false)
289 // GuiToolbars *must* be initialised before the menu bar.
290 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
293 // set ourself as the current view. This is needed for the menu bar
294 // filling, at least for the static special menu item on Mac. Otherwise
295 // they are greyed out.
296 theLyXFunc().setLyXView(this);
298 // Fill up the menu bar.
299 guiApp->menus().fillMenuBar(menuBar(), this, true);
301 setCentralWidget(d.stack_widget_);
303 // Start autosave timer
304 if (lyxrc.autosave) {
305 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
306 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
307 d.autosave_timeout_.start();
309 connect(&d.statusbar_timer_, SIGNAL(timeout()),
310 this, SLOT(clearMessage()));
312 // We don't want to keep the window in memory if it is closed.
313 setAttribute(Qt::WA_DeleteOnClose, true);
315 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
316 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
317 // since the icon is provided in the application bundle.
318 setWindowIcon(QPixmap(":/images/lyx.png"));
322 setAcceptDrops(true);
324 statusBar()->setSizeGripEnabled(true);
326 // Forbid too small unresizable window because it can happen
327 // with some window manager under X11.
328 setMinimumSize(300, 200);
330 if (lyxrc.allow_geometry_session) {
331 // Now take care of session management.
336 // no session handling, default to a sane size.
337 setGeometry(50, 50, 690, 510);
340 // clear session data if any.
342 settings.remove("views");
352 void GuiView::saveLayout() const
355 settings.beginGroup("views");
356 settings.beginGroup(QString::number(id_));
358 settings.setValue("pos", pos());
359 settings.setValue("size", size());
361 settings.setValue("geometry", saveGeometry());
363 settings.setValue("layout", saveState(0));
364 settings.setValue("icon_size", iconSize());
368 bool GuiView::restoreLayout()
371 settings.beginGroup("views");
372 settings.beginGroup(QString::number(id_));
373 QString const icon_key = "icon_size";
374 if (!settings.contains(icon_key))
377 setIconSize(settings.value(icon_key).toSize());
379 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
380 QSize size = settings.value("size", QSize(690, 510)).toSize();
384 if (!restoreGeometry(settings.value("geometry").toByteArray()))
385 setGeometry(50, 50, 690, 510);
387 // Make sure layout is correctly oriented.
388 setLayoutDirection(qApp->layoutDirection());
390 // Allow the toc and view-source dock widget to be restored if needed.
392 if ((d = findOrBuild("toc", true)))
395 if ((d = findOrBuild("view-source", true)))
398 if (!restoreState(settings.value("layout").toByteArray(), 0))
405 GuiToolbar * GuiView::toolbar(string const & name)
407 ToolbarMap::iterator it = d.toolbars_.find(name);
408 if (it != d.toolbars_.end())
411 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
412 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
417 void GuiView::constructToolbars()
419 ToolbarMap::iterator it = d.toolbars_.begin();
420 for (; it != d.toolbars_.end(); ++it)
425 // extracts the toolbars from the backend
426 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
427 Toolbars::Infos::iterator end = guiApp->toolbars().end();
428 for (; cit != end; ++cit)
429 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
433 void GuiView::initToolbars()
435 // extracts the toolbars from the backend
436 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
437 Toolbars::Infos::iterator end = guiApp->toolbars().end();
438 for (; cit != end; ++cit) {
439 GuiToolbar * tb = toolbar(cit->name);
442 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
444 tb->setVisible(false);
445 tb->setVisibility(visibility);
447 if (visibility & Toolbars::TOP) {
449 addToolBarBreak(Qt::TopToolBarArea);
450 addToolBar(Qt::TopToolBarArea, tb);
453 if (visibility & Toolbars::BOTTOM) {
454 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456 addToolBarBreak(Qt::BottomToolBarArea);
458 addToolBar(Qt::BottomToolBarArea, tb);
461 if (visibility & Toolbars::LEFT) {
462 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
463 #if (QT_VERSION >= 0x040202)
464 addToolBarBreak(Qt::LeftToolBarArea);
466 addToolBar(Qt::LeftToolBarArea, tb);
469 if (visibility & Toolbars::RIGHT) {
470 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
471 #if (QT_VERSION >= 0x040202)
472 addToolBarBreak(Qt::RightToolBarArea);
474 addToolBar(Qt::RightToolBarArea, tb);
477 if (visibility & Toolbars::ON)
478 tb->setVisible(true);
483 TocModels & GuiView::tocModels()
485 return d.toc_models_;
489 void GuiView::setFocus()
491 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
492 // Make sure LyXFunc points to the correct view.
493 guiApp->setCurrentView(this);
494 theLyXFunc().setLyXView(this);
495 QMainWindow::setFocus();
496 if (d.current_work_area_)
497 d.current_work_area_->setFocus();
501 QMenu * GuiView::createPopupMenu()
503 return d.toolBarPopup(this);
507 void GuiView::showEvent(QShowEvent * e)
509 LYXERR(Debug::GUI, "Passed Geometry "
510 << size().height() << "x" << size().width()
511 << "+" << pos().x() << "+" << pos().y());
513 if (d.splitter_->count() == 0)
514 // No work area, switch to the background widget.
517 QMainWindow::showEvent(e);
521 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
522 ** is responsibility of the container (e.g., dialog)
524 void GuiView::closeEvent(QCloseEvent * close_event)
526 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
529 // it can happen that this event arrives without selecting the view,
530 // e.g. when clicking the close button on a background window.
532 setCurrentWorkArea(currentMainWorkArea());
533 while (GuiWorkArea * wa = currentMainWorkArea()) {
534 Buffer * b = &wa->bufferView().buffer();
536 // This is a child document, just close the tab
537 // after saving but keep the file loaded.
538 if (!closeBuffer(*b, true)) {
540 close_event->ignore();
546 vector<Buffer *> clist = b->getChildren();
547 for (vector<Buffer *>::const_iterator it = clist.begin();
548 it != clist.end(); ++it) {
549 if ((*it)->isClean())
552 // If a child is dirty, do not close
553 // without user intervention
554 if (!closeBuffer(*c, false)) {
556 close_event->ignore();
561 QList<int> const ids = guiApp->viewIds();
562 for (int i = 0; i != ids.size(); ++i) {
565 if (guiApp->view(ids[i]).workArea(*b)) {
566 // FIXME 1: should we put an alert box here that the buffer
567 // is viewed elsewhere?
568 // FIXME 2: should we try to save this buffer in any case?
571 // This buffer is also opened in another view, so
572 // close the associated work area...
574 // ... but don't close the buffer.
579 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
580 if (b && !closeBuffer(*b, true)) {
582 close_event->ignore();
587 // Make sure that nothing will use this close to be closed View.
588 guiApp->unregisterView(this);
590 if (isFullScreen()) {
591 // Switch off fullscreen before closing.
596 // Make sure the timer time out will not trigger a statusbar update.
597 d.statusbar_timer_.stop();
599 // Saving fullscreen requires additional tweaks in the toolbar code.
600 // It wouldn't also work under linux natively.
601 if (lyxrc.allow_geometry_session) {
602 // Save this window geometry and layout.
604 // Then the toolbar private states.
605 ToolbarMap::iterator end = d.toolbars_.end();
606 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
607 it->second->saveSession();
608 // Now take care of all other dialogs:
609 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
610 for (; it!= d.dialogs_.end(); ++it)
611 it->second->saveSession();
614 close_event->accept();
618 void GuiView::dragEnterEvent(QDragEnterEvent * event)
620 if (event->mimeData()->hasUrls())
622 /// \todo Ask lyx-devel is this is enough:
623 /// if (event->mimeData()->hasFormat("text/plain"))
624 /// event->acceptProposedAction();
628 void GuiView::dropEvent(QDropEvent * event)
630 QList<QUrl> files = event->mimeData()->urls();
634 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
635 for (int i = 0; i != files.size(); ++i) {
636 string const file = os::internal_path(fromqstr(
637 files.at(i).toLocalFile()));
639 // Asynchronously post the event. DropEvent usually come
640 // from the BufferView. But reloading a file might close
641 // the BufferView from within its own event handler.
642 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
649 void GuiView::message(docstring const & str)
651 if (ForkedProcess::iAmAChild())
654 statusBar()->showMessage(toqstr(str));
655 d.statusbar_timer_.stop();
656 d.statusbar_timer_.start(3000);
660 void GuiView::smallSizedIcons()
662 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
666 void GuiView::normalSizedIcons()
668 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
672 void GuiView::bigSizedIcons()
674 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
678 void GuiView::clearMessage()
682 theLyXFunc().setLyXView(this);
683 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
684 d.statusbar_timer_.stop();
688 void GuiView::updateWindowTitle(GuiWorkArea * wa)
690 if (wa != d.current_work_area_)
692 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
693 setWindowIconText(wa->windowIconText());
697 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
700 disconnectBufferView();
701 connectBufferView(wa->bufferView());
702 connectBuffer(wa->bufferView().buffer());
703 d.current_work_area_ = wa;
704 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
705 this, SLOT(updateWindowTitle(GuiWorkArea *)));
706 updateWindowTitle(wa);
710 // The document settings needs to be reinitialised.
711 updateDialog("document", "");
713 // Buffer-dependent dialogs must be updated. This is done here because
714 // some dialogs require buffer()->text.
719 void GuiView::on_lastWorkAreaRemoved()
722 // We already are in a close event. Nothing more to do.
725 if (d.splitter_->count() > 1)
726 // We have a splitter so don't close anything.
729 // Reset and updates the dialogs.
730 d.toc_models_.reset(0);
731 updateDialog("document", "");
734 resetWindowTitleAndIconText();
736 if (lyxrc.open_buffers_in_tabs)
737 // Nothing more to do, the window should stay open.
740 if (guiApp->viewIds().size() > 1) {
746 // On Mac we also close the last window because the application stay
747 // resident in memory. On other platforms we don't close the last
748 // window because this would quit the application.
754 void GuiView::updateStatusBar()
756 // let the user see the explicit message
757 if (d.statusbar_timer_.isActive())
760 theLyXFunc().setLyXView(this);
761 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
765 bool GuiView::hasFocus() const
767 return qApp->activeWindow() == this;
771 bool GuiView::event(QEvent * e)
775 // Useful debug code:
776 //case QEvent::ActivationChange:
777 //case QEvent::WindowDeactivate:
778 //case QEvent::Paint:
779 //case QEvent::Enter:
780 //case QEvent::Leave:
781 //case QEvent::HoverEnter:
782 //case QEvent::HoverLeave:
783 //case QEvent::HoverMove:
784 //case QEvent::StatusTip:
785 //case QEvent::DragEnter:
786 //case QEvent::DragLeave:
790 case QEvent::WindowActivate: {
791 if (this == guiApp->currentView()) {
793 return QMainWindow::event(e);
795 guiApp->setCurrentView(this);
796 theLyXFunc().setLyXView(this);
797 if (d.current_work_area_) {
798 BufferView & bv = d.current_work_area_->bufferView();
799 connectBufferView(bv);
800 connectBuffer(bv.buffer());
801 // The document structure, name and dialogs might have
802 // changed in another view.
804 // The document settings needs to be reinitialised.
805 updateDialog("document", "");
808 resetWindowTitleAndIconText();
811 return QMainWindow::event(e);
814 case QEvent::ShortcutOverride: {
818 if (isFullScreen() && menuBar()->isHidden()) {
819 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
820 // FIXME: we should also try to detect special LyX shortcut such as
821 // Alt-P and Alt-M. Right now there is a hack in
822 // GuiWorkArea::processKeySym() that hides again the menubar for
824 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
826 return QMainWindow::event(e);
831 if (d.current_work_area_)
832 // Nothing special to do.
833 return QMainWindow::event(e);
835 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
836 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
838 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
839 || ke->key() == Qt::Key_Backtab)
840 return QMainWindow::event(e);
842 // Allow processing of shortcuts that are allowed even when no Buffer
844 theLyXFunc().setLyXView(this);
846 setKeySymbol(&sym, ke);
847 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
853 return QMainWindow::event(e);
857 void GuiView::resetWindowTitleAndIconText()
859 setWindowTitle(qt_("LyX"));
860 setWindowIconText(qt_("LyX"));
863 bool GuiView::focusNextPrevChild(bool /*next*/)
870 void GuiView::setBusy(bool busy)
872 if (d.current_work_area_) {
873 d.current_work_area_->setUpdatesEnabled(!busy);
875 d.current_work_area_->stopBlinkingCursor();
877 d.current_work_area_->startBlinkingCursor();
881 QApplication::setOverrideCursor(Qt::WaitCursor);
883 QApplication::restoreOverrideCursor();
887 GuiWorkArea * GuiView::workArea(Buffer & buffer)
889 if (currentWorkArea()
890 && ¤tWorkArea()->bufferView().buffer() == &buffer)
891 return (GuiWorkArea *) currentWorkArea();
892 if (TabWorkArea * twa = d.currentTabWorkArea())
893 return twa->workArea(buffer);
898 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
900 // Automatically create a TabWorkArea if there are none yet.
901 TabWorkArea * tab_widget = d.splitter_->count()
902 ? d.currentTabWorkArea() : addTabWorkArea();
903 return tab_widget->addWorkArea(buffer, *this);
907 TabWorkArea * GuiView::addTabWorkArea()
909 TabWorkArea * twa = new TabWorkArea;
910 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
911 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
912 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
913 this, SLOT(on_lastWorkAreaRemoved()));
915 d.splitter_->addWidget(twa);
916 d.stack_widget_->setCurrentWidget(d.splitter_);
921 GuiWorkArea const * GuiView::currentWorkArea() const
923 return d.current_work_area_;
927 GuiWorkArea * GuiView::currentWorkArea()
929 return d.current_work_area_;
933 GuiWorkArea const * GuiView::currentMainWorkArea() const
935 if (d.currentTabWorkArea() == NULL)
937 return d.currentTabWorkArea()->currentWorkArea();
941 GuiWorkArea * GuiView::currentMainWorkArea()
943 if (d.currentTabWorkArea() == NULL)
945 return d.currentTabWorkArea()->currentWorkArea();
949 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
951 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
953 d.current_work_area_ = NULL;
957 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
961 theGuiApp()->setCurrentView(this);
962 d.current_work_area_ = wa;
963 for (int i = 0; i != d.splitter_->count(); ++i) {
964 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
965 //if (d.current_main_work_area_)
966 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
967 d.current_main_work_area_ = wa;
968 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
969 //d.current_main_work_area_->setLineWidth(2);
970 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
974 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
975 on_currentWorkAreaChanged(wa);
976 BufferView & bv = wa->bufferView();
977 bv.cursor().fixIfBroken();
979 wa->setUpdatesEnabled(true);
980 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
984 void GuiView::removeWorkArea(GuiWorkArea * wa)
987 if (wa == d.current_work_area_) {
989 disconnectBufferView();
990 d.current_work_area_ = 0;
991 d.current_main_work_area_ = 0;
994 bool found_twa = false;
995 for (int i = 0; i != d.splitter_->count(); ++i) {
996 TabWorkArea * twa = d.tabWorkArea(i);
997 if (twa->removeWorkArea(wa)) {
998 // Found in this tab group, and deleted the GuiWorkArea.
1000 if (twa->count() != 0) {
1001 if (d.current_work_area_ == 0)
1002 // This means that we are closing the current GuiWorkArea, so
1003 // switch to the next GuiWorkArea in the found TabWorkArea.
1004 setCurrentWorkArea(twa->currentWorkArea());
1006 // No more WorkAreas in this tab group, so delete it.
1013 // It is not a tabbed work area (i.e., the search work area), so it
1014 // should be deleted by other means.
1015 LASSERT(found_twa, /* */);
1017 if (d.current_work_area_ == 0) {
1018 if (d.splitter_->count() != 0) {
1019 TabWorkArea * twa = d.currentTabWorkArea();
1020 setCurrentWorkArea(twa->currentWorkArea());
1022 // No more work areas, switch to the background widget.
1023 setCurrentWorkArea(0);
1029 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1035 void GuiView::updateLayoutList()
1038 d.layout_->updateContents(false);
1042 void GuiView::updateToolbars()
1044 ToolbarMap::iterator end = d.toolbars_.end();
1045 if (d.current_work_area_) {
1047 d.current_work_area_->bufferView().cursor().inMathed();
1049 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1051 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1052 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1053 bool const mathmacrotemplate =
1054 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1056 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1057 it->second->update(math, table, review, mathmacrotemplate);
1059 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1060 it->second->update(false, false, false, false);
1064 Buffer * GuiView::buffer()
1066 if (d.current_work_area_)
1067 return &d.current_work_area_->bufferView().buffer();
1072 Buffer const * GuiView::buffer() const
1074 if (d.current_work_area_)
1075 return &d.current_work_area_->bufferView().buffer();
1080 void GuiView::setBuffer(Buffer * newBuffer)
1082 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1083 LASSERT(newBuffer, return);
1086 GuiWorkArea * wa = workArea(*newBuffer);
1088 newBuffer->masterBuffer()->updateLabels();
1089 wa = addWorkArea(*newBuffer);
1091 //Disconnect the old buffer...there's no new one.
1094 connectBuffer(*newBuffer);
1095 connectBufferView(wa->bufferView());
1096 setCurrentWorkArea(wa);
1102 void GuiView::connectBuffer(Buffer & buf)
1104 buf.setGuiDelegate(this);
1108 void GuiView::disconnectBuffer()
1110 if (d.current_work_area_)
1111 d.current_work_area_->bufferView().setGuiDelegate(0);
1115 void GuiView::connectBufferView(BufferView & bv)
1117 bv.setGuiDelegate(this);
1121 void GuiView::disconnectBufferView()
1123 if (d.current_work_area_)
1124 d.current_work_area_->bufferView().setGuiDelegate(0);
1128 void GuiView::errors(string const & error_type)
1130 ErrorList & el = buffer()->errorList(error_type);
1132 showDialog("errorlist", error_type);
1136 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1138 d.toc_models_.updateItem(toqstr(type), dit);
1142 void GuiView::structureChanged()
1144 d.toc_models_.reset(view());
1145 // Navigator needs more than a simple update in this case. It needs to be
1147 updateDialog("toc", "");
1151 void GuiView::updateDialog(string const & name, string const & data)
1153 if (!isDialogVisible(name))
1156 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1157 if (it == d.dialogs_.end())
1160 Dialog * const dialog = it->second.get();
1161 if (dialog->isVisibleView())
1162 dialog->initialiseParams(data);
1166 BufferView * GuiView::view()
1168 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1172 void GuiView::autoSave()
1174 LYXERR(Debug::INFO, "Running autoSave()");
1177 view()->buffer().autoSave();
1181 void GuiView::resetAutosaveTimers()
1184 d.autosave_timeout_.restart();
1188 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1191 Buffer * buf = buffer();
1193 /* In LyX/Mac, when a dialog is open, the menus of the
1194 application can still be accessed without giving focus to
1195 the main window. In this case, we want to disable the menu
1196 entries that are buffer-related.
1198 Note that this code is not perfect, as bug 1941 attests:
1199 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1201 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1204 switch(cmd.action) {
1205 case LFUN_BUFFER_WRITE:
1206 enable = buf && (buf->isUnnamed() || !buf->isClean());
1209 case LFUN_BUFFER_WRITE_AS:
1213 case LFUN_SPLIT_VIEW:
1214 if (cmd.getArg(0) == "vertical")
1215 enable = buf && (d.splitter_->count() == 1 ||
1216 d.splitter_->orientation() == Qt::Vertical);
1218 enable = buf && (d.splitter_->count() == 1 ||
1219 d.splitter_->orientation() == Qt::Horizontal);
1222 case LFUN_CLOSE_TAB_GROUP:
1223 enable = d.currentTabWorkArea();
1226 case LFUN_TOOLBAR_TOGGLE:
1227 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1228 flag.setOnOff(t->isVisible());
1231 case LFUN_UI_TOGGLE:
1232 flag.setOnOff(isFullScreen());
1235 case LFUN_DIALOG_TOGGLE:
1236 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1237 // fall through to set "enable"
1238 case LFUN_DIALOG_SHOW: {
1239 string const name = cmd.getArg(0);
1241 enable = name == "aboutlyx"
1242 || name == "file" //FIXME: should be removed.
1244 || name == "texinfo";
1245 else if (name == "print")
1246 enable = buf->isExportable("dvi")
1247 && lyxrc.print_command != "none";
1248 else if (name == "character") {
1252 InsetCode ic = view()->cursor().inset().lyxCode();
1253 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1256 else if (name == "symbols") {
1257 if (!view() || view()->cursor().inMathed())
1260 InsetCode ic = view()->cursor().inset().lyxCode();
1261 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1264 else if (name == "latexlog")
1265 enable = FileName(buf->logName()).isReadableFile();
1266 else if (name == "spellchecker")
1267 #if defined (USE_ASPELL)
1268 enable = !buf->isReadonly();
1272 else if (name == "vclog")
1273 enable = buf->lyxvc().inUse();
1277 case LFUN_DIALOG_UPDATE: {
1278 string const name = cmd.getArg(0);
1280 enable = name == "prefs";
1284 case LFUN_INSET_APPLY: {
1285 string const name = cmd.getArg(0);
1286 Inset * inset = getOpenInset(name);
1288 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1290 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1291 // Every inset is supposed to handle this
1292 LASSERT(false, break);
1296 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1297 flag |= lyx::getStatus(fr);
1299 enable = flag.enabled();
1303 case LFUN_COMPLETION_INLINE:
1304 if (!d.current_work_area_
1305 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1309 case LFUN_COMPLETION_POPUP:
1310 if (!d.current_work_area_
1311 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1315 case LFUN_COMPLETION_COMPLETE:
1316 if (!d.current_work_area_
1317 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1321 case LFUN_COMPLETION_ACCEPT:
1322 if (!d.current_work_area_
1323 || (!d.current_work_area_->completer().popupVisible()
1324 && !d.current_work_area_->completer().inlineVisible()
1325 && !d.current_work_area_->completer().completionAvailable()))
1329 case LFUN_COMPLETION_CANCEL:
1330 if (!d.current_work_area_
1331 || (!d.current_work_area_->completer().popupVisible()
1332 && !d.current_work_area_->completer().inlineVisible()))
1341 flag.setEnabled(false);
1347 static FileName selectTemplateFile()
1349 FileDialog dlg(qt_("Select template file"));
1350 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1351 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1353 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1354 QStringList(qt_("LyX Documents (*.lyx)")));
1356 if (result.first == FileDialog::Later)
1358 if (result.second.isEmpty())
1360 return FileName(fromqstr(result.second));
1364 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1368 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1371 message(_("Document not loaded."));
1376 setBuffer(newBuffer);
1378 // scroll to the position when the file was last closed
1379 if (lyxrc.use_lastfilepos) {
1380 LastFilePosSection::FilePos filepos =
1381 theSession().lastFilePos().load(filename);
1382 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1386 theSession().lastFiles().add(filename);
1393 void GuiView::openDocument(string const & fname)
1395 string initpath = lyxrc.document_path;
1398 string const trypath = buffer()->filePath();
1399 // If directory is writeable, use this as default.
1400 if (FileName(trypath).isDirWritable())
1406 if (fname.empty()) {
1407 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1408 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1409 dlg.setButton2(qt_("Examples|#E#e"),
1410 toqstr(addPath(package().system_support().absFilename(), "examples")));
1412 QStringList filter(qt_("LyX Documents (*.lyx)"));
1413 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1414 << qt_("LyX-1.4.x Documents (*.lyx14)")
1415 << qt_("LyX-1.5.x Documents (*.lyx15)")
1416 << qt_("LyX-1.6.x Documents (*.lyx16)");
1417 FileDialog::Result result =
1418 dlg.open(toqstr(initpath), filter);
1420 if (result.first == FileDialog::Later)
1423 filename = fromqstr(result.second);
1425 // check selected filename
1426 if (filename.empty()) {
1427 message(_("Canceled."));
1433 // get absolute path of file and add ".lyx" to the filename if
1435 FileName const fullname =
1436 fileSearch(string(), filename, "lyx", support::may_not_exist);
1437 if (!fullname.empty())
1438 filename = fullname.absFilename();
1440 if (!fullname.onlyPath().isDirectory()) {
1441 Alert::warning(_("Invalid filename"),
1442 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1443 from_utf8(fullname.absFilename())));
1446 // if the file doesn't exist, let the user create one
1447 if (!fullname.exists()) {
1448 // the user specifically chose this name. Believe him.
1449 Buffer * const b = newFile(filename, string(), true);
1455 docstring const disp_fn = makeDisplayPath(filename);
1456 message(bformat(_("Opening document %1$s..."), disp_fn));
1459 Buffer * buf = loadDocument(fullname);
1461 buf->updateLabels();
1463 buf->errors("Parse");
1464 str2 = bformat(_("Document %1$s opened."), disp_fn);
1465 if (buf->lyxvc().inUse())
1466 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1467 " " + _("Version control detected.");
1469 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1474 // FIXME: clean that
1475 static bool import(GuiView * lv, FileName const & filename,
1476 string const & format, ErrorList & errorList)
1478 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1480 string loader_format;
1481 vector<string> loaders = theConverters().loaders();
1482 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1483 for (vector<string>::const_iterator it = loaders.begin();
1484 it != loaders.end(); ++it) {
1485 if (!theConverters().isReachable(format, *it))
1488 string const tofile =
1489 support::changeExtension(filename.absFilename(),
1490 formats.extension(*it));
1491 if (!theConverters().convert(0, filename, FileName(tofile),
1492 filename, format, *it, errorList))
1494 loader_format = *it;
1497 if (loader_format.empty()) {
1498 frontend::Alert::error(_("Couldn't import file"),
1499 bformat(_("No information for importing the format %1$s."),
1500 formats.prettyName(format)));
1504 loader_format = format;
1506 if (loader_format == "lyx") {
1507 Buffer * buf = lv->loadDocument(lyxfile);
1510 buf->updateLabels();
1512 buf->errors("Parse");
1514 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1518 bool as_paragraphs = loader_format == "textparagraph";
1519 string filename2 = (loader_format == format) ? filename.absFilename()
1520 : support::changeExtension(filename.absFilename(),
1521 formats.extension(loader_format));
1522 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1523 theLyXFunc().setLyXView(lv);
1524 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1531 void GuiView::importDocument(string const & argument)
1534 string filename = split(argument, format, ' ');
1536 LYXERR(Debug::INFO, format << " file: " << filename);
1538 // need user interaction
1539 if (filename.empty()) {
1540 string initpath = lyxrc.document_path;
1542 Buffer const * buf = buffer();
1544 string const trypath = buf->filePath();
1545 // If directory is writeable, use this as default.
1546 if (FileName(trypath).isDirWritable())
1550 docstring const text = bformat(_("Select %1$s file to import"),
1551 formats.prettyName(format));
1553 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1554 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1555 dlg.setButton2(qt_("Examples|#E#e"),
1556 toqstr(addPath(package().system_support().absFilename(), "examples")));
1558 docstring filter = formats.prettyName(format);
1561 filter += from_utf8(formats.extension(format));
1564 FileDialog::Result result =
1565 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1567 if (result.first == FileDialog::Later)
1570 filename = fromqstr(result.second);
1572 // check selected filename
1573 if (filename.empty())
1574 message(_("Canceled."));
1577 if (filename.empty())
1580 // get absolute path of file
1581 FileName const fullname(support::makeAbsPath(filename));
1583 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1585 // Check if the document already is open
1586 Buffer * buf = theBufferList().getBuffer(lyxfile);
1589 if (!closeBuffer()) {
1590 message(_("Canceled."));
1595 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1597 // if the file exists already, and we didn't do
1598 // -i lyx thefile.lyx, warn
1599 if (lyxfile.exists() && fullname != lyxfile) {
1601 docstring text = bformat(_("The document %1$s already exists.\n\n"
1602 "Do you want to overwrite that document?"), displaypath);
1603 int const ret = Alert::prompt(_("Overwrite document?"),
1604 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1607 message(_("Canceled."));
1612 message(bformat(_("Importing %1$s..."), displaypath));
1613 ErrorList errorList;
1614 if (import(this, fullname, format, errorList))
1615 message(_("imported."));
1617 message(_("file not imported!"));
1619 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1623 void GuiView::newDocument(string const & filename, bool from_template)
1625 FileName initpath(lyxrc.document_path);
1626 Buffer * buf = buffer();
1628 FileName const trypath(buf->filePath());
1629 // If directory is writeable, use this as default.
1630 if (trypath.isDirWritable())
1634 string templatefile;
1635 if (from_template) {
1636 templatefile = selectTemplateFile().absFilename();
1637 if (templatefile.empty())
1642 if (filename.empty())
1643 b = newUnnamedFile(templatefile, initpath);
1645 b = newFile(filename, templatefile, true);
1650 // If no new document could be created, it is unsure
1651 // whether there is a valid BufferView.
1653 // Ensure the cursor is correctly positioned on screen.
1654 view()->showCursor();
1658 void GuiView::insertLyXFile(docstring const & fname)
1660 BufferView * bv = view();
1665 FileName filename(to_utf8(fname));
1667 if (!filename.empty()) {
1668 bv->insertLyXFile(filename);
1672 // Launch a file browser
1674 string initpath = lyxrc.document_path;
1675 string const trypath = bv->buffer().filePath();
1676 // If directory is writeable, use this as default.
1677 if (FileName(trypath).isDirWritable())
1681 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1682 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1683 dlg.setButton2(qt_("Examples|#E#e"),
1684 toqstr(addPath(package().system_support().absFilename(),
1687 FileDialog::Result result = dlg.open(toqstr(initpath),
1688 QStringList(qt_("LyX Documents (*.lyx)")));
1690 if (result.first == FileDialog::Later)
1694 filename.set(fromqstr(result.second));
1696 // check selected filename
1697 if (filename.empty()) {
1698 // emit message signal.
1699 message(_("Canceled."));
1703 bv->insertLyXFile(filename);
1707 void GuiView::insertPlaintextFile(docstring const & fname,
1710 BufferView * bv = view();
1715 FileName filename(to_utf8(fname));
1717 if (!filename.empty()) {
1718 bv->insertPlaintextFile(filename, asParagraph);
1722 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1723 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1725 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1726 QStringList(qt_("All Files (*)")));
1728 if (result.first == FileDialog::Later)
1732 filename.set(fromqstr(result.second));
1734 // check selected filename
1735 if (filename.empty()) {
1736 // emit message signal.
1737 message(_("Canceled."));
1741 bv->insertPlaintextFile(filename, asParagraph);
1745 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1747 FileName fname = b.fileName();
1748 FileName const oldname = fname;
1750 if (!newname.empty()) {
1752 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1754 // Switch to this Buffer.
1757 /// No argument? Ask user through dialog.
1759 FileDialog dlg(qt_("Choose a filename to save document as"),
1760 LFUN_BUFFER_WRITE_AS);
1761 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1762 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1764 if (!isLyXFilename(fname.absFilename()))
1765 fname.changeExtension(".lyx");
1767 FileDialog::Result result =
1768 dlg.save(toqstr(fname.onlyPath().absFilename()),
1769 QStringList(qt_("LyX Documents (*.lyx)")),
1770 toqstr(fname.onlyFileName()));
1772 if (result.first == FileDialog::Later)
1775 fname.set(fromqstr(result.second));
1780 if (!isLyXFilename(fname.absFilename()))
1781 fname.changeExtension(".lyx");
1784 if (FileName(fname).exists()) {
1785 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1786 docstring text = bformat(_("The document %1$s already "
1787 "exists.\n\nDo you want to "
1788 "overwrite that document?"),
1790 int const ret = Alert::prompt(_("Overwrite document?"),
1791 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1794 case 1: return renameBuffer(b, docstring());
1795 case 2: return false;
1799 // Ok, change the name of the buffer
1800 b.setFileName(fname.absFilename());
1802 bool unnamed = b.isUnnamed();
1803 b.setUnnamed(false);
1804 b.saveCheckSum(fname);
1806 if (!saveBuffer(b)) {
1807 b.setFileName(oldname.absFilename());
1808 b.setUnnamed(unnamed);
1809 b.saveCheckSum(oldname);
1817 bool GuiView::saveBuffer(Buffer & b)
1819 if (workArea(b) && workArea(b)->inDialogMode())
1823 return renameBuffer(b, docstring());
1826 theSession().lastFiles().add(b.fileName());
1830 // Switch to this Buffer.
1833 // FIXME: we don't tell the user *WHY* the save failed !!
1834 docstring const file = makeDisplayPath(b.absFileName(), 30);
1835 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1836 "Do you want to rename the document and "
1837 "try again?"), file);
1838 int const ret = Alert::prompt(_("Rename and save?"),
1839 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1842 if (!renameBuffer(b, docstring()))
1851 return saveBuffer(b);
1855 bool GuiView::closeBuffer()
1857 Buffer * buf = buffer();
1858 return buf && closeBuffer(*buf);
1862 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1864 // goto bookmark to update bookmark pit.
1865 //FIXME: we should update only the bookmarks related to this buffer!
1866 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1867 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1868 theLyXFunc().gotoBookmark(i+1, false, false);
1870 if (buf.isClean() || buf.paragraphs().empty()) {
1871 // save in sessions if requested
1872 // do not save childs if their master
1873 // is opened as well
1875 theSession().lastOpened().add(buf.fileName());
1877 // Don't close child documents.
1878 removeWorkArea(currentMainWorkArea());
1880 theBufferList().release(&buf);
1883 // Switch to this Buffer.
1888 if (buf.isUnnamed())
1889 file = from_utf8(buf.fileName().onlyFileName());
1891 file = buf.fileName().displayName(30);
1893 // Bring this window to top before asking questions.
1897 docstring const text = bformat(_("The document %1$s has unsaved changes."
1898 "\n\nDo you want to save the document or discard the changes?"), file);
1899 int const ret = Alert::prompt(_("Save changed document?"),
1900 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1904 if (!saveBuffer(buf))
1908 // if we crash after this we could
1909 // have no autosave file but I guess
1910 // this is really improbable (Jug)
1911 removeAutosaveFile(buf.absFileName());
1917 // save file names to .lyx/session
1919 theSession().lastOpened().add(buf.fileName());
1922 // Don't close child documents.
1923 removeWorkArea(currentMainWorkArea());
1925 theBufferList().release(&buf);
1931 bool GuiView::dispatch(FuncRequest const & cmd)
1933 BufferView * bv = view();
1934 // By default we won't need any update.
1936 bv->cursor().updateFlags(Update::None);
1937 bool dispatched = true;
1939 switch(cmd.action) {
1940 case LFUN_BUFFER_IMPORT:
1941 importDocument(to_utf8(cmd.argument()));
1944 case LFUN_BUFFER_SWITCH:
1945 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1948 case LFUN_BUFFER_NEXT:
1949 setBuffer(theBufferList().next(buffer()));
1952 case LFUN_BUFFER_PREVIOUS:
1953 setBuffer(theBufferList().previous(buffer()));
1956 case LFUN_COMMAND_EXECUTE: {
1957 bool const show_it = cmd.argument() != "off";
1958 // FIXME: this is a hack, "minibuffer" should not be
1960 if (GuiToolbar * t = toolbar("minibuffer")) {
1961 t->setVisible(show_it);
1962 if (show_it && t->commandBuffer())
1963 t->commandBuffer()->setFocus();
1967 case LFUN_DROP_LAYOUTS_CHOICE:
1969 d.layout_->showPopup();
1972 case LFUN_MENU_OPEN:
1973 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1974 menu->exec(QCursor::pos());
1977 case LFUN_FILE_INSERT:
1978 insertLyXFile(cmd.argument());
1980 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1981 insertPlaintextFile(cmd.argument(), true);
1984 case LFUN_FILE_INSERT_PLAINTEXT:
1985 insertPlaintextFile(cmd.argument(), false);
1988 case LFUN_BUFFER_WRITE:
1990 saveBuffer(bv->buffer());
1993 case LFUN_BUFFER_WRITE_AS:
1995 renameBuffer(bv->buffer(), cmd.argument());
1998 case LFUN_BUFFER_WRITE_ALL: {
1999 Buffer * first = theBufferList().first();
2002 message(_("Saving all documents..."));
2003 // We cannot use a for loop as the buffer list cycles.
2006 if (!b->isClean()) {
2008 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2010 b = theBufferList().next(b);
2011 } while (b != first);
2012 message(_("All documents saved."));
2016 case LFUN_TOOLBAR_TOGGLE: {
2017 string const name = cmd.getArg(0);
2018 if (GuiToolbar * t = toolbar(name))
2023 case LFUN_DIALOG_UPDATE: {
2024 string const name = to_utf8(cmd.argument());
2025 // Can only update a dialog connected to an existing inset
2026 Inset * inset = getOpenInset(name);
2028 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2029 inset->dispatch(view()->cursor(), fr);
2030 } else if (name == "paragraph") {
2031 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2032 } else if (name == "prefs" || name == "document") {
2033 updateDialog(name, string());
2038 case LFUN_DIALOG_TOGGLE: {
2039 if (isDialogVisible(cmd.getArg(0)))
2040 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2042 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2046 case LFUN_DIALOG_DISCONNECT_INSET:
2047 disconnectDialog(to_utf8(cmd.argument()));
2050 case LFUN_DIALOG_HIDE: {
2051 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2055 case LFUN_DIALOG_SHOW: {
2056 string const name = cmd.getArg(0);
2057 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2059 if (name == "character") {
2060 data = freefont2string();
2062 showDialog("character", data);
2063 } else if (name == "latexlog") {
2064 Buffer::LogType type;
2065 string const logfile = buffer()->logName(&type);
2067 case Buffer::latexlog:
2070 case Buffer::buildlog:
2074 data += Lexer::quoteString(logfile);
2075 showDialog("log", data);
2076 } else if (name == "vclog") {
2077 string const data = "vc " +
2078 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2079 showDialog("log", data);
2080 } else if (name == "symbols") {
2081 data = bv->cursor().getEncoding()->name();
2083 showDialog("symbols", data);
2085 } else if (name == "prefs" && isFullScreen()) {
2086 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2088 showDialog("prefs", data);
2090 showDialog(name, data);
2094 case LFUN_INSET_APPLY: {
2095 string const name = cmd.getArg(0);
2096 Inset * inset = getOpenInset(name);
2098 // put cursor in front of inset.
2099 if (!view()->setCursorFromInset(inset)) {
2100 LASSERT(false, break);
2103 // useful if we are called from a dialog.
2104 view()->cursor().beginUndoGroup();
2105 view()->cursor().recordUndo();
2106 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2107 inset->dispatch(view()->cursor(), fr);
2108 view()->cursor().endUndoGroup();
2110 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2116 case LFUN_UI_TOGGLE:
2118 // Make sure the keyboard focus stays in the work area.
2122 case LFUN_SPLIT_VIEW:
2123 if (Buffer * buf = buffer()) {
2124 string const orientation = cmd.getArg(0);
2125 d.splitter_->setOrientation(orientation == "vertical"
2126 ? Qt::Vertical : Qt::Horizontal);
2127 TabWorkArea * twa = addTabWorkArea();
2128 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2129 setCurrentWorkArea(wa);
2133 case LFUN_CLOSE_TAB_GROUP:
2134 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2136 twa = d.currentTabWorkArea();
2137 // Switch to the next GuiWorkArea in the found TabWorkArea.
2139 // Make sure the work area is up to date.
2140 setCurrentWorkArea(twa->currentWorkArea());
2142 setCurrentWorkArea(0);
2147 case LFUN_COMPLETION_INLINE:
2148 if (d.current_work_area_)
2149 d.current_work_area_->completer().showInline();
2152 case LFUN_COMPLETION_POPUP:
2153 if (d.current_work_area_)
2154 d.current_work_area_->completer().showPopup();
2158 case LFUN_COMPLETION_COMPLETE:
2159 if (d.current_work_area_)
2160 d.current_work_area_->completer().tab();
2163 case LFUN_COMPLETION_CANCEL:
2164 if (d.current_work_area_) {
2165 if (d.current_work_area_->completer().popupVisible())
2166 d.current_work_area_->completer().hidePopup();
2168 d.current_work_area_->completer().hideInline();
2172 case LFUN_COMPLETION_ACCEPT:
2173 if (d.current_work_area_)
2174 d.current_work_area_->completer().activate();
2183 // Part of automatic menu appearance feature.
2184 if (isFullScreen()) {
2185 if (menuBar()->isVisible())
2187 if (statusBar()->isVisible())
2188 statusBar()->hide();
2195 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2197 string const arg = cmd.getArg(0);
2198 if (arg == "scrollbar") {
2199 // hide() is of no help
2200 if (d.current_work_area_->verticalScrollBarPolicy() ==
2201 Qt::ScrollBarAlwaysOff)
2203 d.current_work_area_->setVerticalScrollBarPolicy(
2204 Qt::ScrollBarAsNeeded);
2206 d.current_work_area_->setVerticalScrollBarPolicy(
2207 Qt::ScrollBarAlwaysOff);
2210 if (arg == "statusbar") {
2211 statusBar()->setVisible(!statusBar()->isVisible());
2214 if (arg == "menubar") {
2215 menuBar()->setVisible(!menuBar()->isVisible());
2218 #if QT_VERSION >= 0x040300
2219 if (arg == "frame") {
2221 getContentsMargins(&l, &t, &r, &b);
2222 //are the frames in default state?
2223 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2225 setContentsMargins(-2, -2, -2, -2);
2227 setContentsMargins(0, 0, 0, 0);
2232 if (arg == "fullscreen") {
2237 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2241 void GuiView::toggleFullScreen()
2243 if (isFullScreen()) {
2244 for (int i = 0; i != d.splitter_->count(); ++i)
2245 d.tabWorkArea(i)->setFullScreen(false);
2246 #if QT_VERSION >= 0x040300
2247 setContentsMargins(0, 0, 0, 0);
2249 setWindowState(windowState() ^ Qt::WindowFullScreen);
2252 statusBar()->show();
2255 hideDialogs("prefs", 0);
2256 for (int i = 0; i != d.splitter_->count(); ++i)
2257 d.tabWorkArea(i)->setFullScreen(true);
2258 #if QT_VERSION >= 0x040300
2259 setContentsMargins(-2, -2, -2, -2);
2262 setWindowState(windowState() ^ Qt::WindowFullScreen);
2263 statusBar()->hide();
2265 if (lyxrc.full_screen_toolbars) {
2266 ToolbarMap::iterator end = d.toolbars_.end();
2267 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2272 // give dialogs like the TOC a chance to adapt
2277 Buffer const * GuiView::updateInset(Inset const * inset)
2279 if (!d.current_work_area_)
2283 d.current_work_area_->scheduleRedraw();
2285 return &d.current_work_area_->bufferView().buffer();
2289 void GuiView::restartCursor()
2291 /* When we move around, or type, it's nice to be able to see
2292 * the cursor immediately after the keypress.
2294 if (d.current_work_area_)
2295 d.current_work_area_->startBlinkingCursor();
2297 // Take this occasion to update the other GUI elements.
2303 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2305 if (d.current_work_area_)
2306 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2311 // This list should be kept in sync with the list of insets in
2312 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2313 // dialog should have the same name as the inset.
2314 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2315 // docs in LyXAction.cpp.
2317 char const * const dialognames[] = {
2318 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2319 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2320 "float", "graphics", "include", "index", "info", "nomenclature", "label",
2321 "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2322 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2323 "symbols", "tabular", "tabularcreate",
2325 #ifdef HAVE_LIBAIKSAURUS
2329 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2331 char const * const * const end_dialognames =
2332 dialognames + (sizeof(dialognames) / sizeof(char *));
2336 cmpCStr(char const * name) : name_(name) {}
2337 bool operator()(char const * other) {
2338 return strcmp(other, name_) == 0;
2345 bool isValidName(string const & name)
2347 return find_if(dialognames, end_dialognames,
2348 cmpCStr(name.c_str())) != end_dialognames;
2354 void GuiView::resetDialogs()
2356 // Make sure that no LFUN uses any LyXView.
2357 theLyXFunc().setLyXView(0);
2360 constructToolbars();
2361 guiApp->menus().fillMenuBar(menuBar(), this, false);
2363 d.layout_->updateContents(true);
2364 // Now update controls with current buffer.
2365 theLyXFunc().setLyXView(this);
2371 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2373 if (!isValidName(name))
2376 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2378 if (it != d.dialogs_.end()) {
2380 it->second->hideView();
2381 return it->second.get();
2384 Dialog * dialog = build(name);
2385 d.dialogs_[name].reset(dialog);
2386 if (lyxrc.allow_geometry_session)
2387 dialog->restoreSession();
2394 void GuiView::showDialog(string const & name, string const & data,
2402 Dialog * dialog = findOrBuild(name, false);
2404 dialog->showData(data);
2406 d.open_insets_[name] = inset;
2409 catch (ExceptionMessage const & ex) {
2417 bool GuiView::isDialogVisible(string const & name) const
2419 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2420 if (it == d.dialogs_.end())
2422 return it->second.get()->isVisibleView();
2426 void GuiView::hideDialog(string const & name, Inset * inset)
2428 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2429 if (it == d.dialogs_.end())
2432 if (inset && inset != getOpenInset(name))
2435 Dialog * const dialog = it->second.get();
2436 if (dialog->isVisibleView())
2438 d.open_insets_[name] = 0;
2442 void GuiView::disconnectDialog(string const & name)
2444 if (!isValidName(name))
2447 if (d.open_insets_.find(name) != d.open_insets_.end())
2448 d.open_insets_[name] = 0;
2452 Inset * GuiView::getOpenInset(string const & name) const
2454 if (!isValidName(name))
2457 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2458 return it == d.open_insets_.end() ? 0 : it->second;
2462 void GuiView::hideAll() const
2464 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2465 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2467 for(; it != end; ++it)
2468 it->second->hideView();
2472 void GuiView::updateDialogs()
2474 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2475 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2477 for(; it != end; ++it) {
2478 Dialog * dialog = it->second.get();
2479 if (dialog && dialog->isVisibleView())
2480 dialog->checkStatus();
2487 // will be replaced by a proper factory...
2488 Dialog * createGuiAbout(GuiView & lv);
2489 Dialog * createGuiBibitem(GuiView & lv);
2490 Dialog * createGuiBibtex(GuiView & lv);
2491 Dialog * createGuiBox(GuiView & lv);
2492 Dialog * createGuiBranch(GuiView & lv);
2493 Dialog * createGuiChanges(GuiView & lv);
2494 Dialog * createGuiCharacter(GuiView & lv);
2495 Dialog * createGuiCitation(GuiView & lv);
2496 Dialog * createGuiDelimiter(GuiView & lv);
2497 Dialog * createGuiDocument(GuiView & lv);
2498 Dialog * createGuiErrorList(GuiView & lv);
2499 Dialog * createGuiERT(GuiView & lv);
2500 Dialog * createGuiExternal(GuiView & lv);
2501 Dialog * createGuiFloat(GuiView & lv);
2502 Dialog * createGuiGraphics(GuiView & lv);
2503 Dialog * createGuiInclude(GuiView & lv);
2504 Dialog * createGuiInfo(GuiView & lv);
2505 Dialog * createGuiLabel(GuiView & lv);
2506 Dialog * createGuiListings(GuiView & lv);
2507 Dialog * createGuiLog(GuiView & lv);
2508 Dialog * createGuiMathHSpace(GuiView & lv);
2509 Dialog * createGuiMathMatrix(GuiView & lv);
2510 Dialog * createGuiNomenclature(GuiView & lv);
2511 Dialog * createGuiNote(GuiView & lv);
2512 Dialog * createGuiParagraph(GuiView & lv);
2513 Dialog * createGuiPhantom(GuiView & lv);
2514 Dialog * createGuiPreferences(GuiView & lv);
2515 Dialog * createGuiPrint(GuiView & lv);
2516 Dialog * createGuiRef(GuiView & lv);
2517 Dialog * createGuiSearch(GuiView & lv);
2518 Dialog * createGuiSearchAdv(GuiView & lv);
2519 Dialog * createGuiSendTo(GuiView & lv);
2520 Dialog * createGuiShowFile(GuiView & lv);
2521 Dialog * createGuiSpellchecker(GuiView & lv);
2522 Dialog * createGuiSymbols(GuiView & lv);
2523 Dialog * createGuiTabularCreate(GuiView & lv);
2524 Dialog * createGuiTabular(GuiView & lv);
2525 Dialog * createGuiTexInfo(GuiView & lv);
2526 Dialog * createGuiTextHSpace(GuiView & lv);
2527 Dialog * createGuiToc(GuiView & lv);
2528 Dialog * createGuiThesaurus(GuiView & lv);
2529 Dialog * createGuiHyperlink(GuiView & lv);
2530 Dialog * createGuiVSpace(GuiView & lv);
2531 Dialog * createGuiViewSource(GuiView & lv);
2532 Dialog * createGuiWrap(GuiView & lv);
2535 Dialog * GuiView::build(string const & name)
2537 LASSERT(isValidName(name), return 0);
2539 if (name == "aboutlyx")
2540 return createGuiAbout(*this);
2541 if (name == "bibitem")
2542 return createGuiBibitem(*this);
2543 if (name == "bibtex")
2544 return createGuiBibtex(*this);
2546 return createGuiBox(*this);
2547 if (name == "branch")
2548 return createGuiBranch(*this);
2549 if (name == "changes")
2550 return createGuiChanges(*this);
2551 if (name == "character")
2552 return createGuiCharacter(*this);
2553 if (name == "citation")
2554 return createGuiCitation(*this);
2555 if (name == "document")
2556 return createGuiDocument(*this);
2557 if (name == "errorlist")
2558 return createGuiErrorList(*this);
2560 return createGuiERT(*this);
2561 if (name == "external")
2562 return createGuiExternal(*this);
2564 return createGuiShowFile(*this);
2565 if (name == "findreplace")
2566 return createGuiSearch(*this);
2567 if (name == "findreplaceadv")
2568 return createGuiSearchAdv(*this);
2569 if (name == "float")
2570 return createGuiFloat(*this);
2571 if (name == "graphics")
2572 return createGuiGraphics(*this);
2573 if (name == "include")
2574 return createGuiInclude(*this);
2576 return createGuiInfo(*this);
2577 if (name == "nomenclature")
2578 return createGuiNomenclature(*this);
2579 if (name == "label")
2580 return createGuiLabel(*this);
2582 return createGuiLog(*this);
2583 if (name == "mathdelimiter")
2584 return createGuiDelimiter(*this);
2585 if (name == "mathspace")
2586 return createGuiMathHSpace(*this);
2587 if (name == "mathmatrix")
2588 return createGuiMathMatrix(*this);
2590 return createGuiNote(*this);
2591 if (name == "paragraph")
2592 return createGuiParagraph(*this);
2593 if (name == "phantom")
2594 return createGuiPhantom(*this);
2595 if (name == "prefs")
2596 return createGuiPreferences(*this);
2597 if (name == "print")
2598 return createGuiPrint(*this);
2600 return createGuiRef(*this);
2601 if (name == "sendto")
2602 return createGuiSendTo(*this);
2603 if (name == "space")
2604 return createGuiTextHSpace(*this);
2605 if (name == "spellchecker")
2606 return createGuiSpellchecker(*this);
2607 if (name == "symbols")
2608 return createGuiSymbols(*this);
2609 if (name == "tabular")
2610 return createGuiTabular(*this);
2611 if (name == "tabularcreate")
2612 return createGuiTabularCreate(*this);
2613 if (name == "texinfo")
2614 return createGuiTexInfo(*this);
2615 if (name == "view-source")
2616 return createGuiViewSource(*this);
2617 #ifdef HAVE_LIBAIKSAURUS
2618 if (name == "thesaurus")
2619 return createGuiThesaurus(*this);
2622 return createGuiHyperlink(*this);
2623 if (name == "listings")
2624 return createGuiListings(*this);
2626 return createGuiToc(*this);
2627 if (name == "vspace")
2628 return createGuiVSpace(*this);
2630 return createGuiWrap(*this);
2636 } // namespace frontend
2639 #include "moc_GuiView.cpp"