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>
84 #include <QPixmapCache>
86 #include <QPushButton>
90 #include <QStackedWidget>
97 #include <boost/bind.hpp>
99 #ifdef HAVE_SYS_TIME_H
100 # include <sys/time.h>
107 using namespace lyx::support;
114 class BackgroundWidget : public QWidget
119 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
120 /// The text to be written on top of the pixmap
121 QString const text = lyx_version ?
122 qt_("version ") + lyx_version : qt_("unknown version");
123 splash_ = getPixmap("images/", "banner", "png");
125 QPainter pain(&splash_);
126 pain.setPen(QColor(0, 0, 0));
128 // The font used to display the version info
129 font.setStyleHint(QFont::SansSerif);
130 font.setWeight(QFont::Bold);
131 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
133 pain.drawText(260, 15, text);
136 void paintEvent(QPaintEvent *)
138 int x = (width() - splash_.width()) / 2;
139 int y = (height() - splash_.height()) / 2;
141 pain.drawPixmap(x, y, splash_);
149 /// Toolbar store providing access to individual toolbars by name.
150 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
152 typedef boost::shared_ptr<Dialog> DialogPtr;
157 struct GuiView::GuiViewPrivate
160 : current_work_area_(0), current_main_work_area_(0),
161 layout_(0), autosave_timeout_(5000),
164 // hardcode here the platform specific icon size
165 smallIconSize = 14; // scaling problems
166 normalIconSize = 20; // ok, default
167 bigIconSize = 26; // better for some math icons
169 splitter_ = new QSplitter;
170 bg_widget_ = new BackgroundWidget;
171 stack_widget_ = new QStackedWidget;
172 stack_widget_->addWidget(bg_widget_);
173 stack_widget_->addWidget(splitter_);
181 delete stack_widget_;
184 QMenu * toolBarPopup(GuiView * parent)
186 // FIXME: translation
187 QMenu * menu = new QMenu(parent);
188 QActionGroup * iconSizeGroup = new QActionGroup(parent);
190 QAction * smallIcons = new QAction(iconSizeGroup);
191 smallIcons->setText(qt_("Small-sized icons"));
192 smallIcons->setCheckable(true);
193 QObject::connect(smallIcons, SIGNAL(triggered()),
194 parent, SLOT(smallSizedIcons()));
195 menu->addAction(smallIcons);
197 QAction * normalIcons = new QAction(iconSizeGroup);
198 normalIcons->setText(qt_("Normal-sized icons"));
199 normalIcons->setCheckable(true);
200 QObject::connect(normalIcons, SIGNAL(triggered()),
201 parent, SLOT(normalSizedIcons()));
202 menu->addAction(normalIcons);
204 QAction * bigIcons = new QAction(iconSizeGroup);
205 bigIcons->setText(qt_("Big-sized icons"));
206 bigIcons->setCheckable(true);
207 QObject::connect(bigIcons, SIGNAL(triggered()),
208 parent, SLOT(bigSizedIcons()));
209 menu->addAction(bigIcons);
211 unsigned int cur = parent->iconSize().width();
212 if ( cur == parent->d.smallIconSize)
213 smallIcons->setChecked(true);
214 else if (cur == parent->d.normalIconSize)
215 normalIcons->setChecked(true);
216 else if (cur == parent->d.bigIconSize)
217 bigIcons->setChecked(true);
224 stack_widget_->setCurrentWidget(bg_widget_);
225 bg_widget_->setUpdatesEnabled(true);
228 TabWorkArea * tabWorkArea(int i)
230 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
233 TabWorkArea * currentTabWorkArea()
235 if (splitter_->count() == 1)
236 // The first TabWorkArea is always the first one, if any.
237 return tabWorkArea(0);
239 for (int i = 0; i != splitter_->count(); ++i) {
240 TabWorkArea * twa = tabWorkArea(i);
241 if (current_main_work_area_ == twa->currentWorkArea())
245 // None has the focus so we just take the first one.
246 return tabWorkArea(0);
250 GuiWorkArea * current_work_area_;
251 GuiWorkArea * current_main_work_area_;
252 QSplitter * splitter_;
253 QStackedWidget * stack_widget_;
254 BackgroundWidget * bg_widget_;
256 ToolbarMap toolbars_;
257 /// The main layout box.
259 * \warning Don't Delete! The layout box is actually owned by
260 * whichever toolbar contains it. All the GuiView class needs is a
261 * means of accessing it.
263 * FIXME: replace that with a proper model so that we are not limited
264 * to only one dialog.
266 GuiLayoutBox * layout_;
269 map<string, Inset *> open_insets_;
272 map<string, DialogPtr> dialogs_;
274 unsigned int smallIconSize;
275 unsigned int normalIconSize;
276 unsigned int bigIconSize;
278 QTimer statusbar_timer_;
279 /// auto-saving of buffers
280 Timeout autosave_timeout_;
281 /// flag against a race condition due to multiclicks, see bug #1119
285 TocModels toc_models_;
289 GuiView::GuiView(int id)
290 : d(*new GuiViewPrivate), id_(id), closing_(false)
292 // GuiToolbars *must* be initialised before the menu bar.
293 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
296 // set ourself as the current view. This is needed for the menu bar
297 // filling, at least for the static special menu item on Mac. Otherwise
298 // they are greyed out.
299 theLyXFunc().setLyXView(this);
301 // Fill up the menu bar.
302 guiApp->menus().fillMenuBar(menuBar(), this, true);
304 setCentralWidget(d.stack_widget_);
306 // Start autosave timer
307 if (lyxrc.autosave) {
308 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
309 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
310 d.autosave_timeout_.start();
312 connect(&d.statusbar_timer_, SIGNAL(timeout()),
313 this, SLOT(clearMessage()));
315 // We don't want to keep the window in memory if it is closed.
316 setAttribute(Qt::WA_DeleteOnClose, true);
318 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
319 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
320 // since the icon is provided in the application bundle.
321 setWindowIcon(getPixmap("images/", "lyx", "png"));
325 setAcceptDrops(true);
327 statusBar()->setSizeGripEnabled(true);
329 // Forbid too small unresizable window because it can happen
330 // with some window manager under X11.
331 setMinimumSize(300, 200);
333 if (lyxrc.allow_geometry_session) {
334 // Now take care of session management.
339 // no session handling, default to a sane size.
340 setGeometry(50, 50, 690, 510);
343 // clear session data if any.
345 settings.remove("views");
355 void GuiView::saveLayout() const
358 settings.beginGroup("views");
359 settings.beginGroup(QString::number(id_));
361 settings.setValue("pos", pos());
362 settings.setValue("size", size());
364 settings.setValue("geometry", saveGeometry());
366 settings.setValue("layout", saveState(0));
367 settings.setValue("icon_size", iconSize());
371 bool GuiView::restoreLayout()
374 settings.beginGroup("views");
375 settings.beginGroup(QString::number(id_));
376 QString const icon_key = "icon_size";
377 if (!settings.contains(icon_key))
380 setIconSize(settings.value(icon_key).toSize());
382 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
383 QSize size = settings.value("size", QSize(690, 510)).toSize();
387 if (!restoreGeometry(settings.value("geometry").toByteArray()))
388 setGeometry(50, 50, 690, 510);
390 // Make sure layout is correctly oriented.
391 setLayoutDirection(qApp->layoutDirection());
393 // Allow the toc and view-source dock widget to be restored if needed.
395 if ((d = findOrBuild("toc", true)))
398 if ((d = findOrBuild("view-source", true)))
401 if (!restoreState(settings.value("layout").toByteArray(), 0))
408 GuiToolbar * GuiView::toolbar(string const & name)
410 ToolbarMap::iterator it = d.toolbars_.find(name);
411 if (it != d.toolbars_.end())
414 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
415 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
420 void GuiView::constructToolbars()
422 ToolbarMap::iterator it = d.toolbars_.begin();
423 for (; it != d.toolbars_.end(); ++it)
428 // extracts the toolbars from the backend
429 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
430 Toolbars::Infos::iterator end = guiApp->toolbars().end();
431 for (; cit != end; ++cit)
432 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
436 void GuiView::initToolbars()
438 // extracts the toolbars from the backend
439 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
440 Toolbars::Infos::iterator end = guiApp->toolbars().end();
441 for (; cit != end; ++cit) {
442 GuiToolbar * tb = toolbar(cit->name);
445 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
447 tb->setVisible(false);
448 tb->setVisibility(visibility);
450 if (visibility & Toolbars::TOP) {
452 addToolBarBreak(Qt::TopToolBarArea);
453 addToolBar(Qt::TopToolBarArea, tb);
456 if (visibility & Toolbars::BOTTOM) {
457 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
458 #if (QT_VERSION >= 0x040202)
459 addToolBarBreak(Qt::BottomToolBarArea);
461 addToolBar(Qt::BottomToolBarArea, tb);
464 if (visibility & Toolbars::LEFT) {
465 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
466 #if (QT_VERSION >= 0x040202)
467 addToolBarBreak(Qt::LeftToolBarArea);
469 addToolBar(Qt::LeftToolBarArea, tb);
472 if (visibility & Toolbars::RIGHT) {
473 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
474 #if (QT_VERSION >= 0x040202)
475 addToolBarBreak(Qt::RightToolBarArea);
477 addToolBar(Qt::RightToolBarArea, tb);
480 if (visibility & Toolbars::ON)
481 tb->setVisible(true);
486 TocModels & GuiView::tocModels()
488 return d.toc_models_;
492 void GuiView::setFocus()
494 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
495 // Make sure LyXFunc points to the correct view.
496 guiApp->setCurrentView(this);
497 theLyXFunc().setLyXView(this);
498 QMainWindow::setFocus();
499 if (d.current_work_area_)
500 d.current_work_area_->setFocus();
504 QMenu * GuiView::createPopupMenu()
506 return d.toolBarPopup(this);
510 void GuiView::showEvent(QShowEvent * e)
512 LYXERR(Debug::GUI, "Passed Geometry "
513 << size().height() << "x" << size().width()
514 << "+" << pos().x() << "+" << pos().y());
516 if (d.splitter_->count() == 0)
517 // No work area, switch to the background widget.
520 QMainWindow::showEvent(e);
524 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
525 ** is responsibility of the container (e.g., dialog)
527 void GuiView::closeEvent(QCloseEvent * close_event)
529 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
532 // it can happen that this event arrives without selecting the view,
533 // e.g. when clicking the close button on a background window.
535 setCurrentWorkArea(currentMainWorkArea());
536 while (GuiWorkArea * wa = currentMainWorkArea()) {
537 Buffer * b = &wa->bufferView().buffer();
539 // This is a child document, just close the tab
540 // after saving but keep the file loaded.
541 if (!closeBuffer(*b, true)) {
543 close_event->ignore();
549 vector<Buffer *> clist = b->getChildren();
550 for (vector<Buffer *>::const_iterator it = clist.begin();
551 it != clist.end(); ++it) {
552 if ((*it)->isClean())
555 // If a child is dirty, do not close
556 // without user intervention
557 if (!closeBuffer(*c, false)) {
559 close_event->ignore();
564 QList<int> const ids = guiApp->viewIds();
565 for (int i = 0; i != ids.size(); ++i) {
568 if (guiApp->view(ids[i]).workArea(*b)) {
569 // FIXME 1: should we put an alert box here that the buffer
570 // is viewed elsewhere?
571 // FIXME 2: should we try to save this buffer in any case?
574 // This buffer is also opened in another view, so
575 // close the associated work area...
577 // ... but don't close the buffer.
582 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
583 if (b && !closeBuffer(*b, true)) {
585 close_event->ignore();
590 // Make sure that nothing will use this close to be closed View.
591 guiApp->unregisterView(this);
593 if (isFullScreen()) {
594 // Switch off fullscreen before closing.
599 // Make sure the timer time out will not trigger a statusbar update.
600 d.statusbar_timer_.stop();
602 // Saving fullscreen requires additional tweaks in the toolbar code.
603 // It wouldn't also work under linux natively.
604 if (lyxrc.allow_geometry_session) {
605 // Save this window geometry and layout.
607 // Then the toolbar private states.
608 ToolbarMap::iterator end = d.toolbars_.end();
609 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
610 it->second->saveSession();
611 // Now take care of all other dialogs:
612 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
613 for (; it!= d.dialogs_.end(); ++it)
614 it->second->saveSession();
617 close_event->accept();
621 void GuiView::dragEnterEvent(QDragEnterEvent * event)
623 if (event->mimeData()->hasUrls())
625 /// \todo Ask lyx-devel is this is enough:
626 /// if (event->mimeData()->hasFormat("text/plain"))
627 /// event->acceptProposedAction();
631 void GuiView::dropEvent(QDropEvent * event)
633 QList<QUrl> files = event->mimeData()->urls();
637 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
638 for (int i = 0; i != files.size(); ++i) {
639 string const file = os::internal_path(fromqstr(
640 files.at(i).toLocalFile()));
642 // Asynchronously post the event. DropEvent usually come
643 // from the BufferView. But reloading a file might close
644 // the BufferView from within its own event handler.
645 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
652 void GuiView::message(docstring const & str)
654 if (ForkedProcess::iAmAChild())
657 statusBar()->showMessage(toqstr(str));
658 d.statusbar_timer_.stop();
659 d.statusbar_timer_.start(3000);
663 void GuiView::smallSizedIcons()
665 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
669 void GuiView::normalSizedIcons()
671 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
675 void GuiView::bigSizedIcons()
677 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
681 void GuiView::clearMessage()
685 theLyXFunc().setLyXView(this);
686 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
687 d.statusbar_timer_.stop();
691 void GuiView::updateWindowTitle(GuiWorkArea * wa)
693 if (wa != d.current_work_area_)
695 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
696 setWindowIconText(wa->windowIconText());
700 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
703 disconnectBufferView();
704 connectBufferView(wa->bufferView());
705 connectBuffer(wa->bufferView().buffer());
706 d.current_work_area_ = wa;
707 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
708 this, SLOT(updateWindowTitle(GuiWorkArea *)));
709 updateWindowTitle(wa);
713 // The document settings needs to be reinitialised.
714 updateDialog("document", "");
716 // Buffer-dependent dialogs must be updated. This is done here because
717 // some dialogs require buffer()->text.
722 void GuiView::on_lastWorkAreaRemoved()
725 // We already are in a close event. Nothing more to do.
728 if (d.splitter_->count() > 1)
729 // We have a splitter so don't close anything.
732 // Reset and updates the dialogs.
733 d.toc_models_.reset(0);
734 updateDialog("document", "");
737 resetWindowTitleAndIconText();
739 if (lyxrc.open_buffers_in_tabs)
740 // Nothing more to do, the window should stay open.
743 if (guiApp->viewIds().size() > 1) {
749 // On Mac we also close the last window because the application stay
750 // resident in memory. On other platforms we don't close the last
751 // window because this would quit the application.
757 void GuiView::updateStatusBar()
759 // let the user see the explicit message
760 if (d.statusbar_timer_.isActive())
763 theLyXFunc().setLyXView(this);
764 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
768 bool GuiView::hasFocus() const
770 return qApp->activeWindow() == this;
774 bool GuiView::event(QEvent * e)
778 // Useful debug code:
779 //case QEvent::ActivationChange:
780 //case QEvent::WindowDeactivate:
781 //case QEvent::Paint:
782 //case QEvent::Enter:
783 //case QEvent::Leave:
784 //case QEvent::HoverEnter:
785 //case QEvent::HoverLeave:
786 //case QEvent::HoverMove:
787 //case QEvent::StatusTip:
788 //case QEvent::DragEnter:
789 //case QEvent::DragLeave:
793 case QEvent::WindowActivate: {
794 if (this == guiApp->currentView()) {
796 return QMainWindow::event(e);
798 guiApp->setCurrentView(this);
799 theLyXFunc().setLyXView(this);
800 if (d.current_work_area_) {
801 BufferView & bv = d.current_work_area_->bufferView();
802 connectBufferView(bv);
803 connectBuffer(bv.buffer());
804 // The document structure, name and dialogs might have
805 // changed in another view.
807 // The document settings needs to be reinitialised.
808 updateDialog("document", "");
811 resetWindowTitleAndIconText();
814 return QMainWindow::event(e);
817 case QEvent::ShortcutOverride: {
821 if (isFullScreen() && menuBar()->isHidden()) {
822 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
823 // FIXME: we should also try to detect special LyX shortcut such as
824 // Alt-P and Alt-M. Right now there is a hack in
825 // GuiWorkArea::processKeySym() that hides again the menubar for
827 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
829 return QMainWindow::event(e);
834 if (d.current_work_area_)
835 // Nothing special to do.
836 return QMainWindow::event(e);
838 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
839 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
841 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
842 || ke->key() == Qt::Key_Backtab)
843 return QMainWindow::event(e);
845 // Allow processing of shortcuts that are allowed even when no Buffer
847 theLyXFunc().setLyXView(this);
849 setKeySymbol(&sym, ke);
850 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
856 return QMainWindow::event(e);
860 void GuiView::resetWindowTitleAndIconText()
862 setWindowTitle(qt_("LyX"));
863 setWindowIconText(qt_("LyX"));
866 bool GuiView::focusNextPrevChild(bool /*next*/)
873 void GuiView::setBusy(bool busy)
875 if (d.current_work_area_) {
876 d.current_work_area_->setUpdatesEnabled(!busy);
878 d.current_work_area_->stopBlinkingCursor();
880 d.current_work_area_->startBlinkingCursor();
884 QApplication::setOverrideCursor(Qt::WaitCursor);
886 QApplication::restoreOverrideCursor();
890 GuiWorkArea * GuiView::workArea(Buffer & buffer)
892 if (currentWorkArea()
893 && ¤tWorkArea()->bufferView().buffer() == &buffer)
894 return (GuiWorkArea *) currentWorkArea();
895 if (TabWorkArea * twa = d.currentTabWorkArea())
896 return twa->workArea(buffer);
901 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
903 // Automatically create a TabWorkArea if there are none yet.
904 TabWorkArea * tab_widget = d.splitter_->count()
905 ? d.currentTabWorkArea() : addTabWorkArea();
906 return tab_widget->addWorkArea(buffer, *this);
910 TabWorkArea * GuiView::addTabWorkArea()
912 TabWorkArea * twa = new TabWorkArea;
913 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
914 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
915 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
916 this, SLOT(on_lastWorkAreaRemoved()));
918 d.splitter_->addWidget(twa);
919 d.stack_widget_->setCurrentWidget(d.splitter_);
924 GuiWorkArea const * GuiView::currentWorkArea() const
926 return d.current_work_area_;
930 GuiWorkArea * GuiView::currentWorkArea()
932 return d.current_work_area_;
936 GuiWorkArea const * GuiView::currentMainWorkArea() const
938 if (d.currentTabWorkArea() == NULL)
940 return d.currentTabWorkArea()->currentWorkArea();
944 GuiWorkArea * GuiView::currentMainWorkArea()
946 if (d.currentTabWorkArea() == NULL)
948 return d.currentTabWorkArea()->currentWorkArea();
952 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
954 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
956 d.current_work_area_ = NULL;
960 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
964 theGuiApp()->setCurrentView(this);
965 d.current_work_area_ = wa;
966 for (int i = 0; i != d.splitter_->count(); ++i) {
967 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
968 //if (d.current_main_work_area_)
969 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
970 d.current_main_work_area_ = wa;
971 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
972 //d.current_main_work_area_->setLineWidth(2);
973 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
977 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
978 on_currentWorkAreaChanged(wa);
979 BufferView & bv = wa->bufferView();
980 bv.cursor().fixIfBroken();
982 wa->setUpdatesEnabled(true);
983 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
987 void GuiView::removeWorkArea(GuiWorkArea * wa)
990 if (wa == d.current_work_area_) {
992 disconnectBufferView();
993 d.current_work_area_ = 0;
994 d.current_main_work_area_ = 0;
997 bool found_twa = false;
998 for (int i = 0; i != d.splitter_->count(); ++i) {
999 TabWorkArea * twa = d.tabWorkArea(i);
1000 if (twa->removeWorkArea(wa)) {
1001 // Found in this tab group, and deleted the GuiWorkArea.
1003 if (twa->count() != 0) {
1004 if (d.current_work_area_ == 0)
1005 // This means that we are closing the current GuiWorkArea, so
1006 // switch to the next GuiWorkArea in the found TabWorkArea.
1007 setCurrentWorkArea(twa->currentWorkArea());
1009 // No more WorkAreas in this tab group, so delete it.
1016 // It is not a tabbed work area (i.e., the search work area), so it
1017 // should be deleted by other means.
1018 LASSERT(found_twa, /* */);
1020 if (d.current_work_area_ == 0) {
1021 if (d.splitter_->count() != 0) {
1022 TabWorkArea * twa = d.currentTabWorkArea();
1023 setCurrentWorkArea(twa->currentWorkArea());
1025 // No more work areas, switch to the background widget.
1026 setCurrentWorkArea(0);
1032 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1038 void GuiView::updateLayoutList()
1041 d.layout_->updateContents(false);
1045 void GuiView::updateToolbars()
1047 ToolbarMap::iterator end = d.toolbars_.end();
1048 if (d.current_work_area_) {
1050 d.current_work_area_->bufferView().cursor().inMathed();
1052 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1054 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1055 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1056 bool const mathmacrotemplate =
1057 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1059 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1060 it->second->update(math, table, review, mathmacrotemplate);
1062 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1063 it->second->update(false, false, false, false);
1067 Buffer * GuiView::buffer()
1069 if (d.current_work_area_)
1070 return &d.current_work_area_->bufferView().buffer();
1075 Buffer const * GuiView::buffer() const
1077 if (d.current_work_area_)
1078 return &d.current_work_area_->bufferView().buffer();
1083 void GuiView::setBuffer(Buffer * newBuffer)
1085 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1086 LASSERT(newBuffer, return);
1089 GuiWorkArea * wa = workArea(*newBuffer);
1091 newBuffer->masterBuffer()->updateLabels();
1092 wa = addWorkArea(*newBuffer);
1094 //Disconnect the old buffer...there's no new one.
1097 connectBuffer(*newBuffer);
1098 connectBufferView(wa->bufferView());
1099 setCurrentWorkArea(wa);
1105 void GuiView::connectBuffer(Buffer & buf)
1107 buf.setGuiDelegate(this);
1111 void GuiView::disconnectBuffer()
1113 if (d.current_work_area_)
1114 d.current_work_area_->bufferView().setGuiDelegate(0);
1118 void GuiView::connectBufferView(BufferView & bv)
1120 bv.setGuiDelegate(this);
1124 void GuiView::disconnectBufferView()
1126 if (d.current_work_area_)
1127 d.current_work_area_->bufferView().setGuiDelegate(0);
1131 void GuiView::errors(string const & error_type)
1133 ErrorList & el = buffer()->errorList(error_type);
1135 showDialog("errorlist", error_type);
1139 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1141 d.toc_models_.updateItem(toqstr(type), dit);
1145 void GuiView::structureChanged()
1147 d.toc_models_.reset(view());
1148 // Navigator needs more than a simple update in this case. It needs to be
1150 updateDialog("toc", "");
1154 void GuiView::updateDialog(string const & name, string const & data)
1156 if (!isDialogVisible(name))
1159 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1160 if (it == d.dialogs_.end())
1163 Dialog * const dialog = it->second.get();
1164 if (dialog->isVisibleView())
1165 dialog->initialiseParams(data);
1169 BufferView * GuiView::view()
1171 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1175 void GuiView::autoSave()
1177 LYXERR(Debug::INFO, "Running autoSave()");
1180 view()->buffer().autoSave();
1184 void GuiView::resetAutosaveTimers()
1187 d.autosave_timeout_.restart();
1191 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1194 Buffer * buf = buffer();
1196 /* In LyX/Mac, when a dialog is open, the menus of the
1197 application can still be accessed without giving focus to
1198 the main window. In this case, we want to disable the menu
1199 entries that are buffer-related.
1201 Note that this code is not perfect, as bug 1941 attests:
1202 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1204 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1207 switch(cmd.action) {
1208 case LFUN_BUFFER_WRITE:
1209 enable = buf && (buf->isUnnamed() || !buf->isClean());
1212 case LFUN_BUFFER_WRITE_AS:
1216 case LFUN_SPLIT_VIEW:
1217 if (cmd.getArg(0) == "vertical")
1218 enable = buf && (d.splitter_->count() == 1 ||
1219 d.splitter_->orientation() == Qt::Vertical);
1221 enable = buf && (d.splitter_->count() == 1 ||
1222 d.splitter_->orientation() == Qt::Horizontal);
1225 case LFUN_CLOSE_TAB_GROUP:
1226 enable = d.currentTabWorkArea();
1229 case LFUN_TOOLBAR_TOGGLE:
1230 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1231 flag.setOnOff(t->isVisible());
1234 case LFUN_UI_TOGGLE:
1235 flag.setOnOff(isFullScreen());
1238 case LFUN_DIALOG_TOGGLE:
1239 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1240 // fall through to set "enable"
1241 case LFUN_DIALOG_SHOW: {
1242 string const name = cmd.getArg(0);
1244 enable = name == "aboutlyx"
1245 || name == "file" //FIXME: should be removed.
1247 || name == "texinfo";
1248 else if (name == "print")
1249 enable = buf->isExportable("dvi")
1250 && lyxrc.print_command != "none";
1251 else if (name == "character") {
1255 InsetCode ic = view()->cursor().inset().lyxCode();
1256 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1259 else if (name == "symbols") {
1260 if (!view() || view()->cursor().inMathed())
1263 InsetCode ic = view()->cursor().inset().lyxCode();
1264 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1267 else if (name == "latexlog")
1268 enable = FileName(buf->logName()).isReadableFile();
1269 else if (name == "spellchecker")
1270 #if defined (USE_ASPELL)
1271 enable = !buf->isReadonly();
1275 else if (name == "vclog")
1276 enable = buf->lyxvc().inUse();
1280 case LFUN_DIALOG_UPDATE: {
1281 string const name = cmd.getArg(0);
1283 enable = name == "prefs";
1287 case LFUN_INSET_APPLY: {
1288 string const name = cmd.getArg(0);
1289 Inset * inset = getOpenInset(name);
1291 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1293 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1294 // Every inset is supposed to handle this
1295 LASSERT(false, break);
1299 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1300 flag |= lyx::getStatus(fr);
1302 enable = flag.enabled();
1306 case LFUN_COMPLETION_INLINE:
1307 if (!d.current_work_area_
1308 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1312 case LFUN_COMPLETION_POPUP:
1313 if (!d.current_work_area_
1314 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1318 case LFUN_COMPLETION_COMPLETE:
1319 if (!d.current_work_area_
1320 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1324 case LFUN_COMPLETION_ACCEPT:
1325 if (!d.current_work_area_
1326 || (!d.current_work_area_->completer().popupVisible()
1327 && !d.current_work_area_->completer().inlineVisible()
1328 && !d.current_work_area_->completer().completionAvailable()))
1332 case LFUN_COMPLETION_CANCEL:
1333 if (!d.current_work_area_
1334 || (!d.current_work_area_->completer().popupVisible()
1335 && !d.current_work_area_->completer().inlineVisible()))
1339 case LFUN_BUFFER_ZOOM_OUT:
1340 enable = buf && lyxrc.zoom > 10;
1343 case LFUN_BUFFER_ZOOM_IN:
1352 flag.setEnabled(false);
1358 static FileName selectTemplateFile()
1360 FileDialog dlg(qt_("Select template file"));
1361 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1362 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1364 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1365 QStringList(qt_("LyX Documents (*.lyx)")));
1367 if (result.first == FileDialog::Later)
1369 if (result.second.isEmpty())
1371 return FileName(fromqstr(result.second));
1375 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1379 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1382 message(_("Document not loaded."));
1387 setBuffer(newBuffer);
1389 // scroll to the position when the file was last closed
1390 if (lyxrc.use_lastfilepos) {
1391 LastFilePosSection::FilePos filepos =
1392 theSession().lastFilePos().load(filename);
1393 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1397 theSession().lastFiles().add(filename);
1404 void GuiView::openDocument(string const & fname)
1406 string initpath = lyxrc.document_path;
1409 string const trypath = buffer()->filePath();
1410 // If directory is writeable, use this as default.
1411 if (FileName(trypath).isDirWritable())
1417 if (fname.empty()) {
1418 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1419 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1420 dlg.setButton2(qt_("Examples|#E#e"),
1421 toqstr(addPath(package().system_support().absFilename(), "examples")));
1423 QStringList filter(qt_("LyX Documents (*.lyx)"));
1424 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1425 << qt_("LyX-1.4.x Documents (*.lyx14)")
1426 << qt_("LyX-1.5.x Documents (*.lyx15)")
1427 << qt_("LyX-1.6.x Documents (*.lyx16)");
1428 FileDialog::Result result =
1429 dlg.open(toqstr(initpath), filter);
1431 if (result.first == FileDialog::Later)
1434 filename = fromqstr(result.second);
1436 // check selected filename
1437 if (filename.empty()) {
1438 message(_("Canceled."));
1444 // get absolute path of file and add ".lyx" to the filename if
1446 FileName const fullname =
1447 fileSearch(string(), filename, "lyx", support::may_not_exist);
1448 if (!fullname.empty())
1449 filename = fullname.absFilename();
1451 if (!fullname.onlyPath().isDirectory()) {
1452 Alert::warning(_("Invalid filename"),
1453 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1454 from_utf8(fullname.absFilename())));
1457 // if the file doesn't exist, let the user create one
1458 if (!fullname.exists()) {
1459 // the user specifically chose this name. Believe him.
1460 Buffer * const b = newFile(filename, string(), true);
1466 docstring const disp_fn = makeDisplayPath(filename);
1467 message(bformat(_("Opening document %1$s..."), disp_fn));
1470 Buffer * buf = loadDocument(fullname);
1472 buf->updateLabels();
1474 buf->errors("Parse");
1475 str2 = bformat(_("Document %1$s opened."), disp_fn);
1476 if (buf->lyxvc().inUse())
1477 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1478 " " + _("Version control detected.");
1480 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1485 // FIXME: clean that
1486 static bool import(GuiView * lv, FileName const & filename,
1487 string const & format, ErrorList & errorList)
1489 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1491 string loader_format;
1492 vector<string> loaders = theConverters().loaders();
1493 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1494 for (vector<string>::const_iterator it = loaders.begin();
1495 it != loaders.end(); ++it) {
1496 if (!theConverters().isReachable(format, *it))
1499 string const tofile =
1500 support::changeExtension(filename.absFilename(),
1501 formats.extension(*it));
1502 if (!theConverters().convert(0, filename, FileName(tofile),
1503 filename, format, *it, errorList))
1505 loader_format = *it;
1508 if (loader_format.empty()) {
1509 frontend::Alert::error(_("Couldn't import file"),
1510 bformat(_("No information for importing the format %1$s."),
1511 formats.prettyName(format)));
1515 loader_format = format;
1517 if (loader_format == "lyx") {
1518 Buffer * buf = lv->loadDocument(lyxfile);
1521 buf->updateLabels();
1523 buf->errors("Parse");
1525 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1529 bool as_paragraphs = loader_format == "textparagraph";
1530 string filename2 = (loader_format == format) ? filename.absFilename()
1531 : support::changeExtension(filename.absFilename(),
1532 formats.extension(loader_format));
1533 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1534 theLyXFunc().setLyXView(lv);
1535 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1542 void GuiView::importDocument(string const & argument)
1545 string filename = split(argument, format, ' ');
1547 LYXERR(Debug::INFO, format << " file: " << filename);
1549 // need user interaction
1550 if (filename.empty()) {
1551 string initpath = lyxrc.document_path;
1553 Buffer const * buf = buffer();
1555 string const trypath = buf->filePath();
1556 // If directory is writeable, use this as default.
1557 if (FileName(trypath).isDirWritable())
1561 docstring const text = bformat(_("Select %1$s file to import"),
1562 formats.prettyName(format));
1564 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1565 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1566 dlg.setButton2(qt_("Examples|#E#e"),
1567 toqstr(addPath(package().system_support().absFilename(), "examples")));
1569 docstring filter = formats.prettyName(format);
1572 filter += from_utf8(formats.extension(format));
1575 FileDialog::Result result =
1576 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1578 if (result.first == FileDialog::Later)
1581 filename = fromqstr(result.second);
1583 // check selected filename
1584 if (filename.empty())
1585 message(_("Canceled."));
1588 if (filename.empty())
1591 // get absolute path of file
1592 FileName const fullname(support::makeAbsPath(filename));
1594 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1596 // Check if the document already is open
1597 Buffer * buf = theBufferList().getBuffer(lyxfile);
1600 if (!closeBuffer()) {
1601 message(_("Canceled."));
1606 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1608 // if the file exists already, and we didn't do
1609 // -i lyx thefile.lyx, warn
1610 if (lyxfile.exists() && fullname != lyxfile) {
1612 docstring text = bformat(_("The document %1$s already exists.\n\n"
1613 "Do you want to overwrite that document?"), displaypath);
1614 int const ret = Alert::prompt(_("Overwrite document?"),
1615 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1618 message(_("Canceled."));
1623 message(bformat(_("Importing %1$s..."), displaypath));
1624 ErrorList errorList;
1625 if (import(this, fullname, format, errorList))
1626 message(_("imported."));
1628 message(_("file not imported!"));
1630 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1634 void GuiView::newDocument(string const & filename, bool from_template)
1636 FileName initpath(lyxrc.document_path);
1637 Buffer * buf = buffer();
1639 FileName const trypath(buf->filePath());
1640 // If directory is writeable, use this as default.
1641 if (trypath.isDirWritable())
1645 string templatefile;
1646 if (from_template) {
1647 templatefile = selectTemplateFile().absFilename();
1648 if (templatefile.empty())
1653 if (filename.empty())
1654 b = newUnnamedFile(templatefile, initpath);
1656 b = newFile(filename, templatefile, true);
1661 // If no new document could be created, it is unsure
1662 // whether there is a valid BufferView.
1664 // Ensure the cursor is correctly positioned on screen.
1665 view()->showCursor();
1669 void GuiView::insertLyXFile(docstring const & fname)
1671 BufferView * bv = view();
1676 FileName filename(to_utf8(fname));
1678 if (!filename.empty()) {
1679 bv->insertLyXFile(filename);
1683 // Launch a file browser
1685 string initpath = lyxrc.document_path;
1686 string const trypath = bv->buffer().filePath();
1687 // If directory is writeable, use this as default.
1688 if (FileName(trypath).isDirWritable())
1692 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1693 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1694 dlg.setButton2(qt_("Examples|#E#e"),
1695 toqstr(addPath(package().system_support().absFilename(),
1698 FileDialog::Result result = dlg.open(toqstr(initpath),
1699 QStringList(qt_("LyX Documents (*.lyx)")));
1701 if (result.first == FileDialog::Later)
1705 filename.set(fromqstr(result.second));
1707 // check selected filename
1708 if (filename.empty()) {
1709 // emit message signal.
1710 message(_("Canceled."));
1714 bv->insertLyXFile(filename);
1718 void GuiView::insertPlaintextFile(docstring const & fname,
1721 BufferView * bv = view();
1726 FileName filename(to_utf8(fname));
1728 if (!filename.empty()) {
1729 bv->insertPlaintextFile(filename, asParagraph);
1733 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1734 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1736 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1737 QStringList(qt_("All Files (*)")));
1739 if (result.first == FileDialog::Later)
1743 filename.set(fromqstr(result.second));
1745 // check selected filename
1746 if (filename.empty()) {
1747 // emit message signal.
1748 message(_("Canceled."));
1752 bv->insertPlaintextFile(filename, asParagraph);
1756 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1758 FileName fname = b.fileName();
1759 FileName const oldname = fname;
1761 if (!newname.empty()) {
1763 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1765 // Switch to this Buffer.
1768 // No argument? Ask user through dialog.
1770 FileDialog dlg(qt_("Choose a filename to save document as"),
1771 LFUN_BUFFER_WRITE_AS);
1772 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1773 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1775 if (!isLyXFilename(fname.absFilename()))
1776 fname.changeExtension(".lyx");
1778 FileDialog::Result result =
1779 dlg.save(toqstr(fname.onlyPath().absFilename()),
1780 QStringList(qt_("LyX Documents (*.lyx)")),
1781 toqstr(fname.onlyFileName()));
1783 if (result.first == FileDialog::Later)
1786 fname.set(fromqstr(result.second));
1791 if (!isLyXFilename(fname.absFilename()))
1792 fname.changeExtension(".lyx");
1795 if (FileName(fname).exists()) {
1796 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1797 docstring text = bformat(_("The document %1$s already "
1798 "exists.\n\nDo you want to "
1799 "overwrite that document?"),
1801 int const ret = Alert::prompt(_("Overwrite document?"),
1802 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1805 case 1: return renameBuffer(b, docstring());
1806 case 2: return false;
1810 // Ok, change the name of the buffer
1811 b.setFileName(fname.absFilename());
1813 bool unnamed = b.isUnnamed();
1814 b.setUnnamed(false);
1815 b.saveCheckSum(fname);
1817 if (!saveBuffer(b)) {
1818 b.setFileName(oldname.absFilename());
1819 b.setUnnamed(unnamed);
1820 b.saveCheckSum(oldname);
1828 bool GuiView::saveBuffer(Buffer & b)
1830 if (workArea(b) && workArea(b)->inDialogMode())
1834 return renameBuffer(b, docstring());
1837 theSession().lastFiles().add(b.fileName());
1841 // Switch to this Buffer.
1844 // FIXME: we don't tell the user *WHY* the save failed !!
1845 docstring const file = makeDisplayPath(b.absFileName(), 30);
1846 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1847 "Do you want to rename the document and "
1848 "try again?"), file);
1849 int const ret = Alert::prompt(_("Rename and save?"),
1850 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1853 if (!renameBuffer(b, docstring()))
1862 return saveBuffer(b);
1866 bool GuiView::closeBuffer()
1868 Buffer * buf = buffer();
1869 return buf && closeBuffer(*buf);
1873 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1875 // goto bookmark to update bookmark pit.
1876 //FIXME: we should update only the bookmarks related to this buffer!
1877 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1878 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1879 theLyXFunc().gotoBookmark(i+1, false, false);
1881 if (buf.isClean() || buf.paragraphs().empty()) {
1882 // save in sessions if requested
1883 // do not save childs if their master
1884 // is opened as well
1886 theSession().lastOpened().add(buf.fileName());
1888 // Don't close child documents.
1889 removeWorkArea(currentMainWorkArea());
1891 theBufferList().release(&buf);
1894 // Switch to this Buffer.
1899 if (buf.isUnnamed())
1900 file = from_utf8(buf.fileName().onlyFileName());
1902 file = buf.fileName().displayName(30);
1904 // Bring this window to top before asking questions.
1908 docstring const text = bformat(_("The document %1$s has unsaved changes."
1909 "\n\nDo you want to save the document or discard the changes?"), file);
1910 int const ret = Alert::prompt(_("Save changed document?"),
1911 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1915 if (!saveBuffer(buf))
1919 // if we crash after this we could
1920 // have no autosave file but I guess
1921 // this is really improbable (Jug)
1922 buf.removeAutosaveFile();
1928 // save file names to .lyx/session
1930 theSession().lastOpened().add(buf.fileName());
1933 // Don't close child documents.
1934 removeWorkArea(currentMainWorkArea());
1936 theBufferList().release(&buf);
1942 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1944 Buffer * const curbuf = buffer();
1945 Buffer * nextbuf = curbuf;
1947 if (np == NEXTBUFFER)
1948 nextbuf = theBufferList().next(nextbuf);
1950 nextbuf = theBufferList().previous(nextbuf);
1951 if (nextbuf == curbuf)
1957 if (workArea(*nextbuf))
1964 bool GuiView::dispatch(FuncRequest const & cmd)
1966 BufferView * bv = view();
1967 // By default we won't need any update.
1969 bv->cursor().updateFlags(Update::None);
1970 bool dispatched = true;
1972 switch(cmd.action) {
1973 case LFUN_BUFFER_IMPORT:
1974 importDocument(to_utf8(cmd.argument()));
1977 case LFUN_BUFFER_SWITCH: {
1979 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
1983 bv->cursor().message(_("Document not loaded"));
1987 case LFUN_BUFFER_NEXT:
1988 gotoNextOrPreviousBuffer(NEXTBUFFER);
1991 case LFUN_BUFFER_PREVIOUS:
1992 gotoNextOrPreviousBuffer(PREVBUFFER);
1995 case LFUN_COMMAND_EXECUTE: {
1996 bool const show_it = cmd.argument() != "off";
1997 // FIXME: this is a hack, "minibuffer" should not be
1999 if (GuiToolbar * t = toolbar("minibuffer")) {
2000 t->setVisible(show_it);
2001 if (show_it && t->commandBuffer())
2002 t->commandBuffer()->setFocus();
2006 case LFUN_DROP_LAYOUTS_CHOICE:
2008 d.layout_->showPopup();
2011 case LFUN_MENU_OPEN:
2012 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2013 menu->exec(QCursor::pos());
2016 case LFUN_FILE_INSERT:
2017 insertLyXFile(cmd.argument());
2019 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2020 insertPlaintextFile(cmd.argument(), true);
2023 case LFUN_FILE_INSERT_PLAINTEXT:
2024 insertPlaintextFile(cmd.argument(), false);
2027 case LFUN_BUFFER_WRITE:
2029 saveBuffer(bv->buffer());
2032 case LFUN_BUFFER_WRITE_AS:
2034 renameBuffer(bv->buffer(), cmd.argument());
2037 case LFUN_BUFFER_WRITE_ALL: {
2038 Buffer * first = theBufferList().first();
2041 message(_("Saving all documents..."));
2042 // We cannot use a for loop as the buffer list cycles.
2045 if (!b->isClean()) {
2047 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2049 b = theBufferList().next(b);
2050 } while (b != first);
2051 message(_("All documents saved."));
2055 case LFUN_TOOLBAR_TOGGLE: {
2056 string const name = cmd.getArg(0);
2057 if (GuiToolbar * t = toolbar(name))
2062 case LFUN_DIALOG_UPDATE: {
2063 string const name = to_utf8(cmd.argument());
2064 // Can only update a dialog connected to an existing inset
2065 Inset * inset = getOpenInset(name);
2067 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2068 inset->dispatch(view()->cursor(), fr);
2069 } else if (name == "paragraph") {
2070 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2071 } else if (name == "prefs" || name == "document") {
2072 updateDialog(name, string());
2077 case LFUN_DIALOG_TOGGLE: {
2078 if (isDialogVisible(cmd.getArg(0)))
2079 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2081 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2085 case LFUN_DIALOG_DISCONNECT_INSET:
2086 disconnectDialog(to_utf8(cmd.argument()));
2089 case LFUN_DIALOG_HIDE: {
2090 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2094 case LFUN_DIALOG_SHOW: {
2095 string const name = cmd.getArg(0);
2096 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2098 if (name == "character") {
2099 data = freefont2string();
2101 showDialog("character", data);
2102 } else if (name == "latexlog") {
2103 Buffer::LogType type;
2104 string const logfile = buffer()->logName(&type);
2106 case Buffer::latexlog:
2109 case Buffer::buildlog:
2113 data += Lexer::quoteString(logfile);
2114 showDialog("log", data);
2115 } else if (name == "vclog") {
2116 string const data = "vc " +
2117 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2118 showDialog("log", data);
2119 } else if (name == "symbols") {
2120 data = bv->cursor().getEncoding()->name();
2122 showDialog("symbols", data);
2124 } else if (name == "prefs" && isFullScreen()) {
2125 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2127 showDialog("prefs", data);
2129 showDialog(name, data);
2133 case LFUN_INSET_APPLY: {
2134 string const name = cmd.getArg(0);
2135 Inset * inset = getOpenInset(name);
2137 // put cursor in front of inset.
2138 if (!view()->setCursorFromInset(inset)) {
2139 LASSERT(false, break);
2142 // useful if we are called from a dialog.
2143 view()->cursor().beginUndoGroup();
2144 view()->cursor().recordUndo();
2145 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2146 inset->dispatch(view()->cursor(), fr);
2147 view()->cursor().endUndoGroup();
2149 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2155 case LFUN_UI_TOGGLE:
2157 // Make sure the keyboard focus stays in the work area.
2161 case LFUN_SPLIT_VIEW:
2162 if (Buffer * buf = buffer()) {
2163 string const orientation = cmd.getArg(0);
2164 d.splitter_->setOrientation(orientation == "vertical"
2165 ? Qt::Vertical : Qt::Horizontal);
2166 TabWorkArea * twa = addTabWorkArea();
2167 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2168 setCurrentWorkArea(wa);
2172 case LFUN_CLOSE_TAB_GROUP:
2173 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2175 twa = d.currentTabWorkArea();
2176 // Switch to the next GuiWorkArea in the found TabWorkArea.
2178 // Make sure the work area is up to date.
2179 setCurrentWorkArea(twa->currentWorkArea());
2181 setCurrentWorkArea(0);
2186 case LFUN_COMPLETION_INLINE:
2187 if (d.current_work_area_)
2188 d.current_work_area_->completer().showInline();
2191 case LFUN_COMPLETION_POPUP:
2192 if (d.current_work_area_)
2193 d.current_work_area_->completer().showPopup();
2197 case LFUN_COMPLETION_COMPLETE:
2198 if (d.current_work_area_)
2199 d.current_work_area_->completer().tab();
2202 case LFUN_COMPLETION_CANCEL:
2203 if (d.current_work_area_) {
2204 if (d.current_work_area_->completer().popupVisible())
2205 d.current_work_area_->completer().hidePopup();
2207 d.current_work_area_->completer().hideInline();
2211 case LFUN_COMPLETION_ACCEPT:
2212 if (d.current_work_area_)
2213 d.current_work_area_->completer().activate();
2216 case LFUN_BUFFER_ZOOM_IN:
2217 case LFUN_BUFFER_ZOOM_OUT:
2218 if (cmd.argument().empty()) {
2219 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2224 lyxrc.zoom += convert<int>(cmd.argument());
2226 if (lyxrc.zoom < 10)
2229 // The global QPixmapCache is used in GuiPainter to cache text
2230 // painting so we must reset it.
2231 QPixmapCache::clear();
2232 guiApp->fontLoader().update();
2233 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2241 // Part of automatic menu appearance feature.
2242 if (isFullScreen()) {
2243 if (menuBar()->isVisible())
2245 if (statusBar()->isVisible())
2246 statusBar()->hide();
2253 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2255 string const arg = cmd.getArg(0);
2256 if (arg == "scrollbar") {
2257 // hide() is of no help
2258 if (d.current_work_area_->verticalScrollBarPolicy() ==
2259 Qt::ScrollBarAlwaysOff)
2261 d.current_work_area_->setVerticalScrollBarPolicy(
2262 Qt::ScrollBarAsNeeded);
2264 d.current_work_area_->setVerticalScrollBarPolicy(
2265 Qt::ScrollBarAlwaysOff);
2268 if (arg == "statusbar") {
2269 statusBar()->setVisible(!statusBar()->isVisible());
2272 if (arg == "menubar") {
2273 menuBar()->setVisible(!menuBar()->isVisible());
2276 #if QT_VERSION >= 0x040300
2277 if (arg == "frame") {
2279 getContentsMargins(&l, &t, &r, &b);
2280 //are the frames in default state?
2281 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2283 setContentsMargins(-2, -2, -2, -2);
2285 setContentsMargins(0, 0, 0, 0);
2290 if (arg == "fullscreen") {
2295 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2299 void GuiView::toggleFullScreen()
2301 if (isFullScreen()) {
2302 for (int i = 0; i != d.splitter_->count(); ++i)
2303 d.tabWorkArea(i)->setFullScreen(false);
2304 #if QT_VERSION >= 0x040300
2305 setContentsMargins(0, 0, 0, 0);
2307 setWindowState(windowState() ^ Qt::WindowFullScreen);
2310 statusBar()->show();
2313 hideDialogs("prefs", 0);
2314 for (int i = 0; i != d.splitter_->count(); ++i)
2315 d.tabWorkArea(i)->setFullScreen(true);
2316 #if QT_VERSION >= 0x040300
2317 setContentsMargins(-2, -2, -2, -2);
2320 setWindowState(windowState() ^ Qt::WindowFullScreen);
2321 statusBar()->hide();
2323 if (lyxrc.full_screen_toolbars) {
2324 ToolbarMap::iterator end = d.toolbars_.end();
2325 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2330 // give dialogs like the TOC a chance to adapt
2335 Buffer const * GuiView::updateInset(Inset const * inset)
2337 if (!d.current_work_area_)
2341 d.current_work_area_->scheduleRedraw();
2343 return &d.current_work_area_->bufferView().buffer();
2347 void GuiView::restartCursor()
2349 /* When we move around, or type, it's nice to be able to see
2350 * the cursor immediately after the keypress.
2352 if (d.current_work_area_)
2353 d.current_work_area_->startBlinkingCursor();
2355 // Take this occasion to update the other GUI elements.
2361 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2363 if (d.current_work_area_)
2364 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2369 // This list should be kept in sync with the list of insets in
2370 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2371 // dialog should have the same name as the inset.
2372 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2373 // docs in LyXAction.cpp.
2375 char const * const dialognames[] = {
2376 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2377 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2378 "float", "graphics", "include", "index", "info", "nomenclature", "label",
2379 "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2380 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2381 "symbols", "tabular", "tabularcreate",
2383 #ifdef HAVE_LIBAIKSAURUS
2387 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2389 char const * const * const end_dialognames =
2390 dialognames + (sizeof(dialognames) / sizeof(char *));
2394 cmpCStr(char const * name) : name_(name) {}
2395 bool operator()(char const * other) {
2396 return strcmp(other, name_) == 0;
2403 bool isValidName(string const & name)
2405 return find_if(dialognames, end_dialognames,
2406 cmpCStr(name.c_str())) != end_dialognames;
2412 void GuiView::resetDialogs()
2414 // Make sure that no LFUN uses any LyXView.
2415 theLyXFunc().setLyXView(0);
2418 constructToolbars();
2419 guiApp->menus().fillMenuBar(menuBar(), this, false);
2421 d.layout_->updateContents(true);
2422 // Now update controls with current buffer.
2423 theLyXFunc().setLyXView(this);
2429 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2431 if (!isValidName(name))
2434 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2436 if (it != d.dialogs_.end()) {
2438 it->second->hideView();
2439 return it->second.get();
2442 Dialog * dialog = build(name);
2443 d.dialogs_[name].reset(dialog);
2444 if (lyxrc.allow_geometry_session)
2445 dialog->restoreSession();
2452 void GuiView::showDialog(string const & name, string const & data,
2460 Dialog * dialog = findOrBuild(name, false);
2462 dialog->showData(data);
2464 d.open_insets_[name] = inset;
2467 catch (ExceptionMessage const & ex) {
2475 bool GuiView::isDialogVisible(string const & name) const
2477 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2478 if (it == d.dialogs_.end())
2480 return it->second.get()->isVisibleView();
2484 void GuiView::hideDialog(string const & name, Inset * inset)
2486 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2487 if (it == d.dialogs_.end())
2490 if (inset && inset != getOpenInset(name))
2493 Dialog * const dialog = it->second.get();
2494 if (dialog->isVisibleView())
2496 d.open_insets_[name] = 0;
2500 void GuiView::disconnectDialog(string const & name)
2502 if (!isValidName(name))
2505 if (d.open_insets_.find(name) != d.open_insets_.end())
2506 d.open_insets_[name] = 0;
2510 Inset * GuiView::getOpenInset(string const & name) const
2512 if (!isValidName(name))
2515 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2516 return it == d.open_insets_.end() ? 0 : it->second;
2520 void GuiView::hideAll() const
2522 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2523 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2525 for(; it != end; ++it)
2526 it->second->hideView();
2530 void GuiView::updateDialogs()
2532 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2533 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2535 for(; it != end; ++it) {
2536 Dialog * dialog = it->second.get();
2537 if (dialog && dialog->isVisibleView())
2538 dialog->checkStatus();
2545 // will be replaced by a proper factory...
2546 Dialog * createGuiAbout(GuiView & lv);
2547 Dialog * createGuiBibitem(GuiView & lv);
2548 Dialog * createGuiBibtex(GuiView & lv);
2549 Dialog * createGuiBox(GuiView & lv);
2550 Dialog * createGuiBranch(GuiView & lv);
2551 Dialog * createGuiChanges(GuiView & lv);
2552 Dialog * createGuiCharacter(GuiView & lv);
2553 Dialog * createGuiCitation(GuiView & lv);
2554 Dialog * createGuiDelimiter(GuiView & lv);
2555 Dialog * createGuiDocument(GuiView & lv);
2556 Dialog * createGuiErrorList(GuiView & lv);
2557 Dialog * createGuiERT(GuiView & lv);
2558 Dialog * createGuiExternal(GuiView & lv);
2559 Dialog * createGuiFloat(GuiView & lv);
2560 Dialog * createGuiGraphics(GuiView & lv);
2561 Dialog * createGuiInclude(GuiView & lv);
2562 Dialog * createGuiInfo(GuiView & lv);
2563 Dialog * createGuiLabel(GuiView & lv);
2564 Dialog * createGuiListings(GuiView & lv);
2565 Dialog * createGuiLog(GuiView & lv);
2566 Dialog * createGuiMathHSpace(GuiView & lv);
2567 Dialog * createGuiMathMatrix(GuiView & lv);
2568 Dialog * createGuiNomenclature(GuiView & lv);
2569 Dialog * createGuiNote(GuiView & lv);
2570 Dialog * createGuiParagraph(GuiView & lv);
2571 Dialog * createGuiPhantom(GuiView & lv);
2572 Dialog * createGuiPreferences(GuiView & lv);
2573 Dialog * createGuiPrint(GuiView & lv);
2574 Dialog * createGuiRef(GuiView & lv);
2575 Dialog * createGuiSearch(GuiView & lv);
2576 Dialog * createGuiSearchAdv(GuiView & lv);
2577 Dialog * createGuiSendTo(GuiView & lv);
2578 Dialog * createGuiShowFile(GuiView & lv);
2579 Dialog * createGuiSpellchecker(GuiView & lv);
2580 Dialog * createGuiSymbols(GuiView & lv);
2581 Dialog * createGuiTabularCreate(GuiView & lv);
2582 Dialog * createGuiTabular(GuiView & lv);
2583 Dialog * createGuiTexInfo(GuiView & lv);
2584 Dialog * createGuiTextHSpace(GuiView & lv);
2585 Dialog * createGuiToc(GuiView & lv);
2586 Dialog * createGuiThesaurus(GuiView & lv);
2587 Dialog * createGuiHyperlink(GuiView & lv);
2588 Dialog * createGuiVSpace(GuiView & lv);
2589 Dialog * createGuiViewSource(GuiView & lv);
2590 Dialog * createGuiWrap(GuiView & lv);
2593 Dialog * GuiView::build(string const & name)
2595 LASSERT(isValidName(name), return 0);
2597 if (name == "aboutlyx")
2598 return createGuiAbout(*this);
2599 if (name == "bibitem")
2600 return createGuiBibitem(*this);
2601 if (name == "bibtex")
2602 return createGuiBibtex(*this);
2604 return createGuiBox(*this);
2605 if (name == "branch")
2606 return createGuiBranch(*this);
2607 if (name == "changes")
2608 return createGuiChanges(*this);
2609 if (name == "character")
2610 return createGuiCharacter(*this);
2611 if (name == "citation")
2612 return createGuiCitation(*this);
2613 if (name == "document")
2614 return createGuiDocument(*this);
2615 if (name == "errorlist")
2616 return createGuiErrorList(*this);
2618 return createGuiERT(*this);
2619 if (name == "external")
2620 return createGuiExternal(*this);
2622 return createGuiShowFile(*this);
2623 if (name == "findreplace")
2624 return createGuiSearch(*this);
2625 if (name == "findreplaceadv")
2626 return createGuiSearchAdv(*this);
2627 if (name == "float")
2628 return createGuiFloat(*this);
2629 if (name == "graphics")
2630 return createGuiGraphics(*this);
2631 if (name == "include")
2632 return createGuiInclude(*this);
2634 return createGuiInfo(*this);
2635 if (name == "nomenclature")
2636 return createGuiNomenclature(*this);
2637 if (name == "label")
2638 return createGuiLabel(*this);
2640 return createGuiLog(*this);
2641 if (name == "mathdelimiter")
2642 return createGuiDelimiter(*this);
2643 if (name == "mathspace")
2644 return createGuiMathHSpace(*this);
2645 if (name == "mathmatrix")
2646 return createGuiMathMatrix(*this);
2648 return createGuiNote(*this);
2649 if (name == "paragraph")
2650 return createGuiParagraph(*this);
2651 if (name == "phantom")
2652 return createGuiPhantom(*this);
2653 if (name == "prefs")
2654 return createGuiPreferences(*this);
2655 if (name == "print")
2656 return createGuiPrint(*this);
2658 return createGuiRef(*this);
2659 if (name == "sendto")
2660 return createGuiSendTo(*this);
2661 if (name == "space")
2662 return createGuiTextHSpace(*this);
2663 if (name == "spellchecker")
2664 return createGuiSpellchecker(*this);
2665 if (name == "symbols")
2666 return createGuiSymbols(*this);
2667 if (name == "tabular")
2668 return createGuiTabular(*this);
2669 if (name == "tabularcreate")
2670 return createGuiTabularCreate(*this);
2671 if (name == "texinfo")
2672 return createGuiTexInfo(*this);
2673 if (name == "view-source")
2674 return createGuiViewSource(*this);
2675 #ifdef HAVE_LIBAIKSAURUS
2676 if (name == "thesaurus")
2677 return createGuiThesaurus(*this);
2680 return createGuiHyperlink(*this);
2681 if (name == "listings")
2682 return createGuiListings(*this);
2684 return createGuiToc(*this);
2685 if (name == "vspace")
2686 return createGuiVSpace(*this);
2688 return createGuiWrap(*this);
2694 } // namespace frontend
2697 #include "moc_GuiView.cpp"