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_ = getPixmap("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(getPixmap("images/", "lyx", "png"));
322 setAcceptDrops(true);
324 statusBar()->setSizeGripEnabled(true);
326 // Forbid too small unresizable window because it can happen
327 // with some window manager under X11.
328 setMinimumSize(300, 200);
330 if (lyxrc.allow_geometry_session) {
331 // Now take care of session management.
336 // no session handling, default to a sane size.
337 setGeometry(50, 50, 690, 510);
340 // clear session data if any.
342 settings.remove("views");
352 void GuiView::saveLayout() const
355 settings.beginGroup("views");
356 settings.beginGroup(QString::number(id_));
358 settings.setValue("pos", pos());
359 settings.setValue("size", size());
361 settings.setValue("geometry", saveGeometry());
363 settings.setValue("layout", saveState(0));
364 settings.setValue("icon_size", iconSize());
368 bool GuiView::restoreLayout()
371 settings.beginGroup("views");
372 settings.beginGroup(QString::number(id_));
373 QString const icon_key = "icon_size";
374 if (!settings.contains(icon_key))
377 setIconSize(settings.value(icon_key).toSize());
379 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
380 QSize size = settings.value("size", QSize(690, 510)).toSize();
384 if (!restoreGeometry(settings.value("geometry").toByteArray()))
385 setGeometry(50, 50, 690, 510);
387 // Make sure layout is correctly oriented.
388 setLayoutDirection(qApp->layoutDirection());
390 // Allow the toc and view-source dock widget to be restored if needed.
392 if ((d = findOrBuild("toc", true)))
395 if ((d = findOrBuild("view-source", true)))
398 if (!restoreState(settings.value("layout").toByteArray(), 0))
405 GuiToolbar * GuiView::toolbar(string const & name)
407 ToolbarMap::iterator it = d.toolbars_.find(name);
408 if (it != d.toolbars_.end())
411 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
412 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
417 void GuiView::constructToolbars()
419 ToolbarMap::iterator it = d.toolbars_.begin();
420 for (; it != d.toolbars_.end(); ++it)
425 // extracts the toolbars from the backend
426 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
427 Toolbars::Infos::iterator end = guiApp->toolbars().end();
428 for (; cit != end; ++cit)
429 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
433 void GuiView::initToolbars()
435 // extracts the toolbars from the backend
436 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
437 Toolbars::Infos::iterator end = guiApp->toolbars().end();
438 for (; cit != end; ++cit) {
439 GuiToolbar * tb = toolbar(cit->name);
442 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
444 tb->setVisible(false);
445 tb->setVisibility(visibility);
447 if (visibility & Toolbars::TOP) {
449 addToolBarBreak(Qt::TopToolBarArea);
450 addToolBar(Qt::TopToolBarArea, tb);
453 if (visibility & Toolbars::BOTTOM) {
454 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
455 #if (QT_VERSION >= 0x040202)
456 addToolBarBreak(Qt::BottomToolBarArea);
458 addToolBar(Qt::BottomToolBarArea, tb);
461 if (visibility & Toolbars::LEFT) {
462 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
463 #if (QT_VERSION >= 0x040202)
464 addToolBarBreak(Qt::LeftToolBarArea);
466 addToolBar(Qt::LeftToolBarArea, tb);
469 if (visibility & Toolbars::RIGHT) {
470 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
471 #if (QT_VERSION >= 0x040202)
472 addToolBarBreak(Qt::RightToolBarArea);
474 addToolBar(Qt::RightToolBarArea, tb);
477 if (visibility & Toolbars::ON)
478 tb->setVisible(true);
483 TocModels & GuiView::tocModels()
485 return d.toc_models_;
489 void GuiView::setFocus()
491 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
492 // Make sure LyXFunc points to the correct view.
493 guiApp->setCurrentView(this);
494 theLyXFunc().setLyXView(this);
495 QMainWindow::setFocus();
496 if (d.current_work_area_)
497 d.current_work_area_->setFocus();
501 QMenu * GuiView::createPopupMenu()
503 return d.toolBarPopup(this);
507 void GuiView::showEvent(QShowEvent * e)
509 LYXERR(Debug::GUI, "Passed Geometry "
510 << size().height() << "x" << size().width()
511 << "+" << pos().x() << "+" << pos().y());
513 if (d.splitter_->count() == 0)
514 // No work area, switch to the background widget.
517 QMainWindow::showEvent(e);
521 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
522 ** is responsibility of the container (e.g., dialog)
524 void GuiView::closeEvent(QCloseEvent * close_event)
526 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
529 // it can happen that this event arrives without selecting the view,
530 // e.g. when clicking the close button on a background window.
532 setCurrentWorkArea(currentMainWorkArea());
533 while (GuiWorkArea * wa = currentMainWorkArea()) {
534 Buffer * b = &wa->bufferView().buffer();
536 // This is a child document, just close the tab
537 // after saving but keep the file loaded.
538 if (!closeBuffer(*b, true)) {
540 close_event->ignore();
546 vector<Buffer *> clist = b->getChildren();
547 for (vector<Buffer *>::const_iterator it = clist.begin();
548 it != clist.end(); ++it) {
549 if ((*it)->isClean())
552 // If a child is dirty, do not close
553 // without user intervention
554 if (!closeBuffer(*c, false)) {
556 close_event->ignore();
561 QList<int> const ids = guiApp->viewIds();
562 for (int i = 0; i != ids.size(); ++i) {
565 if (guiApp->view(ids[i]).workArea(*b)) {
566 // FIXME 1: should we put an alert box here that the buffer
567 // is viewed elsewhere?
568 // FIXME 2: should we try to save this buffer in any case?
571 // This buffer is also opened in another view, so
572 // close the associated work area...
574 // ... but don't close the buffer.
579 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
580 if (b && !closeBuffer(*b, true)) {
582 close_event->ignore();
587 // Make sure that nothing will use this close to be closed View.
588 guiApp->unregisterView(this);
590 if (isFullScreen()) {
591 // Switch off fullscreen before closing.
596 // Make sure the timer time out will not trigger a statusbar update.
597 d.statusbar_timer_.stop();
599 // Saving fullscreen requires additional tweaks in the toolbar code.
600 // It wouldn't also work under linux natively.
601 if (lyxrc.allow_geometry_session) {
602 // Save this window geometry and layout.
604 // Then the toolbar private states.
605 ToolbarMap::iterator end = d.toolbars_.end();
606 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
607 it->second->saveSession();
608 // Now take care of all other dialogs:
609 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
610 for (; it!= d.dialogs_.end(); ++it)
611 it->second->saveSession();
614 close_event->accept();
618 void GuiView::dragEnterEvent(QDragEnterEvent * event)
620 if (event->mimeData()->hasUrls())
622 /// \todo Ask lyx-devel is this is enough:
623 /// if (event->mimeData()->hasFormat("text/plain"))
624 /// event->acceptProposedAction();
628 void GuiView::dropEvent(QDropEvent * event)
630 QList<QUrl> files = event->mimeData()->urls();
634 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
635 for (int i = 0; i != files.size(); ++i) {
636 string const file = os::internal_path(fromqstr(
637 files.at(i).toLocalFile()));
639 // Asynchronously post the event. DropEvent usually come
640 // from the BufferView. But reloading a file might close
641 // the BufferView from within its own event handler.
642 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
649 void GuiView::message(docstring const & str)
651 if (ForkedProcess::iAmAChild())
654 statusBar()->showMessage(toqstr(str));
655 d.statusbar_timer_.stop();
656 d.statusbar_timer_.start(3000);
660 void GuiView::smallSizedIcons()
662 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
666 void GuiView::normalSizedIcons()
668 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
672 void GuiView::bigSizedIcons()
674 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
678 void GuiView::clearMessage()
682 theLyXFunc().setLyXView(this);
683 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
684 d.statusbar_timer_.stop();
688 void GuiView::updateWindowTitle(GuiWorkArea * wa)
690 if (wa != d.current_work_area_)
692 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
693 setWindowIconText(wa->windowIconText());
697 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
700 disconnectBufferView();
701 connectBufferView(wa->bufferView());
702 connectBuffer(wa->bufferView().buffer());
703 d.current_work_area_ = wa;
704 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
705 this, SLOT(updateWindowTitle(GuiWorkArea *)));
706 updateWindowTitle(wa);
710 // The document settings needs to be reinitialised.
711 updateDialog("document", "");
713 // Buffer-dependent dialogs must be updated. This is done here because
714 // some dialogs require buffer()->text.
719 void GuiView::on_lastWorkAreaRemoved()
722 // We already are in a close event. Nothing more to do.
725 if (d.splitter_->count() > 1)
726 // We have a splitter so don't close anything.
729 // Reset and updates the dialogs.
730 d.toc_models_.reset(0);
731 updateDialog("document", "");
734 resetWindowTitleAndIconText();
736 if (lyxrc.open_buffers_in_tabs)
737 // Nothing more to do, the window should stay open.
740 if (guiApp->viewIds().size() > 1) {
746 // On Mac we also close the last window because the application stay
747 // resident in memory. On other platforms we don't close the last
748 // window because this would quit the application.
754 void GuiView::updateStatusBar()
756 // let the user see the explicit message
757 if (d.statusbar_timer_.isActive())
760 theLyXFunc().setLyXView(this);
761 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
765 bool GuiView::hasFocus() const
767 return qApp->activeWindow() == this;
771 bool GuiView::event(QEvent * e)
775 // Useful debug code:
776 //case QEvent::ActivationChange:
777 //case QEvent::WindowDeactivate:
778 //case QEvent::Paint:
779 //case QEvent::Enter:
780 //case QEvent::Leave:
781 //case QEvent::HoverEnter:
782 //case QEvent::HoverLeave:
783 //case QEvent::HoverMove:
784 //case QEvent::StatusTip:
785 //case QEvent::DragEnter:
786 //case QEvent::DragLeave:
790 case QEvent::WindowActivate: {
791 if (this == guiApp->currentView()) {
793 return QMainWindow::event(e);
795 guiApp->setCurrentView(this);
796 theLyXFunc().setLyXView(this);
797 if (d.current_work_area_) {
798 BufferView & bv = d.current_work_area_->bufferView();
799 connectBufferView(bv);
800 connectBuffer(bv.buffer());
801 // The document structure, name and dialogs might have
802 // changed in another view.
804 // The document settings needs to be reinitialised.
805 updateDialog("document", "");
808 resetWindowTitleAndIconText();
811 return QMainWindow::event(e);
814 case QEvent::ShortcutOverride: {
818 if (isFullScreen() && menuBar()->isHidden()) {
819 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
820 // FIXME: we should also try to detect special LyX shortcut such as
821 // Alt-P and Alt-M. Right now there is a hack in
822 // GuiWorkArea::processKeySym() that hides again the menubar for
824 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
826 return QMainWindow::event(e);
831 if (d.current_work_area_)
832 // Nothing special to do.
833 return QMainWindow::event(e);
835 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
836 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
838 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
839 || ke->key() == Qt::Key_Backtab)
840 return QMainWindow::event(e);
842 // Allow processing of shortcuts that are allowed even when no Buffer
844 theLyXFunc().setLyXView(this);
846 setKeySymbol(&sym, ke);
847 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
853 return QMainWindow::event(e);
857 void GuiView::resetWindowTitleAndIconText()
859 setWindowTitle(qt_("LyX"));
860 setWindowIconText(qt_("LyX"));
863 bool GuiView::focusNextPrevChild(bool /*next*/)
870 void GuiView::setBusy(bool busy)
872 if (d.current_work_area_) {
873 d.current_work_area_->setUpdatesEnabled(!busy);
875 d.current_work_area_->stopBlinkingCursor();
877 d.current_work_area_->startBlinkingCursor();
881 QApplication::setOverrideCursor(Qt::WaitCursor);
883 QApplication::restoreOverrideCursor();
887 GuiWorkArea * GuiView::workArea(Buffer & buffer)
889 if (currentWorkArea()
890 && ¤tWorkArea()->bufferView().buffer() == &buffer)
891 return (GuiWorkArea *) currentWorkArea();
892 if (TabWorkArea * twa = d.currentTabWorkArea())
893 return twa->workArea(buffer);
898 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
900 // Automatically create a TabWorkArea if there are none yet.
901 TabWorkArea * tab_widget = d.splitter_->count()
902 ? d.currentTabWorkArea() : addTabWorkArea();
903 return tab_widget->addWorkArea(buffer, *this);
907 TabWorkArea * GuiView::addTabWorkArea()
909 TabWorkArea * twa = new TabWorkArea;
910 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
911 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
912 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
913 this, SLOT(on_lastWorkAreaRemoved()));
915 d.splitter_->addWidget(twa);
916 d.stack_widget_->setCurrentWidget(d.splitter_);
921 GuiWorkArea const * GuiView::currentWorkArea() const
923 return d.current_work_area_;
927 GuiWorkArea * GuiView::currentWorkArea()
929 return d.current_work_area_;
933 GuiWorkArea const * GuiView::currentMainWorkArea() const
935 if (d.currentTabWorkArea() == NULL)
937 return d.currentTabWorkArea()->currentWorkArea();
941 GuiWorkArea * GuiView::currentMainWorkArea()
943 if (d.currentTabWorkArea() == NULL)
945 return d.currentTabWorkArea()->currentWorkArea();
949 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
951 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
953 d.current_work_area_ = NULL;
957 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
961 theGuiApp()->setCurrentView(this);
962 d.current_work_area_ = wa;
963 for (int i = 0; i != d.splitter_->count(); ++i) {
964 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
965 //if (d.current_main_work_area_)
966 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
967 d.current_main_work_area_ = wa;
968 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
969 //d.current_main_work_area_->setLineWidth(2);
970 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
974 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
975 on_currentWorkAreaChanged(wa);
976 BufferView & bv = wa->bufferView();
977 bv.cursor().fixIfBroken();
979 wa->setUpdatesEnabled(true);
980 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
984 void GuiView::removeWorkArea(GuiWorkArea * wa)
987 if (wa == d.current_work_area_) {
989 disconnectBufferView();
990 d.current_work_area_ = 0;
991 d.current_main_work_area_ = 0;
994 bool found_twa = false;
995 for (int i = 0; i != d.splitter_->count(); ++i) {
996 TabWorkArea * twa = d.tabWorkArea(i);
997 if (twa->removeWorkArea(wa)) {
998 // Found in this tab group, and deleted the GuiWorkArea.
1000 if (twa->count() != 0) {
1001 if (d.current_work_area_ == 0)
1002 // This means that we are closing the current GuiWorkArea, so
1003 // switch to the next GuiWorkArea in the found TabWorkArea.
1004 setCurrentWorkArea(twa->currentWorkArea());
1006 // No more WorkAreas in this tab group, so delete it.
1013 // It is not a tabbed work area (i.e., the search work area), so it
1014 // should be deleted by other means.
1015 LASSERT(found_twa, /* */);
1017 if (d.current_work_area_ == 0) {
1018 if (d.splitter_->count() != 0) {
1019 TabWorkArea * twa = d.currentTabWorkArea();
1020 setCurrentWorkArea(twa->currentWorkArea());
1022 // No more work areas, switch to the background widget.
1023 setCurrentWorkArea(0);
1029 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1035 void GuiView::updateLayoutList()
1038 d.layout_->updateContents(false);
1042 void GuiView::updateToolbars()
1044 ToolbarMap::iterator end = d.toolbars_.end();
1045 if (d.current_work_area_) {
1047 d.current_work_area_->bufferView().cursor().inMathed();
1049 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1051 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1052 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1053 bool const mathmacrotemplate =
1054 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1056 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1057 it->second->update(math, table, review, mathmacrotemplate);
1059 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1060 it->second->update(false, false, false, false);
1064 Buffer * GuiView::buffer()
1066 if (d.current_work_area_)
1067 return &d.current_work_area_->bufferView().buffer();
1072 Buffer const * GuiView::buffer() const
1074 if (d.current_work_area_)
1075 return &d.current_work_area_->bufferView().buffer();
1080 void GuiView::setBuffer(Buffer * newBuffer)
1082 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1083 LASSERT(newBuffer, return);
1086 GuiWorkArea * wa = workArea(*newBuffer);
1088 newBuffer->masterBuffer()->updateLabels();
1089 wa = addWorkArea(*newBuffer);
1091 //Disconnect the old buffer...there's no new one.
1094 connectBuffer(*newBuffer);
1095 connectBufferView(wa->bufferView());
1096 setCurrentWorkArea(wa);
1102 void GuiView::connectBuffer(Buffer & buf)
1104 buf.setGuiDelegate(this);
1108 void GuiView::disconnectBuffer()
1110 if (d.current_work_area_)
1111 d.current_work_area_->bufferView().setGuiDelegate(0);
1115 void GuiView::connectBufferView(BufferView & bv)
1117 bv.setGuiDelegate(this);
1121 void GuiView::disconnectBufferView()
1123 if (d.current_work_area_)
1124 d.current_work_area_->bufferView().setGuiDelegate(0);
1128 void GuiView::errors(string const & error_type)
1130 ErrorList & el = buffer()->errorList(error_type);
1132 showDialog("errorlist", error_type);
1136 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1138 d.toc_models_.updateItem(toqstr(type), dit);
1142 void GuiView::structureChanged()
1144 d.toc_models_.reset(view());
1145 // Navigator needs more than a simple update in this case. It needs to be
1147 updateDialog("toc", "");
1151 void GuiView::updateDialog(string const & name, string const & data)
1153 if (!isDialogVisible(name))
1156 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1157 if (it == d.dialogs_.end())
1160 Dialog * const dialog = it->second.get();
1161 if (dialog->isVisibleView())
1162 dialog->initialiseParams(data);
1166 BufferView * GuiView::view()
1168 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1172 void GuiView::autoSave()
1174 LYXERR(Debug::INFO, "Running autoSave()");
1177 view()->buffer().autoSave();
1181 void GuiView::resetAutosaveTimers()
1184 d.autosave_timeout_.restart();
1188 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1191 Buffer * buf = buffer();
1193 /* In LyX/Mac, when a dialog is open, the menus of the
1194 application can still be accessed without giving focus to
1195 the main window. In this case, we want to disable the menu
1196 entries that are buffer-related.
1198 Note that this code is not perfect, as bug 1941 attests:
1199 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1201 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1204 switch(cmd.action) {
1205 case LFUN_BUFFER_WRITE:
1206 enable = buf && (buf->isUnnamed() || !buf->isClean());
1209 case LFUN_BUFFER_WRITE_AS:
1213 case LFUN_SPLIT_VIEW:
1214 if (cmd.getArg(0) == "vertical")
1215 enable = buf && (d.splitter_->count() == 1 ||
1216 d.splitter_->orientation() == Qt::Vertical);
1218 enable = buf && (d.splitter_->count() == 1 ||
1219 d.splitter_->orientation() == Qt::Horizontal);
1222 case LFUN_CLOSE_TAB_GROUP:
1223 enable = d.currentTabWorkArea();
1226 case LFUN_TOOLBAR_TOGGLE:
1227 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1228 flag.setOnOff(t->isVisible());
1231 case LFUN_UI_TOGGLE:
1232 flag.setOnOff(isFullScreen());
1235 case LFUN_DIALOG_TOGGLE:
1236 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1237 // fall through to set "enable"
1238 case LFUN_DIALOG_SHOW: {
1239 string const name = cmd.getArg(0);
1241 enable = name == "aboutlyx"
1242 || name == "file" //FIXME: should be removed.
1244 || name == "texinfo";
1245 else if (name == "print")
1246 enable = buf->isExportable("dvi")
1247 && lyxrc.print_command != "none";
1248 else if (name == "character") {
1252 InsetCode ic = view()->cursor().inset().lyxCode();
1253 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1256 else if (name == "symbols") {
1257 if (!view() || view()->cursor().inMathed())
1260 InsetCode ic = view()->cursor().inset().lyxCode();
1261 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1264 else if (name == "latexlog")
1265 enable = FileName(buf->logName()).isReadableFile();
1266 else if (name == "spellchecker")
1267 #if defined (USE_ASPELL)
1268 enable = !buf->isReadonly();
1272 else if (name == "vclog")
1273 enable = buf->lyxvc().inUse();
1277 case LFUN_DIALOG_UPDATE: {
1278 string const name = cmd.getArg(0);
1280 enable = name == "prefs";
1284 case LFUN_INSET_APPLY: {
1285 string const name = cmd.getArg(0);
1286 Inset * inset = getOpenInset(name);
1288 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1290 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1291 // Every inset is supposed to handle this
1292 LASSERT(false, break);
1296 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1297 flag |= lyx::getStatus(fr);
1299 enable = flag.enabled();
1303 case LFUN_COMPLETION_INLINE:
1304 if (!d.current_work_area_
1305 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1309 case LFUN_COMPLETION_POPUP:
1310 if (!d.current_work_area_
1311 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1315 case LFUN_COMPLETION_COMPLETE:
1316 if (!d.current_work_area_
1317 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1321 case LFUN_COMPLETION_ACCEPT:
1322 if (!d.current_work_area_
1323 || (!d.current_work_area_->completer().popupVisible()
1324 && !d.current_work_area_->completer().inlineVisible()
1325 && !d.current_work_area_->completer().completionAvailable()))
1329 case LFUN_COMPLETION_CANCEL:
1330 if (!d.current_work_area_
1331 || (!d.current_work_area_->completer().popupVisible()
1332 && !d.current_work_area_->completer().inlineVisible()))
1341 flag.setEnabled(false);
1347 static FileName selectTemplateFile()
1349 FileDialog dlg(qt_("Select template file"));
1350 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1351 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1353 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1354 QStringList(qt_("LyX Documents (*.lyx)")));
1356 if (result.first == FileDialog::Later)
1358 if (result.second.isEmpty())
1360 return FileName(fromqstr(result.second));
1364 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1368 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1371 message(_("Document not loaded."));
1376 setBuffer(newBuffer);
1378 // scroll to the position when the file was last closed
1379 if (lyxrc.use_lastfilepos) {
1380 LastFilePosSection::FilePos filepos =
1381 theSession().lastFilePos().load(filename);
1382 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1386 theSession().lastFiles().add(filename);
1393 void GuiView::openDocument(string const & fname)
1395 string initpath = lyxrc.document_path;
1398 string const trypath = buffer()->filePath();
1399 // If directory is writeable, use this as default.
1400 if (FileName(trypath).isDirWritable())
1406 if (fname.empty()) {
1407 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1408 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1409 dlg.setButton2(qt_("Examples|#E#e"),
1410 toqstr(addPath(package().system_support().absFilename(), "examples")));
1412 QStringList filter(qt_("LyX Documents (*.lyx)"));
1413 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1414 << qt_("LyX-1.4.x Documents (*.lyx14)")
1415 << qt_("LyX-1.5.x Documents (*.lyx15)")
1416 << qt_("LyX-1.6.x Documents (*.lyx16)");
1417 FileDialog::Result result =
1418 dlg.open(toqstr(initpath), filter);
1420 if (result.first == FileDialog::Later)
1423 filename = fromqstr(result.second);
1425 // check selected filename
1426 if (filename.empty()) {
1427 message(_("Canceled."));
1433 // get absolute path of file and add ".lyx" to the filename if
1435 FileName const fullname =
1436 fileSearch(string(), filename, "lyx", support::may_not_exist);
1437 if (!fullname.empty())
1438 filename = fullname.absFilename();
1440 if (!fullname.onlyPath().isDirectory()) {
1441 Alert::warning(_("Invalid filename"),
1442 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1443 from_utf8(fullname.absFilename())));
1446 // if the file doesn't exist, let the user create one
1447 if (!fullname.exists()) {
1448 // the user specifically chose this name. Believe him.
1449 Buffer * const b = newFile(filename, string(), true);
1455 docstring const disp_fn = makeDisplayPath(filename);
1456 message(bformat(_("Opening document %1$s..."), disp_fn));
1459 Buffer * buf = loadDocument(fullname);
1461 buf->updateLabels();
1463 buf->errors("Parse");
1464 str2 = bformat(_("Document %1$s opened."), disp_fn);
1465 if (buf->lyxvc().inUse())
1466 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1467 " " + _("Version control detected.");
1469 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1474 // FIXME: clean that
1475 static bool import(GuiView * lv, FileName const & filename,
1476 string const & format, ErrorList & errorList)
1478 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1480 string loader_format;
1481 vector<string> loaders = theConverters().loaders();
1482 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1483 for (vector<string>::const_iterator it = loaders.begin();
1484 it != loaders.end(); ++it) {
1485 if (!theConverters().isReachable(format, *it))
1488 string const tofile =
1489 support::changeExtension(filename.absFilename(),
1490 formats.extension(*it));
1491 if (!theConverters().convert(0, filename, FileName(tofile),
1492 filename, format, *it, errorList))
1494 loader_format = *it;
1497 if (loader_format.empty()) {
1498 frontend::Alert::error(_("Couldn't import file"),
1499 bformat(_("No information for importing the format %1$s."),
1500 formats.prettyName(format)));
1504 loader_format = format;
1506 if (loader_format == "lyx") {
1507 Buffer * buf = lv->loadDocument(lyxfile);
1510 buf->updateLabels();
1512 buf->errors("Parse");
1514 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1518 bool as_paragraphs = loader_format == "textparagraph";
1519 string filename2 = (loader_format == format) ? filename.absFilename()
1520 : support::changeExtension(filename.absFilename(),
1521 formats.extension(loader_format));
1522 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1523 theLyXFunc().setLyXView(lv);
1524 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1531 void GuiView::importDocument(string const & argument)
1534 string filename = split(argument, format, ' ');
1536 LYXERR(Debug::INFO, format << " file: " << filename);
1538 // need user interaction
1539 if (filename.empty()) {
1540 string initpath = lyxrc.document_path;
1542 Buffer const * buf = buffer();
1544 string const trypath = buf->filePath();
1545 // If directory is writeable, use this as default.
1546 if (FileName(trypath).isDirWritable())
1550 docstring const text = bformat(_("Select %1$s file to import"),
1551 formats.prettyName(format));
1553 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1554 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1555 dlg.setButton2(qt_("Examples|#E#e"),
1556 toqstr(addPath(package().system_support().absFilename(), "examples")));
1558 docstring filter = formats.prettyName(format);
1561 filter += from_utf8(formats.extension(format));
1564 FileDialog::Result result =
1565 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1567 if (result.first == FileDialog::Later)
1570 filename = fromqstr(result.second);
1572 // check selected filename
1573 if (filename.empty())
1574 message(_("Canceled."));
1577 if (filename.empty())
1580 // get absolute path of file
1581 FileName const fullname(support::makeAbsPath(filename));
1583 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1585 // Check if the document already is open
1586 Buffer * buf = theBufferList().getBuffer(lyxfile);
1589 if (!closeBuffer()) {
1590 message(_("Canceled."));
1595 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1597 // if the file exists already, and we didn't do
1598 // -i lyx thefile.lyx, warn
1599 if (lyxfile.exists() && fullname != lyxfile) {
1601 docstring text = bformat(_("The document %1$s already exists.\n\n"
1602 "Do you want to overwrite that document?"), displaypath);
1603 int const ret = Alert::prompt(_("Overwrite document?"),
1604 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1607 message(_("Canceled."));
1612 message(bformat(_("Importing %1$s..."), displaypath));
1613 ErrorList errorList;
1614 if (import(this, fullname, format, errorList))
1615 message(_("imported."));
1617 message(_("file not imported!"));
1619 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1623 void GuiView::newDocument(string const & filename, bool from_template)
1625 FileName initpath(lyxrc.document_path);
1626 Buffer * buf = buffer();
1628 FileName const trypath(buf->filePath());
1629 // If directory is writeable, use this as default.
1630 if (trypath.isDirWritable())
1634 string templatefile;
1635 if (from_template) {
1636 templatefile = selectTemplateFile().absFilename();
1637 if (templatefile.empty())
1642 if (filename.empty())
1643 b = newUnnamedFile(templatefile, initpath);
1645 b = newFile(filename, templatefile, true);
1650 // If no new document could be created, it is unsure
1651 // whether there is a valid BufferView.
1653 // Ensure the cursor is correctly positioned on screen.
1654 view()->showCursor();
1658 void GuiView::insertLyXFile(docstring const & fname)
1660 BufferView * bv = view();
1665 FileName filename(to_utf8(fname));
1667 if (!filename.empty()) {
1668 bv->insertLyXFile(filename);
1672 // Launch a file browser
1674 string initpath = lyxrc.document_path;
1675 string const trypath = bv->buffer().filePath();
1676 // If directory is writeable, use this as default.
1677 if (FileName(trypath).isDirWritable())
1681 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1682 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1683 dlg.setButton2(qt_("Examples|#E#e"),
1684 toqstr(addPath(package().system_support().absFilename(),
1687 FileDialog::Result result = dlg.open(toqstr(initpath),
1688 QStringList(qt_("LyX Documents (*.lyx)")));
1690 if (result.first == FileDialog::Later)
1694 filename.set(fromqstr(result.second));
1696 // check selected filename
1697 if (filename.empty()) {
1698 // emit message signal.
1699 message(_("Canceled."));
1703 bv->insertLyXFile(filename);
1707 void GuiView::insertPlaintextFile(docstring const & fname,
1710 BufferView * bv = view();
1715 FileName filename(to_utf8(fname));
1717 if (!filename.empty()) {
1718 bv->insertPlaintextFile(filename, asParagraph);
1722 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1723 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1725 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1726 QStringList(qt_("All Files (*)")));
1728 if (result.first == FileDialog::Later)
1732 filename.set(fromqstr(result.second));
1734 // check selected filename
1735 if (filename.empty()) {
1736 // emit message signal.
1737 message(_("Canceled."));
1741 bv->insertPlaintextFile(filename, asParagraph);
1745 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1747 FileName fname = b.fileName();
1748 FileName const oldname = fname;
1750 if (!newname.empty()) {
1752 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1754 // Switch to this Buffer.
1757 // No argument? Ask user through dialog.
1759 FileDialog dlg(qt_("Choose a filename to save document as"),
1760 LFUN_BUFFER_WRITE_AS);
1761 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1762 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1764 if (!isLyXFilename(fname.absFilename()))
1765 fname.changeExtension(".lyx");
1767 FileDialog::Result result =
1768 dlg.save(toqstr(fname.onlyPath().absFilename()),
1769 QStringList(qt_("LyX Documents (*.lyx)")),
1770 toqstr(fname.onlyFileName()));
1772 if (result.first == FileDialog::Later)
1775 fname.set(fromqstr(result.second));
1780 if (!isLyXFilename(fname.absFilename()))
1781 fname.changeExtension(".lyx");
1784 if (FileName(fname).exists()) {
1785 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1786 docstring text = bformat(_("The document %1$s already "
1787 "exists.\n\nDo you want to "
1788 "overwrite that document?"),
1790 int const ret = Alert::prompt(_("Overwrite document?"),
1791 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1794 case 1: return renameBuffer(b, docstring());
1795 case 2: return false;
1799 // Ok, change the name of the buffer
1800 b.setFileName(fname.absFilename());
1802 bool unnamed = b.isUnnamed();
1803 b.setUnnamed(false);
1804 b.saveCheckSum(fname);
1806 if (!saveBuffer(b)) {
1807 b.setFileName(oldname.absFilename());
1808 b.setUnnamed(unnamed);
1809 b.saveCheckSum(oldname);
1817 bool GuiView::saveBuffer(Buffer & b)
1819 if (workArea(b) && workArea(b)->inDialogMode())
1823 return renameBuffer(b, docstring());
1826 theSession().lastFiles().add(b.fileName());
1830 // Switch to this Buffer.
1833 // FIXME: we don't tell the user *WHY* the save failed !!
1834 docstring const file = makeDisplayPath(b.absFileName(), 30);
1835 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1836 "Do you want to rename the document and "
1837 "try again?"), file);
1838 int const ret = Alert::prompt(_("Rename and save?"),
1839 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1842 if (!renameBuffer(b, docstring()))
1851 return saveBuffer(b);
1855 bool GuiView::closeBuffer()
1857 Buffer * buf = buffer();
1858 return buf && closeBuffer(*buf);
1862 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1864 // goto bookmark to update bookmark pit.
1865 //FIXME: we should update only the bookmarks related to this buffer!
1866 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1867 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1868 theLyXFunc().gotoBookmark(i+1, false, false);
1870 if (buf.isClean() || buf.paragraphs().empty()) {
1871 // save in sessions if requested
1872 // do not save childs if their master
1873 // is opened as well
1875 theSession().lastOpened().add(buf.fileName());
1877 // Don't close child documents.
1878 removeWorkArea(currentMainWorkArea());
1880 theBufferList().release(&buf);
1883 // Switch to this Buffer.
1888 if (buf.isUnnamed())
1889 file = from_utf8(buf.fileName().onlyFileName());
1891 file = buf.fileName().displayName(30);
1893 // Bring this window to top before asking questions.
1897 docstring const text = bformat(_("The document %1$s has unsaved changes."
1898 "\n\nDo you want to save the document or discard the changes?"), file);
1899 int const ret = Alert::prompt(_("Save changed document?"),
1900 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1904 if (!saveBuffer(buf))
1908 // if we crash after this we could
1909 // have no autosave file but I guess
1910 // this is really improbable (Jug)
1911 removeAutosaveFile(buf.absFileName());
1917 // save file names to .lyx/session
1919 theSession().lastOpened().add(buf.fileName());
1922 // Don't close child documents.
1923 removeWorkArea(currentMainWorkArea());
1925 theBufferList().release(&buf);
1931 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1933 Buffer * const curbuf = buffer();
1934 Buffer * nextbuf = curbuf;
1936 if (np == NEXTBUFFER)
1937 nextbuf = theBufferList().next(nextbuf);
1939 nextbuf = theBufferList().previous(nextbuf);
1940 if (nextbuf == curbuf)
1946 if (workArea(*nextbuf))
1953 bool GuiView::dispatch(FuncRequest const & cmd)
1955 BufferView * bv = view();
1956 // By default we won't need any update.
1958 bv->cursor().updateFlags(Update::None);
1959 bool dispatched = true;
1961 switch(cmd.action) {
1962 case LFUN_BUFFER_IMPORT:
1963 importDocument(to_utf8(cmd.argument()));
1966 case LFUN_BUFFER_SWITCH: {
1968 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
1972 bv->cursor().message(_("Document not loaded"));
1976 case LFUN_BUFFER_NEXT:
1977 gotoNextOrPreviousBuffer(NEXTBUFFER);
1980 case LFUN_BUFFER_PREVIOUS:
1981 gotoNextOrPreviousBuffer(PREVBUFFER);
1984 case LFUN_COMMAND_EXECUTE: {
1985 bool const show_it = cmd.argument() != "off";
1986 // FIXME: this is a hack, "minibuffer" should not be
1988 if (GuiToolbar * t = toolbar("minibuffer")) {
1989 t->setVisible(show_it);
1990 if (show_it && t->commandBuffer())
1991 t->commandBuffer()->setFocus();
1995 case LFUN_DROP_LAYOUTS_CHOICE:
1997 d.layout_->showPopup();
2000 case LFUN_MENU_OPEN:
2001 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2002 menu->exec(QCursor::pos());
2005 case LFUN_FILE_INSERT:
2006 insertLyXFile(cmd.argument());
2008 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2009 insertPlaintextFile(cmd.argument(), true);
2012 case LFUN_FILE_INSERT_PLAINTEXT:
2013 insertPlaintextFile(cmd.argument(), false);
2016 case LFUN_BUFFER_WRITE:
2018 saveBuffer(bv->buffer());
2021 case LFUN_BUFFER_WRITE_AS:
2023 renameBuffer(bv->buffer(), cmd.argument());
2026 case LFUN_BUFFER_WRITE_ALL: {
2027 Buffer * first = theBufferList().first();
2030 message(_("Saving all documents..."));
2031 // We cannot use a for loop as the buffer list cycles.
2034 if (!b->isClean()) {
2036 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2038 b = theBufferList().next(b);
2039 } while (b != first);
2040 message(_("All documents saved."));
2044 case LFUN_TOOLBAR_TOGGLE: {
2045 string const name = cmd.getArg(0);
2046 if (GuiToolbar * t = toolbar(name))
2051 case LFUN_DIALOG_UPDATE: {
2052 string const name = to_utf8(cmd.argument());
2053 // Can only update a dialog connected to an existing inset
2054 Inset * inset = getOpenInset(name);
2056 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2057 inset->dispatch(view()->cursor(), fr);
2058 } else if (name == "paragraph") {
2059 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2060 } else if (name == "prefs" || name == "document") {
2061 updateDialog(name, string());
2066 case LFUN_DIALOG_TOGGLE: {
2067 if (isDialogVisible(cmd.getArg(0)))
2068 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2070 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2074 case LFUN_DIALOG_DISCONNECT_INSET:
2075 disconnectDialog(to_utf8(cmd.argument()));
2078 case LFUN_DIALOG_HIDE: {
2079 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2083 case LFUN_DIALOG_SHOW: {
2084 string const name = cmd.getArg(0);
2085 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2087 if (name == "character") {
2088 data = freefont2string();
2090 showDialog("character", data);
2091 } else if (name == "latexlog") {
2092 Buffer::LogType type;
2093 string const logfile = buffer()->logName(&type);
2095 case Buffer::latexlog:
2098 case Buffer::buildlog:
2102 data += Lexer::quoteString(logfile);
2103 showDialog("log", data);
2104 } else if (name == "vclog") {
2105 string const data = "vc " +
2106 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2107 showDialog("log", data);
2108 } else if (name == "symbols") {
2109 data = bv->cursor().getEncoding()->name();
2111 showDialog("symbols", data);
2113 } else if (name == "prefs" && isFullScreen()) {
2114 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2116 showDialog("prefs", data);
2118 showDialog(name, data);
2122 case LFUN_INSET_APPLY: {
2123 string const name = cmd.getArg(0);
2124 Inset * inset = getOpenInset(name);
2126 // put cursor in front of inset.
2127 if (!view()->setCursorFromInset(inset)) {
2128 LASSERT(false, break);
2131 // useful if we are called from a dialog.
2132 view()->cursor().beginUndoGroup();
2133 view()->cursor().recordUndo();
2134 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2135 inset->dispatch(view()->cursor(), fr);
2136 view()->cursor().endUndoGroup();
2138 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2144 case LFUN_UI_TOGGLE:
2146 // Make sure the keyboard focus stays in the work area.
2150 case LFUN_SPLIT_VIEW:
2151 if (Buffer * buf = buffer()) {
2152 string const orientation = cmd.getArg(0);
2153 d.splitter_->setOrientation(orientation == "vertical"
2154 ? Qt::Vertical : Qt::Horizontal);
2155 TabWorkArea * twa = addTabWorkArea();
2156 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2157 setCurrentWorkArea(wa);
2161 case LFUN_CLOSE_TAB_GROUP:
2162 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2164 twa = d.currentTabWorkArea();
2165 // Switch to the next GuiWorkArea in the found TabWorkArea.
2167 // Make sure the work area is up to date.
2168 setCurrentWorkArea(twa->currentWorkArea());
2170 setCurrentWorkArea(0);
2175 case LFUN_COMPLETION_INLINE:
2176 if (d.current_work_area_)
2177 d.current_work_area_->completer().showInline();
2180 case LFUN_COMPLETION_POPUP:
2181 if (d.current_work_area_)
2182 d.current_work_area_->completer().showPopup();
2186 case LFUN_COMPLETION_COMPLETE:
2187 if (d.current_work_area_)
2188 d.current_work_area_->completer().tab();
2191 case LFUN_COMPLETION_CANCEL:
2192 if (d.current_work_area_) {
2193 if (d.current_work_area_->completer().popupVisible())
2194 d.current_work_area_->completer().hidePopup();
2196 d.current_work_area_->completer().hideInline();
2200 case LFUN_COMPLETION_ACCEPT:
2201 if (d.current_work_area_)
2202 d.current_work_area_->completer().activate();
2211 // Part of automatic menu appearance feature.
2212 if (isFullScreen()) {
2213 if (menuBar()->isVisible())
2215 if (statusBar()->isVisible())
2216 statusBar()->hide();
2223 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2225 string const arg = cmd.getArg(0);
2226 if (arg == "scrollbar") {
2227 // hide() is of no help
2228 if (d.current_work_area_->verticalScrollBarPolicy() ==
2229 Qt::ScrollBarAlwaysOff)
2231 d.current_work_area_->setVerticalScrollBarPolicy(
2232 Qt::ScrollBarAsNeeded);
2234 d.current_work_area_->setVerticalScrollBarPolicy(
2235 Qt::ScrollBarAlwaysOff);
2238 if (arg == "statusbar") {
2239 statusBar()->setVisible(!statusBar()->isVisible());
2242 if (arg == "menubar") {
2243 menuBar()->setVisible(!menuBar()->isVisible());
2246 #if QT_VERSION >= 0x040300
2247 if (arg == "frame") {
2249 getContentsMargins(&l, &t, &r, &b);
2250 //are the frames in default state?
2251 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2253 setContentsMargins(-2, -2, -2, -2);
2255 setContentsMargins(0, 0, 0, 0);
2260 if (arg == "fullscreen") {
2265 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2269 void GuiView::toggleFullScreen()
2271 if (isFullScreen()) {
2272 for (int i = 0; i != d.splitter_->count(); ++i)
2273 d.tabWorkArea(i)->setFullScreen(false);
2274 #if QT_VERSION >= 0x040300
2275 setContentsMargins(0, 0, 0, 0);
2277 setWindowState(windowState() ^ Qt::WindowFullScreen);
2280 statusBar()->show();
2283 hideDialogs("prefs", 0);
2284 for (int i = 0; i != d.splitter_->count(); ++i)
2285 d.tabWorkArea(i)->setFullScreen(true);
2286 #if QT_VERSION >= 0x040300
2287 setContentsMargins(-2, -2, -2, -2);
2290 setWindowState(windowState() ^ Qt::WindowFullScreen);
2291 statusBar()->hide();
2293 if (lyxrc.full_screen_toolbars) {
2294 ToolbarMap::iterator end = d.toolbars_.end();
2295 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2300 // give dialogs like the TOC a chance to adapt
2305 Buffer const * GuiView::updateInset(Inset const * inset)
2307 if (!d.current_work_area_)
2311 d.current_work_area_->scheduleRedraw();
2313 return &d.current_work_area_->bufferView().buffer();
2317 void GuiView::restartCursor()
2319 /* When we move around, or type, it's nice to be able to see
2320 * the cursor immediately after the keypress.
2322 if (d.current_work_area_)
2323 d.current_work_area_->startBlinkingCursor();
2325 // Take this occasion to update the other GUI elements.
2331 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2333 if (d.current_work_area_)
2334 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2339 // This list should be kept in sync with the list of insets in
2340 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2341 // dialog should have the same name as the inset.
2342 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2343 // docs in LyXAction.cpp.
2345 char const * const dialognames[] = {
2346 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2347 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2348 "float", "graphics", "include", "index", "info", "nomenclature", "label",
2349 "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2350 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2351 "symbols", "tabular", "tabularcreate",
2353 #ifdef HAVE_LIBAIKSAURUS
2357 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2359 char const * const * const end_dialognames =
2360 dialognames + (sizeof(dialognames) / sizeof(char *));
2364 cmpCStr(char const * name) : name_(name) {}
2365 bool operator()(char const * other) {
2366 return strcmp(other, name_) == 0;
2373 bool isValidName(string const & name)
2375 return find_if(dialognames, end_dialognames,
2376 cmpCStr(name.c_str())) != end_dialognames;
2382 void GuiView::resetDialogs()
2384 // Make sure that no LFUN uses any LyXView.
2385 theLyXFunc().setLyXView(0);
2388 constructToolbars();
2389 guiApp->menus().fillMenuBar(menuBar(), this, false);
2391 d.layout_->updateContents(true);
2392 // Now update controls with current buffer.
2393 theLyXFunc().setLyXView(this);
2399 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2401 if (!isValidName(name))
2404 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2406 if (it != d.dialogs_.end()) {
2408 it->second->hideView();
2409 return it->second.get();
2412 Dialog * dialog = build(name);
2413 d.dialogs_[name].reset(dialog);
2414 if (lyxrc.allow_geometry_session)
2415 dialog->restoreSession();
2422 void GuiView::showDialog(string const & name, string const & data,
2430 Dialog * dialog = findOrBuild(name, false);
2432 dialog->showData(data);
2434 d.open_insets_[name] = inset;
2437 catch (ExceptionMessage const & ex) {
2445 bool GuiView::isDialogVisible(string const & name) const
2447 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2448 if (it == d.dialogs_.end())
2450 return it->second.get()->isVisibleView();
2454 void GuiView::hideDialog(string const & name, Inset * inset)
2456 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2457 if (it == d.dialogs_.end())
2460 if (inset && inset != getOpenInset(name))
2463 Dialog * const dialog = it->second.get();
2464 if (dialog->isVisibleView())
2466 d.open_insets_[name] = 0;
2470 void GuiView::disconnectDialog(string const & name)
2472 if (!isValidName(name))
2475 if (d.open_insets_.find(name) != d.open_insets_.end())
2476 d.open_insets_[name] = 0;
2480 Inset * GuiView::getOpenInset(string const & name) const
2482 if (!isValidName(name))
2485 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2486 return it == d.open_insets_.end() ? 0 : it->second;
2490 void GuiView::hideAll() const
2492 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2493 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2495 for(; it != end; ++it)
2496 it->second->hideView();
2500 void GuiView::updateDialogs()
2502 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2503 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2505 for(; it != end; ++it) {
2506 Dialog * dialog = it->second.get();
2507 if (dialog && dialog->isVisibleView())
2508 dialog->checkStatus();
2515 // will be replaced by a proper factory...
2516 Dialog * createGuiAbout(GuiView & lv);
2517 Dialog * createGuiBibitem(GuiView & lv);
2518 Dialog * createGuiBibtex(GuiView & lv);
2519 Dialog * createGuiBox(GuiView & lv);
2520 Dialog * createGuiBranch(GuiView & lv);
2521 Dialog * createGuiChanges(GuiView & lv);
2522 Dialog * createGuiCharacter(GuiView & lv);
2523 Dialog * createGuiCitation(GuiView & lv);
2524 Dialog * createGuiDelimiter(GuiView & lv);
2525 Dialog * createGuiDocument(GuiView & lv);
2526 Dialog * createGuiErrorList(GuiView & lv);
2527 Dialog * createGuiERT(GuiView & lv);
2528 Dialog * createGuiExternal(GuiView & lv);
2529 Dialog * createGuiFloat(GuiView & lv);
2530 Dialog * createGuiGraphics(GuiView & lv);
2531 Dialog * createGuiInclude(GuiView & lv);
2532 Dialog * createGuiInfo(GuiView & lv);
2533 Dialog * createGuiLabel(GuiView & lv);
2534 Dialog * createGuiListings(GuiView & lv);
2535 Dialog * createGuiLog(GuiView & lv);
2536 Dialog * createGuiMathHSpace(GuiView & lv);
2537 Dialog * createGuiMathMatrix(GuiView & lv);
2538 Dialog * createGuiNomenclature(GuiView & lv);
2539 Dialog * createGuiNote(GuiView & lv);
2540 Dialog * createGuiParagraph(GuiView & lv);
2541 Dialog * createGuiPhantom(GuiView & lv);
2542 Dialog * createGuiPreferences(GuiView & lv);
2543 Dialog * createGuiPrint(GuiView & lv);
2544 Dialog * createGuiRef(GuiView & lv);
2545 Dialog * createGuiSearch(GuiView & lv);
2546 Dialog * createGuiSearchAdv(GuiView & lv);
2547 Dialog * createGuiSendTo(GuiView & lv);
2548 Dialog * createGuiShowFile(GuiView & lv);
2549 Dialog * createGuiSpellchecker(GuiView & lv);
2550 Dialog * createGuiSymbols(GuiView & lv);
2551 Dialog * createGuiTabularCreate(GuiView & lv);
2552 Dialog * createGuiTabular(GuiView & lv);
2553 Dialog * createGuiTexInfo(GuiView & lv);
2554 Dialog * createGuiTextHSpace(GuiView & lv);
2555 Dialog * createGuiToc(GuiView & lv);
2556 Dialog * createGuiThesaurus(GuiView & lv);
2557 Dialog * createGuiHyperlink(GuiView & lv);
2558 Dialog * createGuiVSpace(GuiView & lv);
2559 Dialog * createGuiViewSource(GuiView & lv);
2560 Dialog * createGuiWrap(GuiView & lv);
2563 Dialog * GuiView::build(string const & name)
2565 LASSERT(isValidName(name), return 0);
2567 if (name == "aboutlyx")
2568 return createGuiAbout(*this);
2569 if (name == "bibitem")
2570 return createGuiBibitem(*this);
2571 if (name == "bibtex")
2572 return createGuiBibtex(*this);
2574 return createGuiBox(*this);
2575 if (name == "branch")
2576 return createGuiBranch(*this);
2577 if (name == "changes")
2578 return createGuiChanges(*this);
2579 if (name == "character")
2580 return createGuiCharacter(*this);
2581 if (name == "citation")
2582 return createGuiCitation(*this);
2583 if (name == "document")
2584 return createGuiDocument(*this);
2585 if (name == "errorlist")
2586 return createGuiErrorList(*this);
2588 return createGuiERT(*this);
2589 if (name == "external")
2590 return createGuiExternal(*this);
2592 return createGuiShowFile(*this);
2593 if (name == "findreplace")
2594 return createGuiSearch(*this);
2595 if (name == "findreplaceadv")
2596 return createGuiSearchAdv(*this);
2597 if (name == "float")
2598 return createGuiFloat(*this);
2599 if (name == "graphics")
2600 return createGuiGraphics(*this);
2601 if (name == "include")
2602 return createGuiInclude(*this);
2604 return createGuiInfo(*this);
2605 if (name == "nomenclature")
2606 return createGuiNomenclature(*this);
2607 if (name == "label")
2608 return createGuiLabel(*this);
2610 return createGuiLog(*this);
2611 if (name == "mathdelimiter")
2612 return createGuiDelimiter(*this);
2613 if (name == "mathspace")
2614 return createGuiMathHSpace(*this);
2615 if (name == "mathmatrix")
2616 return createGuiMathMatrix(*this);
2618 return createGuiNote(*this);
2619 if (name == "paragraph")
2620 return createGuiParagraph(*this);
2621 if (name == "phantom")
2622 return createGuiPhantom(*this);
2623 if (name == "prefs")
2624 return createGuiPreferences(*this);
2625 if (name == "print")
2626 return createGuiPrint(*this);
2628 return createGuiRef(*this);
2629 if (name == "sendto")
2630 return createGuiSendTo(*this);
2631 if (name == "space")
2632 return createGuiTextHSpace(*this);
2633 if (name == "spellchecker")
2634 return createGuiSpellchecker(*this);
2635 if (name == "symbols")
2636 return createGuiSymbols(*this);
2637 if (name == "tabular")
2638 return createGuiTabular(*this);
2639 if (name == "tabularcreate")
2640 return createGuiTabularCreate(*this);
2641 if (name == "texinfo")
2642 return createGuiTexInfo(*this);
2643 if (name == "view-source")
2644 return createGuiViewSource(*this);
2645 #ifdef HAVE_LIBAIKSAURUS
2646 if (name == "thesaurus")
2647 return createGuiThesaurus(*this);
2650 return createGuiHyperlink(*this);
2651 if (name == "listings")
2652 return createGuiListings(*this);
2654 return createGuiToc(*this);
2655 if (name == "vspace")
2656 return createGuiVSpace(*this);
2658 return createGuiWrap(*this);
2664 } // namespace frontend
2667 #include "moc_GuiView.cpp"