3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiWorkArea.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
31 #include "qt_helpers.h"
33 #include "frontends/alert.h"
35 #include "buffer_funcs.h"
37 #include "BufferList.h"
38 #include "BufferParams.h"
39 #include "BufferView.h"
40 #include "Converter.h"
43 #include "ErrorList.h"
45 #include "FuncStatus.h"
46 #include "FuncRequest.h"
54 #include "Paragraph.h"
55 #include "TextClass.h"
60 #include "support/convert.h"
61 #include "support/debug.h"
62 #include "support/ExceptionMessage.h"
63 #include "support/FileName.h"
64 #include "support/filetools.h"
65 #include "support/gettext.h"
66 #include "support/ForkedCalls.h"
67 #include "support/lassert.h"
68 #include "support/lstrings.h"
69 #include "support/os.h"
70 #include "support/Package.h"
71 #include "support/Timeout.h"
74 #include <QApplication>
75 #include <QCloseEvent>
77 #include <QDesktopWidget>
78 #include <QDragEnterEvent>
85 #include <QPixmapCache>
87 #include <QPushButton>
91 #include <QStackedWidget>
98 #include <boost/bind.hpp>
100 #ifdef HAVE_SYS_TIME_H
101 # include <sys/time.h>
108 using namespace lyx::support;
115 class BackgroundWidget : public QWidget
120 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
121 /// The text to be written on top of the pixmap
122 QString const text = lyx_version ?
123 qt_("version ") + lyx_version : qt_("unknown version");
124 splash_ = getPixmap("images/", "banner", "png");
126 QPainter pain(&splash_);
127 pain.setPen(QColor(0, 0, 0));
129 // The font used to display the version info
130 font.setStyleHint(QFont::SansSerif);
131 font.setWeight(QFont::Bold);
132 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
134 pain.drawText(260, 15, text);
137 void paintEvent(QPaintEvent *)
139 int x = (width() - splash_.width()) / 2;
140 int y = (height() - splash_.height()) / 2;
142 pain.drawPixmap(x, y, splash_);
150 /// Toolbar store providing access to individual toolbars by name.
151 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
153 typedef boost::shared_ptr<Dialog> DialogPtr;
158 struct GuiView::GuiViewPrivate
161 : current_work_area_(0), current_main_work_area_(0),
162 layout_(0), autosave_timeout_(5000),
165 // hardcode here the platform specific icon size
166 smallIconSize = 14; // scaling problems
167 normalIconSize = 20; // ok, default
168 bigIconSize = 26; // better for some math icons
170 splitter_ = new QSplitter;
171 bg_widget_ = new BackgroundWidget;
172 stack_widget_ = new QStackedWidget;
173 stack_widget_->addWidget(bg_widget_);
174 stack_widget_->addWidget(splitter_);
182 delete stack_widget_;
185 QMenu * toolBarPopup(GuiView * parent)
187 // FIXME: translation
188 QMenu * menu = new QMenu(parent);
189 QActionGroup * iconSizeGroup = new QActionGroup(parent);
191 QAction * smallIcons = new QAction(iconSizeGroup);
192 smallIcons->setText(qt_("Small-sized icons"));
193 smallIcons->setCheckable(true);
194 QObject::connect(smallIcons, SIGNAL(triggered()),
195 parent, SLOT(smallSizedIcons()));
196 menu->addAction(smallIcons);
198 QAction * normalIcons = new QAction(iconSizeGroup);
199 normalIcons->setText(qt_("Normal-sized icons"));
200 normalIcons->setCheckable(true);
201 QObject::connect(normalIcons, SIGNAL(triggered()),
202 parent, SLOT(normalSizedIcons()));
203 menu->addAction(normalIcons);
205 QAction * bigIcons = new QAction(iconSizeGroup);
206 bigIcons->setText(qt_("Big-sized icons"));
207 bigIcons->setCheckable(true);
208 QObject::connect(bigIcons, SIGNAL(triggered()),
209 parent, SLOT(bigSizedIcons()));
210 menu->addAction(bigIcons);
212 unsigned int cur = parent->iconSize().width();
213 if ( cur == parent->d.smallIconSize)
214 smallIcons->setChecked(true);
215 else if (cur == parent->d.normalIconSize)
216 normalIcons->setChecked(true);
217 else if (cur == parent->d.bigIconSize)
218 bigIcons->setChecked(true);
225 stack_widget_->setCurrentWidget(bg_widget_);
226 bg_widget_->setUpdatesEnabled(true);
229 TabWorkArea * tabWorkArea(int i)
231 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
234 TabWorkArea * currentTabWorkArea()
236 if (splitter_->count() == 1)
237 // The first TabWorkArea is always the first one, if any.
238 return tabWorkArea(0);
240 for (int i = 0; i != splitter_->count(); ++i) {
241 TabWorkArea * twa = tabWorkArea(i);
242 if (current_main_work_area_ == twa->currentWorkArea())
246 // None has the focus so we just take the first one.
247 return tabWorkArea(0);
251 GuiWorkArea * current_work_area_;
252 GuiWorkArea * current_main_work_area_;
253 QSplitter * splitter_;
254 QStackedWidget * stack_widget_;
255 BackgroundWidget * bg_widget_;
257 ToolbarMap toolbars_;
258 /// The main layout box.
260 * \warning Don't Delete! The layout box is actually owned by
261 * whichever toolbar contains it. All the GuiView class needs is a
262 * means of accessing it.
264 * FIXME: replace that with a proper model so that we are not limited
265 * to only one dialog.
267 GuiLayoutBox * layout_;
270 map<string, Inset *> open_insets_;
273 map<string, DialogPtr> dialogs_;
275 unsigned int smallIconSize;
276 unsigned int normalIconSize;
277 unsigned int bigIconSize;
279 QTimer statusbar_timer_;
280 /// auto-saving of buffers
281 Timeout autosave_timeout_;
282 /// flag against a race condition due to multiclicks, see bug #1119
286 TocModels toc_models_;
290 GuiView::GuiView(int id)
291 : d(*new GuiViewPrivate), id_(id), closing_(false)
293 // GuiToolbars *must* be initialised before the menu bar.
294 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
297 // set ourself as the current view. This is needed for the menu bar
298 // filling, at least for the static special menu item on Mac. Otherwise
299 // they are greyed out.
300 theLyXFunc().setLyXView(this);
302 // Fill up the menu bar.
303 guiApp->menus().fillMenuBar(menuBar(), this, true);
305 setCentralWidget(d.stack_widget_);
307 // Start autosave timer
308 if (lyxrc.autosave) {
309 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
310 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
311 d.autosave_timeout_.start();
313 connect(&d.statusbar_timer_, SIGNAL(timeout()),
314 this, SLOT(clearMessage()));
316 // We don't want to keep the window in memory if it is closed.
317 setAttribute(Qt::WA_DeleteOnClose, true);
319 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
320 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
321 // since the icon is provided in the application bundle.
322 setWindowIcon(getPixmap("images/", "lyx", "png"));
326 setAcceptDrops(true);
328 statusBar()->setSizeGripEnabled(true);
330 // Forbid too small unresizable window because it can happen
331 // with some window manager under X11.
332 setMinimumSize(300, 200);
334 if (lyxrc.allow_geometry_session) {
335 // Now take care of session management.
340 // no session handling, default to a sane size.
341 setGeometry(50, 50, 690, 510);
344 // clear session data if any.
346 settings.remove("views");
356 void GuiView::saveLayout() const
359 settings.beginGroup("views");
360 settings.beginGroup(QString::number(id_));
362 settings.setValue("pos", pos());
363 settings.setValue("size", size());
365 settings.setValue("geometry", saveGeometry());
367 settings.setValue("layout", saveState(0));
368 settings.setValue("icon_size", iconSize());
372 bool GuiView::restoreLayout()
375 settings.beginGroup("views");
376 settings.beginGroup(QString::number(id_));
377 QString const icon_key = "icon_size";
378 if (!settings.contains(icon_key))
381 setIconSize(settings.value(icon_key).toSize());
383 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
384 QSize size = settings.value("size", QSize(690, 510)).toSize();
388 if (!restoreGeometry(settings.value("geometry").toByteArray()))
389 setGeometry(50, 50, 690, 510);
391 // Make sure layout is correctly oriented.
392 setLayoutDirection(qApp->layoutDirection());
394 // Allow the toc and view-source dock widget to be restored if needed.
396 if ((d = findOrBuild("toc", true)))
399 if ((d = findOrBuild("view-source", true)))
402 if (!restoreState(settings.value("layout").toByteArray(), 0))
409 GuiToolbar * GuiView::toolbar(string const & name)
411 ToolbarMap::iterator it = d.toolbars_.find(name);
412 if (it != d.toolbars_.end())
415 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
416 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
421 void GuiView::constructToolbars()
423 ToolbarMap::iterator it = d.toolbars_.begin();
424 for (; it != d.toolbars_.end(); ++it)
429 // extracts the toolbars from the backend
430 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
431 Toolbars::Infos::iterator end = guiApp->toolbars().end();
432 for (; cit != end; ++cit)
433 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
437 void GuiView::initToolbars()
439 // extracts the toolbars from the backend
440 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
441 Toolbars::Infos::iterator end = guiApp->toolbars().end();
442 for (; cit != end; ++cit) {
443 GuiToolbar * tb = toolbar(cit->name);
446 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
448 tb->setVisible(false);
449 tb->setVisibility(visibility);
451 if (visibility & Toolbars::TOP) {
453 addToolBarBreak(Qt::TopToolBarArea);
454 addToolBar(Qt::TopToolBarArea, tb);
457 if (visibility & Toolbars::BOTTOM) {
458 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
459 #if (QT_VERSION >= 0x040202)
460 addToolBarBreak(Qt::BottomToolBarArea);
462 addToolBar(Qt::BottomToolBarArea, tb);
465 if (visibility & Toolbars::LEFT) {
466 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
467 #if (QT_VERSION >= 0x040202)
468 addToolBarBreak(Qt::LeftToolBarArea);
470 addToolBar(Qt::LeftToolBarArea, tb);
473 if (visibility & Toolbars::RIGHT) {
474 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
475 #if (QT_VERSION >= 0x040202)
476 addToolBarBreak(Qt::RightToolBarArea);
478 addToolBar(Qt::RightToolBarArea, tb);
481 if (visibility & Toolbars::ON)
482 tb->setVisible(true);
487 TocModels & GuiView::tocModels()
489 return d.toc_models_;
493 void GuiView::setFocus()
495 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
496 // Make sure LyXFunc points to the correct view.
497 guiApp->setCurrentView(this);
498 theLyXFunc().setLyXView(this);
499 QMainWindow::setFocus();
500 if (d.current_work_area_)
501 d.current_work_area_->setFocus();
505 QMenu * GuiView::createPopupMenu()
507 return d.toolBarPopup(this);
511 void GuiView::showEvent(QShowEvent * e)
513 LYXERR(Debug::GUI, "Passed Geometry "
514 << size().height() << "x" << size().width()
515 << "+" << pos().x() << "+" << pos().y());
517 if (d.splitter_->count() == 0)
518 // No work area, switch to the background widget.
521 QMainWindow::showEvent(e);
525 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
526 ** is responsibility of the container (e.g., dialog)
528 void GuiView::closeEvent(QCloseEvent * close_event)
530 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
533 // it can happen that this event arrives without selecting the view,
534 // e.g. when clicking the close button on a background window.
536 setCurrentWorkArea(currentMainWorkArea());
538 int splitter_count = d.splitter_->count();
539 for (; splitter_count; --splitter_count) {
540 TabWorkArea * twa = d.tabWorkArea(0);
542 int twa_count = twa->count();
543 for (; twa_count; --twa_count) {
544 twa->setCurrentIndex(twa_count-1);
546 GuiWorkArea * wa = twa->currentWorkArea();
547 Buffer * b = &wa->bufferView().buffer();
549 // This is a child document, just close the tab
550 // after saving but keep the file loaded.
551 if (!closeBuffer(*b, true)) {
553 close_event->ignore();
559 vector<Buffer *> clist = b->getChildren();
560 for (vector<Buffer *>::const_iterator it = clist.begin();
561 it != clist.end(); ++it) {
562 if ((*it)->isClean())
565 // If a child is dirty, do not close
566 // without user intervention
567 if (!closeBuffer(*c, false)) {
569 close_event->ignore();
574 QList<int> const ids = guiApp->viewIds();
575 for (int i = 0; i != ids.size(); ++i) {
578 if (guiApp->view(ids[i]).workArea(*b)) {
579 // FIXME 1: should we put an alert box here
580 // that the buffer is viewed elsewhere?
581 // FIXME 2: should we try to save this buffer in any case?
584 // This buffer is also opened in another view, so
585 // close the associated work area...
587 // ... but don't close the buffer.
592 // closeBuffer() needs buffer workArea still alive and
593 // set as currrent one, and destroys it
594 if (b && !closeBuffer(*b, true)) {
596 close_event->ignore();
601 // Make sure that nothing will use this close to be closed View.
602 guiApp->unregisterView(this);
604 if (isFullScreen()) {
605 // Switch off fullscreen before closing.
610 // Make sure the timer time out will not trigger a statusbar update.
611 d.statusbar_timer_.stop();
613 // Saving fullscreen requires additional tweaks in the toolbar code.
614 // It wouldn't also work under linux natively.
615 if (lyxrc.allow_geometry_session) {
616 // Save this window geometry and layout.
618 // Then the toolbar private states.
619 ToolbarMap::iterator end = d.toolbars_.end();
620 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
621 it->second->saveSession();
622 // Now take care of all other dialogs:
623 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
624 for (; it!= d.dialogs_.end(); ++it)
625 it->second->saveSession();
628 close_event->accept();
632 void GuiView::dragEnterEvent(QDragEnterEvent * event)
634 if (event->mimeData()->hasUrls())
636 /// \todo Ask lyx-devel is this is enough:
637 /// if (event->mimeData()->hasFormat("text/plain"))
638 /// event->acceptProposedAction();
642 void GuiView::dropEvent(QDropEvent * event)
644 QList<QUrl> files = event->mimeData()->urls();
648 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
649 for (int i = 0; i != files.size(); ++i) {
650 string const file = os::internal_path(fromqstr(
651 files.at(i).toLocalFile()));
653 // Asynchronously post the event. DropEvent usually come
654 // from the BufferView. But reloading a file might close
655 // the BufferView from within its own event handler.
656 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
663 void GuiView::message(docstring const & str)
665 if (ForkedProcess::iAmAChild())
668 statusBar()->showMessage(toqstr(str));
669 d.statusbar_timer_.stop();
670 d.statusbar_timer_.start(3000);
674 void GuiView::smallSizedIcons()
676 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
680 void GuiView::normalSizedIcons()
682 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
686 void GuiView::bigSizedIcons()
688 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
692 void GuiView::clearMessage()
696 theLyXFunc().setLyXView(this);
697 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
698 d.statusbar_timer_.stop();
702 void GuiView::updateWindowTitle(GuiWorkArea * wa)
704 if (wa != d.current_work_area_)
706 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
707 setWindowIconText(wa->windowIconText());
711 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
714 disconnectBufferView();
715 connectBufferView(wa->bufferView());
716 connectBuffer(wa->bufferView().buffer());
717 d.current_work_area_ = wa;
718 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
719 this, SLOT(updateWindowTitle(GuiWorkArea *)));
720 updateWindowTitle(wa);
724 // The document settings needs to be reinitialised.
725 updateDialog("document", "");
727 // Buffer-dependent dialogs must be updated. This is done here because
728 // some dialogs require buffer()->text.
733 void GuiView::on_lastWorkAreaRemoved()
736 // We already are in a close event. Nothing more to do.
739 if (d.splitter_->count() > 1)
740 // We have a splitter so don't close anything.
743 // Reset and updates the dialogs.
744 d.toc_models_.reset(0);
745 updateDialog("document", "");
748 resetWindowTitleAndIconText();
750 if (lyxrc.open_buffers_in_tabs)
751 // Nothing more to do, the window should stay open.
754 if (guiApp->viewIds().size() > 1) {
760 // On Mac we also close the last window because the application stay
761 // resident in memory. On other platforms we don't close the last
762 // window because this would quit the application.
768 void GuiView::updateStatusBar()
770 // let the user see the explicit message
771 if (d.statusbar_timer_.isActive())
774 theLyXFunc().setLyXView(this);
775 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
779 bool GuiView::hasFocus() const
781 return qApp->activeWindow() == this;
785 bool GuiView::event(QEvent * e)
789 // Useful debug code:
790 //case QEvent::ActivationChange:
791 //case QEvent::WindowDeactivate:
792 //case QEvent::Paint:
793 //case QEvent::Enter:
794 //case QEvent::Leave:
795 //case QEvent::HoverEnter:
796 //case QEvent::HoverLeave:
797 //case QEvent::HoverMove:
798 //case QEvent::StatusTip:
799 //case QEvent::DragEnter:
800 //case QEvent::DragLeave:
804 case QEvent::WindowActivate: {
805 if (this == guiApp->currentView()) {
807 return QMainWindow::event(e);
809 guiApp->setCurrentView(this);
810 theLyXFunc().setLyXView(this);
811 if (d.current_work_area_) {
812 BufferView & bv = d.current_work_area_->bufferView();
813 connectBufferView(bv);
814 connectBuffer(bv.buffer());
815 // The document structure, name and dialogs might have
816 // changed in another view.
818 // The document settings needs to be reinitialised.
819 updateDialog("document", "");
822 resetWindowTitleAndIconText();
825 return QMainWindow::event(e);
828 case QEvent::ShortcutOverride: {
831 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
832 if (isFullScreen() && menuBar()->isHidden()) {
833 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
834 // FIXME: we should also try to detect special LyX shortcut such as
835 // Alt-P and Alt-M. Right now there is a hack in
836 // GuiWorkArea::processKeySym() that hides again the menubar for
838 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
840 return QMainWindow::event(e);
845 if (d.current_work_area_)
846 // Nothing special to do.
847 return QMainWindow::event(e);
849 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
850 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
852 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
853 || ke->key() == Qt::Key_Backtab)
854 return QMainWindow::event(e);
856 // Allow processing of shortcuts that are allowed even when no Buffer
858 theLyXFunc().setLyXView(this);
860 setKeySymbol(&sym, ke);
861 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
867 return QMainWindow::event(e);
871 void GuiView::resetWindowTitleAndIconText()
873 setWindowTitle(qt_("LyX"));
874 setWindowIconText(qt_("LyX"));
877 bool GuiView::focusNextPrevChild(bool /*next*/)
884 void GuiView::setBusy(bool busy)
886 if (d.current_work_area_) {
887 d.current_work_area_->setUpdatesEnabled(!busy);
889 d.current_work_area_->stopBlinkingCursor();
891 d.current_work_area_->startBlinkingCursor();
895 QApplication::setOverrideCursor(Qt::WaitCursor);
897 QApplication::restoreOverrideCursor();
901 GuiWorkArea * GuiView::workArea(Buffer & buffer)
903 if (currentWorkArea()
904 && ¤tWorkArea()->bufferView().buffer() == &buffer)
905 return (GuiWorkArea *) currentWorkArea();
906 if (TabWorkArea * twa = d.currentTabWorkArea())
907 return twa->workArea(buffer);
912 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
914 // Automatically create a TabWorkArea if there are none yet.
915 TabWorkArea * tab_widget = d.splitter_->count()
916 ? d.currentTabWorkArea() : addTabWorkArea();
917 return tab_widget->addWorkArea(buffer, *this);
921 TabWorkArea * GuiView::addTabWorkArea()
923 TabWorkArea * twa = new TabWorkArea;
924 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
925 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
926 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
927 this, SLOT(on_lastWorkAreaRemoved()));
929 d.splitter_->addWidget(twa);
930 d.stack_widget_->setCurrentWidget(d.splitter_);
935 GuiWorkArea const * GuiView::currentWorkArea() const
937 return d.current_work_area_;
941 GuiWorkArea * GuiView::currentWorkArea()
943 return d.current_work_area_;
947 GuiWorkArea const * GuiView::currentMainWorkArea() const
949 if (d.currentTabWorkArea() == NULL)
951 return d.currentTabWorkArea()->currentWorkArea();
955 GuiWorkArea * GuiView::currentMainWorkArea()
957 if (d.currentTabWorkArea() == NULL)
959 return d.currentTabWorkArea()->currentWorkArea();
963 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
965 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
967 d.current_work_area_ = NULL;
971 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
975 theGuiApp()->setCurrentView(this);
976 d.current_work_area_ = wa;
977 for (int i = 0; i != d.splitter_->count(); ++i) {
978 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
979 //if (d.current_main_work_area_)
980 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
981 d.current_main_work_area_ = wa;
982 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
983 //d.current_main_work_area_->setLineWidth(2);
984 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
988 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
989 on_currentWorkAreaChanged(wa);
990 BufferView & bv = wa->bufferView();
991 bv.cursor().fixIfBroken();
993 wa->setUpdatesEnabled(true);
994 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
998 void GuiView::removeWorkArea(GuiWorkArea * wa)
1000 LASSERT(wa, return);
1001 if (wa == d.current_work_area_) {
1003 disconnectBufferView();
1004 d.current_work_area_ = 0;
1005 d.current_main_work_area_ = 0;
1008 bool found_twa = false;
1009 for (int i = 0; i != d.splitter_->count(); ++i) {
1010 TabWorkArea * twa = d.tabWorkArea(i);
1011 if (twa->removeWorkArea(wa)) {
1012 // Found in this tab group, and deleted the GuiWorkArea.
1014 if (twa->count() != 0) {
1015 if (d.current_work_area_ == 0)
1016 // This means that we are closing the current GuiWorkArea, so
1017 // switch to the next GuiWorkArea in the found TabWorkArea.
1018 setCurrentWorkArea(twa->currentWorkArea());
1020 // No more WorkAreas in this tab group, so delete it.
1027 // It is not a tabbed work area (i.e., the search work area), so it
1028 // should be deleted by other means.
1029 LASSERT(found_twa, /* */);
1031 if (d.current_work_area_ == 0) {
1032 if (d.splitter_->count() != 0) {
1033 TabWorkArea * twa = d.currentTabWorkArea();
1034 setCurrentWorkArea(twa->currentWorkArea());
1036 // No more work areas, switch to the background widget.
1037 setCurrentWorkArea(0);
1043 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1049 void GuiView::updateLayoutList()
1052 d.layout_->updateContents(false);
1056 void GuiView::updateToolbars()
1058 ToolbarMap::iterator end = d.toolbars_.end();
1059 if (d.current_work_area_) {
1061 d.current_work_area_->bufferView().cursor().inMathed();
1063 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1065 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1066 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1067 bool const mathmacrotemplate =
1068 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1070 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1071 it->second->update(math, table, review, mathmacrotemplate);
1073 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1074 it->second->update(false, false, false, false);
1078 Buffer * GuiView::buffer()
1080 if (d.current_work_area_)
1081 return &d.current_work_area_->bufferView().buffer();
1086 Buffer const * GuiView::buffer() const
1088 if (d.current_work_area_)
1089 return &d.current_work_area_->bufferView().buffer();
1094 void GuiView::setBuffer(Buffer * newBuffer)
1096 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1097 LASSERT(newBuffer, return);
1100 GuiWorkArea * wa = workArea(*newBuffer);
1102 newBuffer->masterBuffer()->updateLabels();
1103 wa = addWorkArea(*newBuffer);
1105 //Disconnect the old buffer...there's no new one.
1108 connectBuffer(*newBuffer);
1109 connectBufferView(wa->bufferView());
1110 setCurrentWorkArea(wa);
1116 void GuiView::connectBuffer(Buffer & buf)
1118 buf.setGuiDelegate(this);
1122 void GuiView::disconnectBuffer()
1124 if (d.current_work_area_)
1125 d.current_work_area_->bufferView().setGuiDelegate(0);
1129 void GuiView::connectBufferView(BufferView & bv)
1131 bv.setGuiDelegate(this);
1135 void GuiView::disconnectBufferView()
1137 if (d.current_work_area_)
1138 d.current_work_area_->bufferView().setGuiDelegate(0);
1142 void GuiView::errors(string const & error_type)
1144 ErrorList & el = buffer()->errorList(error_type);
1146 showDialog("errorlist", error_type);
1150 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1152 d.toc_models_.updateItem(toqstr(type), dit);
1156 void GuiView::structureChanged()
1158 d.toc_models_.reset(view());
1159 // Navigator needs more than a simple update in this case. It needs to be
1161 updateDialog("toc", "");
1165 void GuiView::updateDialog(string const & name, string const & data)
1167 if (!isDialogVisible(name))
1170 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1171 if (it == d.dialogs_.end())
1174 Dialog * const dialog = it->second.get();
1175 if (dialog->isVisibleView())
1176 dialog->initialiseParams(data);
1180 BufferView * GuiView::view()
1182 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1186 void GuiView::autoSave()
1188 LYXERR(Debug::INFO, "Running autoSave()");
1191 view()->buffer().autoSave();
1195 void GuiView::resetAutosaveTimers()
1198 d.autosave_timeout_.restart();
1202 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1205 Buffer * buf = buffer();
1207 /* In LyX/Mac, when a dialog is open, the menus of the
1208 application can still be accessed without giving focus to
1209 the main window. In this case, we want to disable the menu
1210 entries that are buffer-related.
1212 Note that this code is not perfect, as bug 1941 attests:
1213 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1215 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1218 if (cmd.origin == FuncRequest::TOC) {
1219 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1221 if (toc->getStatus(view()->cursor(), cmd, fs))
1224 flag.setEnabled(false);
1228 switch(cmd.action) {
1229 case LFUN_BUFFER_WRITE:
1230 enable = buf && (buf->isUnnamed() || !buf->isClean());
1233 case LFUN_BUFFER_WRITE_AS:
1237 case LFUN_SPLIT_VIEW:
1238 if (cmd.getArg(0) == "vertical")
1239 enable = buf && (d.splitter_->count() == 1 ||
1240 d.splitter_->orientation() == Qt::Vertical);
1242 enable = buf && (d.splitter_->count() == 1 ||
1243 d.splitter_->orientation() == Qt::Horizontal);
1246 case LFUN_CLOSE_TAB_GROUP:
1247 enable = d.currentTabWorkArea();
1250 case LFUN_TOOLBAR_TOGGLE:
1251 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1252 flag.setOnOff(t->isVisible());
1255 case LFUN_UI_TOGGLE:
1256 flag.setOnOff(isFullScreen());
1259 case LFUN_DIALOG_TOGGLE:
1260 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1261 // fall through to set "enable"
1262 case LFUN_DIALOG_SHOW: {
1263 string const name = cmd.getArg(0);
1265 enable = name == "aboutlyx"
1266 || name == "file" //FIXME: should be removed.
1268 || name == "texinfo";
1269 else if (name == "print")
1270 enable = buf->isExportable("dvi")
1271 && lyxrc.print_command != "none";
1272 else if (name == "character") {
1276 InsetCode ic = view()->cursor().inset().lyxCode();
1277 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1280 else if (name == "symbols") {
1281 if (!view() || view()->cursor().inMathed())
1284 InsetCode ic = view()->cursor().inset().lyxCode();
1285 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1288 else if (name == "latexlog")
1289 enable = FileName(buf->logName()).isReadableFile();
1290 else if (name == "spellchecker")
1291 #if defined (USE_ASPELL)
1292 enable = !buf->isReadonly();
1296 else if (name == "vclog")
1297 enable = buf->lyxvc().inUse();
1301 case LFUN_DIALOG_UPDATE: {
1302 string const name = cmd.getArg(0);
1304 enable = name == "prefs";
1308 case LFUN_INSET_APPLY: {
1309 string const name = cmd.getArg(0);
1310 Inset * inset = getOpenInset(name);
1312 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1314 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1315 // Every inset is supposed to handle this
1316 LASSERT(false, break);
1320 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1321 flag |= lyx::getStatus(fr);
1323 enable = flag.enabled();
1327 case LFUN_COMPLETION_INLINE:
1328 if (!d.current_work_area_
1329 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1333 case LFUN_COMPLETION_POPUP:
1334 if (!d.current_work_area_
1335 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1339 case LFUN_COMPLETION_COMPLETE:
1340 if (!d.current_work_area_
1341 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1345 case LFUN_COMPLETION_ACCEPT:
1346 if (!d.current_work_area_
1347 || (!d.current_work_area_->completer().popupVisible()
1348 && !d.current_work_area_->completer().inlineVisible()
1349 && !d.current_work_area_->completer().completionAvailable()))
1353 case LFUN_COMPLETION_CANCEL:
1354 if (!d.current_work_area_
1355 || (!d.current_work_area_->completer().popupVisible()
1356 && !d.current_work_area_->completer().inlineVisible()))
1360 case LFUN_BUFFER_ZOOM_OUT:
1361 enable = buf && lyxrc.zoom > 10;
1364 case LFUN_BUFFER_ZOOM_IN:
1373 flag.setEnabled(false);
1379 static FileName selectTemplateFile()
1381 FileDialog dlg(qt_("Select template file"));
1382 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1383 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1385 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1386 QStringList(qt_("LyX Documents (*.lyx)")));
1388 if (result.first == FileDialog::Later)
1390 if (result.second.isEmpty())
1392 return FileName(fromqstr(result.second));
1396 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1400 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1403 message(_("Document not loaded."));
1408 setBuffer(newBuffer);
1410 // scroll to the position when the file was last closed
1411 if (lyxrc.use_lastfilepos) {
1412 LastFilePosSection::FilePos filepos =
1413 theSession().lastFilePos().load(filename);
1414 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1418 theSession().lastFiles().add(filename);
1425 void GuiView::openDocument(string const & fname)
1427 string initpath = lyxrc.document_path;
1430 string const trypath = buffer()->filePath();
1431 // If directory is writeable, use this as default.
1432 if (FileName(trypath).isDirWritable())
1438 if (fname.empty()) {
1439 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1440 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1441 dlg.setButton2(qt_("Examples|#E#e"),
1442 toqstr(addPath(package().system_support().absFilename(), "examples")));
1444 QStringList filter(qt_("LyX Documents (*.lyx)"));
1445 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1446 << qt_("LyX-1.4.x Documents (*.lyx14)")
1447 << qt_("LyX-1.5.x Documents (*.lyx15)")
1448 << qt_("LyX-1.6.x Documents (*.lyx16)");
1449 FileDialog::Result result =
1450 dlg.open(toqstr(initpath), filter);
1452 if (result.first == FileDialog::Later)
1455 filename = fromqstr(result.second);
1457 // check selected filename
1458 if (filename.empty()) {
1459 message(_("Canceled."));
1465 // get absolute path of file and add ".lyx" to the filename if
1467 FileName const fullname =
1468 fileSearch(string(), filename, "lyx", support::may_not_exist);
1469 if (!fullname.empty())
1470 filename = fullname.absFilename();
1472 if (!fullname.onlyPath().isDirectory()) {
1473 Alert::warning(_("Invalid filename"),
1474 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1475 from_utf8(fullname.absFilename())));
1478 // if the file doesn't exist, let the user create one
1479 if (!fullname.exists()) {
1480 // the user specifically chose this name. Believe him.
1481 Buffer * const b = newFile(filename, string(), true);
1487 docstring const disp_fn = makeDisplayPath(filename);
1488 message(bformat(_("Opening document %1$s..."), disp_fn));
1491 Buffer * buf = loadDocument(fullname);
1493 buf->updateLabels();
1495 buf->errors("Parse");
1496 str2 = bformat(_("Document %1$s opened."), disp_fn);
1497 if (buf->lyxvc().inUse())
1498 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1499 " " + _("Version control detected.");
1501 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1506 // FIXME: clean that
1507 static bool import(GuiView * lv, FileName const & filename,
1508 string const & format, ErrorList & errorList)
1510 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1512 string loader_format;
1513 vector<string> loaders = theConverters().loaders();
1514 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1515 for (vector<string>::const_iterator it = loaders.begin();
1516 it != loaders.end(); ++it) {
1517 if (!theConverters().isReachable(format, *it))
1520 string const tofile =
1521 support::changeExtension(filename.absFilename(),
1522 formats.extension(*it));
1523 if (!theConverters().convert(0, filename, FileName(tofile),
1524 filename, format, *it, errorList))
1526 loader_format = *it;
1529 if (loader_format.empty()) {
1530 frontend::Alert::error(_("Couldn't import file"),
1531 bformat(_("No information for importing the format %1$s."),
1532 formats.prettyName(format)));
1536 loader_format = format;
1538 if (loader_format == "lyx") {
1539 Buffer * buf = lv->loadDocument(lyxfile);
1542 buf->updateLabels();
1544 buf->errors("Parse");
1546 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1550 bool as_paragraphs = loader_format == "textparagraph";
1551 string filename2 = (loader_format == format) ? filename.absFilename()
1552 : support::changeExtension(filename.absFilename(),
1553 formats.extension(loader_format));
1554 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1555 theLyXFunc().setLyXView(lv);
1556 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1563 void GuiView::importDocument(string const & argument)
1566 string filename = split(argument, format, ' ');
1568 LYXERR(Debug::INFO, format << " file: " << filename);
1570 // need user interaction
1571 if (filename.empty()) {
1572 string initpath = lyxrc.document_path;
1574 Buffer const * buf = buffer();
1576 string const trypath = buf->filePath();
1577 // If directory is writeable, use this as default.
1578 if (FileName(trypath).isDirWritable())
1582 docstring const text = bformat(_("Select %1$s file to import"),
1583 formats.prettyName(format));
1585 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1586 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1587 dlg.setButton2(qt_("Examples|#E#e"),
1588 toqstr(addPath(package().system_support().absFilename(), "examples")));
1590 docstring filter = formats.prettyName(format);
1593 filter += from_utf8(formats.extension(format));
1596 FileDialog::Result result =
1597 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1599 if (result.first == FileDialog::Later)
1602 filename = fromqstr(result.second);
1604 // check selected filename
1605 if (filename.empty())
1606 message(_("Canceled."));
1609 if (filename.empty())
1612 // get absolute path of file
1613 FileName const fullname(support::makeAbsPath(filename));
1615 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1617 // Check if the document already is open
1618 Buffer * buf = theBufferList().getBuffer(lyxfile);
1621 if (!closeBuffer()) {
1622 message(_("Canceled."));
1627 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1629 // if the file exists already, and we didn't do
1630 // -i lyx thefile.lyx, warn
1631 if (lyxfile.exists() && fullname != lyxfile) {
1633 docstring text = bformat(_("The document %1$s already exists.\n\n"
1634 "Do you want to overwrite that document?"), displaypath);
1635 int const ret = Alert::prompt(_("Overwrite document?"),
1636 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1639 message(_("Canceled."));
1644 message(bformat(_("Importing %1$s..."), displaypath));
1645 ErrorList errorList;
1646 if (import(this, fullname, format, errorList))
1647 message(_("imported."));
1649 message(_("file not imported!"));
1651 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1655 void GuiView::newDocument(string const & filename, bool from_template)
1657 FileName initpath(lyxrc.document_path);
1658 Buffer * buf = buffer();
1660 FileName const trypath(buf->filePath());
1661 // If directory is writeable, use this as default.
1662 if (trypath.isDirWritable())
1666 string templatefile;
1667 if (from_template) {
1668 templatefile = selectTemplateFile().absFilename();
1669 if (templatefile.empty())
1674 if (filename.empty())
1675 b = newUnnamedFile(templatefile, initpath);
1677 b = newFile(filename, templatefile, true);
1682 // If no new document could be created, it is unsure
1683 // whether there is a valid BufferView.
1685 // Ensure the cursor is correctly positioned on screen.
1686 view()->showCursor();
1690 void GuiView::insertLyXFile(docstring const & fname)
1692 BufferView * bv = view();
1697 FileName filename(to_utf8(fname));
1699 if (!filename.empty()) {
1700 bv->insertLyXFile(filename);
1704 // Launch a file browser
1706 string initpath = lyxrc.document_path;
1707 string const trypath = bv->buffer().filePath();
1708 // If directory is writeable, use this as default.
1709 if (FileName(trypath).isDirWritable())
1713 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1714 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1715 dlg.setButton2(qt_("Examples|#E#e"),
1716 toqstr(addPath(package().system_support().absFilename(),
1719 FileDialog::Result result = dlg.open(toqstr(initpath),
1720 QStringList(qt_("LyX Documents (*.lyx)")));
1722 if (result.first == FileDialog::Later)
1726 filename.set(fromqstr(result.second));
1728 // check selected filename
1729 if (filename.empty()) {
1730 // emit message signal.
1731 message(_("Canceled."));
1735 bv->insertLyXFile(filename);
1739 void GuiView::insertPlaintextFile(docstring const & fname,
1742 BufferView * bv = view();
1747 FileName filename(to_utf8(fname));
1749 if (!filename.empty()) {
1750 bv->insertPlaintextFile(filename, asParagraph);
1754 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1755 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1757 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1758 QStringList(qt_("All Files (*)")));
1760 if (result.first == FileDialog::Later)
1764 filename.set(fromqstr(result.second));
1766 // check selected filename
1767 if (filename.empty()) {
1768 // emit message signal.
1769 message(_("Canceled."));
1773 bv->insertPlaintextFile(filename, asParagraph);
1777 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1779 FileName fname = b.fileName();
1780 FileName const oldname = fname;
1782 if (!newname.empty()) {
1784 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1786 // Switch to this Buffer.
1789 // No argument? Ask user through dialog.
1791 FileDialog dlg(qt_("Choose a filename to save document as"),
1792 LFUN_BUFFER_WRITE_AS);
1793 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1794 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1796 if (!isLyXFilename(fname.absFilename()))
1797 fname.changeExtension(".lyx");
1799 FileDialog::Result result =
1800 dlg.save(toqstr(fname.onlyPath().absFilename()),
1801 QStringList(qt_("LyX Documents (*.lyx)")),
1802 toqstr(fname.onlyFileName()));
1804 if (result.first == FileDialog::Later)
1807 fname.set(fromqstr(result.second));
1812 if (!isLyXFilename(fname.absFilename()))
1813 fname.changeExtension(".lyx");
1816 if (FileName(fname).exists()) {
1817 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1818 docstring text = bformat(_("The document %1$s already "
1819 "exists.\n\nDo you want to "
1820 "overwrite that document?"),
1822 int const ret = Alert::prompt(_("Overwrite document?"),
1823 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1826 case 1: return renameBuffer(b, docstring());
1827 case 2: return false;
1831 FileName oldauto = b.getAutosaveFilename();
1833 // Ok, change the name of the buffer
1834 b.setFileName(fname.absFilename());
1836 bool unnamed = b.isUnnamed();
1837 b.setUnnamed(false);
1838 b.saveCheckSum(fname);
1840 // bring the autosave file with us, just in case.
1841 b.moveAutosaveFile(oldauto);
1843 if (!saveBuffer(b)) {
1844 oldauto = b.getAutosaveFilename();
1845 b.setFileName(oldname.absFilename());
1846 b.setUnnamed(unnamed);
1847 b.saveCheckSum(oldname);
1848 b.moveAutosaveFile(oldauto);
1856 bool GuiView::saveBuffer(Buffer & b)
1858 if (workArea(b) && workArea(b)->inDialogMode())
1862 return renameBuffer(b, docstring());
1865 theSession().lastFiles().add(b.fileName());
1869 // Switch to this Buffer.
1872 // FIXME: we don't tell the user *WHY* the save failed !!
1873 docstring const file = makeDisplayPath(b.absFileName(), 30);
1874 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1875 "Do you want to rename the document and "
1876 "try again?"), file);
1877 int const ret = Alert::prompt(_("Rename and save?"),
1878 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1881 if (!renameBuffer(b, docstring()))
1890 return saveBuffer(b);
1894 bool GuiView::closeBuffer()
1896 Buffer * buf = buffer();
1897 return buf && closeBuffer(*buf);
1901 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1903 // goto bookmark to update bookmark pit.
1904 //FIXME: we should update only the bookmarks related to this buffer!
1905 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1906 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1907 theLyXFunc().gotoBookmark(i+1, false, false);
1909 if (buf.isClean() || buf.paragraphs().empty()) {
1910 // save in sessions if requested
1911 // do not save childs if their master
1912 // is opened as well
1914 theSession().lastOpened().add(buf.fileName());
1916 // Don't close child documents.
1917 removeWorkArea(currentMainWorkArea());
1919 theBufferList().release(&buf);
1922 // Switch to this Buffer.
1927 if (buf.isUnnamed())
1928 file = from_utf8(buf.fileName().onlyFileName());
1930 file = buf.fileName().displayName(30);
1932 // Bring this window to top before asking questions.
1936 docstring const text = bformat(_("The document %1$s has unsaved changes."
1937 "\n\nDo you want to save the document or discard the changes?"), file);
1938 int const ret = Alert::prompt(_("Save changed document?"),
1939 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1943 if (!saveBuffer(buf))
1947 // if we crash after this we could
1948 // have no autosave file but I guess
1949 // this is really improbable (Jug)
1950 buf.removeAutosaveFile();
1956 // save file names to .lyx/session
1958 theSession().lastOpened().add(buf.fileName());
1961 // Don't close child documents.
1962 removeWorkArea(currentMainWorkArea());
1964 theBufferList().release(&buf);
1970 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1972 Buffer * const curbuf = buffer();
1973 Buffer * nextbuf = curbuf;
1975 if (np == NEXTBUFFER)
1976 nextbuf = theBufferList().next(nextbuf);
1978 nextbuf = theBufferList().previous(nextbuf);
1979 if (nextbuf == curbuf)
1985 if (workArea(*nextbuf))
1992 bool GuiView::dispatch(FuncRequest const & cmd)
1994 BufferView * bv = view();
1995 // By default we won't need any update.
1997 bv->cursor().updateFlags(Update::None);
1998 bool dispatched = true;
2000 if (cmd.origin == FuncRequest::TOC) {
2001 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2002 toc->doDispatch(bv->cursor(), cmd);
2006 switch(cmd.action) {
2007 case LFUN_BUFFER_IMPORT:
2008 importDocument(to_utf8(cmd.argument()));
2011 case LFUN_BUFFER_SWITCH: {
2013 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2017 bv->cursor().message(_("Document not loaded"));
2021 case LFUN_BUFFER_NEXT:
2022 gotoNextOrPreviousBuffer(NEXTBUFFER);
2025 case LFUN_BUFFER_PREVIOUS:
2026 gotoNextOrPreviousBuffer(PREVBUFFER);
2029 case LFUN_COMMAND_EXECUTE: {
2030 bool const show_it = cmd.argument() != "off";
2031 // FIXME: this is a hack, "minibuffer" should not be
2033 if (GuiToolbar * t = toolbar("minibuffer")) {
2034 t->setVisible(show_it);
2035 if (show_it && t->commandBuffer())
2036 t->commandBuffer()->setFocus();
2040 case LFUN_DROP_LAYOUTS_CHOICE:
2042 d.layout_->showPopup();
2045 case LFUN_MENU_OPEN:
2046 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2047 menu->exec(QCursor::pos());
2050 case LFUN_FILE_INSERT:
2051 insertLyXFile(cmd.argument());
2053 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2054 insertPlaintextFile(cmd.argument(), true);
2057 case LFUN_FILE_INSERT_PLAINTEXT:
2058 insertPlaintextFile(cmd.argument(), false);
2061 case LFUN_BUFFER_WRITE:
2063 saveBuffer(bv->buffer());
2066 case LFUN_BUFFER_WRITE_AS:
2068 renameBuffer(bv->buffer(), cmd.argument());
2071 case LFUN_BUFFER_WRITE_ALL: {
2072 Buffer * first = theBufferList().first();
2075 message(_("Saving all documents..."));
2076 // We cannot use a for loop as the buffer list cycles.
2079 if (!b->isClean()) {
2081 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2083 b = theBufferList().next(b);
2084 } while (b != first);
2085 message(_("All documents saved."));
2089 case LFUN_TOOLBAR_TOGGLE: {
2090 string const name = cmd.getArg(0);
2091 if (GuiToolbar * t = toolbar(name))
2096 case LFUN_DIALOG_UPDATE: {
2097 string const name = to_utf8(cmd.argument());
2098 // Can only update a dialog connected to an existing inset
2099 Inset * inset = getOpenInset(name);
2101 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2102 inset->dispatch(view()->cursor(), fr);
2103 } else if (name == "paragraph") {
2104 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2105 } else if (name == "prefs" || name == "document") {
2106 updateDialog(name, string());
2111 case LFUN_DIALOG_TOGGLE: {
2112 if (isDialogVisible(cmd.getArg(0)))
2113 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2115 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2119 case LFUN_DIALOG_DISCONNECT_INSET:
2120 disconnectDialog(to_utf8(cmd.argument()));
2123 case LFUN_DIALOG_HIDE: {
2124 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2128 case LFUN_DIALOG_SHOW: {
2129 string const name = cmd.getArg(0);
2130 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2132 if (name == "character") {
2133 data = freefont2string();
2135 showDialog("character", data);
2136 } else if (name == "latexlog") {
2137 Buffer::LogType type;
2138 string const logfile = buffer()->logName(&type);
2140 case Buffer::latexlog:
2143 case Buffer::buildlog:
2147 data += Lexer::quoteString(logfile);
2148 showDialog("log", data);
2149 } else if (name == "vclog") {
2150 string const data = "vc " +
2151 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2152 showDialog("log", data);
2153 } else if (name == "symbols") {
2154 data = bv->cursor().getEncoding()->name();
2156 showDialog("symbols", data);
2158 } else if (name == "prefs" && isFullScreen()) {
2159 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2161 showDialog("prefs", data);
2163 showDialog(name, data);
2167 case LFUN_INSET_APPLY: {
2168 string const name = cmd.getArg(0);
2169 Inset * inset = getOpenInset(name);
2171 // put cursor in front of inset.
2172 if (!view()->setCursorFromInset(inset)) {
2173 LASSERT(false, break);
2176 // useful if we are called from a dialog.
2177 view()->cursor().beginUndoGroup();
2178 view()->cursor().recordUndo();
2179 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2180 inset->dispatch(view()->cursor(), fr);
2181 view()->cursor().endUndoGroup();
2183 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2189 case LFUN_UI_TOGGLE:
2191 // Make sure the keyboard focus stays in the work area.
2195 case LFUN_SPLIT_VIEW:
2196 if (Buffer * buf = buffer()) {
2197 string const orientation = cmd.getArg(0);
2198 d.splitter_->setOrientation(orientation == "vertical"
2199 ? Qt::Vertical : Qt::Horizontal);
2200 TabWorkArea * twa = addTabWorkArea();
2201 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2202 setCurrentWorkArea(wa);
2206 case LFUN_CLOSE_TAB_GROUP:
2207 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2209 twa = d.currentTabWorkArea();
2210 // Switch to the next GuiWorkArea in the found TabWorkArea.
2212 // Make sure the work area is up to date.
2213 setCurrentWorkArea(twa->currentWorkArea());
2215 setCurrentWorkArea(0);
2220 case LFUN_COMPLETION_INLINE:
2221 if (d.current_work_area_)
2222 d.current_work_area_->completer().showInline();
2225 case LFUN_COMPLETION_POPUP:
2226 if (d.current_work_area_)
2227 d.current_work_area_->completer().showPopup();
2231 case LFUN_COMPLETION_COMPLETE:
2232 if (d.current_work_area_)
2233 d.current_work_area_->completer().tab();
2236 case LFUN_COMPLETION_CANCEL:
2237 if (d.current_work_area_) {
2238 if (d.current_work_area_->completer().popupVisible())
2239 d.current_work_area_->completer().hidePopup();
2241 d.current_work_area_->completer().hideInline();
2245 case LFUN_COMPLETION_ACCEPT:
2246 if (d.current_work_area_)
2247 d.current_work_area_->completer().activate();
2250 case LFUN_BUFFER_ZOOM_IN:
2251 case LFUN_BUFFER_ZOOM_OUT:
2252 if (cmd.argument().empty()) {
2253 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2258 lyxrc.zoom += convert<int>(cmd.argument());
2260 if (lyxrc.zoom < 10)
2263 // The global QPixmapCache is used in GuiPainter to cache text
2264 // painting so we must reset it.
2265 QPixmapCache::clear();
2266 guiApp->fontLoader().update();
2267 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2275 // Part of automatic menu appearance feature.
2276 if (isFullScreen()) {
2277 if (menuBar()->isVisible())
2279 if (statusBar()->isVisible())
2280 statusBar()->hide();
2287 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2289 string const arg = cmd.getArg(0);
2290 if (arg == "scrollbar") {
2291 // hide() is of no help
2292 if (d.current_work_area_->verticalScrollBarPolicy() ==
2293 Qt::ScrollBarAlwaysOff)
2295 d.current_work_area_->setVerticalScrollBarPolicy(
2296 Qt::ScrollBarAsNeeded);
2298 d.current_work_area_->setVerticalScrollBarPolicy(
2299 Qt::ScrollBarAlwaysOff);
2302 if (arg == "statusbar") {
2303 statusBar()->setVisible(!statusBar()->isVisible());
2306 if (arg == "menubar") {
2307 menuBar()->setVisible(!menuBar()->isVisible());
2310 #if QT_VERSION >= 0x040300
2311 if (arg == "frame") {
2313 getContentsMargins(&l, &t, &r, &b);
2314 //are the frames in default state?
2315 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2317 setContentsMargins(-2, -2, -2, -2);
2319 setContentsMargins(0, 0, 0, 0);
2324 if (arg == "fullscreen") {
2329 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2333 void GuiView::toggleFullScreen()
2335 if (isFullScreen()) {
2336 for (int i = 0; i != d.splitter_->count(); ++i)
2337 d.tabWorkArea(i)->setFullScreen(false);
2338 #if QT_VERSION >= 0x040300
2339 setContentsMargins(0, 0, 0, 0);
2341 setWindowState(windowState() ^ Qt::WindowFullScreen);
2344 statusBar()->show();
2347 hideDialogs("prefs", 0);
2348 for (int i = 0; i != d.splitter_->count(); ++i)
2349 d.tabWorkArea(i)->setFullScreen(true);
2350 #if QT_VERSION >= 0x040300
2351 setContentsMargins(-2, -2, -2, -2);
2354 setWindowState(windowState() ^ Qt::WindowFullScreen);
2355 statusBar()->hide();
2357 if (lyxrc.full_screen_toolbars) {
2358 ToolbarMap::iterator end = d.toolbars_.end();
2359 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2364 // give dialogs like the TOC a chance to adapt
2369 Buffer const * GuiView::updateInset(Inset const * inset)
2371 if (!d.current_work_area_)
2375 d.current_work_area_->scheduleRedraw();
2377 return &d.current_work_area_->bufferView().buffer();
2381 void GuiView::restartCursor()
2383 /* When we move around, or type, it's nice to be able to see
2384 * the cursor immediately after the keypress.
2386 if (d.current_work_area_)
2387 d.current_work_area_->startBlinkingCursor();
2389 // Take this occasion to update the other GUI elements.
2395 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2397 if (d.current_work_area_)
2398 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2403 // This list should be kept in sync with the list of insets in
2404 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2405 // dialog should have the same name as the inset.
2406 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2407 // docs in LyXAction.cpp.
2409 char const * const dialognames[] = {
2410 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2411 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2412 "float", "graphics", "include", "index", "index_print", "info", "nomenclature",
2413 "label", "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2414 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2415 "symbols", "tabular", "tabularcreate",
2417 #ifdef HAVE_LIBAIKSAURUS
2421 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2423 char const * const * const end_dialognames =
2424 dialognames + (sizeof(dialognames) / sizeof(char *));
2428 cmpCStr(char const * name) : name_(name) {}
2429 bool operator()(char const * other) {
2430 return strcmp(other, name_) == 0;
2437 bool isValidName(string const & name)
2439 return find_if(dialognames, end_dialognames,
2440 cmpCStr(name.c_str())) != end_dialognames;
2446 void GuiView::resetDialogs()
2448 // Make sure that no LFUN uses any LyXView.
2449 theLyXFunc().setLyXView(0);
2452 constructToolbars();
2453 guiApp->menus().fillMenuBar(menuBar(), this, false);
2455 d.layout_->updateContents(true);
2456 // Now update controls with current buffer.
2457 theLyXFunc().setLyXView(this);
2463 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2465 if (!isValidName(name))
2468 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2470 if (it != d.dialogs_.end()) {
2472 it->second->hideView();
2473 return it->second.get();
2476 Dialog * dialog = build(name);
2477 d.dialogs_[name].reset(dialog);
2478 if (lyxrc.allow_geometry_session)
2479 dialog->restoreSession();
2486 void GuiView::showDialog(string const & name, string const & data,
2494 Dialog * dialog = findOrBuild(name, false);
2496 dialog->showData(data);
2498 d.open_insets_[name] = inset;
2501 catch (ExceptionMessage const & ex) {
2509 bool GuiView::isDialogVisible(string const & name) const
2511 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2512 if (it == d.dialogs_.end())
2514 return it->second.get()->isVisibleView();
2518 void GuiView::hideDialog(string const & name, Inset * inset)
2520 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2521 if (it == d.dialogs_.end())
2524 if (inset && inset != getOpenInset(name))
2527 Dialog * const dialog = it->second.get();
2528 if (dialog->isVisibleView())
2530 d.open_insets_[name] = 0;
2534 void GuiView::disconnectDialog(string const & name)
2536 if (!isValidName(name))
2539 if (d.open_insets_.find(name) != d.open_insets_.end())
2540 d.open_insets_[name] = 0;
2544 Inset * GuiView::getOpenInset(string const & name) const
2546 if (!isValidName(name))
2549 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2550 return it == d.open_insets_.end() ? 0 : it->second;
2554 void GuiView::hideAll() const
2556 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2557 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2559 for(; it != end; ++it)
2560 it->second->hideView();
2564 void GuiView::updateDialogs()
2566 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2567 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2569 for(; it != end; ++it) {
2570 Dialog * dialog = it->second.get();
2571 if (dialog && dialog->isVisibleView())
2572 dialog->checkStatus();
2579 // will be replaced by a proper factory...
2580 Dialog * createGuiAbout(GuiView & lv);
2581 Dialog * createGuiBibitem(GuiView & lv);
2582 Dialog * createGuiBibtex(GuiView & lv);
2583 Dialog * createGuiBox(GuiView & lv);
2584 Dialog * createGuiBranch(GuiView & lv);
2585 Dialog * createGuiChanges(GuiView & lv);
2586 Dialog * createGuiCharacter(GuiView & lv);
2587 Dialog * createGuiCitation(GuiView & lv);
2588 Dialog * createGuiDelimiter(GuiView & lv);
2589 Dialog * createGuiDocument(GuiView & lv);
2590 Dialog * createGuiErrorList(GuiView & lv);
2591 Dialog * createGuiERT(GuiView & lv);
2592 Dialog * createGuiExternal(GuiView & lv);
2593 Dialog * createGuiFloat(GuiView & lv);
2594 Dialog * createGuiGraphics(GuiView & lv);
2595 Dialog * createGuiInclude(GuiView & lv);
2596 Dialog * createGuiIndex(GuiView & lv);
2597 Dialog * createGuiInfo(GuiView & lv);
2598 Dialog * createGuiLabel(GuiView & lv);
2599 Dialog * createGuiListings(GuiView & lv);
2600 Dialog * createGuiLog(GuiView & lv);
2601 Dialog * createGuiMathHSpace(GuiView & lv);
2602 Dialog * createGuiMathMatrix(GuiView & lv);
2603 Dialog * createGuiNomenclature(GuiView & lv);
2604 Dialog * createGuiNote(GuiView & lv);
2605 Dialog * createGuiParagraph(GuiView & lv);
2606 Dialog * createGuiPhantom(GuiView & lv);
2607 Dialog * createGuiPreferences(GuiView & lv);
2608 Dialog * createGuiPrint(GuiView & lv);
2609 Dialog * createGuiPrintindex(GuiView & lv);
2610 Dialog * createGuiRef(GuiView & lv);
2611 Dialog * createGuiSearch(GuiView & lv);
2612 Dialog * createGuiSearchAdv(GuiView & lv);
2613 Dialog * createGuiSendTo(GuiView & lv);
2614 Dialog * createGuiShowFile(GuiView & lv);
2615 Dialog * createGuiSpellchecker(GuiView & lv);
2616 Dialog * createGuiSymbols(GuiView & lv);
2617 Dialog * createGuiTabularCreate(GuiView & lv);
2618 Dialog * createGuiTabular(GuiView & lv);
2619 Dialog * createGuiTexInfo(GuiView & lv);
2620 Dialog * createGuiTextHSpace(GuiView & lv);
2621 Dialog * createGuiToc(GuiView & lv);
2622 Dialog * createGuiThesaurus(GuiView & lv);
2623 Dialog * createGuiHyperlink(GuiView & lv);
2624 Dialog * createGuiVSpace(GuiView & lv);
2625 Dialog * createGuiViewSource(GuiView & lv);
2626 Dialog * createGuiWrap(GuiView & lv);
2629 Dialog * GuiView::build(string const & name)
2631 LASSERT(isValidName(name), return 0);
2633 if (name == "aboutlyx")
2634 return createGuiAbout(*this);
2635 if (name == "bibitem")
2636 return createGuiBibitem(*this);
2637 if (name == "bibtex")
2638 return createGuiBibtex(*this);
2640 return createGuiBox(*this);
2641 if (name == "branch")
2642 return createGuiBranch(*this);
2643 if (name == "changes")
2644 return createGuiChanges(*this);
2645 if (name == "character")
2646 return createGuiCharacter(*this);
2647 if (name == "citation")
2648 return createGuiCitation(*this);
2649 if (name == "document")
2650 return createGuiDocument(*this);
2651 if (name == "errorlist")
2652 return createGuiErrorList(*this);
2654 return createGuiERT(*this);
2655 if (name == "external")
2656 return createGuiExternal(*this);
2658 return createGuiShowFile(*this);
2659 if (name == "findreplace")
2660 return createGuiSearch(*this);
2661 if (name == "findreplaceadv")
2662 return createGuiSearchAdv(*this);
2663 if (name == "float")
2664 return createGuiFloat(*this);
2665 if (name == "graphics")
2666 return createGuiGraphics(*this);
2667 if (name == "include")
2668 return createGuiInclude(*this);
2669 if (name == "index")
2670 return createGuiIndex(*this);
2672 return createGuiInfo(*this);
2673 if (name == "nomenclature")
2674 return createGuiNomenclature(*this);
2675 if (name == "label")
2676 return createGuiLabel(*this);
2678 return createGuiLog(*this);
2679 if (name == "mathdelimiter")
2680 return createGuiDelimiter(*this);
2681 if (name == "mathspace")
2682 return createGuiMathHSpace(*this);
2683 if (name == "mathmatrix")
2684 return createGuiMathMatrix(*this);
2686 return createGuiNote(*this);
2687 if (name == "paragraph")
2688 return createGuiParagraph(*this);
2689 if (name == "phantom")
2690 return createGuiPhantom(*this);
2691 if (name == "prefs")
2692 return createGuiPreferences(*this);
2693 if (name == "print")
2694 return createGuiPrint(*this);
2696 return createGuiRef(*this);
2697 if (name == "sendto")
2698 return createGuiSendTo(*this);
2699 if (name == "space")
2700 return createGuiTextHSpace(*this);
2701 if (name == "spellchecker")
2702 return createGuiSpellchecker(*this);
2703 if (name == "symbols")
2704 return createGuiSymbols(*this);
2705 if (name == "tabular")
2706 return createGuiTabular(*this);
2707 if (name == "tabularcreate")
2708 return createGuiTabularCreate(*this);
2709 if (name == "texinfo")
2710 return createGuiTexInfo(*this);
2711 if (name == "view-source")
2712 return createGuiViewSource(*this);
2713 #ifdef HAVE_LIBAIKSAURUS
2714 if (name == "thesaurus")
2715 return createGuiThesaurus(*this);
2718 return createGuiHyperlink(*this);
2719 if (name == "index_print")
2720 return createGuiPrintindex(*this);
2721 if (name == "listings")
2722 return createGuiListings(*this);
2724 return createGuiToc(*this);
2725 if (name == "vspace")
2726 return createGuiVSpace(*this);
2728 return createGuiWrap(*this);
2734 } // namespace frontend
2737 #include "moc_GuiView.cpp"