3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiCommandBuffer.h"
22 #include "GuiCompleter.h"
23 #include "GuiWorkArea.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToolbar.h"
29 #include "qt_helpers.h"
31 #include "frontends/alert.h"
33 #include "buffer_funcs.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
41 #include "ErrorList.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
58 #include "support/lassert.h"
59 #include "support/debug.h"
60 #include "support/ExceptionMessage.h"
61 #include "support/FileName.h"
62 #include "support/filetools.h"
63 #include "support/gettext.h"
64 #include "support/ForkedCalls.h"
65 #include "support/lstrings.h"
66 #include "support/os.h"
67 #include "support/Package.h"
68 #include "support/Timeout.h"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
116 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
117 /// The text to be written on top of the pixmap
118 QString const text = lyx_version ?
119 qt_("version ") + lyx_version : qt_("unknown version");
120 splash_ = QPixmap(":/images/banner.png");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
125 // The font used to display the version info
126 font.setStyleHint(QFont::SansSerif);
127 font.setWeight(QFont::Bold);
128 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
130 pain.drawText(260, 15, text);
133 void paintEvent(QPaintEvent *)
135 int x = (width() - splash_.width()) / 2;
136 int y = (height() - splash_.height()) / 2;
138 pain.drawPixmap(x, y, splash_);
146 /// Toolbar store providing access to individual toolbars by name.
147 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
149 typedef boost::shared_ptr<Dialog> DialogPtr;
154 struct GuiView::GuiViewPrivate
157 : current_work_area_(0), current_main_work_area_(0),
158 layout_(0), autosave_timeout_(5000),
161 // hardcode here the platform specific icon size
162 smallIconSize = 14; // scaling problems
163 normalIconSize = 20; // ok, default
164 bigIconSize = 26; // better for some math icons
166 splitter_ = new QSplitter;
167 bg_widget_ = new BackgroundWidget;
168 stack_widget_ = new QStackedWidget;
169 stack_widget_->addWidget(bg_widget_);
170 stack_widget_->addWidget(splitter_);
178 delete stack_widget_;
181 QMenu * toolBarPopup(GuiView * parent)
183 // FIXME: translation
184 QMenu * menu = new QMenu(parent);
185 QActionGroup * iconSizeGroup = new QActionGroup(parent);
187 QAction * smallIcons = new QAction(iconSizeGroup);
188 smallIcons->setText(qt_("Small-sized icons"));
189 smallIcons->setCheckable(true);
190 QObject::connect(smallIcons, SIGNAL(triggered()),
191 parent, SLOT(smallSizedIcons()));
192 menu->addAction(smallIcons);
194 QAction * normalIcons = new QAction(iconSizeGroup);
195 normalIcons->setText(qt_("Normal-sized icons"));
196 normalIcons->setCheckable(true);
197 QObject::connect(normalIcons, SIGNAL(triggered()),
198 parent, SLOT(normalSizedIcons()));
199 menu->addAction(normalIcons);
201 QAction * bigIcons = new QAction(iconSizeGroup);
202 bigIcons->setText(qt_("Big-sized icons"));
203 bigIcons->setCheckable(true);
204 QObject::connect(bigIcons, SIGNAL(triggered()),
205 parent, SLOT(bigSizedIcons()));
206 menu->addAction(bigIcons);
208 unsigned int cur = parent->iconSize().width();
209 if ( cur == parent->d.smallIconSize)
210 smallIcons->setChecked(true);
211 else if (cur == parent->d.normalIconSize)
212 normalIcons->setChecked(true);
213 else if (cur == parent->d.bigIconSize)
214 bigIcons->setChecked(true);
221 stack_widget_->setCurrentWidget(bg_widget_);
222 bg_widget_->setUpdatesEnabled(true);
225 TabWorkArea * tabWorkArea(int i)
227 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
230 TabWorkArea * currentTabWorkArea()
232 if (splitter_->count() == 1)
233 // The first TabWorkArea is always the first one, if any.
234 return tabWorkArea(0);
236 for (int i = 0; i != splitter_->count(); ++i) {
237 TabWorkArea * twa = tabWorkArea(i);
238 if (current_main_work_area_ == twa->currentWorkArea())
242 // None has the focus so we just take the first one.
243 return tabWorkArea(0);
247 GuiWorkArea * current_work_area_;
248 GuiWorkArea * current_main_work_area_;
249 QSplitter * splitter_;
250 QStackedWidget * stack_widget_;
251 BackgroundWidget * bg_widget_;
253 ToolbarMap toolbars_;
254 /// The main layout box.
256 * \warning Don't Delete! The layout box is actually owned by
257 * whichever toolbar contains it. All the GuiView class needs is a
258 * means of accessing it.
260 * FIXME: replace that with a proper model so that we are not limited
261 * to only one dialog.
263 GuiLayoutBox * layout_;
266 map<string, Inset *> open_insets_;
269 map<string, DialogPtr> dialogs_;
271 unsigned int smallIconSize;
272 unsigned int normalIconSize;
273 unsigned int bigIconSize;
275 QTimer statusbar_timer_;
276 /// auto-saving of buffers
277 Timeout autosave_timeout_;
278 /// flag against a race condition due to multiclicks, see bug #1119
282 TocModels toc_models_;
286 GuiView::GuiView(int id)
287 : d(*new GuiViewPrivate), id_(id), closing_(false)
289 // GuiToolbars *must* be initialised before the menu bar.
290 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
293 // set ourself as the current view. This is needed for the menu bar
294 // filling, at least for the static special menu item on Mac. Otherwise
295 // they are greyed out.
296 theLyXFunc().setLyXView(this);
298 // Fill up the menu bar.
299 guiApp->menus().fillMenuBar(menuBar(), this, true);
301 setCentralWidget(d.stack_widget_);
303 // Start autosave timer
304 if (lyxrc.autosave) {
305 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
306 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
307 d.autosave_timeout_.start();
309 connect(&d.statusbar_timer_, SIGNAL(timeout()),
310 this, SLOT(clearMessage()));
312 // We don't want to keep the window in memory if it is closed.
313 setAttribute(Qt::WA_DeleteOnClose, true);
315 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
316 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
317 // since the icon is provided in the application bundle.
318 setWindowIcon(QPixmap(":/images/lyx.png"));
322 setAcceptDrops(true);
324 statusBar()->setSizeGripEnabled(true);
326 // Forbid too small unresizable window because it can happen
327 // with some window manager under X11.
328 setMinimumSize(300, 200);
330 if (lyxrc.allow_geometry_session) {
331 // Now take care of session management.
336 // no session handling, default to a sane size.
337 setGeometry(50, 50, 690, 510);
340 // clear session data if any.
342 settings.remove("views");
352 void GuiView::saveLayout() const
355 settings.beginGroup("views");
356 settings.beginGroup(QString::number(id_));
358 settings.setValue("pos", pos());
359 settings.setValue("size", size());
361 settings.setValue("geometry", saveGeometry());
363 settings.setValue("layout", saveState(0));
364 settings.setValue("icon_size", iconSize());
368 bool GuiView::restoreLayout()
371 settings.beginGroup("views");
372 settings.beginGroup(QString::number(id_));
373 QString const icon_key = "icon_size";
374 if (!settings.contains(icon_key))
377 setIconSize(settings.value(icon_key).toSize());
379 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
380 QSize size = settings.value("size", QSize(690, 510)).toSize();
384 if (!restoreGeometry(settings.value("geometry").toByteArray()))
385 setGeometry(50, 50, 690, 510);
387 // Make sure layout is correctly oriented.
388 setLayoutDirection(qApp->layoutDirection());
390 // Allow the toc and view-source dock widget to be restored if needed.
391 findOrBuild("toc", true);
392 findOrBuild("view-source", true);
394 if (!restoreState(settings.value("layout").toByteArray(), 0))
401 GuiToolbar * GuiView::toolbar(string const & name)
403 ToolbarMap::iterator it = d.toolbars_.find(name);
404 if (it != d.toolbars_.end())
407 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
408 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
413 void GuiView::constructToolbars()
415 ToolbarMap::iterator it = d.toolbars_.begin();
416 for (; it != d.toolbars_.end(); ++it)
421 // extracts the toolbars from the backend
422 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
423 Toolbars::Infos::iterator end = guiApp->toolbars().end();
424 for (; cit != end; ++cit)
425 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
429 void GuiView::initToolbars()
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 GuiToolbar * tb = toolbar(cit->name);
438 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
440 tb->setVisible(false);
441 tb->setVisibility(visibility);
443 if (visibility & Toolbars::TOP) {
445 addToolBarBreak(Qt::TopToolBarArea);
446 addToolBar(Qt::TopToolBarArea, tb);
449 if (visibility & Toolbars::BOTTOM) {
450 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
451 #if (QT_VERSION >= 0x040202)
452 addToolBarBreak(Qt::BottomToolBarArea);
454 addToolBar(Qt::BottomToolBarArea, tb);
457 if (visibility & Toolbars::LEFT) {
458 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
459 #if (QT_VERSION >= 0x040202)
460 addToolBarBreak(Qt::LeftToolBarArea);
462 addToolBar(Qt::LeftToolBarArea, tb);
465 if (visibility & Toolbars::RIGHT) {
466 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
467 #if (QT_VERSION >= 0x040202)
468 addToolBarBreak(Qt::RightToolBarArea);
470 addToolBar(Qt::RightToolBarArea, tb);
473 if (visibility & Toolbars::ON)
474 tb->setVisible(true);
479 TocModels & GuiView::tocModels()
481 return d.toc_models_;
485 void GuiView::setFocus()
487 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
488 // Make sure LyXFunc points to the correct view.
489 guiApp->setCurrentView(this);
490 theLyXFunc().setLyXView(this);
491 QMainWindow::setFocus();
492 if (d.current_work_area_)
493 d.current_work_area_->setFocus();
497 QMenu * GuiView::createPopupMenu()
499 return d.toolBarPopup(this);
503 void GuiView::showEvent(QShowEvent * e)
505 LYXERR(Debug::GUI, "Passed Geometry "
506 << size().height() << "x" << size().width()
507 << "+" << pos().x() << "+" << pos().y());
509 if (d.splitter_->count() == 0)
510 // No work area, switch to the background widget.
513 QMainWindow::showEvent(e);
517 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
518 ** is responsibility of the container (e.g., dialog)
520 void GuiView::closeEvent(QCloseEvent * close_event)
522 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
525 // it can happen that this event arrives without selecting the view,
526 // e.g. when clicking the close button on a background window.
528 setCurrentWorkArea(currentMainWorkArea());
529 while (GuiWorkArea * wa = currentMainWorkArea()) {
530 Buffer * b = &wa->bufferView().buffer();
532 // This is a child document, just close the tab after saving
533 // but keep the file loaded.
534 if (!saveBuffer(*b)) {
536 close_event->ignore();
543 QList<int> const ids = guiApp->viewIds();
544 for (int i = 0; i != ids.size(); ++i) {
547 if (guiApp->view(ids[i]).workArea(*b)) {
548 // FIXME 1: should we put an alert box here that the buffer
549 // is viewed elsewhere?
550 // FIXME 2: should we try to save this buffer in any case?
553 // This buffer is also opened in another view, so
554 // close the associated work area...
556 // ... but don't close the buffer.
561 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
562 if (b && !closeBuffer(*b, true)) {
564 close_event->ignore();
569 // Make sure that nothing will use this close to be closed View.
570 guiApp->unregisterView(this);
572 if (isFullScreen()) {
573 // Switch off fullscreen before closing.
578 // Make sure the timer time out will not trigger a statusbar update.
579 d.statusbar_timer_.stop();
581 // Saving fullscreen requires additional tweaks in the toolbar code.
582 // It wouldn't also work under linux natively.
583 if (lyxrc.allow_geometry_session) {
584 // Save this window geometry and layout.
586 // Then the toolbar private states.
587 ToolbarMap::iterator end = d.toolbars_.end();
588 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
589 it->second->saveSession();
590 // Now take care of all other dialogs:
591 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
592 for (; it!= d.dialogs_.end(); ++it)
593 it->second->saveSession();
596 close_event->accept();
600 void GuiView::dragEnterEvent(QDragEnterEvent * event)
602 if (event->mimeData()->hasUrls())
604 /// \todo Ask lyx-devel is this is enough:
605 /// if (event->mimeData()->hasFormat("text/plain"))
606 /// event->acceptProposedAction();
610 void GuiView::dropEvent(QDropEvent * event)
612 QList<QUrl> files = event->mimeData()->urls();
616 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
617 for (int i = 0; i != files.size(); ++i) {
618 string const file = os::internal_path(fromqstr(
619 files.at(i).toLocalFile()));
621 // Asynchronously post the event. DropEvent usually come
622 // from the BufferView. But reloading a file might close
623 // the BufferView from within its own event handler.
624 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
631 void GuiView::message(docstring const & str)
633 if (ForkedProcess::iAmAChild())
636 statusBar()->showMessage(toqstr(str));
637 d.statusbar_timer_.stop();
638 d.statusbar_timer_.start(3000);
642 void GuiView::smallSizedIcons()
644 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
648 void GuiView::normalSizedIcons()
650 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
654 void GuiView::bigSizedIcons()
656 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
660 void GuiView::clearMessage()
664 theLyXFunc().setLyXView(this);
665 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
666 d.statusbar_timer_.stop();
670 void GuiView::updateWindowTitle(GuiWorkArea * wa)
672 if (wa != d.current_work_area_)
674 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
675 setWindowIconText(wa->windowIconText());
679 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
682 disconnectBufferView();
683 connectBufferView(wa->bufferView());
684 connectBuffer(wa->bufferView().buffer());
685 d.current_work_area_ = wa;
686 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
687 this, SLOT(updateWindowTitle(GuiWorkArea *)));
688 updateWindowTitle(wa);
692 // The document settings needs to be reinitialised.
693 updateDialog("document", "");
695 // Buffer-dependent dialogs must be updated. This is done here because
696 // some dialogs require buffer()->text.
701 void GuiView::on_lastWorkAreaRemoved()
704 // We already are in a close event. Nothing more to do.
707 if (d.splitter_->count() > 1)
708 // We have a splitter so don't close anything.
711 // Reset and updates the dialogs.
712 d.toc_models_.reset(0);
713 updateDialog("document", "");
716 resetWindowTitleAndIconText();
718 if (lyxrc.open_buffers_in_tabs)
719 // Nothing more to do, the window should stay open.
722 if (guiApp->viewIds().size() > 1) {
728 // On Mac we also close the last window because the application stay
729 // resident in memory. On other platforms we don't close the last
730 // window because this would quit the application.
736 void GuiView::updateStatusBar()
738 // let the user see the explicit message
739 if (d.statusbar_timer_.isActive())
742 theLyXFunc().setLyXView(this);
743 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
747 bool GuiView::hasFocus() const
749 return qApp->activeWindow() == this;
753 bool GuiView::event(QEvent * e)
757 // Useful debug code:
758 //case QEvent::ActivationChange:
759 //case QEvent::WindowDeactivate:
760 //case QEvent::Paint:
761 //case QEvent::Enter:
762 //case QEvent::Leave:
763 //case QEvent::HoverEnter:
764 //case QEvent::HoverLeave:
765 //case QEvent::HoverMove:
766 //case QEvent::StatusTip:
767 //case QEvent::DragEnter:
768 //case QEvent::DragLeave:
772 case QEvent::WindowActivate: {
773 if (this == guiApp->currentView()) {
775 return QMainWindow::event(e);
777 guiApp->setCurrentView(this);
778 theLyXFunc().setLyXView(this);
779 if (d.current_work_area_) {
780 BufferView & bv = d.current_work_area_->bufferView();
781 connectBufferView(bv);
782 connectBuffer(bv.buffer());
783 // The document structure, name and dialogs might have
784 // changed in another view.
786 // The document settings needs to be reinitialised.
787 updateDialog("document", "");
790 resetWindowTitleAndIconText();
793 return QMainWindow::event(e);
796 case QEvent::ShortcutOverride: {
800 if (isFullScreen() && menuBar()->isHidden()) {
801 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
802 // FIXME: we should also try to detect special LyX shortcut such as
803 // Alt-P and Alt-M. Right now there is a hack in
804 // GuiWorkArea::processKeySym() that hides again the menubar for
806 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
808 return QMainWindow::event(e);
813 if (d.current_work_area_)
814 // Nothing special to do.
815 return QMainWindow::event(e);
817 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
818 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
820 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
821 || ke->key() == Qt::Key_Backtab)
822 return QMainWindow::event(e);
824 // Allow processing of shortcuts that are allowed even when no Buffer
826 theLyXFunc().setLyXView(this);
828 setKeySymbol(&sym, ke);
829 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
835 return QMainWindow::event(e);
839 void GuiView::resetWindowTitleAndIconText()
841 setWindowTitle(qt_("LyX"));
842 setWindowIconText(qt_("LyX"));
845 bool GuiView::focusNextPrevChild(bool /*next*/)
852 void GuiView::setBusy(bool busy)
854 if (d.current_work_area_) {
855 d.current_work_area_->setUpdatesEnabled(!busy);
857 d.current_work_area_->stopBlinkingCursor();
859 d.current_work_area_->startBlinkingCursor();
863 QApplication::setOverrideCursor(Qt::WaitCursor);
865 QApplication::restoreOverrideCursor();
869 GuiWorkArea * GuiView::workArea(Buffer & buffer)
871 if (currentWorkArea()
872 && ¤tWorkArea()->bufferView().buffer() == &buffer)
873 return (GuiWorkArea *) currentWorkArea();
874 if (TabWorkArea * twa = d.currentTabWorkArea())
875 return twa->workArea(buffer);
880 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
882 // Automatically create a TabWorkArea if there are none yet.
883 TabWorkArea * tab_widget = d.splitter_->count()
884 ? d.currentTabWorkArea() : addTabWorkArea();
885 return tab_widget->addWorkArea(buffer, *this);
889 TabWorkArea * GuiView::addTabWorkArea()
891 TabWorkArea * twa = new TabWorkArea;
892 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
893 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
894 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
895 this, SLOT(on_lastWorkAreaRemoved()));
897 d.splitter_->addWidget(twa);
898 d.stack_widget_->setCurrentWidget(d.splitter_);
903 GuiWorkArea const * GuiView::currentWorkArea() const
905 return d.current_work_area_;
909 GuiWorkArea * GuiView::currentWorkArea()
911 return d.current_work_area_;
915 GuiWorkArea const * GuiView::currentMainWorkArea() const
917 if (d.currentTabWorkArea() == NULL)
919 return d.currentTabWorkArea()->currentWorkArea();
923 GuiWorkArea * GuiView::currentMainWorkArea()
925 if (d.currentTabWorkArea() == NULL)
927 return d.currentTabWorkArea()->currentWorkArea();
931 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
933 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
935 d.current_work_area_ = NULL;
939 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
943 theGuiApp()->setCurrentView(this);
944 d.current_work_area_ = wa;
945 for (int i = 0; i != d.splitter_->count(); ++i) {
946 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
947 //if (d.current_main_work_area_)
948 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
949 d.current_main_work_area_ = wa;
950 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
951 //d.current_main_work_area_->setLineWidth(2);
952 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
956 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
957 on_currentWorkAreaChanged(wa);
958 BufferView & bv = wa->bufferView();
959 bv.cursor().fixIfBroken();
961 wa->setUpdatesEnabled(true);
962 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
966 void GuiView::removeWorkArea(GuiWorkArea * wa)
969 if (wa == d.current_work_area_) {
971 disconnectBufferView();
972 d.current_work_area_ = 0;
973 d.current_main_work_area_ = 0;
976 bool found_twa = false;
977 for (int i = 0; i != d.splitter_->count(); ++i) {
978 TabWorkArea * twa = d.tabWorkArea(i);
979 if (twa->removeWorkArea(wa)) {
980 // Found in this tab group, and deleted the GuiWorkArea.
982 if (twa->count() != 0) {
983 if (d.current_work_area_ == 0)
984 // This means that we are closing the current GuiWorkArea, so
985 // switch to the next GuiWorkArea in the found TabWorkArea.
986 setCurrentWorkArea(twa->currentWorkArea());
988 // No more WorkAreas in this tab group, so delete it.
995 // It is not a tabbed work area (i.e., the search work area), so it
996 // should be deleted by other means.
997 LASSERT(found_twa, /* */);
999 if (d.current_work_area_ == 0) {
1000 if (d.splitter_->count() != 0) {
1001 TabWorkArea * twa = d.currentTabWorkArea();
1002 setCurrentWorkArea(twa->currentWorkArea());
1004 // No more work areas, switch to the background widget.
1005 setCurrentWorkArea(0);
1011 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1017 void GuiView::updateLayoutList()
1020 d.layout_->updateContents(false);
1024 void GuiView::updateToolbars()
1026 ToolbarMap::iterator end = d.toolbars_.end();
1027 if (d.current_work_area_) {
1029 d.current_work_area_->bufferView().cursor().inMathed();
1031 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1033 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1034 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1035 bool const mathmacrotemplate =
1036 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1038 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1039 it->second->update(math, table, review, mathmacrotemplate);
1041 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1042 it->second->update(false, false, false, false);
1046 Buffer * GuiView::buffer()
1048 if (d.current_work_area_)
1049 return &d.current_work_area_->bufferView().buffer();
1054 Buffer const * GuiView::buffer() const
1056 if (d.current_work_area_)
1057 return &d.current_work_area_->bufferView().buffer();
1062 void GuiView::setBuffer(Buffer * newBuffer)
1064 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1065 LASSERT(newBuffer, return);
1068 GuiWorkArea * wa = workArea(*newBuffer);
1070 newBuffer->masterBuffer()->updateLabels();
1071 wa = addWorkArea(*newBuffer);
1073 //Disconnect the old buffer...there's no new one.
1076 connectBuffer(*newBuffer);
1077 connectBufferView(wa->bufferView());
1078 setCurrentWorkArea(wa);
1084 void GuiView::connectBuffer(Buffer & buf)
1086 buf.setGuiDelegate(this);
1090 void GuiView::disconnectBuffer()
1092 if (d.current_work_area_)
1093 d.current_work_area_->bufferView().setGuiDelegate(0);
1097 void GuiView::connectBufferView(BufferView & bv)
1099 bv.setGuiDelegate(this);
1103 void GuiView::disconnectBufferView()
1105 if (d.current_work_area_)
1106 d.current_work_area_->bufferView().setGuiDelegate(0);
1110 void GuiView::errors(string const & error_type)
1112 ErrorList & el = buffer()->errorList(error_type);
1114 showDialog("errorlist", error_type);
1118 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1120 d.toc_models_.updateItem(toqstr(type), dit);
1124 void GuiView::structureChanged()
1126 d.toc_models_.reset(view());
1127 // Navigator needs more than a simple update in this case. It needs to be
1129 updateDialog("toc", "");
1133 void GuiView::updateDialog(string const & name, string const & data)
1135 if (!isDialogVisible(name))
1138 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1139 if (it == d.dialogs_.end())
1142 Dialog * const dialog = it->second.get();
1143 if (dialog->isVisibleView())
1144 dialog->initialiseParams(data);
1148 BufferView * GuiView::view()
1150 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1154 void GuiView::autoSave()
1156 LYXERR(Debug::INFO, "Running autoSave()");
1159 view()->buffer().autoSave();
1163 void GuiView::resetAutosaveTimers()
1166 d.autosave_timeout_.restart();
1170 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1173 Buffer * buf = buffer();
1175 /* In LyX/Mac, when a dialog is open, the menus of the
1176 application can still be accessed without giving focus to
1177 the main window. In this case, we want to disable the menu
1178 entries that are buffer-related.
1180 Note that this code is not perfect, as bug 1941 attests:
1181 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1183 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1186 switch(cmd.action) {
1187 case LFUN_BUFFER_WRITE:
1188 enable = buf && (buf->isUnnamed() || !buf->isClean());
1191 case LFUN_BUFFER_WRITE_AS:
1195 case LFUN_SPLIT_VIEW:
1196 if (cmd.getArg(0) == "vertical")
1197 enable = buf && (d.splitter_->count() == 1 ||
1198 d.splitter_->orientation() == Qt::Vertical);
1200 enable = buf && (d.splitter_->count() == 1 ||
1201 d.splitter_->orientation() == Qt::Horizontal);
1204 case LFUN_CLOSE_TAB_GROUP:
1205 enable = d.currentTabWorkArea();
1208 case LFUN_TOOLBAR_TOGGLE:
1209 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1210 flag.setOnOff(t->isVisible());
1213 case LFUN_UI_TOGGLE:
1214 flag.setOnOff(isFullScreen());
1217 case LFUN_DIALOG_TOGGLE:
1218 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1219 // fall through to set "enable"
1220 case LFUN_DIALOG_SHOW: {
1221 string const name = cmd.getArg(0);
1223 enable = name == "aboutlyx"
1224 || name == "file" //FIXME: should be removed.
1226 || name == "texinfo";
1227 else if (name == "print")
1228 enable = buf->isExportable("dvi")
1229 && lyxrc.print_command != "none";
1230 else if (name == "character") {
1234 InsetCode ic = view()->cursor().inset().lyxCode();
1235 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1238 else if (name == "symbols") {
1239 if (!view() || view()->cursor().inMathed())
1242 InsetCode ic = view()->cursor().inset().lyxCode();
1243 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1246 else if (name == "latexlog")
1247 enable = FileName(buf->logName()).isReadableFile();
1248 else if (name == "spellchecker")
1249 #if defined (USE_ASPELL)
1250 enable = !buf->isReadonly();
1254 else if (name == "vclog")
1255 enable = buf->lyxvc().inUse();
1259 case LFUN_DIALOG_UPDATE: {
1260 string const name = cmd.getArg(0);
1262 enable = name == "prefs";
1266 case LFUN_INSET_APPLY: {
1267 string const name = cmd.getArg(0);
1268 Inset * inset = getOpenInset(name);
1270 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1272 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1273 // Every inset is supposed to handle this
1274 LASSERT(false, break);
1278 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1279 flag |= lyx::getStatus(fr);
1281 enable = flag.enabled();
1285 case LFUN_COMPLETION_INLINE:
1286 if (!d.current_work_area_
1287 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1291 case LFUN_COMPLETION_POPUP:
1292 if (!d.current_work_area_
1293 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1297 case LFUN_COMPLETION_COMPLETE:
1298 if (!d.current_work_area_
1299 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1303 case LFUN_COMPLETION_ACCEPT:
1304 if (!d.current_work_area_
1305 || (!d.current_work_area_->completer().popupVisible()
1306 && !d.current_work_area_->completer().inlineVisible()
1307 && !d.current_work_area_->completer().completionAvailable()))
1311 case LFUN_COMPLETION_CANCEL:
1312 if (!d.current_work_area_
1313 || (!d.current_work_area_->completer().popupVisible()
1314 && !d.current_work_area_->completer().inlineVisible()))
1323 flag.setEnabled(false);
1329 static FileName selectTemplateFile()
1331 FileDialog dlg(qt_("Select template file"));
1332 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1333 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1335 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1336 QStringList(qt_("LyX Documents (*.lyx)")));
1338 if (result.first == FileDialog::Later)
1340 if (result.second.isEmpty())
1342 return FileName(fromqstr(result.second));
1346 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1350 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1353 message(_("Document not loaded."));
1358 setBuffer(newBuffer);
1360 // scroll to the position when the file was last closed
1361 if (lyxrc.use_lastfilepos) {
1362 LastFilePosSection::FilePos filepos =
1363 theSession().lastFilePos().load(filename);
1364 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1368 theSession().lastFiles().add(filename);
1375 void GuiView::openDocument(string const & fname)
1377 string initpath = lyxrc.document_path;
1380 string const trypath = buffer()->filePath();
1381 // If directory is writeable, use this as default.
1382 if (FileName(trypath).isDirWritable())
1388 if (fname.empty()) {
1389 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1390 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1391 dlg.setButton2(qt_("Examples|#E#e"),
1392 toqstr(addPath(package().system_support().absFilename(), "examples")));
1394 QStringList filter(qt_("LyX Documents (*.lyx)"));
1395 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1396 << qt_("LyX-1.4.x Documents (*.lyx14)")
1397 << qt_("LyX-1.5.x Documents (*.lyx15)")
1398 << qt_("LyX-1.6.x Documents (*.lyx16)");
1399 FileDialog::Result result =
1400 dlg.open(toqstr(initpath), filter);
1402 if (result.first == FileDialog::Later)
1405 filename = fromqstr(result.second);
1407 // check selected filename
1408 if (filename.empty()) {
1409 message(_("Canceled."));
1415 // get absolute path of file and add ".lyx" to the filename if
1417 FileName const fullname =
1418 fileSearch(string(), filename, "lyx", support::may_not_exist);
1419 if (!fullname.empty())
1420 filename = fullname.absFilename();
1422 if (!fullname.onlyPath().isDirectory()) {
1423 Alert::warning(_("Invalid filename"),
1424 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1425 from_utf8(fullname.absFilename())));
1428 // if the file doesn't exist, let the user create one
1429 if (!fullname.exists()) {
1430 // the user specifically chose this name. Believe him.
1431 Buffer * const b = newFile(filename, string(), true);
1437 docstring const disp_fn = makeDisplayPath(filename);
1438 message(bformat(_("Opening document %1$s..."), disp_fn));
1441 Buffer * buf = loadDocument(fullname);
1443 buf->updateLabels();
1445 buf->errors("Parse");
1446 str2 = bformat(_("Document %1$s opened."), disp_fn);
1447 if (buf->lyxvc().inUse())
1448 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1449 " " + _("Version control detected.");
1451 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1456 // FIXME: clean that
1457 static bool import(GuiView * lv, FileName const & filename,
1458 string const & format, ErrorList & errorList)
1460 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1462 string loader_format;
1463 vector<string> loaders = theConverters().loaders();
1464 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1465 for (vector<string>::const_iterator it = loaders.begin();
1466 it != loaders.end(); ++it) {
1467 if (!theConverters().isReachable(format, *it))
1470 string const tofile =
1471 support::changeExtension(filename.absFilename(),
1472 formats.extension(*it));
1473 if (!theConverters().convert(0, filename, FileName(tofile),
1474 filename, format, *it, errorList))
1476 loader_format = *it;
1479 if (loader_format.empty()) {
1480 frontend::Alert::error(_("Couldn't import file"),
1481 bformat(_("No information for importing the format %1$s."),
1482 formats.prettyName(format)));
1486 loader_format = format;
1488 if (loader_format == "lyx") {
1489 Buffer * buf = lv->loadDocument(lyxfile);
1492 buf->updateLabels();
1494 buf->errors("Parse");
1496 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1500 bool as_paragraphs = loader_format == "textparagraph";
1501 string filename2 = (loader_format == format) ? filename.absFilename()
1502 : support::changeExtension(filename.absFilename(),
1503 formats.extension(loader_format));
1504 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1505 theLyXFunc().setLyXView(lv);
1506 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1513 void GuiView::importDocument(string const & argument)
1516 string filename = split(argument, format, ' ');
1518 LYXERR(Debug::INFO, format << " file: " << filename);
1520 // need user interaction
1521 if (filename.empty()) {
1522 string initpath = lyxrc.document_path;
1524 Buffer const * buf = buffer();
1526 string const trypath = buf->filePath();
1527 // If directory is writeable, use this as default.
1528 if (FileName(trypath).isDirWritable())
1532 docstring const text = bformat(_("Select %1$s file to import"),
1533 formats.prettyName(format));
1535 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1536 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1537 dlg.setButton2(qt_("Examples|#E#e"),
1538 toqstr(addPath(package().system_support().absFilename(), "examples")));
1540 docstring filter = formats.prettyName(format);
1543 filter += from_utf8(formats.extension(format));
1546 FileDialog::Result result =
1547 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1549 if (result.first == FileDialog::Later)
1552 filename = fromqstr(result.second);
1554 // check selected filename
1555 if (filename.empty())
1556 message(_("Canceled."));
1559 if (filename.empty())
1562 // get absolute path of file
1563 FileName const fullname(support::makeAbsPath(filename));
1565 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1567 // Check if the document already is open
1568 Buffer * buf = theBufferList().getBuffer(lyxfile);
1571 if (!closeBuffer()) {
1572 message(_("Canceled."));
1577 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1579 // if the file exists already, and we didn't do
1580 // -i lyx thefile.lyx, warn
1581 if (lyxfile.exists() && fullname != lyxfile) {
1583 docstring text = bformat(_("The document %1$s already exists.\n\n"
1584 "Do you want to overwrite that document?"), displaypath);
1585 int const ret = Alert::prompt(_("Overwrite document?"),
1586 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1589 message(_("Canceled."));
1594 message(bformat(_("Importing %1$s..."), displaypath));
1595 ErrorList errorList;
1596 if (import(this, fullname, format, errorList))
1597 message(_("imported."));
1599 message(_("file not imported!"));
1601 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1605 void GuiView::newDocument(string const & filename, bool from_template)
1607 FileName initpath(lyxrc.document_path);
1608 Buffer * buf = buffer();
1610 FileName const trypath(buf->filePath());
1611 // If directory is writeable, use this as default.
1612 if (trypath.isDirWritable())
1616 string templatefile;
1617 if (from_template) {
1618 templatefile = selectTemplateFile().absFilename();
1619 if (templatefile.empty())
1624 if (filename.empty())
1625 b = newUnnamedFile(templatefile, initpath);
1627 b = newFile(filename, templatefile, true);
1632 // If no new document could be created, it is unsure
1633 // whether there is a valid BufferView.
1635 // Ensure the cursor is correctly positioned on screen.
1636 view()->showCursor();
1640 void GuiView::insertLyXFile(docstring const & fname)
1642 BufferView * bv = view();
1647 FileName filename(to_utf8(fname));
1649 if (!filename.empty()) {
1650 bv->insertLyXFile(filename);
1654 // Launch a file browser
1656 string initpath = lyxrc.document_path;
1657 string const trypath = bv->buffer().filePath();
1658 // If directory is writeable, use this as default.
1659 if (FileName(trypath).isDirWritable())
1663 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1664 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1665 dlg.setButton2(qt_("Examples|#E#e"),
1666 toqstr(addPath(package().system_support().absFilename(),
1669 FileDialog::Result result = dlg.open(toqstr(initpath),
1670 QStringList(qt_("LyX Documents (*.lyx)")));
1672 if (result.first == FileDialog::Later)
1676 filename.set(fromqstr(result.second));
1678 // check selected filename
1679 if (filename.empty()) {
1680 // emit message signal.
1681 message(_("Canceled."));
1685 bv->insertLyXFile(filename);
1689 void GuiView::insertPlaintextFile(docstring const & fname,
1692 BufferView * bv = view();
1697 FileName filename(to_utf8(fname));
1699 if (!filename.empty()) {
1700 bv->insertPlaintextFile(filename, asParagraph);
1704 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1705 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1707 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1710 if (result.first == FileDialog::Later)
1714 filename.set(fromqstr(result.second));
1716 // check selected filename
1717 if (filename.empty()) {
1718 // emit message signal.
1719 message(_("Canceled."));
1723 bv->insertPlaintextFile(filename, asParagraph);
1727 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1729 FileName fname = b.fileName();
1730 FileName const oldname = fname;
1732 if (!newname.empty()) {
1734 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1736 // Switch to this Buffer.
1739 /// No argument? Ask user through dialog.
1741 FileDialog dlg(qt_("Choose a filename to save document as"),
1742 LFUN_BUFFER_WRITE_AS);
1743 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1744 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1746 if (!isLyXFilename(fname.absFilename()))
1747 fname.changeExtension(".lyx");
1749 FileDialog::Result result =
1750 dlg.save(toqstr(fname.onlyPath().absFilename()),
1751 QStringList(qt_("LyX Documents (*.lyx)")),
1752 toqstr(fname.onlyFileName()));
1754 if (result.first == FileDialog::Later)
1757 fname.set(fromqstr(result.second));
1762 if (!isLyXFilename(fname.absFilename()))
1763 fname.changeExtension(".lyx");
1766 if (FileName(fname).exists()) {
1767 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1768 docstring text = bformat(_("The document %1$s already "
1769 "exists.\n\nDo you want to "
1770 "overwrite that document?"),
1772 int const ret = Alert::prompt(_("Overwrite document?"),
1773 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1776 case 1: return renameBuffer(b, docstring());
1777 case 2: return false;
1781 // Ok, change the name of the buffer
1782 b.setFileName(fname.absFilename());
1784 bool unnamed = b.isUnnamed();
1785 b.setUnnamed(false);
1786 b.saveCheckSum(fname);
1788 if (!saveBuffer(b)) {
1789 b.setFileName(oldname.absFilename());
1790 b.setUnnamed(unnamed);
1791 b.saveCheckSum(oldname);
1799 bool GuiView::saveBuffer(Buffer & b)
1801 if (workArea(b) && workArea(b)->inDialogMode())
1805 return renameBuffer(b, docstring());
1808 theSession().lastFiles().add(b.fileName());
1812 // Switch to this Buffer.
1815 // FIXME: we don't tell the user *WHY* the save failed !!
1816 docstring const file = makeDisplayPath(b.absFileName(), 30);
1817 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1818 "Do you want to rename the document and "
1819 "try again?"), file);
1820 int const ret = Alert::prompt(_("Rename and save?"),
1821 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1824 if (!renameBuffer(b, docstring()))
1833 return saveBuffer(b);
1837 bool GuiView::closeBuffer()
1839 Buffer * buf = buffer();
1840 return buf && closeBuffer(*buf);
1844 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1846 // goto bookmark to update bookmark pit.
1847 //FIXME: we should update only the bookmarks related to this buffer!
1848 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1849 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1850 theLyXFunc().gotoBookmark(i+1, false, false);
1852 if (buf.isClean() || buf.paragraphs().empty()) {
1853 if (buf.masterBuffer() == &buf && tolastopened)
1854 theSession().lastOpened().add(buf.fileName());
1856 // Don't close child documents.
1857 removeWorkArea(currentMainWorkArea());
1859 theBufferList().release(&buf);
1862 // Switch to this Buffer.
1867 if (buf.isUnnamed())
1868 file = from_utf8(buf.fileName().onlyFileName());
1870 file = buf.fileName().displayName(30);
1872 // Bring this window to top before asking questions.
1876 docstring const text = bformat(_("The document %1$s has unsaved changes."
1877 "\n\nDo you want to save the document or discard the changes?"), file);
1878 int const ret = Alert::prompt(_("Save changed document?"),
1879 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1883 if (!saveBuffer(buf))
1887 // if we crash after this we could
1888 // have no autosave file but I guess
1889 // this is really improbable (Jug)
1890 removeAutosaveFile(buf.absFileName());
1896 // save file names to .lyx/session
1897 // if master/slave are both open, do not save slave since it
1898 // will be automatically loaded when the master is loaded
1899 if (buf.masterBuffer() == &buf && tolastopened)
1900 theSession().lastOpened().add(buf.fileName());
1903 // Don't close child documents.
1904 removeWorkArea(currentMainWorkArea());
1906 theBufferList().release(&buf);
1912 bool GuiView::dispatch(FuncRequest const & cmd)
1914 BufferView * bv = view();
1915 // By default we won't need any update.
1917 bv->cursor().updateFlags(Update::None);
1918 bool dispatched = true;
1920 switch(cmd.action) {
1921 case LFUN_BUFFER_IMPORT:
1922 importDocument(to_utf8(cmd.argument()));
1925 case LFUN_BUFFER_SWITCH:
1926 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1929 case LFUN_BUFFER_NEXT:
1930 setBuffer(theBufferList().next(buffer()));
1933 case LFUN_BUFFER_PREVIOUS:
1934 setBuffer(theBufferList().previous(buffer()));
1937 case LFUN_COMMAND_EXECUTE: {
1938 bool const show_it = cmd.argument() != "off";
1939 // FIXME: this is a hack, "minibuffer" should not be
1941 if (GuiToolbar * t = toolbar("minibuffer")) {
1942 t->setVisible(show_it);
1943 if (show_it && t->commandBuffer())
1944 t->commandBuffer()->setFocus();
1948 case LFUN_DROP_LAYOUTS_CHOICE:
1950 d.layout_->showPopup();
1953 case LFUN_MENU_OPEN:
1954 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1955 menu->exec(QCursor::pos());
1958 case LFUN_FILE_INSERT:
1959 insertLyXFile(cmd.argument());
1961 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1962 insertPlaintextFile(cmd.argument(), true);
1965 case LFUN_FILE_INSERT_PLAINTEXT:
1966 insertPlaintextFile(cmd.argument(), false);
1969 case LFUN_BUFFER_WRITE:
1971 saveBuffer(bv->buffer());
1974 case LFUN_BUFFER_WRITE_AS:
1976 renameBuffer(bv->buffer(), cmd.argument());
1979 case LFUN_BUFFER_WRITE_ALL: {
1980 Buffer * first = theBufferList().first();
1983 message(_("Saving all documents..."));
1984 // We cannot use a for loop as the buffer list cycles.
1987 if (!b->isClean()) {
1989 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1991 b = theBufferList().next(b);
1992 } while (b != first);
1993 message(_("All documents saved."));
1997 case LFUN_TOOLBAR_TOGGLE: {
1998 string const name = cmd.getArg(0);
1999 if (GuiToolbar * t = toolbar(name))
2004 case LFUN_DIALOG_UPDATE: {
2005 string const name = to_utf8(cmd.argument());
2006 // Can only update a dialog connected to an existing inset
2007 Inset * inset = getOpenInset(name);
2009 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2010 inset->dispatch(view()->cursor(), fr);
2011 } else if (name == "paragraph") {
2012 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2013 } else if (name == "prefs" || name == "document") {
2014 updateDialog(name, string());
2019 case LFUN_DIALOG_TOGGLE: {
2020 if (isDialogVisible(cmd.getArg(0)))
2021 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2023 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2027 case LFUN_DIALOG_DISCONNECT_INSET:
2028 disconnectDialog(to_utf8(cmd.argument()));
2031 case LFUN_DIALOG_HIDE: {
2032 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2036 case LFUN_DIALOG_SHOW: {
2037 string const name = cmd.getArg(0);
2038 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2040 if (name == "character") {
2041 data = freefont2string();
2043 showDialog("character", data);
2044 } else if (name == "latexlog") {
2045 Buffer::LogType type;
2046 string const logfile = buffer()->logName(&type);
2048 case Buffer::latexlog:
2051 case Buffer::buildlog:
2055 data += Lexer::quoteString(logfile);
2056 showDialog("log", data);
2057 } else if (name == "vclog") {
2058 string const data = "vc " +
2059 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2060 showDialog("log", data);
2061 } else if (name == "symbols") {
2062 data = bv->cursor().getEncoding()->name();
2064 showDialog("symbols", data);
2066 } else if (name == "prefs" && isFullScreen()) {
2067 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2069 showDialog("prefs", data);
2071 showDialog(name, data);
2075 case LFUN_INSET_APPLY: {
2076 string const name = cmd.getArg(0);
2077 Inset * inset = getOpenInset(name);
2079 // put cursor in front of inset.
2080 if (!view()->setCursorFromInset(inset)) {
2081 LASSERT(false, break);
2084 // useful if we are called from a dialog.
2085 view()->cursor().beginUndoGroup();
2086 view()->cursor().recordUndo();
2087 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2088 inset->dispatch(view()->cursor(), fr);
2089 view()->cursor().endUndoGroup();
2091 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2097 case LFUN_UI_TOGGLE:
2099 // Make sure the keyboard focus stays in the work area.
2103 case LFUN_SPLIT_VIEW:
2104 if (Buffer * buf = buffer()) {
2105 string const orientation = cmd.getArg(0);
2106 d.splitter_->setOrientation(orientation == "vertical"
2107 ? Qt::Vertical : Qt::Horizontal);
2108 TabWorkArea * twa = addTabWorkArea();
2109 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2110 setCurrentWorkArea(wa);
2114 case LFUN_CLOSE_TAB_GROUP:
2115 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2117 twa = d.currentTabWorkArea();
2118 // Switch to the next GuiWorkArea in the found TabWorkArea.
2120 // Make sure the work area is up to date.
2121 setCurrentWorkArea(twa->currentWorkArea());
2123 setCurrentWorkArea(0);
2128 case LFUN_COMPLETION_INLINE:
2129 if (d.current_work_area_)
2130 d.current_work_area_->completer().showInline();
2133 case LFUN_COMPLETION_POPUP:
2134 if (d.current_work_area_)
2135 d.current_work_area_->completer().showPopup();
2139 case LFUN_COMPLETION_COMPLETE:
2140 if (d.current_work_area_)
2141 d.current_work_area_->completer().tab();
2144 case LFUN_COMPLETION_CANCEL:
2145 if (d.current_work_area_) {
2146 if (d.current_work_area_->completer().popupVisible())
2147 d.current_work_area_->completer().hidePopup();
2149 d.current_work_area_->completer().hideInline();
2153 case LFUN_COMPLETION_ACCEPT:
2154 if (d.current_work_area_)
2155 d.current_work_area_->completer().activate();
2164 // Part of automatic menu appearance feature.
2165 if (isFullScreen()) {
2166 if (menuBar()->isVisible())
2168 if (statusBar()->isVisible())
2169 statusBar()->hide();
2176 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2178 string const arg = cmd.getArg(0);
2179 if (arg == "scrollbar") {
2180 // hide() is of no help
2181 if (d.current_work_area_->verticalScrollBarPolicy() ==
2182 Qt::ScrollBarAlwaysOff)
2184 d.current_work_area_->setVerticalScrollBarPolicy(
2185 Qt::ScrollBarAsNeeded);
2187 d.current_work_area_->setVerticalScrollBarPolicy(
2188 Qt::ScrollBarAlwaysOff);
2191 if (arg == "statusbar") {
2192 statusBar()->setVisible(!statusBar()->isVisible());
2195 if (arg == "menubar") {
2196 menuBar()->setVisible(!menuBar()->isVisible());
2199 #if QT_VERSION >= 0x040300
2200 if (arg == "frame") {
2202 getContentsMargins(&l, &t, &r, &b);
2203 //are the frames in default state?
2204 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2206 setContentsMargins(-2, -2, -2, -2);
2208 setContentsMargins(0, 0, 0, 0);
2213 if (arg == "fullscreen") {
2218 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2222 void GuiView::toggleFullScreen()
2224 if (isFullScreen()) {
2225 for (int i = 0; i != d.splitter_->count(); ++i)
2226 d.tabWorkArea(i)->setFullScreen(false);
2227 #if QT_VERSION >= 0x040300
2228 setContentsMargins(0, 0, 0, 0);
2230 setWindowState(windowState() ^ Qt::WindowFullScreen);
2233 statusBar()->show();
2236 hideDialogs("prefs", 0);
2237 for (int i = 0; i != d.splitter_->count(); ++i)
2238 d.tabWorkArea(i)->setFullScreen(true);
2239 #if QT_VERSION >= 0x040300
2240 setContentsMargins(-2, -2, -2, -2);
2243 setWindowState(windowState() ^ Qt::WindowFullScreen);
2244 statusBar()->hide();
2246 if (lyxrc.full_screen_toolbars) {
2247 ToolbarMap::iterator end = d.toolbars_.end();
2248 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2253 // give dialogs like the TOC a chance to adapt
2258 Buffer const * GuiView::updateInset(Inset const * inset)
2260 if (!d.current_work_area_)
2264 d.current_work_area_->scheduleRedraw();
2266 return &d.current_work_area_->bufferView().buffer();
2270 void GuiView::restartCursor()
2272 /* When we move around, or type, it's nice to be able to see
2273 * the cursor immediately after the keypress.
2275 if (d.current_work_area_)
2276 d.current_work_area_->startBlinkingCursor();
2278 // Take this occasion to update the other GUI elements.
2284 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2286 if (d.current_work_area_)
2287 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2292 // This list should be kept in sync with the list of insets in
2293 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2294 // dialog should have the same name as the inset.
2295 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2296 // docs in LyXAction.cpp.
2298 char const * const dialognames[] = {
2299 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2300 "citation", "document", "errorlist", "ert", "external", "file",
2301 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2302 "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph", "prefs", "print",
2303 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2305 #ifdef HAVE_LIBAIKSAURUS
2309 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2311 char const * const * const end_dialognames =
2312 dialognames + (sizeof(dialognames) / sizeof(char *));
2316 cmpCStr(char const * name) : name_(name) {}
2317 bool operator()(char const * other) {
2318 return strcmp(other, name_) == 0;
2325 bool isValidName(string const & name)
2327 return find_if(dialognames, end_dialognames,
2328 cmpCStr(name.c_str())) != end_dialognames;
2334 void GuiView::resetDialogs()
2336 // Make sure that no LFUN uses any LyXView.
2337 theLyXFunc().setLyXView(0);
2340 constructToolbars();
2341 guiApp->menus().fillMenuBar(menuBar(), this, false);
2343 d.layout_->updateContents(true);
2344 // Now update controls with current buffer.
2345 theLyXFunc().setLyXView(this);
2351 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2353 if (!isValidName(name))
2356 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2358 if (it != d.dialogs_.end()) {
2360 it->second->hideView();
2361 return it->second.get();
2364 Dialog * dialog = build(name);
2365 d.dialogs_[name].reset(dialog);
2366 if (lyxrc.allow_geometry_session)
2367 dialog->restoreSession();
2374 void GuiView::showDialog(string const & name, string const & data,
2382 Dialog * dialog = findOrBuild(name, false);
2384 dialog->showData(data);
2386 d.open_insets_[name] = inset;
2389 catch (ExceptionMessage const & ex) {
2397 bool GuiView::isDialogVisible(string const & name) const
2399 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2400 if (it == d.dialogs_.end())
2402 return it->second.get()->isVisibleView();
2406 void GuiView::hideDialog(string const & name, Inset * inset)
2408 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2409 if (it == d.dialogs_.end())
2412 if (inset && inset != getOpenInset(name))
2415 Dialog * const dialog = it->second.get();
2416 if (dialog->isVisibleView())
2418 d.open_insets_[name] = 0;
2422 void GuiView::disconnectDialog(string const & name)
2424 if (!isValidName(name))
2427 if (d.open_insets_.find(name) != d.open_insets_.end())
2428 d.open_insets_[name] = 0;
2432 Inset * GuiView::getOpenInset(string const & name) const
2434 if (!isValidName(name))
2437 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2438 return it == d.open_insets_.end() ? 0 : it->second;
2442 void GuiView::hideAll() const
2444 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2445 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2447 for(; it != end; ++it)
2448 it->second->hideView();
2452 void GuiView::updateDialogs()
2454 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2455 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2457 for(; it != end; ++it) {
2458 Dialog * dialog = it->second.get();
2459 if (dialog && dialog->isVisibleView())
2460 dialog->checkStatus();
2467 // will be replaced by a proper factory...
2468 Dialog * createGuiAbout(GuiView & lv);
2469 Dialog * createGuiBibitem(GuiView & lv);
2470 Dialog * createGuiBibtex(GuiView & lv);
2471 Dialog * createGuiBox(GuiView & lv);
2472 Dialog * createGuiBranch(GuiView & lv);
2473 Dialog * createGuiChanges(GuiView & lv);
2474 Dialog * createGuiCharacter(GuiView & lv);
2475 Dialog * createGuiCitation(GuiView & lv);
2476 Dialog * createGuiDelimiter(GuiView & lv);
2477 Dialog * createGuiDocument(GuiView & lv);
2478 Dialog * createGuiErrorList(GuiView & lv);
2479 Dialog * createGuiERT(GuiView & lv);
2480 Dialog * createGuiExternal(GuiView & lv);
2481 Dialog * createGuiFloat(GuiView & lv);
2482 Dialog * createGuiGraphics(GuiView & lv);
2483 Dialog * createGuiInclude(GuiView & lv);
2484 Dialog * createGuiInfo(GuiView & lv);
2485 Dialog * createGuiLabel(GuiView & lv);
2486 Dialog * createGuiListings(GuiView & lv);
2487 Dialog * createGuiLog(GuiView & lv);
2488 Dialog * createGuiMathHSpace(GuiView & lv);
2489 Dialog * createGuiMathMatrix(GuiView & lv);
2490 Dialog * createGuiNomenclature(GuiView & lv);
2491 Dialog * createGuiNote(GuiView & lv);
2492 Dialog * createGuiParagraph(GuiView & lv);
2493 Dialog * createGuiPreferences(GuiView & lv);
2494 Dialog * createGuiPrint(GuiView & lv);
2495 Dialog * createGuiRef(GuiView & lv);
2496 Dialog * createGuiSearch(GuiView & lv);
2497 Dialog * createGuiSearchAdv(GuiView & lv);
2498 Dialog * createGuiSendTo(GuiView & lv);
2499 Dialog * createGuiShowFile(GuiView & lv);
2500 Dialog * createGuiSpellchecker(GuiView & lv);
2501 Dialog * createGuiSymbols(GuiView & lv);
2502 Dialog * createGuiTabularCreate(GuiView & lv);
2503 Dialog * createGuiTabular(GuiView & lv);
2504 Dialog * createGuiTexInfo(GuiView & lv);
2505 Dialog * createGuiTextHSpace(GuiView & lv);
2506 Dialog * createGuiToc(GuiView & lv);
2507 Dialog * createGuiThesaurus(GuiView & lv);
2508 Dialog * createGuiHyperlink(GuiView & lv);
2509 Dialog * createGuiVSpace(GuiView & lv);
2510 Dialog * createGuiViewSource(GuiView & lv);
2511 Dialog * createGuiWrap(GuiView & lv);
2514 Dialog * GuiView::build(string const & name)
2516 LASSERT(isValidName(name), return 0);
2518 if (name == "aboutlyx")
2519 return createGuiAbout(*this);
2520 if (name == "bibitem")
2521 return createGuiBibitem(*this);
2522 if (name == "bibtex")
2523 return createGuiBibtex(*this);
2525 return createGuiBox(*this);
2526 if (name == "branch")
2527 return createGuiBranch(*this);
2528 if (name == "changes")
2529 return createGuiChanges(*this);
2530 if (name == "character")
2531 return createGuiCharacter(*this);
2532 if (name == "citation")
2533 return createGuiCitation(*this);
2534 if (name == "document")
2535 return createGuiDocument(*this);
2536 if (name == "errorlist")
2537 return createGuiErrorList(*this);
2539 return createGuiERT(*this);
2540 if (name == "external")
2541 return createGuiExternal(*this);
2543 return createGuiShowFile(*this);
2544 if (name == "findreplace")
2545 return createGuiSearch(*this);
2546 if (name == "findreplaceadv")
2547 return createGuiSearchAdv(*this);
2548 if (name == "float")
2549 return createGuiFloat(*this);
2550 if (name == "graphics")
2551 return createGuiGraphics(*this);
2552 if (name == "include")
2553 return createGuiInclude(*this);
2555 return createGuiInfo(*this);
2556 if (name == "nomenclature")
2557 return createGuiNomenclature(*this);
2558 if (name == "label")
2559 return createGuiLabel(*this);
2561 return createGuiLog(*this);
2562 if (name == "view-source")
2563 return createGuiViewSource(*this);
2564 if (name == "mathdelimiter")
2565 return createGuiDelimiter(*this);
2566 if (name == "mathspace")
2567 return createGuiMathHSpace(*this);
2568 if (name == "mathmatrix")
2569 return createGuiMathMatrix(*this);
2571 return createGuiNote(*this);
2572 if (name == "paragraph")
2573 return createGuiParagraph(*this);
2574 if (name == "prefs")
2575 return createGuiPreferences(*this);
2576 if (name == "print")
2577 return createGuiPrint(*this);
2579 return createGuiRef(*this);
2580 if (name == "sendto")
2581 return createGuiSendTo(*this);
2582 if (name == "space")
2583 return createGuiTextHSpace(*this);
2584 if (name == "spellchecker")
2585 return createGuiSpellchecker(*this);
2586 if (name == "symbols")
2587 return createGuiSymbols(*this);
2588 if (name == "tabular")
2589 return createGuiTabular(*this);
2590 if (name == "tabularcreate")
2591 return createGuiTabularCreate(*this);
2592 if (name == "texinfo")
2593 return createGuiTexInfo(*this);
2594 #ifdef HAVE_LIBAIKSAURUS
2595 if (name == "thesaurus")
2596 return createGuiThesaurus(*this);
2599 return createGuiToc(*this);
2601 return createGuiHyperlink(*this);
2602 if (name == "vspace")
2603 return createGuiVSpace(*this);
2605 return createGuiWrap(*this);
2606 if (name == "listings")
2607 return createGuiListings(*this);
2613 } // namespace frontend
2616 #include "moc_GuiView.cpp"