3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiWorkArea.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
31 #include "qt_helpers.h"
33 #include "frontends/alert.h"
35 #include "buffer_funcs.h"
37 #include "BufferList.h"
38 #include "BufferParams.h"
39 #include "BufferView.h"
40 #include "Converter.h"
43 #include "ErrorList.h"
45 #include "FuncStatus.h"
46 #include "FuncRequest.h"
54 #include "Paragraph.h"
55 #include "TextClass.h"
60 #include "support/convert.h"
61 #include "support/debug.h"
62 #include "support/ExceptionMessage.h"
63 #include "support/FileName.h"
64 #include "support/filetools.h"
65 #include "support/gettext.h"
66 #include "support/ForkedCalls.h"
67 #include "support/lassert.h"
68 #include "support/lstrings.h"
69 #include "support/os.h"
70 #include "support/Package.h"
71 #include "support/Timeout.h"
74 #include <QApplication>
75 #include <QCloseEvent>
77 #include <QDesktopWidget>
78 #include <QDragEnterEvent>
85 #include <QPixmapCache>
87 #include <QPushButton>
91 #include <QStackedWidget>
98 #include <boost/bind.hpp>
100 #ifdef HAVE_SYS_TIME_H
101 # include <sys/time.h>
108 using namespace lyx::support;
115 class BackgroundWidget : public QWidget
120 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
121 /// The text to be written on top of the pixmap
122 QString const text = lyx_version ?
123 qt_("version ") + lyx_version : qt_("unknown version");
124 splash_ = getPixmap("images/", "banner", "png");
126 QPainter pain(&splash_);
127 pain.setPen(QColor(0, 0, 0));
129 // The font used to display the version info
130 font.setStyleHint(QFont::SansSerif);
131 font.setWeight(QFont::Bold);
132 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
134 pain.drawText(260, 15, text);
137 void paintEvent(QPaintEvent *)
139 int x = (width() - splash_.width()) / 2;
140 int y = (height() - splash_.height()) / 2;
142 pain.drawPixmap(x, y, splash_);
150 /// Toolbar store providing access to individual toolbars by name.
151 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
153 typedef boost::shared_ptr<Dialog> DialogPtr;
158 struct GuiView::GuiViewPrivate
161 : current_work_area_(0), current_main_work_area_(0),
162 layout_(0), autosave_timeout_(5000),
165 // hardcode here the platform specific icon size
166 smallIconSize = 14; // scaling problems
167 normalIconSize = 20; // ok, default
168 bigIconSize = 26; // better for some math icons
170 splitter_ = new QSplitter;
171 bg_widget_ = new BackgroundWidget;
172 stack_widget_ = new QStackedWidget;
173 stack_widget_->addWidget(bg_widget_);
174 stack_widget_->addWidget(splitter_);
182 delete stack_widget_;
185 QMenu * toolBarPopup(GuiView * parent)
187 // FIXME: translation
188 QMenu * menu = new QMenu(parent);
189 QActionGroup * iconSizeGroup = new QActionGroup(parent);
191 QAction * smallIcons = new QAction(iconSizeGroup);
192 smallIcons->setText(qt_("Small-sized icons"));
193 smallIcons->setCheckable(true);
194 QObject::connect(smallIcons, SIGNAL(triggered()),
195 parent, SLOT(smallSizedIcons()));
196 menu->addAction(smallIcons);
198 QAction * normalIcons = new QAction(iconSizeGroup);
199 normalIcons->setText(qt_("Normal-sized icons"));
200 normalIcons->setCheckable(true);
201 QObject::connect(normalIcons, SIGNAL(triggered()),
202 parent, SLOT(normalSizedIcons()));
203 menu->addAction(normalIcons);
205 QAction * bigIcons = new QAction(iconSizeGroup);
206 bigIcons->setText(qt_("Big-sized icons"));
207 bigIcons->setCheckable(true);
208 QObject::connect(bigIcons, SIGNAL(triggered()),
209 parent, SLOT(bigSizedIcons()));
210 menu->addAction(bigIcons);
212 unsigned int cur = parent->iconSize().width();
213 if ( cur == parent->d.smallIconSize)
214 smallIcons->setChecked(true);
215 else if (cur == parent->d.normalIconSize)
216 normalIcons->setChecked(true);
217 else if (cur == parent->d.bigIconSize)
218 bigIcons->setChecked(true);
225 stack_widget_->setCurrentWidget(bg_widget_);
226 bg_widget_->setUpdatesEnabled(true);
229 TabWorkArea * tabWorkArea(int i)
231 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
234 TabWorkArea * currentTabWorkArea()
236 if (splitter_->count() == 1)
237 // The first TabWorkArea is always the first one, if any.
238 return tabWorkArea(0);
240 for (int i = 0; i != splitter_->count(); ++i) {
241 TabWorkArea * twa = tabWorkArea(i);
242 if (current_main_work_area_ == twa->currentWorkArea())
246 // None has the focus so we just take the first one.
247 return tabWorkArea(0);
251 GuiWorkArea * current_work_area_;
252 GuiWorkArea * current_main_work_area_;
253 QSplitter * splitter_;
254 QStackedWidget * stack_widget_;
255 BackgroundWidget * bg_widget_;
257 ToolbarMap toolbars_;
258 /// The main layout box.
260 * \warning Don't Delete! The layout box is actually owned by
261 * whichever toolbar contains it. All the GuiView class needs is a
262 * means of accessing it.
264 * FIXME: replace that with a proper model so that we are not limited
265 * to only one dialog.
267 GuiLayoutBox * layout_;
270 map<string, Inset *> open_insets_;
273 map<string, DialogPtr> dialogs_;
275 unsigned int smallIconSize;
276 unsigned int normalIconSize;
277 unsigned int bigIconSize;
279 QTimer statusbar_timer_;
280 /// auto-saving of buffers
281 Timeout autosave_timeout_;
282 /// flag against a race condition due to multiclicks, see bug #1119
286 TocModels toc_models_;
290 GuiView::GuiView(int id)
291 : d(*new GuiViewPrivate), id_(id), closing_(false)
293 // GuiToolbars *must* be initialised before the menu bar.
294 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
297 // set ourself as the current view. This is needed for the menu bar
298 // filling, at least for the static special menu item on Mac. Otherwise
299 // they are greyed out.
300 theLyXFunc().setLyXView(this);
302 // Fill up the menu bar.
303 guiApp->menus().fillMenuBar(menuBar(), this, true);
305 setCentralWidget(d.stack_widget_);
307 // Start autosave timer
308 if (lyxrc.autosave) {
309 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
310 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
311 d.autosave_timeout_.start();
313 connect(&d.statusbar_timer_, SIGNAL(timeout()),
314 this, SLOT(clearMessage()));
316 // We don't want to keep the window in memory if it is closed.
317 setAttribute(Qt::WA_DeleteOnClose, true);
319 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
320 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
321 // since the icon is provided in the application bundle.
322 setWindowIcon(getPixmap("images/", "lyx", "png"));
326 setAcceptDrops(true);
328 statusBar()->setSizeGripEnabled(true);
330 // Forbid too small unresizable window because it can happen
331 // with some window manager under X11.
332 setMinimumSize(300, 200);
334 if (lyxrc.allow_geometry_session) {
335 // Now take care of session management.
340 // no session handling, default to a sane size.
341 setGeometry(50, 50, 690, 510);
344 // clear session data if any.
346 settings.remove("views");
356 void GuiView::saveLayout() const
359 settings.beginGroup("views");
360 settings.beginGroup(QString::number(id_));
362 settings.setValue("pos", pos());
363 settings.setValue("size", size());
365 settings.setValue("geometry", saveGeometry());
367 settings.setValue("layout", saveState(0));
368 settings.setValue("icon_size", iconSize());
372 bool GuiView::restoreLayout()
375 settings.beginGroup("views");
376 settings.beginGroup(QString::number(id_));
377 QString const icon_key = "icon_size";
378 if (!settings.contains(icon_key))
381 //code below is skipped when when ~/.config/LyX is (re)created
382 setIconSize(settings.value(icon_key).toSize());
384 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
385 QSize size = settings.value("size", QSize(690, 510)).toSize();
389 if (!restoreGeometry(settings.value("geometry").toByteArray()))
390 setGeometry(50, 50, 690, 510);
392 // Make sure layout is correctly oriented.
393 setLayoutDirection(qApp->layoutDirection());
395 // Allow the toc and view-source dock widget to be restored if needed.
397 if (d = findOrBuild("toc", true))
398 // see bug 5082. At least setup title and enabled state.
399 // Visibility will be adjusted by restoreState below.
401 if (d = findOrBuild("view-source", true))
404 if (!restoreState(settings.value("layout").toByteArray(), 0))
411 GuiToolbar * GuiView::toolbar(string const & name)
413 ToolbarMap::iterator it = d.toolbars_.find(name);
414 if (it != d.toolbars_.end())
417 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
418 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
423 void GuiView::constructToolbars()
425 ToolbarMap::iterator it = d.toolbars_.begin();
426 for (; it != d.toolbars_.end(); ++it)
431 // extracts the toolbars from the backend
432 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
433 Toolbars::Infos::iterator end = guiApp->toolbars().end();
434 for (; cit != end; ++cit)
435 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
439 void GuiView::initToolbars()
441 // extracts the toolbars from the backend
442 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
443 Toolbars::Infos::iterator end = guiApp->toolbars().end();
444 for (; cit != end; ++cit) {
445 GuiToolbar * tb = toolbar(cit->name);
448 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
450 tb->setVisible(false);
451 tb->setVisibility(visibility);
453 if (visibility & Toolbars::TOP) {
455 addToolBarBreak(Qt::TopToolBarArea);
456 addToolBar(Qt::TopToolBarArea, tb);
459 if (visibility & Toolbars::BOTTOM) {
460 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
461 #if (QT_VERSION >= 0x040202)
462 addToolBarBreak(Qt::BottomToolBarArea);
464 addToolBar(Qt::BottomToolBarArea, tb);
467 if (visibility & Toolbars::LEFT) {
468 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
469 #if (QT_VERSION >= 0x040202)
470 addToolBarBreak(Qt::LeftToolBarArea);
472 addToolBar(Qt::LeftToolBarArea, tb);
475 if (visibility & Toolbars::RIGHT) {
476 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
477 #if (QT_VERSION >= 0x040202)
478 addToolBarBreak(Qt::RightToolBarArea);
480 addToolBar(Qt::RightToolBarArea, tb);
483 if (visibility & Toolbars::ON)
484 tb->setVisible(true);
489 TocModels & GuiView::tocModels()
491 return d.toc_models_;
495 void GuiView::setFocus()
497 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
498 // Make sure LyXFunc points to the correct view.
499 guiApp->setCurrentView(this);
500 theLyXFunc().setLyXView(this);
501 QMainWindow::setFocus();
502 if (d.current_work_area_)
503 d.current_work_area_->setFocus();
507 QMenu * GuiView::createPopupMenu()
509 return d.toolBarPopup(this);
513 void GuiView::showEvent(QShowEvent * e)
515 LYXERR(Debug::GUI, "Passed Geometry "
516 << size().height() << "x" << size().width()
517 << "+" << pos().x() << "+" << pos().y());
519 if (d.splitter_->count() == 0)
520 // No work area, switch to the background widget.
523 QMainWindow::showEvent(e);
527 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
528 ** is responsibility of the container (e.g., dialog)
530 void GuiView::closeEvent(QCloseEvent * close_event)
532 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
535 // it can happen that this event arrives without selecting the view,
536 // e.g. when clicking the close button on a background window.
538 GuiWorkArea * active_wa = currentMainWorkArea();
539 setCurrentWorkArea(active_wa);
541 int splitter_count = d.splitter_->count();
542 for (; splitter_count; --splitter_count) {
543 TabWorkArea * twa = d.tabWorkArea(0);
545 int twa_count = twa->count();
546 for (; twa_count; --twa_count) {
547 twa->setCurrentIndex(twa_count-1);
549 GuiWorkArea * wa = twa->currentWorkArea();
550 bool const is_active_wa = active_wa == wa;
551 Buffer * b = &wa->bufferView().buffer();
553 // This is a child document, just close the tab
554 // after saving but keep the file loaded.
555 if (!closeBuffer(*b, true, is_active_wa)) {
557 close_event->ignore();
563 vector<Buffer *> clist = b->getChildren();
564 for (vector<Buffer *>::const_iterator it = clist.begin();
565 it != clist.end(); ++it) {
566 if ((*it)->isClean())
569 // If a child is dirty, do not close
570 // without user intervention
571 if (!closeBuffer(*c, false)) {
573 close_event->ignore();
578 QList<int> const ids = guiApp->viewIds();
579 for (int i = 0; i != ids.size(); ++i) {
582 if (guiApp->view(ids[i]).workArea(*b)) {
583 // FIXME 1: should we put an alert box here
584 // that the buffer is viewed elsewhere?
585 // FIXME 2: should we try to save this buffer in any case?
588 // This buffer is also opened in another view, so
589 // close the associated work area...
591 // ... but don't close the buffer.
596 // closeBuffer() needs buffer workArea still alive and
597 // set as currrent one, and destroys it
598 if (b && !closeBuffer(*b, true, is_active_wa)) {
600 close_event->ignore();
605 // Make sure that nothing will use this close to be closed View.
606 guiApp->unregisterView(this);
608 if (isFullScreen()) {
609 // Switch off fullscreen before closing.
614 // Make sure the timer time out will not trigger a statusbar update.
615 d.statusbar_timer_.stop();
617 // Saving fullscreen requires additional tweaks in the toolbar code.
618 // It wouldn't also work under linux natively.
619 if (lyxrc.allow_geometry_session) {
620 // Save this window geometry and layout.
622 // Then the toolbar private states.
623 ToolbarMap::iterator end = d.toolbars_.end();
624 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
625 it->second->saveSession();
626 // Now take care of all other dialogs:
627 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
628 for (; it!= d.dialogs_.end(); ++it)
629 it->second->saveSession();
632 close_event->accept();
636 void GuiView::dragEnterEvent(QDragEnterEvent * event)
638 if (event->mimeData()->hasUrls())
640 /// \todo Ask lyx-devel is this is enough:
641 /// if (event->mimeData()->hasFormat("text/plain"))
642 /// event->acceptProposedAction();
646 void GuiView::dropEvent(QDropEvent * event)
648 QList<QUrl> files = event->mimeData()->urls();
652 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
653 for (int i = 0; i != files.size(); ++i) {
654 string const file = os::internal_path(fromqstr(
655 files.at(i).toLocalFile()));
657 // Asynchronously post the event. DropEvent usually come
658 // from the BufferView. But reloading a file might close
659 // the BufferView from within its own event handler.
660 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
667 void GuiView::message(docstring const & str)
669 if (ForkedProcess::iAmAChild())
672 statusBar()->showMessage(toqstr(str));
673 d.statusbar_timer_.stop();
674 d.statusbar_timer_.start(3000);
678 void GuiView::smallSizedIcons()
680 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
684 void GuiView::normalSizedIcons()
686 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
690 void GuiView::bigSizedIcons()
692 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
696 void GuiView::clearMessage()
700 theLyXFunc().setLyXView(this);
701 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
702 d.statusbar_timer_.stop();
706 void GuiView::updateWindowTitle(GuiWorkArea * wa)
708 if (wa != d.current_work_area_)
710 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
711 setWindowIconText(wa->windowIconText());
715 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
718 disconnectBufferView();
719 connectBufferView(wa->bufferView());
720 connectBuffer(wa->bufferView().buffer());
721 d.current_work_area_ = wa;
722 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
723 this, SLOT(updateWindowTitle(GuiWorkArea *)));
724 updateWindowTitle(wa);
728 // The document settings needs to be reinitialised.
729 updateDialog("document", "");
731 // Buffer-dependent dialogs must be updated. This is done here because
732 // some dialogs require buffer()->text.
737 void GuiView::on_lastWorkAreaRemoved()
740 // We already are in a close event. Nothing more to do.
743 if (d.splitter_->count() > 1)
744 // We have a splitter so don't close anything.
747 // Reset and updates the dialogs.
748 d.toc_models_.reset(0);
749 updateDialog("document", "");
752 resetWindowTitleAndIconText();
754 if (lyxrc.open_buffers_in_tabs)
755 // Nothing more to do, the window should stay open.
758 if (guiApp->viewIds().size() > 1) {
764 // On Mac we also close the last window because the application stay
765 // resident in memory. On other platforms we don't close the last
766 // window because this would quit the application.
772 void GuiView::updateStatusBar()
774 // let the user see the explicit message
775 if (d.statusbar_timer_.isActive())
778 theLyXFunc().setLyXView(this);
779 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
783 bool GuiView::hasFocus() const
785 return qApp->activeWindow() == this;
789 bool GuiView::event(QEvent * e)
793 // Useful debug code:
794 //case QEvent::ActivationChange:
795 //case QEvent::WindowDeactivate:
796 //case QEvent::Paint:
797 //case QEvent::Enter:
798 //case QEvent::Leave:
799 //case QEvent::HoverEnter:
800 //case QEvent::HoverLeave:
801 //case QEvent::HoverMove:
802 //case QEvent::StatusTip:
803 //case QEvent::DragEnter:
804 //case QEvent::DragLeave:
808 case QEvent::WindowActivate: {
809 if (this == guiApp->currentView()) {
811 return QMainWindow::event(e);
813 guiApp->setCurrentView(this);
814 theLyXFunc().setLyXView(this);
815 if (d.current_work_area_) {
816 BufferView & bv = d.current_work_area_->bufferView();
817 connectBufferView(bv);
818 connectBuffer(bv.buffer());
819 // The document structure, name and dialogs might have
820 // changed in another view.
822 // The document settings needs to be reinitialised.
823 updateDialog("document", "");
826 resetWindowTitleAndIconText();
829 return QMainWindow::event(e);
832 case QEvent::ShortcutOverride: {
835 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
836 if (isFullScreen() && menuBar()->isHidden()) {
837 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
838 // FIXME: we should also try to detect special LyX shortcut such as
839 // Alt-P and Alt-M. Right now there is a hack in
840 // GuiWorkArea::processKeySym() that hides again the menubar for
842 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
844 return QMainWindow::event(e);
849 if (d.current_work_area_)
850 // Nothing special to do.
851 return QMainWindow::event(e);
853 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
854 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
856 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
857 || ke->key() == Qt::Key_Backtab)
858 return QMainWindow::event(e);
860 // Allow processing of shortcuts that are allowed even when no Buffer
862 theLyXFunc().setLyXView(this);
864 setKeySymbol(&sym, ke);
865 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
871 return QMainWindow::event(e);
875 void GuiView::resetWindowTitleAndIconText()
877 setWindowTitle(qt_("LyX"));
878 setWindowIconText(qt_("LyX"));
881 bool GuiView::focusNextPrevChild(bool /*next*/)
888 void GuiView::setBusy(bool busy)
890 if (d.current_work_area_) {
891 d.current_work_area_->setUpdatesEnabled(!busy);
893 d.current_work_area_->stopBlinkingCursor();
895 d.current_work_area_->startBlinkingCursor();
899 QApplication::setOverrideCursor(Qt::WaitCursor);
901 QApplication::restoreOverrideCursor();
905 GuiWorkArea * GuiView::workArea(Buffer & buffer)
907 if (currentWorkArea()
908 && ¤tWorkArea()->bufferView().buffer() == &buffer)
909 return (GuiWorkArea *) currentWorkArea();
910 if (TabWorkArea * twa = d.currentTabWorkArea())
911 return twa->workArea(buffer);
916 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
918 // Automatically create a TabWorkArea if there are none yet.
919 TabWorkArea * tab_widget = d.splitter_->count()
920 ? d.currentTabWorkArea() : addTabWorkArea();
921 return tab_widget->addWorkArea(buffer, *this);
925 TabWorkArea * GuiView::addTabWorkArea()
927 TabWorkArea * twa = new TabWorkArea;
928 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
929 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
930 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
931 this, SLOT(on_lastWorkAreaRemoved()));
933 d.splitter_->addWidget(twa);
934 d.stack_widget_->setCurrentWidget(d.splitter_);
939 GuiWorkArea const * GuiView::currentWorkArea() const
941 return d.current_work_area_;
945 GuiWorkArea * GuiView::currentWorkArea()
947 return d.current_work_area_;
951 GuiWorkArea const * GuiView::currentMainWorkArea() const
953 if (d.currentTabWorkArea() == NULL)
955 return d.currentTabWorkArea()->currentWorkArea();
959 GuiWorkArea * GuiView::currentMainWorkArea()
961 if (d.currentTabWorkArea() == NULL)
963 return d.currentTabWorkArea()->currentWorkArea();
967 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
969 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
971 d.current_work_area_ = NULL;
975 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
979 theGuiApp()->setCurrentView(this);
980 d.current_work_area_ = wa;
981 for (int i = 0; i != d.splitter_->count(); ++i) {
982 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
983 //if (d.current_main_work_area_)
984 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
985 d.current_main_work_area_ = wa;
986 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
987 //d.current_main_work_area_->setLineWidth(2);
988 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
992 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
993 on_currentWorkAreaChanged(wa);
994 BufferView & bv = wa->bufferView();
995 bv.cursor().fixIfBroken();
997 wa->setUpdatesEnabled(true);
998 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1002 void GuiView::removeWorkArea(GuiWorkArea * wa)
1004 LASSERT(wa, return);
1005 if (wa == d.current_work_area_) {
1007 disconnectBufferView();
1008 d.current_work_area_ = 0;
1009 d.current_main_work_area_ = 0;
1012 bool found_twa = false;
1013 for (int i = 0; i != d.splitter_->count(); ++i) {
1014 TabWorkArea * twa = d.tabWorkArea(i);
1015 if (twa->removeWorkArea(wa)) {
1016 // Found in this tab group, and deleted the GuiWorkArea.
1018 if (twa->count() != 0) {
1019 if (d.current_work_area_ == 0)
1020 // This means that we are closing the current GuiWorkArea, so
1021 // switch to the next GuiWorkArea in the found TabWorkArea.
1022 setCurrentWorkArea(twa->currentWorkArea());
1024 // No more WorkAreas in this tab group, so delete it.
1031 // It is not a tabbed work area (i.e., the search work area), so it
1032 // should be deleted by other means.
1033 LASSERT(found_twa, /* */);
1035 if (d.current_work_area_ == 0) {
1036 if (d.splitter_->count() != 0) {
1037 TabWorkArea * twa = d.currentTabWorkArea();
1038 setCurrentWorkArea(twa->currentWorkArea());
1040 // No more work areas, switch to the background widget.
1041 setCurrentWorkArea(0);
1047 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1053 void GuiView::updateLayoutList()
1056 d.layout_->updateContents(false);
1060 void GuiView::updateToolbars()
1062 ToolbarMap::iterator end = d.toolbars_.end();
1063 if (d.current_work_area_) {
1065 d.current_work_area_->bufferView().cursor().inMathed();
1067 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1069 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1070 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1071 bool const mathmacrotemplate =
1072 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1074 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1075 it->second->update(math, table, review, mathmacrotemplate);
1077 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1078 it->second->update(false, false, false, false);
1082 Buffer * GuiView::buffer()
1084 if (d.current_work_area_)
1085 return &d.current_work_area_->bufferView().buffer();
1090 Buffer const * GuiView::buffer() const
1092 if (d.current_work_area_)
1093 return &d.current_work_area_->bufferView().buffer();
1098 void GuiView::setBuffer(Buffer * newBuffer)
1100 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1101 LASSERT(newBuffer, return);
1104 GuiWorkArea * wa = workArea(*newBuffer);
1106 newBuffer->masterBuffer()->updateLabels();
1107 wa = addWorkArea(*newBuffer);
1109 //Disconnect the old buffer...there's no new one.
1112 connectBuffer(*newBuffer);
1113 connectBufferView(wa->bufferView());
1114 setCurrentWorkArea(wa);
1120 void GuiView::connectBuffer(Buffer & buf)
1122 buf.setGuiDelegate(this);
1126 void GuiView::disconnectBuffer()
1128 if (d.current_work_area_)
1129 d.current_work_area_->bufferView().setGuiDelegate(0);
1133 void GuiView::connectBufferView(BufferView & bv)
1135 bv.setGuiDelegate(this);
1139 void GuiView::disconnectBufferView()
1141 if (d.current_work_area_)
1142 d.current_work_area_->bufferView().setGuiDelegate(0);
1146 void GuiView::errors(string const & error_type, bool from_master)
1148 ErrorList & el = from_master ?
1149 buffer()->masterBuffer()->errorList(error_type)
1150 : buffer()->errorList(error_type);
1151 string data = error_type;
1153 data = "from_master|" + error_type;
1155 showDialog("errorlist", data);
1159 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1161 d.toc_models_.updateItem(toqstr(type), dit);
1165 void GuiView::structureChanged()
1167 d.toc_models_.reset(view());
1168 // Navigator needs more than a simple update in this case. It needs to be
1170 updateDialog("toc", "");
1174 void GuiView::updateDialog(string const & name, string const & data)
1176 if (!isDialogVisible(name))
1179 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1180 if (it == d.dialogs_.end())
1183 Dialog * const dialog = it->second.get();
1184 if (dialog->isVisibleView())
1185 dialog->initialiseParams(data);
1189 BufferView * GuiView::view()
1191 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1195 void GuiView::autoSave()
1197 LYXERR(Debug::INFO, "Running autoSave()");
1200 view()->buffer().autoSave();
1204 void GuiView::resetAutosaveTimers()
1207 d.autosave_timeout_.restart();
1211 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1214 Buffer * buf = buffer();
1216 /* In LyX/Mac, when a dialog is open, the menus of the
1217 application can still be accessed without giving focus to
1218 the main window. In this case, we want to disable the menu
1219 entries that are buffer-related.
1221 Note that this code is not perfect, as bug 1941 attests:
1222 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1224 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1227 if (cmd.origin == FuncRequest::TOC) {
1228 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1230 if (toc->getStatus(view()->cursor(), cmd, fs))
1233 flag.setEnabled(false);
1237 switch(cmd.action) {
1238 case LFUN_BUFFER_WRITE:
1239 enable = buf && (buf->isUnnamed() || !buf->isClean());
1242 case LFUN_BUFFER_WRITE_AS:
1246 case LFUN_SPLIT_VIEW:
1247 if (cmd.getArg(0) == "vertical")
1248 enable = buf && (d.splitter_->count() == 1 ||
1249 d.splitter_->orientation() == Qt::Vertical);
1251 enable = buf && (d.splitter_->count() == 1 ||
1252 d.splitter_->orientation() == Qt::Horizontal);
1255 case LFUN_CLOSE_TAB_GROUP:
1256 enable = d.currentTabWorkArea();
1259 case LFUN_TOOLBAR_TOGGLE:
1260 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1261 flag.setOnOff(t->isVisible());
1264 case LFUN_UI_TOGGLE:
1265 flag.setOnOff(isFullScreen());
1268 case LFUN_DIALOG_TOGGLE:
1269 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1270 // fall through to set "enable"
1271 case LFUN_DIALOG_SHOW: {
1272 string const name = cmd.getArg(0);
1274 enable = name == "aboutlyx"
1275 || name == "file" //FIXME: should be removed.
1277 || name == "texinfo";
1278 else if (name == "print")
1279 enable = buf->isExportable("dvi")
1280 && lyxrc.print_command != "none";
1281 else if (name == "character") {
1285 InsetCode ic = view()->cursor().inset().lyxCode();
1286 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1289 else if (name == "symbols") {
1290 if (!view() || view()->cursor().inMathed())
1293 InsetCode ic = view()->cursor().inset().lyxCode();
1294 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1297 else if (name == "latexlog")
1298 enable = FileName(buf->logName()).isReadableFile();
1299 else if (name == "spellchecker")
1300 #if defined (USE_ASPELL)
1301 enable = !buf->isReadonly();
1305 else if (name == "vclog")
1306 enable = buf->lyxvc().inUse();
1310 case LFUN_DIALOG_UPDATE: {
1311 string const name = cmd.getArg(0);
1313 enable = name == "prefs";
1317 case LFUN_INSET_APPLY: {
1318 string const name = cmd.getArg(0);
1319 Inset * inset = getOpenInset(name);
1321 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1323 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1324 // Every inset is supposed to handle this
1325 LASSERT(false, break);
1329 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1330 flag |= lyx::getStatus(fr);
1332 enable = flag.enabled();
1336 case LFUN_COMPLETION_INLINE:
1337 if (!d.current_work_area_
1338 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1342 case LFUN_COMPLETION_POPUP:
1343 if (!d.current_work_area_
1344 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1348 case LFUN_COMPLETION_COMPLETE:
1349 if (!d.current_work_area_
1350 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1354 case LFUN_COMPLETION_ACCEPT:
1355 if (!d.current_work_area_
1356 || (!d.current_work_area_->completer().popupVisible()
1357 && !d.current_work_area_->completer().inlineVisible()
1358 && !d.current_work_area_->completer().completionAvailable()))
1362 case LFUN_COMPLETION_CANCEL:
1363 if (!d.current_work_area_
1364 || (!d.current_work_area_->completer().popupVisible()
1365 && !d.current_work_area_->completer().inlineVisible()))
1369 case LFUN_BUFFER_ZOOM_OUT:
1370 enable = buf && lyxrc.zoom > 10;
1373 case LFUN_BUFFER_ZOOM_IN:
1382 flag.setEnabled(false);
1388 static FileName selectTemplateFile()
1390 FileDialog dlg(qt_("Select template file"));
1391 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1392 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1394 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1395 QStringList(qt_("LyX Documents (*.lyx)")));
1397 if (result.first == FileDialog::Later)
1399 if (result.second.isEmpty())
1401 return FileName(fromqstr(result.second));
1405 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1409 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1412 message(_("Document not loaded."));
1417 setBuffer(newBuffer);
1419 // scroll to the position when the file was last closed
1420 if (lyxrc.use_lastfilepos) {
1421 LastFilePosSection::FilePos filepos =
1422 theSession().lastFilePos().load(filename);
1423 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1427 theSession().lastFiles().add(filename);
1434 void GuiView::openDocument(string const & fname)
1436 string initpath = lyxrc.document_path;
1439 string const trypath = buffer()->filePath();
1440 // If directory is writeable, use this as default.
1441 if (FileName(trypath).isDirWritable())
1447 if (fname.empty()) {
1448 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1449 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1450 dlg.setButton2(qt_("Examples|#E#e"),
1451 toqstr(addPath(package().system_support().absFilename(), "examples")));
1453 QStringList filter(qt_("LyX Documents (*.lyx)"));
1454 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1455 << qt_("LyX-1.4.x Documents (*.lyx14)")
1456 << qt_("LyX-1.5.x Documents (*.lyx15)")
1457 << qt_("LyX-1.6.x Documents (*.lyx16)");
1458 FileDialog::Result result =
1459 dlg.open(toqstr(initpath), filter);
1461 if (result.first == FileDialog::Later)
1464 filename = fromqstr(result.second);
1466 // check selected filename
1467 if (filename.empty()) {
1468 message(_("Canceled."));
1474 // get absolute path of file and add ".lyx" to the filename if
1476 FileName const fullname =
1477 fileSearch(string(), filename, "lyx", support::may_not_exist);
1478 if (!fullname.empty())
1479 filename = fullname.absFilename();
1481 if (!fullname.onlyPath().isDirectory()) {
1482 Alert::warning(_("Invalid filename"),
1483 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1484 from_utf8(fullname.absFilename())));
1487 // if the file doesn't exist, let the user create one
1488 if (!fullname.exists()) {
1489 // the user specifically chose this name. Believe him.
1490 Buffer * const b = newFile(filename, string(), true);
1496 docstring const disp_fn = makeDisplayPath(filename);
1497 message(bformat(_("Opening document %1$s..."), disp_fn));
1500 Buffer * buf = loadDocument(fullname);
1502 buf->updateLabels();
1504 buf->errors("Parse");
1505 str2 = bformat(_("Document %1$s opened."), disp_fn);
1506 if (buf->lyxvc().inUse())
1507 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1508 " " + _("Version control detected.");
1510 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1515 // FIXME: clean that
1516 static bool import(GuiView * lv, FileName const & filename,
1517 string const & format, ErrorList & errorList)
1519 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1521 string loader_format;
1522 vector<string> loaders = theConverters().loaders();
1523 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1524 for (vector<string>::const_iterator it = loaders.begin();
1525 it != loaders.end(); ++it) {
1526 if (!theConverters().isReachable(format, *it))
1529 string const tofile =
1530 support::changeExtension(filename.absFilename(),
1531 formats.extension(*it));
1532 if (!theConverters().convert(0, filename, FileName(tofile),
1533 filename, format, *it, errorList))
1535 loader_format = *it;
1538 if (loader_format.empty()) {
1539 frontend::Alert::error(_("Couldn't import file"),
1540 bformat(_("No information for importing the format %1$s."),
1541 formats.prettyName(format)));
1545 loader_format = format;
1547 if (loader_format == "lyx") {
1548 Buffer * buf = lv->loadDocument(lyxfile);
1551 buf->updateLabels();
1553 buf->errors("Parse");
1555 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1559 bool as_paragraphs = loader_format == "textparagraph";
1560 string filename2 = (loader_format == format) ? filename.absFilename()
1561 : support::changeExtension(filename.absFilename(),
1562 formats.extension(loader_format));
1563 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1564 theLyXFunc().setLyXView(lv);
1565 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1572 void GuiView::importDocument(string const & argument)
1575 string filename = split(argument, format, ' ');
1577 LYXERR(Debug::INFO, format << " file: " << filename);
1579 // need user interaction
1580 if (filename.empty()) {
1581 string initpath = lyxrc.document_path;
1583 Buffer const * buf = buffer();
1585 string const trypath = buf->filePath();
1586 // If directory is writeable, use this as default.
1587 if (FileName(trypath).isDirWritable())
1591 docstring const text = bformat(_("Select %1$s file to import"),
1592 formats.prettyName(format));
1594 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1595 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1596 dlg.setButton2(qt_("Examples|#E#e"),
1597 toqstr(addPath(package().system_support().absFilename(), "examples")));
1599 docstring filter = formats.prettyName(format);
1602 filter += from_utf8(formats.extension(format));
1605 FileDialog::Result result =
1606 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1608 if (result.first == FileDialog::Later)
1611 filename = fromqstr(result.second);
1613 // check selected filename
1614 if (filename.empty())
1615 message(_("Canceled."));
1618 if (filename.empty())
1621 // get absolute path of file
1622 FileName const fullname(support::makeAbsPath(filename));
1624 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1626 // Check if the document already is open
1627 Buffer * buf = theBufferList().getBuffer(lyxfile);
1630 if (!closeBuffer()) {
1631 message(_("Canceled."));
1636 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1638 // if the file exists already, and we didn't do
1639 // -i lyx thefile.lyx, warn
1640 if (lyxfile.exists() && fullname != lyxfile) {
1642 docstring text = bformat(_("The document %1$s already exists.\n\n"
1643 "Do you want to overwrite that document?"), displaypath);
1644 int const ret = Alert::prompt(_("Overwrite document?"),
1645 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1648 message(_("Canceled."));
1653 message(bformat(_("Importing %1$s..."), displaypath));
1654 ErrorList errorList;
1655 if (import(this, fullname, format, errorList))
1656 message(_("imported."));
1658 message(_("file not imported!"));
1660 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1664 void GuiView::newDocument(string const & filename, bool from_template)
1666 FileName initpath(lyxrc.document_path);
1667 Buffer * buf = buffer();
1669 FileName const trypath(buf->filePath());
1670 // If directory is writeable, use this as default.
1671 if (trypath.isDirWritable())
1675 string templatefile;
1676 if (from_template) {
1677 templatefile = selectTemplateFile().absFilename();
1678 if (templatefile.empty())
1683 if (filename.empty())
1684 b = newUnnamedFile(templatefile, initpath);
1686 b = newFile(filename, templatefile, true);
1691 // If no new document could be created, it is unsure
1692 // whether there is a valid BufferView.
1694 // Ensure the cursor is correctly positioned on screen.
1695 view()->showCursor();
1699 void GuiView::insertLyXFile(docstring const & fname)
1701 BufferView * bv = view();
1706 FileName filename(to_utf8(fname));
1708 if (!filename.empty()) {
1709 bv->insertLyXFile(filename);
1713 // Launch a file browser
1715 string initpath = lyxrc.document_path;
1716 string const trypath = bv->buffer().filePath();
1717 // If directory is writeable, use this as default.
1718 if (FileName(trypath).isDirWritable())
1722 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1723 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1724 dlg.setButton2(qt_("Examples|#E#e"),
1725 toqstr(addPath(package().system_support().absFilename(),
1728 FileDialog::Result result = dlg.open(toqstr(initpath),
1729 QStringList(qt_("LyX Documents (*.lyx)")));
1731 if (result.first == FileDialog::Later)
1735 filename.set(fromqstr(result.second));
1737 // check selected filename
1738 if (filename.empty()) {
1739 // emit message signal.
1740 message(_("Canceled."));
1744 bv->insertLyXFile(filename);
1748 void GuiView::insertPlaintextFile(docstring const & fname,
1751 BufferView * bv = view();
1755 if (!fname.empty() && !FileName::isAbsolute(to_utf8(fname))) {
1756 message(_("Absolute filename expected."));
1761 FileName filename(to_utf8(fname));
1763 if (!filename.empty()) {
1764 bv->insertPlaintextFile(filename, asParagraph);
1768 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1769 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1771 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1772 QStringList(qt_("All Files (*)")));
1774 if (result.first == FileDialog::Later)
1778 filename.set(fromqstr(result.second));
1780 // check selected filename
1781 if (filename.empty()) {
1782 // emit message signal.
1783 message(_("Canceled."));
1787 bv->insertPlaintextFile(filename, asParagraph);
1791 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1793 FileName fname = b.fileName();
1794 FileName const oldname = fname;
1796 if (!newname.empty()) {
1798 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1800 // Switch to this Buffer.
1803 // No argument? Ask user through dialog.
1805 FileDialog dlg(qt_("Choose a filename to save document as"),
1806 LFUN_BUFFER_WRITE_AS);
1807 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1808 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1810 if (!isLyXFilename(fname.absFilename()))
1811 fname.changeExtension(".lyx");
1813 FileDialog::Result result =
1814 dlg.save(toqstr(fname.onlyPath().absFilename()),
1815 QStringList(qt_("LyX Documents (*.lyx)")),
1816 toqstr(fname.onlyFileName()));
1818 if (result.first == FileDialog::Later)
1821 fname.set(fromqstr(result.second));
1826 if (!isLyXFilename(fname.absFilename()))
1827 fname.changeExtension(".lyx");
1830 if (FileName(fname).exists()) {
1831 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1832 docstring text = bformat(_("The document %1$s already "
1833 "exists.\n\nDo you want to "
1834 "overwrite that document?"),
1836 int const ret = Alert::prompt(_("Overwrite document?"),
1837 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1840 case 1: return renameBuffer(b, docstring());
1841 case 2: return false;
1845 FileName oldauto = b.getAutosaveFilename();
1847 // Ok, change the name of the buffer
1848 b.setFileName(fname.absFilename());
1850 bool unnamed = b.isUnnamed();
1851 b.setUnnamed(false);
1852 b.saveCheckSum(fname);
1854 // bring the autosave file with us, just in case.
1855 b.moveAutosaveFile(oldauto);
1857 if (!saveBuffer(b)) {
1858 oldauto = b.getAutosaveFilename();
1859 b.setFileName(oldname.absFilename());
1860 b.setUnnamed(unnamed);
1861 b.saveCheckSum(oldname);
1862 b.moveAutosaveFile(oldauto);
1870 bool GuiView::saveBuffer(Buffer & b)
1872 if (workArea(b) && workArea(b)->inDialogMode())
1876 return renameBuffer(b, docstring());
1879 theSession().lastFiles().add(b.fileName());
1883 // Switch to this Buffer.
1886 // FIXME: we don't tell the user *WHY* the save failed !!
1887 docstring const file = makeDisplayPath(b.absFileName(), 30);
1888 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1889 "Do you want to rename the document and "
1890 "try again?"), file);
1891 int const ret = Alert::prompt(_("Rename and save?"),
1892 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1895 if (!renameBuffer(b, docstring()))
1904 return saveBuffer(b);
1908 bool GuiView::closeBuffer()
1910 Buffer * buf = buffer();
1911 return buf && closeBuffer(*buf);
1915 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
1917 // goto bookmark to update bookmark pit.
1918 //FIXME: we should update only the bookmarks related to this buffer!
1919 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1920 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1921 theLyXFunc().gotoBookmark(i+1, false, false);
1923 if (buf.isClean() || buf.paragraphs().empty()) {
1924 // save in sessions if requested
1925 // do not save childs if their master
1926 // is opened as well
1928 theSession().lastOpened().add(buf.fileName(), mark_active);
1930 // Don't close child documents.
1931 removeWorkArea(currentMainWorkArea());
1933 theBufferList().release(&buf);
1936 // Switch to this Buffer.
1941 if (buf.isUnnamed())
1942 file = from_utf8(buf.fileName().onlyFileName());
1944 file = buf.fileName().displayName(30);
1946 // Bring this window to top before asking questions.
1950 docstring const text = bformat(_("The document %1$s has unsaved changes."
1951 "\n\nDo you want to save the document or discard the changes?"), file);
1952 int const ret = Alert::prompt(_("Save changed document?"),
1953 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1957 if (!saveBuffer(buf))
1961 // if we crash after this we could
1962 // have no autosave file but I guess
1963 // this is really improbable (Jug)
1964 buf.removeAutosaveFile();
1970 // save file names to .lyx/session
1972 theSession().lastOpened().add(buf.fileName(), mark_active);
1975 // Don't close child documents.
1976 removeWorkArea(currentMainWorkArea());
1978 theBufferList().release(&buf);
1984 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1986 Buffer * const curbuf = buffer();
1987 Buffer * nextbuf = curbuf;
1989 if (np == NEXTBUFFER)
1990 nextbuf = theBufferList().next(nextbuf);
1992 nextbuf = theBufferList().previous(nextbuf);
1993 if (nextbuf == curbuf)
1999 if (workArea(*nextbuf))
2006 bool GuiView::dispatch(FuncRequest const & cmd)
2008 BufferView * bv = view();
2009 // By default we won't need any update.
2011 bv->cursor().updateFlags(Update::None);
2012 bool dispatched = true;
2014 if (cmd.origin == FuncRequest::TOC) {
2015 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2016 toc->doDispatch(bv->cursor(), cmd);
2020 switch(cmd.action) {
2021 case LFUN_BUFFER_IMPORT:
2022 importDocument(to_utf8(cmd.argument()));
2025 case LFUN_BUFFER_SWITCH:
2026 if (FileName::isAbsolute(to_utf8(cmd.argument()))) {
2028 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2032 bv->cursor().message(_("Document not loaded"));
2036 case LFUN_BUFFER_NEXT:
2037 gotoNextOrPreviousBuffer(NEXTBUFFER);
2040 case LFUN_BUFFER_PREVIOUS:
2041 gotoNextOrPreviousBuffer(PREVBUFFER);
2044 case LFUN_COMMAND_EXECUTE: {
2045 bool const show_it = cmd.argument() != "off";
2046 // FIXME: this is a hack, "minibuffer" should not be
2048 if (GuiToolbar * t = toolbar("minibuffer")) {
2049 t->setVisible(show_it);
2050 if (show_it && t->commandBuffer())
2051 t->commandBuffer()->setFocus();
2055 case LFUN_DROP_LAYOUTS_CHOICE:
2057 d.layout_->showPopup();
2060 case LFUN_MENU_OPEN:
2061 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2062 menu->exec(QCursor::pos());
2065 case LFUN_FILE_INSERT:
2066 insertLyXFile(cmd.argument());
2068 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2069 insertPlaintextFile(cmd.argument(), true);
2072 case LFUN_FILE_INSERT_PLAINTEXT:
2073 insertPlaintextFile(cmd.argument(), false);
2076 case LFUN_BUFFER_WRITE:
2078 saveBuffer(bv->buffer());
2081 case LFUN_BUFFER_WRITE_AS:
2083 renameBuffer(bv->buffer(), cmd.argument());
2086 case LFUN_BUFFER_WRITE_ALL: {
2087 Buffer * first = theBufferList().first();
2090 message(_("Saving all documents..."));
2091 // We cannot use a for loop as the buffer list cycles.
2094 if (!b->isClean()) {
2096 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2098 b = theBufferList().next(b);
2099 } while (b != first);
2100 message(_("All documents saved."));
2104 case LFUN_TOOLBAR_TOGGLE: {
2105 string const name = cmd.getArg(0);
2106 if (GuiToolbar * t = toolbar(name))
2111 case LFUN_DIALOG_UPDATE: {
2112 string const name = to_utf8(cmd.argument());
2113 // Can only update a dialog connected to an existing inset
2114 Inset * inset = getOpenInset(name);
2116 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2117 inset->dispatch(view()->cursor(), fr);
2118 } else if (name == "paragraph") {
2119 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2120 } else if (name == "prefs" || name == "document") {
2121 updateDialog(name, string());
2126 case LFUN_DIALOG_TOGGLE: {
2127 if (isDialogVisible(cmd.getArg(0)))
2128 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2130 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2134 case LFUN_DIALOG_DISCONNECT_INSET:
2135 disconnectDialog(to_utf8(cmd.argument()));
2138 case LFUN_DIALOG_HIDE: {
2139 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2143 case LFUN_DIALOG_SHOW: {
2144 string const name = cmd.getArg(0);
2145 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2147 if (name == "character") {
2148 data = freefont2string();
2150 showDialog("character", data);
2151 } else if (name == "latexlog") {
2152 Buffer::LogType type;
2153 string const logfile = buffer()->logName(&type);
2155 case Buffer::latexlog:
2158 case Buffer::buildlog:
2162 data += Lexer::quoteString(logfile);
2163 showDialog("log", data);
2164 } else if (name == "vclog") {
2165 string const data = "vc " +
2166 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2167 showDialog("log", data);
2168 } else if (name == "symbols") {
2169 data = bv->cursor().getEncoding()->name();
2171 showDialog("symbols", data);
2173 } else if (name == "prefs" && isFullScreen()) {
2174 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2176 showDialog("prefs", data);
2178 showDialog(name, data);
2182 case LFUN_INSET_APPLY: {
2183 string const name = cmd.getArg(0);
2184 Inset * inset = getOpenInset(name);
2186 // put cursor in front of inset.
2187 if (!view()->setCursorFromInset(inset)) {
2188 LASSERT(false, break);
2191 // useful if we are called from a dialog.
2192 view()->cursor().beginUndoGroup();
2193 view()->cursor().recordUndo();
2194 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2195 inset->dispatch(view()->cursor(), fr);
2196 view()->cursor().endUndoGroup();
2198 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2204 case LFUN_UI_TOGGLE:
2206 // Make sure the keyboard focus stays in the work area.
2210 case LFUN_SPLIT_VIEW:
2211 if (Buffer * buf = buffer()) {
2212 string const orientation = cmd.getArg(0);
2213 d.splitter_->setOrientation(orientation == "vertical"
2214 ? Qt::Vertical : Qt::Horizontal);
2215 TabWorkArea * twa = addTabWorkArea();
2216 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2217 setCurrentWorkArea(wa);
2221 case LFUN_CLOSE_TAB_GROUP:
2222 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2224 twa = d.currentTabWorkArea();
2225 // Switch to the next GuiWorkArea in the found TabWorkArea.
2227 // Make sure the work area is up to date.
2228 setCurrentWorkArea(twa->currentWorkArea());
2230 setCurrentWorkArea(0);
2235 case LFUN_COMPLETION_INLINE:
2236 if (d.current_work_area_)
2237 d.current_work_area_->completer().showInline();
2240 case LFUN_COMPLETION_POPUP:
2241 if (d.current_work_area_)
2242 d.current_work_area_->completer().showPopup();
2246 case LFUN_COMPLETION_COMPLETE:
2247 if (d.current_work_area_)
2248 d.current_work_area_->completer().tab();
2251 case LFUN_COMPLETION_CANCEL:
2252 if (d.current_work_area_) {
2253 if (d.current_work_area_->completer().popupVisible())
2254 d.current_work_area_->completer().hidePopup();
2256 d.current_work_area_->completer().hideInline();
2260 case LFUN_COMPLETION_ACCEPT:
2261 if (d.current_work_area_)
2262 d.current_work_area_->completer().activate();
2265 case LFUN_BUFFER_ZOOM_IN:
2266 case LFUN_BUFFER_ZOOM_OUT:
2267 if (cmd.argument().empty()) {
2268 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2273 lyxrc.zoom += convert<int>(cmd.argument());
2275 if (lyxrc.zoom < 10)
2278 // The global QPixmapCache is used in GuiPainter to cache text
2279 // painting so we must reset it.
2280 QPixmapCache::clear();
2281 guiApp->fontLoader().update();
2282 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2290 // Part of automatic menu appearance feature.
2291 if (isFullScreen()) {
2292 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
2294 if (statusBar()->isVisible())
2295 statusBar()->hide();
2302 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2304 string const arg = cmd.getArg(0);
2305 if (arg == "scrollbar") {
2306 // hide() is of no help
2307 if (d.current_work_area_->verticalScrollBarPolicy() ==
2308 Qt::ScrollBarAlwaysOff)
2310 d.current_work_area_->setVerticalScrollBarPolicy(
2311 Qt::ScrollBarAsNeeded);
2313 d.current_work_area_->setVerticalScrollBarPolicy(
2314 Qt::ScrollBarAlwaysOff);
2317 if (arg == "statusbar") {
2318 statusBar()->setVisible(!statusBar()->isVisible());
2321 if (arg == "menubar") {
2322 menuBar()->setVisible(!menuBar()->isVisible());
2325 #if QT_VERSION >= 0x040300
2326 if (arg == "frame") {
2328 getContentsMargins(&l, &t, &r, &b);
2329 //are the frames in default state?
2330 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2332 setContentsMargins(-2, -2, -2, -2);
2334 setContentsMargins(0, 0, 0, 0);
2339 if (arg == "fullscreen") {
2344 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2348 void GuiView::toggleFullScreen()
2350 if (isFullScreen()) {
2351 for (int i = 0; i != d.splitter_->count(); ++i)
2352 d.tabWorkArea(i)->setFullScreen(false);
2353 #if QT_VERSION >= 0x040300
2354 setContentsMargins(0, 0, 0, 0);
2356 setWindowState(windowState() ^ Qt::WindowFullScreen);
2359 statusBar()->show();
2362 hideDialogs("prefs", 0);
2363 for (int i = 0; i != d.splitter_->count(); ++i)
2364 d.tabWorkArea(i)->setFullScreen(true);
2365 #if QT_VERSION >= 0x040300
2366 setContentsMargins(-2, -2, -2, -2);
2369 setWindowState(windowState() ^ Qt::WindowFullScreen);
2370 statusBar()->hide();
2371 if (lyxrc.full_screen_menubar)
2373 if (lyxrc.full_screen_toolbars) {
2374 ToolbarMap::iterator end = d.toolbars_.end();
2375 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2380 // give dialogs like the TOC a chance to adapt
2385 Buffer const * GuiView::updateInset(Inset const * inset)
2387 if (!d.current_work_area_)
2391 d.current_work_area_->scheduleRedraw();
2393 return &d.current_work_area_->bufferView().buffer();
2397 void GuiView::restartCursor()
2399 /* When we move around, or type, it's nice to be able to see
2400 * the cursor immediately after the keypress.
2402 if (d.current_work_area_)
2403 d.current_work_area_->startBlinkingCursor();
2405 // Take this occasion to update the other GUI elements.
2411 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2413 if (d.current_work_area_)
2414 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2419 // This list should be kept in sync with the list of insets in
2420 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2421 // dialog should have the same name as the inset.
2422 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2423 // docs in LyXAction.cpp.
2425 char const * const dialognames[] = {
2426 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2427 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2428 "findreplaceadv", "float", "graphics", "href", "include", "index",
2429 "index_print", "info", "listings", "label", "log", "mathdelimiter",
2430 "mathmatrix", "mathspace", "nomenclature", "nomencl_print", "note",
2431 "paragraph", "phantom", "prefs", "print", "ref", "sendto", "space",
2432 "spellchecker", "symbols", "tabular", "tabularcreate", "thesaurus", "texinfo",
2433 "toc", "view-source", "vspace", "wrap" };
2435 char const * const * const end_dialognames =
2436 dialognames + (sizeof(dialognames) / sizeof(char *));
2440 cmpCStr(char const * name) : name_(name) {}
2441 bool operator()(char const * other) {
2442 return strcmp(other, name_) == 0;
2449 bool isValidName(string const & name)
2451 return find_if(dialognames, end_dialognames,
2452 cmpCStr(name.c_str())) != end_dialognames;
2458 void GuiView::resetDialogs()
2460 // Make sure that no LFUN uses any LyXView.
2461 theLyXFunc().setLyXView(0);
2464 constructToolbars();
2465 guiApp->menus().fillMenuBar(menuBar(), this, false);
2467 d.layout_->updateContents(true);
2468 // Now update controls with current buffer.
2469 theLyXFunc().setLyXView(this);
2475 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2477 if (!isValidName(name))
2480 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2482 if (it != d.dialogs_.end()) {
2484 it->second->hideView();
2485 return it->second.get();
2488 Dialog * dialog = build(name);
2489 d.dialogs_[name].reset(dialog);
2490 if (lyxrc.allow_geometry_session)
2491 dialog->restoreSession();
2498 void GuiView::showDialog(string const & name, string const & data,
2506 Dialog * dialog = findOrBuild(name, false);
2508 dialog->showData(data);
2510 d.open_insets_[name] = inset;
2513 catch (ExceptionMessage const & ex) {
2521 bool GuiView::isDialogVisible(string const & name) const
2523 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2524 if (it == d.dialogs_.end())
2526 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
2530 void GuiView::hideDialog(string const & name, Inset * inset)
2532 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2533 if (it == d.dialogs_.end())
2536 if (inset && inset != getOpenInset(name))
2539 Dialog * const dialog = it->second.get();
2540 if (dialog->isVisibleView())
2542 d.open_insets_[name] = 0;
2546 void GuiView::disconnectDialog(string const & name)
2548 if (!isValidName(name))
2551 if (d.open_insets_.find(name) != d.open_insets_.end())
2552 d.open_insets_[name] = 0;
2556 Inset * GuiView::getOpenInset(string const & name) const
2558 if (!isValidName(name))
2561 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2562 return it == d.open_insets_.end() ? 0 : it->second;
2566 void GuiView::hideAll() const
2568 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2569 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2571 for(; it != end; ++it)
2572 it->second->hideView();
2576 void GuiView::updateDialogs()
2578 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2579 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2581 for(; it != end; ++it) {
2582 Dialog * dialog = it->second.get();
2583 if (dialog && dialog->isVisibleView())
2584 dialog->checkStatus();
2591 // will be replaced by a proper factory...
2592 Dialog * createGuiAbout(GuiView & lv);
2593 Dialog * createGuiBibitem(GuiView & lv);
2594 Dialog * createGuiBibtex(GuiView & lv);
2595 Dialog * createGuiBox(GuiView & lv);
2596 Dialog * createGuiBranch(GuiView & lv);
2597 Dialog * createGuiChanges(GuiView & lv);
2598 Dialog * createGuiCharacter(GuiView & lv);
2599 Dialog * createGuiCitation(GuiView & lv);
2600 Dialog * createGuiDelimiter(GuiView & lv);
2601 Dialog * createGuiDocument(GuiView & lv);
2602 Dialog * createGuiErrorList(GuiView & lv);
2603 Dialog * createGuiERT(GuiView & lv);
2604 Dialog * createGuiExternal(GuiView & lv);
2605 Dialog * createGuiFloat(GuiView & lv);
2606 Dialog * createGuiGraphics(GuiView & lv);
2607 Dialog * createGuiInclude(GuiView & lv);
2608 Dialog * createGuiIndex(GuiView & lv);
2609 Dialog * createGuiInfo(GuiView & lv);
2610 Dialog * createGuiLabel(GuiView & lv);
2611 Dialog * createGuiListings(GuiView & lv);
2612 Dialog * createGuiLog(GuiView & lv);
2613 Dialog * createGuiMathHSpace(GuiView & lv);
2614 Dialog * createGuiMathMatrix(GuiView & lv);
2615 Dialog * createGuiNomenclature(GuiView & lv);
2616 Dialog * createGuiNote(GuiView & lv);
2617 Dialog * createGuiParagraph(GuiView & lv);
2618 Dialog * createGuiPhantom(GuiView & lv);
2619 Dialog * createGuiPreferences(GuiView & lv);
2620 Dialog * createGuiPrint(GuiView & lv);
2621 Dialog * createGuiPrintindex(GuiView & lv);
2622 Dialog * createGuiPrintNomencl(GuiView & lv);
2623 Dialog * createGuiRef(GuiView & lv);
2624 Dialog * createGuiSearch(GuiView & lv);
2625 Dialog * createGuiSearchAdv(GuiView & lv);
2626 Dialog * createGuiSendTo(GuiView & lv);
2627 Dialog * createGuiShowFile(GuiView & lv);
2628 Dialog * createGuiSpellchecker(GuiView & lv);
2629 Dialog * createGuiSymbols(GuiView & lv);
2630 Dialog * createGuiTabularCreate(GuiView & lv);
2631 Dialog * createGuiTabular(GuiView & lv);
2632 Dialog * createGuiTexInfo(GuiView & lv);
2633 Dialog * createGuiTextHSpace(GuiView & lv);
2634 Dialog * createGuiToc(GuiView & lv);
2635 Dialog * createGuiThesaurus(GuiView & lv);
2636 Dialog * createGuiHyperlink(GuiView & lv);
2637 Dialog * createGuiVSpace(GuiView & lv);
2638 Dialog * createGuiViewSource(GuiView & lv);
2639 Dialog * createGuiWrap(GuiView & lv);
2642 Dialog * GuiView::build(string const & name)
2644 LASSERT(isValidName(name), return 0);
2646 if (name == "aboutlyx")
2647 return createGuiAbout(*this);
2648 if (name == "bibitem")
2649 return createGuiBibitem(*this);
2650 if (name == "bibtex")
2651 return createGuiBibtex(*this);
2653 return createGuiBox(*this);
2654 if (name == "branch")
2655 return createGuiBranch(*this);
2656 if (name == "changes")
2657 return createGuiChanges(*this);
2658 if (name == "character")
2659 return createGuiCharacter(*this);
2660 if (name == "citation")
2661 return createGuiCitation(*this);
2662 if (name == "document")
2663 return createGuiDocument(*this);
2664 if (name == "errorlist")
2665 return createGuiErrorList(*this);
2667 return createGuiERT(*this);
2668 if (name == "external")
2669 return createGuiExternal(*this);
2671 return createGuiShowFile(*this);
2672 if (name == "findreplace")
2673 return createGuiSearch(*this);
2674 if (name == "findreplaceadv")
2675 return createGuiSearchAdv(*this);
2676 if (name == "float")
2677 return createGuiFloat(*this);
2678 if (name == "graphics")
2679 return createGuiGraphics(*this);
2681 return createGuiHyperlink(*this);
2682 if (name == "include")
2683 return createGuiInclude(*this);
2684 if (name == "index")
2685 return createGuiIndex(*this);
2686 if (name == "index_print")
2687 return createGuiPrintindex(*this);
2689 return createGuiInfo(*this);
2690 if (name == "label")
2691 return createGuiLabel(*this);
2692 if (name == "listings")
2693 return createGuiListings(*this);
2695 return createGuiLog(*this);
2696 if (name == "mathdelimiter")
2697 return createGuiDelimiter(*this);
2698 if (name == "mathspace")
2699 return createGuiMathHSpace(*this);
2700 if (name == "mathmatrix")
2701 return createGuiMathMatrix(*this);
2702 if (name == "nomenclature")
2703 return createGuiNomenclature(*this);
2704 if (name == "nomencl_print")
2705 return createGuiPrintNomencl(*this);
2707 return createGuiNote(*this);
2708 if (name == "paragraph")
2709 return createGuiParagraph(*this);
2710 if (name == "phantom")
2711 return createGuiPhantom(*this);
2712 if (name == "prefs")
2713 return createGuiPreferences(*this);
2714 if (name == "print")
2715 return createGuiPrint(*this);
2717 return createGuiRef(*this);
2718 if (name == "sendto")
2719 return createGuiSendTo(*this);
2720 if (name == "space")
2721 return createGuiTextHSpace(*this);
2722 if (name == "spellchecker")
2723 return createGuiSpellchecker(*this);
2724 if (name == "symbols")
2725 return createGuiSymbols(*this);
2726 if (name == "tabular")
2727 return createGuiTabular(*this);
2728 if (name == "tabularcreate")
2729 return createGuiTabularCreate(*this);
2730 if (name == "texinfo")
2731 return createGuiTexInfo(*this);
2732 if (name == "thesaurus")
2733 return createGuiThesaurus(*this);
2735 return createGuiToc(*this);
2736 if (name == "view-source")
2737 return createGuiViewSource(*this);
2738 if (name == "vspace")
2739 return createGuiVSpace(*this);
2741 return createGuiWrap(*this);
2747 } // namespace frontend
2750 #include "moc_GuiView.cpp"