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/FileName.h"
61 #include "support/filetools.h"
62 #include "support/gettext.h"
63 #include "support/ForkedCalls.h"
64 #include "support/lstrings.h"
65 #include "support/os.h"
66 #include "support/Package.h"
67 #include "support/Timeout.h"
70 #include <QApplication>
71 #include <QCloseEvent>
73 #include <QDesktopWidget>
74 #include <QDragEnterEvent>
82 #include <QPushButton>
86 #include <QStackedWidget>
93 #include <boost/bind.hpp>
95 #ifdef HAVE_SYS_TIME_H
96 # include <sys/time.h>
103 using namespace lyx::support;
110 class BackgroundWidget : public QWidget
115 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
116 /// The text to be written on top of the pixmap
117 QString const text = lyx_version ?
118 qt_("version ") + lyx_version : qt_("unknown version");
119 splash_ = QPixmap(":/images/banner.png");
121 QPainter pain(&splash_);
122 pain.setPen(QColor(0, 0, 0));
124 // The font used to display the version info
125 font.setStyleHint(QFont::SansSerif);
126 font.setWeight(QFont::Bold);
127 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
129 pain.drawText(190, 225, text);
132 void paintEvent(QPaintEvent *)
134 int x = (width() - splash_.width()) / 2;
135 int y = (height() - splash_.height()) / 2;
137 pain.drawPixmap(x, y, splash_);
144 /// Toolbar store providing access to individual toolbars by name.
145 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
147 typedef boost::shared_ptr<Dialog> DialogPtr;
152 struct GuiView::GuiViewPrivate
155 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
158 // hardcode here the platform specific icon size
159 smallIconSize = 14; // scaling problems
160 normalIconSize = 20; // ok, default
161 bigIconSize = 26; // better for some math icons
163 splitter_ = new QSplitter;
164 bg_widget_ = new BackgroundWidget;
165 stack_widget_ = new QStackedWidget;
166 stack_widget_->addWidget(bg_widget_);
167 stack_widget_->addWidget(splitter_);
175 delete stack_widget_;
178 QMenu * toolBarPopup(GuiView * parent)
180 // FIXME: translation
181 QMenu * menu = new QMenu(parent);
182 QActionGroup * iconSizeGroup = new QActionGroup(parent);
184 QAction * smallIcons = new QAction(iconSizeGroup);
185 smallIcons->setText(qt_("Small-sized icons"));
186 smallIcons->setCheckable(true);
187 QObject::connect(smallIcons, SIGNAL(triggered()),
188 parent, SLOT(smallSizedIcons()));
189 menu->addAction(smallIcons);
191 QAction * normalIcons = new QAction(iconSizeGroup);
192 normalIcons->setText(qt_("Normal-sized icons"));
193 normalIcons->setCheckable(true);
194 QObject::connect(normalIcons, SIGNAL(triggered()),
195 parent, SLOT(normalSizedIcons()));
196 menu->addAction(normalIcons);
198 QAction * bigIcons = new QAction(iconSizeGroup);
199 bigIcons->setText(qt_("Big-sized icons"));
200 bigIcons->setCheckable(true);
201 QObject::connect(bigIcons, SIGNAL(triggered()),
202 parent, SLOT(bigSizedIcons()));
203 menu->addAction(bigIcons);
205 unsigned int cur = parent->iconSize().width();
206 if ( cur == parent->d.smallIconSize)
207 smallIcons->setChecked(true);
208 else if (cur == parent->d.normalIconSize)
209 normalIcons->setChecked(true);
210 else if (cur == parent->d.bigIconSize)
211 bigIcons->setChecked(true);
218 stack_widget_->setCurrentWidget(bg_widget_);
219 bg_widget_->setUpdatesEnabled(true);
222 TabWorkArea * tabWorkArea(int i)
224 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
227 TabWorkArea * currentTabWorkArea()
229 if (splitter_->count() == 1)
230 // The first TabWorkArea is always the first one, if any.
231 return tabWorkArea(0);
233 for (int i = 0; i != splitter_->count(); ++i) {
234 TabWorkArea * twa = tabWorkArea(i);
235 if (current_work_area_ == twa->currentWorkArea())
239 // None has the focus so we just take the first one.
240 return tabWorkArea(0);
244 GuiWorkArea * current_work_area_;
245 QSplitter * splitter_;
246 QStackedWidget * stack_widget_;
247 BackgroundWidget * bg_widget_;
249 ToolbarMap toolbars_;
250 /// The main layout box.
252 * \warning Don't Delete! The layout box is actually owned by
253 * whichever toolbar contains it. All the GuiView class needs is a
254 * means of accessing it.
256 * FIXME: replace that with a proper model so that we are not limited
257 * to only one dialog.
259 GuiLayoutBox * layout_;
262 map<string, Inset *> open_insets_;
265 map<string, DialogPtr> dialogs_;
267 unsigned int smallIconSize;
268 unsigned int normalIconSize;
269 unsigned int bigIconSize;
271 QTimer statusbar_timer_;
272 /// auto-saving of buffers
273 Timeout autosave_timeout_;
274 /// flag against a race condition due to multiclicks, see bug #1119
278 TocModels toc_models_;
282 GuiView::GuiView(int id)
283 : d(*new GuiViewPrivate), id_(id), closing_(false)
285 // GuiToolbars *must* be initialised before the menu bar.
288 // set ourself as the current view. This is needed for the menu bar
289 // filling, at least for the static special menu item on Mac. Otherwise
290 // they are greyed out.
291 theLyXFunc().setLyXView(this);
293 // Fill up the menu bar.
294 guiApp->menus().fillMenuBar(menuBar(), this, true);
296 setCentralWidget(d.stack_widget_);
298 // Start autosave timer
299 if (lyxrc.autosave) {
300 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
301 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
302 d.autosave_timeout_.start();
304 connect(&d.statusbar_timer_, SIGNAL(timeout()),
305 this, SLOT(clearMessage()));
307 // We don't want to keep the window in memory if it is closed.
308 setAttribute(Qt::WA_DeleteOnClose, true);
310 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
311 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
312 // since the icon is provided in the application bundle.
313 setWindowIcon(QPixmap(":/images/lyx.png"));
317 setAcceptDrops(true);
319 statusBar()->setSizeGripEnabled(true);
321 // Forbid too small unresizable window because it can happen
322 // with some window manager under X11.
323 setMinimumSize(300, 200);
325 if (lyxrc.allow_geometry_session) {
326 // Now take care of session management.
331 // No session handling, default to a sane size.
332 setGeometry(50, 50, 690, 510);
334 // This enables to clear session data if any.
346 void GuiView::saveLayout() const
349 QString const key = "view-" + QString::number(id_);
351 settings.setValue(key + "/pos", pos());
352 settings.setValue(key + "/size", size());
354 settings.setValue(key + "/geometry", saveGeometry());
356 settings.setValue(key + "/layout", saveState(0));
357 settings.setValue(key + "/icon_size", iconSize());
361 bool GuiView::restoreLayout()
364 QString const key = "view-" + QString::number(id_);
365 QString const icon_key = key + "/icon_size";
366 if (!settings.contains(icon_key))
369 setIconSize(settings.value(icon_key).toSize());
371 QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
372 QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
376 if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
377 setGeometry(50, 50, 690, 510);
379 // Allow the toc and view-source dock widget to be restored if needed.
380 find_or_build("toc");
381 find_or_build("view-source");
382 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
389 GuiToolbar * GuiView::toolbar(string const & name)
391 ToolbarMap::iterator it = d.toolbars_.find(name);
392 if (it != d.toolbars_.end())
395 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
396 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
401 void GuiView::constructToolbars()
403 ToolbarMap::iterator it = d.toolbars_.begin();
404 for (; it != d.toolbars_.end(); ++it)
408 // extracts the toolbars from the backend
409 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
410 Toolbars::Infos::iterator end = guiApp->toolbars().end();
411 for (; cit != end; ++cit)
412 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
416 void GuiView::initToolbars()
418 // extracts the toolbars from the backend
419 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
420 Toolbars::Infos::iterator end = guiApp->toolbars().end();
421 for (; cit != end; ++cit) {
422 GuiToolbar * tb = toolbar(cit->name);
425 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
427 tb->setVisible(false);
428 tb->setVisibility(visibility);
430 if (visibility & Toolbars::TOP) {
432 addToolBarBreak(Qt::TopToolBarArea);
433 addToolBar(Qt::TopToolBarArea, tb);
436 if (visibility & Toolbars::BOTTOM) {
437 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
438 #if (QT_VERSION >= 0x040202)
439 addToolBarBreak(Qt::BottomToolBarArea);
441 addToolBar(Qt::BottomToolBarArea, tb);
444 if (visibility & Toolbars::LEFT) {
445 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
446 #if (QT_VERSION >= 0x040202)
447 addToolBarBreak(Qt::LeftToolBarArea);
449 addToolBar(Qt::LeftToolBarArea, tb);
452 if (visibility & Toolbars::RIGHT) {
453 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
454 #if (QT_VERSION >= 0x040202)
455 addToolBarBreak(Qt::RightToolBarArea);
457 addToolBar(Qt::RightToolBarArea, tb);
460 if (visibility & Toolbars::ON)
461 tb->setVisible(true);
466 TocModels & GuiView::tocModels()
468 return d.toc_models_;
472 void GuiView::setFocus()
474 // Make sure LyXFunc points to the correct view.
475 theLyXFunc().setLyXView(this);
476 if (d.current_work_area_)
477 d.current_work_area_->setFocus();
483 QMenu * GuiView::createPopupMenu()
485 return d.toolBarPopup(this);
489 void GuiView::showEvent(QShowEvent * e)
491 LYXERR(Debug::GUI, "Passed Geometry "
492 << size().height() << "x" << size().width()
493 << "+" << pos().x() << "+" << pos().y());
495 if (d.splitter_->count() == 0)
496 // No work area, switch to the background widget.
499 QMainWindow::showEvent(e);
503 void GuiView::closeEvent(QCloseEvent * close_event)
507 // it can happen that this event arrives without selecting the view,
508 // e.g. when clicking the close button on a background window.
509 theLyXFunc().setLyXView(this);
511 while (Buffer * b = buffer()) {
513 // This is a child document, just close the tab after saving
514 // but keep the file loaded.
515 if (!saveBuffer(*b)) {
517 close_event->ignore();
520 removeWorkArea(d.current_work_area_);
524 QList<int> const ids = guiApp->viewIds();
525 for (int i = 0; i != ids.size(); ++i) {
528 if (guiApp->view(ids[i]).workArea(*b)) {
529 // FIXME 1: should we put an alert box here that the buffer
530 // is viewed elsewhere?
531 // FIXME 2: should we try to save this buffer in any case?
534 // This buffer is also opened in another view, so
535 // close the associated work area...
536 removeWorkArea(d.current_work_area_);
537 // ... but don't close the buffer.
542 if (b && !closeBuffer(*b, true)) {
544 close_event->ignore();
549 // Make sure that nothing will use this close to be closed View.
550 guiApp->unregisterView(this);
552 if (isFullScreen()) {
553 // Switch off fullscreen before closing.
558 // Make sure the timer time out will not trigger a statusbar update.
559 d.statusbar_timer_.stop();
561 // Saving fullscreen requires additional tweaks in the toolbar code.
562 // It wouldn't also work under linux natively.
563 if (lyxrc.allow_geometry_session) {
564 // Save this window geometry and layout.
566 // Then the toolbar private states.
567 ToolbarMap::iterator end = d.toolbars_.end();
568 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
569 it->second->saveSession();
570 // Now take care of all other dialogs:
571 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
572 for (; it!= d.dialogs_.end(); ++it)
573 it->second->saveSession();
576 close_event->accept();
580 void GuiView::dragEnterEvent(QDragEnterEvent * event)
582 if (event->mimeData()->hasUrls())
584 /// \todo Ask lyx-devel is this is enough:
585 /// if (event->mimeData()->hasFormat("text/plain"))
586 /// event->acceptProposedAction();
590 void GuiView::dropEvent(QDropEvent* event)
592 QList<QUrl> files = event->mimeData()->urls();
596 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
597 for (int i = 0; i != files.size(); ++i) {
598 string const file = os::internal_path(fromqstr(
599 files.at(i).toLocalFile()));
601 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
606 void GuiView::message(docstring const & str)
608 if (ForkedProcess::iAmAChild())
611 statusBar()->showMessage(toqstr(str));
612 d.statusbar_timer_.stop();
613 d.statusbar_timer_.start(3000);
617 void GuiView::smallSizedIcons()
619 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
623 void GuiView::normalSizedIcons()
625 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
629 void GuiView::bigSizedIcons()
631 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
635 void GuiView::clearMessage()
639 theLyXFunc().setLyXView(this);
640 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
641 d.statusbar_timer_.stop();
645 void GuiView::updateWindowTitle(GuiWorkArea * wa)
647 if (wa != d.current_work_area_)
649 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
650 setWindowIconText(wa->windowIconText());
654 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
657 disconnectBufferView();
658 connectBufferView(wa->bufferView());
659 connectBuffer(wa->bufferView().buffer());
660 d.current_work_area_ = wa;
661 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
662 this, SLOT(updateWindowTitle(GuiWorkArea *)));
663 updateWindowTitle(wa);
667 // The document settings needs to be reinitialised.
668 updateDialog("document", "");
670 // Buffer-dependent dialogs must be updated. This is done here because
671 // some dialogs require buffer()->text.
676 void GuiView::on_lastWorkAreaRemoved()
679 // We already are in a close event. Nothing more to do.
682 if (d.splitter_->count() > 1)
683 // We have a splitter so don't close anything.
686 // Reset and updates the dialogs.
687 d.toc_models_.reset(0);
688 updateDialog("document", "");
691 if (lyxrc.open_buffers_in_tabs)
692 // Nothing more to do, the window should stay open.
695 if (guiApp->viewIds().size() > 1) {
701 // On Mac we also close the last window because the application stay
702 // resident in memory. On other platforms we don't close the last
703 // window because this would quit the application.
709 void GuiView::updateStatusBar()
711 // let the user see the explicit message
712 if (d.statusbar_timer_.isActive())
715 theLyXFunc().setLyXView(this);
716 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
720 bool GuiView::hasFocus() const
722 return qApp->activeWindow() == this;
726 bool GuiView::event(QEvent * e)
730 // Useful debug code:
731 //case QEvent::ActivationChange:
732 //case QEvent::WindowDeactivate:
733 //case QEvent::Paint:
734 //case QEvent::Enter:
735 //case QEvent::Leave:
736 //case QEvent::HoverEnter:
737 //case QEvent::HoverLeave:
738 //case QEvent::HoverMove:
739 //case QEvent::StatusTip:
740 //case QEvent::DragEnter:
741 //case QEvent::DragLeave:
745 case QEvent::WindowActivate: {
746 if (this == guiApp->currentView()) {
748 return QMainWindow::event(e);
750 guiApp->setCurrentView(this);
751 theLyXFunc().setLyXView(this);
752 if (d.current_work_area_) {
753 BufferView & bv = d.current_work_area_->bufferView();
754 connectBufferView(bv);
755 connectBuffer(bv.buffer());
756 // The document structure, name and dialogs might have
757 // changed in another view.
759 // The document settings needs to be reinitialised.
760 updateDialog("document", "");
763 setWindowTitle(qt_("LyX"));
764 setWindowIconText(qt_("LyX"));
767 return QMainWindow::event(e);
770 case QEvent::ShortcutOverride: {
772 if (isFullScreen() && menuBar()->isHidden()) {
773 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
774 // FIXME: we should also try to detect special LyX shortcut such as
775 // Alt-P and Alt-M. Right now there is a hack in
776 // GuiWorkArea::processKeySym() that hides again the menubar for
778 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
780 return QMainWindow::event(e);
783 if (d.current_work_area_)
784 // Nothing special to do.
785 return QMainWindow::event(e);
787 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
788 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
790 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
791 || ke->key() == Qt::Key_Backtab)
792 return QMainWindow::event(e);
794 // Allow processing of shortcuts that are allowed even when no Buffer
796 theLyXFunc().setLyXView(this);
798 setKeySymbol(&sym, ke);
799 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
805 return QMainWindow::event(e);
810 bool GuiView::focusNextPrevChild(bool /*next*/)
817 void GuiView::setBusy(bool busy)
819 if (d.current_work_area_) {
820 d.current_work_area_->setUpdatesEnabled(!busy);
822 d.current_work_area_->stopBlinkingCursor();
824 d.current_work_area_->startBlinkingCursor();
828 QApplication::setOverrideCursor(Qt::WaitCursor);
830 QApplication::restoreOverrideCursor();
834 GuiWorkArea * GuiView::workArea(Buffer & buffer)
836 if (TabWorkArea * twa = d.currentTabWorkArea())
837 return twa->workArea(buffer);
842 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
844 // Automatically create a TabWorkArea if there are none yet.
845 TabWorkArea * tab_widget = d.splitter_->count()
846 ? d.currentTabWorkArea() : addTabWorkArea();
847 return tab_widget->addWorkArea(buffer, *this);
851 TabWorkArea * GuiView::addTabWorkArea()
853 TabWorkArea * twa = new TabWorkArea;
854 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
855 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
856 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
857 this, SLOT(on_lastWorkAreaRemoved()));
859 d.splitter_->addWidget(twa);
860 d.stack_widget_->setCurrentWidget(d.splitter_);
865 GuiWorkArea const * GuiView::currentWorkArea() const
867 return d.current_work_area_;
871 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
874 d.current_work_area_ = wa;
875 for (int i = 0; i != d.splitter_->count(); ++i) {
876 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
882 void GuiView::removeWorkArea(GuiWorkArea * wa)
885 if (wa == d.current_work_area_) {
887 disconnectBufferView();
888 d.current_work_area_ = 0;
891 for (int i = 0; i != d.splitter_->count(); ++i) {
892 TabWorkArea * twa = d.tabWorkArea(i);
893 if (!twa->removeWorkArea(wa))
894 // Not found in this tab group.
897 // We found and removed the GuiWorkArea.
899 // No more WorkAreas in this tab group, so delete it.
904 if (d.current_work_area_)
905 // This means that we are not closing the current GuiWorkArea;
908 // Switch to the next GuiWorkArea in the found TabWorkArea.
909 d.current_work_area_ = twa->currentWorkArea();
913 if (d.splitter_->count() == 0)
914 // No more work area, switch to the background widget.
919 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
925 void GuiView::updateLayoutList()
928 d.layout_->updateContents(false);
932 void GuiView::updateToolbars()
934 ToolbarMap::iterator end = d.toolbars_.end();
935 if (d.current_work_area_) {
937 d.current_work_area_->bufferView().cursor().inMathed();
939 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
941 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
942 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
943 bool const mathmacrotemplate =
944 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
946 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
947 it->second->update(math, table, review, mathmacrotemplate);
949 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
950 it->second->update(false, false, false, false);
954 Buffer * GuiView::buffer()
956 if (d.current_work_area_)
957 return &d.current_work_area_->bufferView().buffer();
962 Buffer const * GuiView::buffer() const
964 if (d.current_work_area_)
965 return &d.current_work_area_->bufferView().buffer();
970 void GuiView::setBuffer(Buffer * newBuffer)
972 LASSERT(newBuffer, /**/);
975 GuiWorkArea * wa = workArea(*newBuffer);
977 updateLabels(*newBuffer->masterBuffer());
978 wa = addWorkArea(*newBuffer);
980 //Disconnect the old buffer...there's no new one.
983 connectBuffer(*newBuffer);
984 connectBufferView(wa->bufferView());
985 setCurrentWorkArea(wa);
991 void GuiView::connectBuffer(Buffer & buf)
993 buf.setGuiDelegate(this);
997 void GuiView::disconnectBuffer()
999 if (d.current_work_area_)
1000 d.current_work_area_->bufferView().setGuiDelegate(0);
1004 void GuiView::connectBufferView(BufferView & bv)
1006 bv.setGuiDelegate(this);
1010 void GuiView::disconnectBufferView()
1012 if (d.current_work_area_)
1013 d.current_work_area_->bufferView().setGuiDelegate(0);
1017 void GuiView::errors(string const & error_type)
1019 ErrorList & el = buffer()->errorList(error_type);
1021 showDialog("errorlist", error_type);
1025 void GuiView::structureChanged()
1027 d.toc_models_.reset(view());
1028 // Navigator needs more than a simple update in this case. It needs to be
1030 updateDialog("toc", "");
1034 void GuiView::updateDialog(string const & name, string const & data)
1036 if (!isDialogVisible(name))
1039 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1040 if (it == d.dialogs_.end())
1043 Dialog * const dialog = it->second.get();
1044 if (dialog->isVisibleView())
1045 dialog->initialiseParams(data);
1049 BufferView * GuiView::view()
1051 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1055 void GuiView::autoSave()
1057 LYXERR(Debug::INFO, "Running autoSave()");
1060 view()->buffer().autoSave();
1064 void GuiView::resetAutosaveTimers()
1067 d.autosave_timeout_.restart();
1071 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1074 Buffer * buf = buffer();
1076 /* In LyX/Mac, when a dialog is open, the menus of the
1077 application can still be accessed without giving focus to
1078 the main window. In this case, we want to disable the menu
1079 entries that are buffer-related.
1081 Note that this code is not perfect, as bug 1941 attests:
1082 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1084 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1087 switch(cmd.action) {
1088 case LFUN_BUFFER_WRITE:
1089 enable = buf && (buf->isUnnamed() || !buf->isClean());
1092 case LFUN_BUFFER_WRITE_AS:
1096 case LFUN_SPLIT_VIEW:
1100 case LFUN_CLOSE_TAB_GROUP:
1101 enable = d.currentTabWorkArea();
1104 case LFUN_TOOLBAR_TOGGLE:
1105 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1106 flag.setOnOff(t->isVisible());
1109 case LFUN_UI_TOGGLE:
1110 flag.setOnOff(isFullScreen());
1113 case LFUN_DIALOG_TOGGLE:
1114 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1115 // fall through to set "enable"
1116 case LFUN_DIALOG_SHOW: {
1117 string const name = cmd.getArg(0);
1119 enable = name == "aboutlyx"
1120 || name == "file" //FIXME: should be removed.
1122 || name == "texinfo";
1123 else if (name == "print")
1124 enable = buf->isExportable("dvi")
1125 && lyxrc.print_command != "none";
1126 else if (name == "character") {
1130 InsetCode ic = view()->cursor().inset().lyxCode();
1131 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1134 else if (name == "symbols") {
1135 if (!view() || view()->cursor().inMathed())
1138 InsetCode ic = view()->cursor().inset().lyxCode();
1139 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1142 else if (name == "latexlog")
1143 enable = FileName(buf->logName()).isReadableFile();
1144 else if (name == "spellchecker")
1145 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1146 enable = !buf->isReadonly();
1150 else if (name == "vclog")
1151 enable = buf->lyxvc().inUse();
1155 case LFUN_DIALOG_UPDATE: {
1156 string const name = cmd.getArg(0);
1158 enable = name == "prefs";
1162 case LFUN_INSET_APPLY: {
1163 string const name = cmd.getArg(0);
1164 Inset * inset = getOpenInset(name);
1166 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1168 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1169 // Every inset is supposed to handle this
1170 LASSERT(false, /**/);
1174 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1175 flag |= lyx::getStatus(fr);
1177 enable = flag.enabled();
1181 case LFUN_COMPLETION_INLINE:
1182 if (!d.current_work_area_
1183 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1187 case LFUN_COMPLETION_POPUP:
1188 if (!d.current_work_area_
1189 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1193 case LFUN_COMPLETION_COMPLETE:
1194 if (!d.current_work_area_
1195 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1204 flag.setEnabled(false);
1210 static FileName selectTemplateFile()
1212 FileDialog dlg(qt_("Select template file"));
1213 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1214 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1216 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1217 QStringList(qt_("LyX Documents (*.lyx)")));
1219 if (result.first == FileDialog::Later)
1221 if (result.second.isEmpty())
1223 return FileName(fromqstr(result.second));
1227 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1231 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1234 message(_("Document not loaded."));
1239 setBuffer(newBuffer);
1241 // scroll to the position when the file was last closed
1242 if (lyxrc.use_lastfilepos) {
1243 LastFilePosSection::FilePos filepos =
1244 LyX::ref().session().lastFilePos().load(filename);
1245 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1249 LyX::ref().session().lastFiles().add(filename);
1256 void GuiView::openDocument(string const & fname)
1258 string initpath = lyxrc.document_path;
1261 string const trypath = buffer()->filePath();
1262 // If directory is writeable, use this as default.
1263 if (FileName(trypath).isDirWritable())
1269 if (fname.empty()) {
1270 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1271 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1272 dlg.setButton2(qt_("Examples|#E#e"),
1273 toqstr(addPath(package().system_support().absFilename(), "examples")));
1275 FileDialog::Result result =
1276 dlg.open(toqstr(initpath), QStringList(qt_("LyX Documents (*.lyx)")));
1278 if (result.first == FileDialog::Later)
1281 filename = fromqstr(result.second);
1283 // check selected filename
1284 if (filename.empty()) {
1285 message(_("Canceled."));
1291 // get absolute path of file and add ".lyx" to the filename if
1293 FileName const fullname =
1294 fileSearch(string(), filename, "lyx", support::may_not_exist);
1295 if (!fullname.empty())
1296 filename = fullname.absFilename();
1298 // if the file doesn't exist, let the user create one
1299 if (!fullname.exists()) {
1300 // the user specifically chose this name. Believe him.
1301 Buffer * const b = newFile(filename, string(), true);
1307 docstring const disp_fn = makeDisplayPath(filename);
1308 message(bformat(_("Opening document %1$s..."), disp_fn));
1311 Buffer * buf = loadDocument(fullname);
1316 buf->errors("Parse");
1317 str2 = bformat(_("Document %1$s opened."), disp_fn);
1319 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1324 // FIXME: clean that
1325 static bool import(GuiView * lv, FileName const & filename,
1326 string const & format, ErrorList & errorList)
1328 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1330 string loader_format;
1331 vector<string> loaders = theConverters().loaders();
1332 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1333 for (vector<string>::const_iterator it = loaders.begin();
1334 it != loaders.end(); ++it) {
1335 if (!theConverters().isReachable(format, *it))
1338 string const tofile =
1339 support::changeExtension(filename.absFilename(),
1340 formats.extension(*it));
1341 if (!theConverters().convert(0, filename, FileName(tofile),
1342 filename, format, *it, errorList))
1344 loader_format = *it;
1347 if (loader_format.empty()) {
1348 frontend::Alert::error(_("Couldn't import file"),
1349 bformat(_("No information for importing the format %1$s."),
1350 formats.prettyName(format)));
1354 loader_format = format;
1356 if (loader_format == "lyx") {
1357 Buffer * buf = lv->loadDocument(lyxfile);
1362 buf->errors("Parse");
1364 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1368 bool as_paragraphs = loader_format == "textparagraph";
1369 string filename2 = (loader_format == format) ? filename.absFilename()
1370 : support::changeExtension(filename.absFilename(),
1371 formats.extension(loader_format));
1372 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1373 theLyXFunc().setLyXView(lv);
1374 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1381 void GuiView::importDocument(string const & argument)
1384 string filename = split(argument, format, ' ');
1386 LYXERR(Debug::INFO, format << " file: " << filename);
1388 // need user interaction
1389 if (filename.empty()) {
1390 string initpath = lyxrc.document_path;
1392 Buffer const * buf = buffer();
1394 string const trypath = buf->filePath();
1395 // If directory is writeable, use this as default.
1396 if (FileName(trypath).isDirWritable())
1400 docstring const text = bformat(_("Select %1$s file to import"),
1401 formats.prettyName(format));
1403 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1404 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1405 dlg.setButton2(qt_("Examples|#E#e"),
1406 toqstr(addPath(package().system_support().absFilename(), "examples")));
1408 docstring filter = formats.prettyName(format);
1411 filter += from_utf8(formats.extension(format));
1414 FileDialog::Result result =
1415 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1417 if (result.first == FileDialog::Later)
1420 filename = fromqstr(result.second);
1422 // check selected filename
1423 if (filename.empty())
1424 message(_("Canceled."));
1427 if (filename.empty())
1430 // get absolute path of file
1431 FileName const fullname(support::makeAbsPath(filename));
1433 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1435 // Check if the document already is open
1436 Buffer * buf = theBufferList().getBuffer(lyxfile.absFilename());
1439 if (!closeBuffer()) {
1440 message(_("Canceled."));
1445 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1447 // if the file exists already, and we didn't do
1448 // -i lyx thefile.lyx, warn
1449 if (lyxfile.exists() && fullname != lyxfile) {
1451 docstring text = bformat(_("The document %1$s already exists.\n\n"
1452 "Do you want to overwrite that document?"), displaypath);
1453 int const ret = Alert::prompt(_("Overwrite document?"),
1454 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1457 message(_("Canceled."));
1462 message(bformat(_("Importing %1$s..."), displaypath));
1463 ErrorList errorList;
1464 if (import(this, fullname, format, errorList))
1465 message(_("imported."));
1467 message(_("file not imported!"));
1469 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1473 void GuiView::newDocument(string const & filename, bool from_template)
1475 FileName initpath(lyxrc.document_path);
1476 Buffer * buf = buffer();
1478 FileName const trypath(buf->filePath());
1479 // If directory is writeable, use this as default.
1480 if (trypath.isDirWritable())
1484 string templatefile = from_template ?
1485 selectTemplateFile().absFilename() : string();
1487 if (filename.empty())
1488 b = newUnnamedFile(templatefile, initpath);
1490 b = newFile(filename, templatefile, true);
1494 // Ensure the cursor is correctly positionned on screen.
1495 view()->showCursor();
1499 void GuiView::insertLyXFile(docstring const & fname)
1501 BufferView * bv = view();
1506 FileName filename(to_utf8(fname));
1508 if (!filename.empty()) {
1509 bv->insertLyXFile(filename);
1513 // Launch a file browser
1515 string initpath = lyxrc.document_path;
1516 string const trypath = bv->buffer().filePath();
1517 // If directory is writeable, use this as default.
1518 if (FileName(trypath).isDirWritable())
1522 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1523 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1524 dlg.setButton2(qt_("Examples|#E#e"),
1525 toqstr(addPath(package().system_support().absFilename(),
1528 FileDialog::Result result = dlg.open(toqstr(initpath),
1529 QStringList(qt_("LyX Documents (*.lyx)")));
1531 if (result.first == FileDialog::Later)
1535 filename.set(fromqstr(result.second));
1537 // check selected filename
1538 if (filename.empty()) {
1539 // emit message signal.
1540 message(_("Canceled."));
1544 bv->insertLyXFile(filename);
1548 void GuiView::insertPlaintextFile(docstring const & fname,
1551 BufferView * bv = view();
1556 FileName filename(to_utf8(fname));
1558 if (!filename.empty()) {
1559 bv->insertPlaintextFile(filename, asParagraph);
1563 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1564 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1566 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1569 if (result.first == FileDialog::Later)
1573 filename.set(fromqstr(result.second));
1575 // check selected filename
1576 if (filename.empty()) {
1577 // emit message signal.
1578 message(_("Canceled."));
1582 bv->insertPlaintextFile(filename, asParagraph);
1586 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1588 FileName fname = b.fileName();
1589 FileName const oldname = fname;
1591 if (!newname.empty()) {
1593 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1595 // Switch to this Buffer.
1598 /// No argument? Ask user through dialog.
1600 FileDialog dlg(qt_("Choose a filename to save document as"),
1601 LFUN_BUFFER_WRITE_AS);
1602 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1603 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1605 if (!isLyXFilename(fname.absFilename()))
1606 fname.changeExtension(".lyx");
1608 FileDialog::Result result =
1609 dlg.save(toqstr(fname.onlyPath().absFilename()),
1610 QStringList(qt_("LyX Documents (*.lyx)")),
1611 toqstr(fname.onlyFileName()));
1613 if (result.first == FileDialog::Later)
1616 fname.set(fromqstr(result.second));
1621 if (!isLyXFilename(fname.absFilename()))
1622 fname.changeExtension(".lyx");
1625 if (FileName(fname).exists()) {
1626 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1627 docstring text = bformat(_("The document %1$s already "
1628 "exists.\n\nDo you want to "
1629 "overwrite that document?"),
1631 int const ret = Alert::prompt(_("Overwrite document?"),
1632 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1635 case 1: return renameBuffer(b, docstring());
1636 case 2: return false;
1640 // Ok, change the name of the buffer
1641 b.setFileName(fname.absFilename());
1643 bool unnamed = b.isUnnamed();
1644 b.setUnnamed(false);
1645 b.saveCheckSum(fname);
1647 if (!saveBuffer(b)) {
1648 b.setFileName(oldname.absFilename());
1649 b.setUnnamed(unnamed);
1650 b.saveCheckSum(oldname);
1658 bool GuiView::saveBuffer(Buffer & b)
1661 return renameBuffer(b, docstring());
1664 LyX::ref().session().lastFiles().add(b.fileName());
1668 // Switch to this Buffer.
1671 // FIXME: we don't tell the user *WHY* the save failed !!
1672 docstring const file = makeDisplayPath(b.absFileName(), 30);
1673 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1674 "Do you want to rename the document and "
1675 "try again?"), file);
1676 int const ret = Alert::prompt(_("Rename and save?"),
1677 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1680 if (!renameBuffer(b, docstring()))
1689 return saveBuffer(b);
1693 bool GuiView::closeBuffer()
1695 Buffer * buf = buffer();
1696 return buf && closeBuffer(*buf);
1700 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1702 // goto bookmark to update bookmark pit.
1703 //FIXME: we should update only the bookmarks related to this buffer!
1704 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1705 theLyXFunc().gotoBookmark(i+1, false, false);
1707 if (buf.isClean() || buf.paragraphs().empty()) {
1708 if (buf.masterBuffer() == &buf && tolastopened)
1709 LyX::ref().session().lastOpened().add(buf.fileName());
1710 theBufferList().release(&buf);
1713 // Switch to this Buffer.
1718 if (buf.isUnnamed())
1719 file = from_utf8(buf.fileName().onlyFileName());
1721 file = buf.fileName().displayName(30);
1723 // Bring this window to top before asking questions.
1727 docstring const text = bformat(_("The document %1$s has unsaved changes."
1728 "\n\nDo you want to save the document or discard the changes?"), file);
1729 int const ret = Alert::prompt(_("Save changed document?"),
1730 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1734 if (!saveBuffer(buf))
1738 // if we crash after this we could
1739 // have no autosave file but I guess
1740 // this is really improbable (Jug)
1741 removeAutosaveFile(buf.absFileName());
1747 // save file names to .lyx/session
1748 // if master/slave are both open, do not save slave since it
1749 // will be automatically loaded when the master is loaded
1750 if (buf.masterBuffer() == &buf && tolastopened)
1751 LyX::ref().session().lastOpened().add(buf.fileName());
1754 // Don't close child documents.
1755 removeWorkArea(d.current_work_area_);
1757 theBufferList().release(&buf);
1763 bool GuiView::dispatch(FuncRequest const & cmd)
1765 BufferView * bv = view();
1766 // By default we won't need any update.
1768 bv->cursor().updateFlags(Update::None);
1769 bool dispatched = true;
1771 switch(cmd.action) {
1772 case LFUN_BUFFER_IMPORT:
1773 importDocument(to_utf8(cmd.argument()));
1776 case LFUN_BUFFER_SWITCH:
1777 setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
1780 case LFUN_BUFFER_NEXT:
1781 setBuffer(theBufferList().next(buffer()));
1784 case LFUN_BUFFER_PREVIOUS:
1785 setBuffer(theBufferList().previous(buffer()));
1788 case LFUN_COMMAND_EXECUTE: {
1789 bool const show_it = cmd.argument() != "off";
1790 // FIXME: this is a hack, "minibuffer" should not be
1792 if (GuiToolbar * t = toolbar("minibuffer")) {
1793 t->setVisible(show_it);
1794 if (show_it && t->commandBuffer())
1795 t->commandBuffer()->setFocus();
1799 case LFUN_DROP_LAYOUTS_CHOICE:
1801 d.layout_->showPopup();
1804 case LFUN_MENU_OPEN:
1805 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1806 menu->exec(QCursor::pos());
1809 case LFUN_FILE_INSERT:
1810 insertLyXFile(cmd.argument());
1812 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1813 insertPlaintextFile(cmd.argument(), true);
1816 case LFUN_FILE_INSERT_PLAINTEXT:
1817 insertPlaintextFile(cmd.argument(), false);
1820 case LFUN_BUFFER_WRITE:
1822 saveBuffer(bv->buffer());
1825 case LFUN_BUFFER_WRITE_AS:
1827 renameBuffer(bv->buffer(), cmd.argument());
1830 case LFUN_BUFFER_WRITE_ALL: {
1831 Buffer * first = theBufferList().first();
1834 message(_("Saving all documents..."));
1835 // We cannot use a for loop as the buffer list cycles.
1838 if (!b->isClean()) {
1840 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1842 b = theBufferList().next(b);
1843 } while (b != first);
1844 message(_("All documents saved."));
1848 case LFUN_TOOLBAR_TOGGLE: {
1849 string const name = cmd.getArg(0);
1850 if (GuiToolbar * t = toolbar(name))
1855 case LFUN_DIALOG_UPDATE: {
1856 string const name = to_utf8(cmd.argument());
1857 // Can only update a dialog connected to an existing inset
1858 Inset * inset = getOpenInset(name);
1860 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1861 inset->dispatch(view()->cursor(), fr);
1862 } else if (name == "paragraph") {
1863 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1864 } else if (name == "prefs") {
1865 updateDialog(name, string());
1870 case LFUN_DIALOG_TOGGLE: {
1871 if (isDialogVisible(cmd.getArg(0)))
1872 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1874 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1878 case LFUN_DIALOG_DISCONNECT_INSET:
1879 disconnectDialog(to_utf8(cmd.argument()));
1882 case LFUN_DIALOG_HIDE: {
1883 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1887 case LFUN_DIALOG_SHOW: {
1888 string const name = cmd.getArg(0);
1889 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1891 if (name == "character") {
1892 data = freefont2string();
1894 showDialog("character", data);
1895 } else if (name == "latexlog") {
1896 Buffer::LogType type;
1897 string const logfile = buffer()->logName(&type);
1899 case Buffer::latexlog:
1902 case Buffer::buildlog:
1906 data += Lexer::quoteString(logfile);
1907 showDialog("log", data);
1908 } else if (name == "vclog") {
1909 string const data = "vc " +
1910 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1911 showDialog("log", data);
1912 } else if (name == "symbols") {
1913 data = bv->cursor().getEncoding()->name();
1915 showDialog("symbols", data);
1917 showDialog(name, data);
1921 case LFUN_INSET_APPLY: {
1922 view()->cursor().recordUndoFullDocument();
1923 string const name = cmd.getArg(0);
1924 Inset * inset = getOpenInset(name);
1926 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1927 inset->dispatch(view()->cursor(), fr);
1929 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1935 case LFUN_UI_TOGGLE:
1937 // Make sure the keyboard focus stays in the work area.
1941 case LFUN_COMPLETION_INLINE:
1942 if (d.current_work_area_)
1943 d.current_work_area_->completer().showInline();
1946 case LFUN_SPLIT_VIEW:
1947 if (Buffer * buf = buffer()) {
1948 string const orientation = cmd.getArg(0);
1949 d.splitter_->setOrientation(orientation == "vertical"
1950 ? Qt::Vertical : Qt::Horizontal);
1951 TabWorkArea * twa = addTabWorkArea();
1952 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1953 setCurrentWorkArea(wa);
1957 case LFUN_CLOSE_TAB_GROUP:
1958 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1960 twa = d.currentTabWorkArea();
1961 // Switch to the next GuiWorkArea in the found TabWorkArea.
1962 d.current_work_area_ = twa? twa->currentWorkArea() : 0;
1963 if (d.splitter_->count() == 0)
1964 // No more work area, switch to the background widget.
1969 case LFUN_COMPLETION_POPUP:
1970 if (d.current_work_area_)
1971 d.current_work_area_->completer().showPopup();
1975 case LFUN_COMPLETION_COMPLETE:
1976 if (d.current_work_area_)
1977 d.current_work_area_->completer().tab();
1985 if (isFullScreen()) {
1986 if (menuBar()->isVisible())
1988 if (statusBar()->isVisible())
1989 statusBar()->hide();
1996 void GuiView::lfunUiToggle(FuncRequest const & cmd)
1998 string const arg = cmd.getArg(0);
1999 if (arg == "scrollbar") {
2000 // hide() is of no help
2001 if (d.current_work_area_->verticalScrollBarPolicy() ==
2002 Qt::ScrollBarAlwaysOff)
2004 d.current_work_area_->setVerticalScrollBarPolicy(
2005 Qt::ScrollBarAsNeeded);
2007 d.current_work_area_->setVerticalScrollBarPolicy(
2008 Qt::ScrollBarAlwaysOff);
2011 if (arg == "statusbar") {
2012 statusBar()->setVisible(!statusBar()->isVisible());
2015 if (arg == "menubar") {
2016 menuBar()->setVisible(!menuBar()->isVisible());
2019 #if QT_VERSION >= 0x040300
2020 if (arg == "frame") {
2022 getContentsMargins(&l, &t, &r, &b);
2023 //are the frames in default state?
2024 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2026 setContentsMargins(-2, -2, -2, -2);
2028 setContentsMargins(0, 0, 0, 0);
2033 if (arg == "fullscreen") {
2038 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2042 void GuiView::toggleFullScreen()
2044 if (isFullScreen()) {
2045 for (int i = 0; i != d.splitter_->count(); ++i)
2046 d.tabWorkArea(i)->setFullScreen(false);
2047 #if QT_VERSION >= 0x040300
2048 setContentsMargins(0, 0, 0, 0);
2050 setWindowState(windowState() ^ Qt::WindowFullScreen);
2053 statusBar()->show();
2055 for (int i = 0; i != d.splitter_->count(); ++i)
2056 d.tabWorkArea(i)->setFullScreen(true);
2057 #if QT_VERSION >= 0x040300
2058 setContentsMargins(-2, -2, -2, -2);
2061 setWindowState(windowState() ^ Qt::WindowFullScreen);
2062 statusBar()->hide();
2064 if (lyxrc.full_screen_toolbars) {
2065 ToolbarMap::iterator end = d.toolbars_.end();
2066 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2073 Buffer const * GuiView::updateInset(Inset const * inset)
2075 if (!d.current_work_area_)
2079 d.current_work_area_->scheduleRedraw();
2081 return &d.current_work_area_->bufferView().buffer();
2085 void GuiView::restartCursor()
2087 /* When we move around, or type, it's nice to be able to see
2088 * the cursor immediately after the keypress.
2090 if (d.current_work_area_)
2091 d.current_work_area_->startBlinkingCursor();
2093 // Take this occasion to update the other GUI elements.
2099 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2101 if (d.current_work_area_)
2102 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2107 // This list should be kept in sync with the list of insets in
2108 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2109 // dialog should have the same name as the inset.
2111 char const * const dialognames[] = {
2112 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2113 "citation", "document", "errorlist", "ert", "external", "file",
2114 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2115 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2116 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2118 #ifdef HAVE_LIBAIKSAURUS
2122 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2124 char const * const * const end_dialognames =
2125 dialognames + (sizeof(dialognames) / sizeof(char *));
2129 cmpCStr(char const * name) : name_(name) {}
2130 bool operator()(char const * other) {
2131 return strcmp(other, name_) == 0;
2138 bool isValidName(string const & name)
2140 return find_if(dialognames, end_dialognames,
2141 cmpCStr(name.c_str())) != end_dialognames;
2147 void GuiView::resetDialogs()
2149 // Make sure that no LFUN uses any LyXView.
2150 theLyXFunc().setLyXView(0);
2151 // FIXME: the "math panels" toolbar takes an awful lot of time to
2152 // initialise so we don't do that for the time being.
2154 guiApp->menus().fillMenuBar(menuBar(), this);
2156 d.layout_->updateContents(true);
2157 // Now update controls with current buffer.
2158 theLyXFunc().setLyXView(this);
2163 Dialog * GuiView::find_or_build(string const & name)
2165 if (!isValidName(name))
2168 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2170 if (it != d.dialogs_.end())
2171 return it->second.get();
2173 Dialog * dialog = build(name);
2174 d.dialogs_[name].reset(dialog);
2175 if (lyxrc.allow_geometry_session)
2176 dialog->restoreSession();
2181 void GuiView::showDialog(string const & name, string const & data,
2188 Dialog * dialog = find_or_build(name);
2190 dialog->showData(data);
2192 d.open_insets_[name] = inset;
2198 bool GuiView::isDialogVisible(string const & name) const
2200 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2201 if (it == d.dialogs_.end())
2203 return it->second.get()->isVisibleView();
2207 void GuiView::hideDialog(string const & name, Inset * inset)
2209 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2210 if (it == d.dialogs_.end())
2213 if (inset && inset != getOpenInset(name))
2216 Dialog * const dialog = it->second.get();
2217 if (dialog->isVisibleView())
2219 d.open_insets_[name] = 0;
2223 void GuiView::disconnectDialog(string const & name)
2225 if (!isValidName(name))
2228 if (d.open_insets_.find(name) != d.open_insets_.end())
2229 d.open_insets_[name] = 0;
2233 Inset * GuiView::getOpenInset(string const & name) const
2235 if (!isValidName(name))
2238 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2239 return it == d.open_insets_.end() ? 0 : it->second;
2243 void GuiView::hideAll() const
2245 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2246 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2248 for(; it != end; ++it)
2249 it->second->hideView();
2253 void GuiView::updateDialogs()
2255 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2256 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2258 for(; it != end; ++it) {
2259 Dialog * dialog = it->second.get();
2260 if (dialog && dialog->isVisibleView())
2261 dialog->checkStatus();
2268 // will be replaced by a proper factory...
2269 Dialog * createGuiAbout(GuiView & lv);
2270 Dialog * createGuiBibitem(GuiView & lv);
2271 Dialog * createGuiBibtex(GuiView & lv);
2272 Dialog * createGuiBox(GuiView & lv);
2273 Dialog * createGuiBranch(GuiView & lv);
2274 Dialog * createGuiChanges(GuiView & lv);
2275 Dialog * createGuiCharacter(GuiView & lv);
2276 Dialog * createGuiCitation(GuiView & lv);
2277 Dialog * createGuiDelimiter(GuiView & lv);
2278 Dialog * createGuiDocument(GuiView & lv);
2279 Dialog * createGuiErrorList(GuiView & lv);
2280 Dialog * createGuiERT(GuiView & lv);
2281 Dialog * createGuiExternal(GuiView & lv);
2282 Dialog * createGuiFloat(GuiView & lv);
2283 Dialog * createGuiGraphics(GuiView & lv);
2284 Dialog * createGuiHSpace(GuiView & lv);
2285 Dialog * createGuiInclude(GuiView & lv);
2286 Dialog * createGuiInfo(GuiView & lv);
2287 Dialog * createGuiLabel(GuiView & lv);
2288 Dialog * createGuiListings(GuiView & lv);
2289 Dialog * createGuiLog(GuiView & lv);
2290 Dialog * createGuiMathMatrix(GuiView & lv);
2291 Dialog * createGuiNomenclature(GuiView & lv);
2292 Dialog * createGuiNote(GuiView & lv);
2293 Dialog * createGuiParagraph(GuiView & lv);
2294 Dialog * createGuiPreferences(GuiView & lv);
2295 Dialog * createGuiPrint(GuiView & lv);
2296 Dialog * createGuiRef(GuiView & lv);
2297 Dialog * createGuiSearch(GuiView & lv);
2298 Dialog * createGuiSendTo(GuiView & lv);
2299 Dialog * createGuiShowFile(GuiView & lv);
2300 Dialog * createGuiSpellchecker(GuiView & lv);
2301 Dialog * createGuiSymbols(GuiView & lv);
2302 Dialog * createGuiTabularCreate(GuiView & lv);
2303 Dialog * createGuiTabular(GuiView & lv);
2304 Dialog * createGuiTexInfo(GuiView & lv);
2305 Dialog * createGuiToc(GuiView & lv);
2306 Dialog * createGuiThesaurus(GuiView & lv);
2307 Dialog * createGuiHyperlink(GuiView & lv);
2308 Dialog * createGuiVSpace(GuiView & lv);
2309 Dialog * createGuiViewSource(GuiView & lv);
2310 Dialog * createGuiWrap(GuiView & lv);
2313 Dialog * GuiView::build(string const & name)
2315 LASSERT(isValidName(name), /**/);
2317 if (name == "aboutlyx")
2318 return createGuiAbout(*this);
2319 if (name == "bibitem")
2320 return createGuiBibitem(*this);
2321 if (name == "bibtex")
2322 return createGuiBibtex(*this);
2324 return createGuiBox(*this);
2325 if (name == "branch")
2326 return createGuiBranch(*this);
2327 if (name == "changes")
2328 return createGuiChanges(*this);
2329 if (name == "character")
2330 return createGuiCharacter(*this);
2331 if (name == "citation")
2332 return createGuiCitation(*this);
2333 if (name == "document")
2334 return createGuiDocument(*this);
2335 if (name == "errorlist")
2336 return createGuiErrorList(*this);
2338 return createGuiERT(*this);
2339 if (name == "external")
2340 return createGuiExternal(*this);
2342 return createGuiShowFile(*this);
2343 if (name == "findreplace")
2344 return createGuiSearch(*this);
2345 if (name == "float")
2346 return createGuiFloat(*this);
2347 if (name == "graphics")
2348 return createGuiGraphics(*this);
2349 if (name == "include")
2350 return createGuiInclude(*this);
2352 return createGuiInfo(*this);
2353 if (name == "nomenclature")
2354 return createGuiNomenclature(*this);
2355 if (name == "label")
2356 return createGuiLabel(*this);
2358 return createGuiLog(*this);
2359 if (name == "view-source")
2360 return createGuiViewSource(*this);
2361 if (name == "mathdelimiter")
2362 return createGuiDelimiter(*this);
2363 if (name == "mathmatrix")
2364 return createGuiMathMatrix(*this);
2366 return createGuiNote(*this);
2367 if (name == "paragraph")
2368 return createGuiParagraph(*this);
2369 if (name == "prefs")
2370 return createGuiPreferences(*this);
2371 if (name == "print")
2372 return createGuiPrint(*this);
2374 return createGuiRef(*this);
2375 if (name == "sendto")
2376 return createGuiSendTo(*this);
2377 if (name == "space")
2378 return createGuiHSpace(*this);
2379 if (name == "spellchecker")
2380 return createGuiSpellchecker(*this);
2381 if (name == "symbols")
2382 return createGuiSymbols(*this);
2383 if (name == "tabular")
2384 return createGuiTabular(*this);
2385 if (name == "tabularcreate")
2386 return createGuiTabularCreate(*this);
2387 if (name == "texinfo")
2388 return createGuiTexInfo(*this);
2389 #ifdef HAVE_LIBAIKSAURUS
2390 if (name == "thesaurus")
2391 return createGuiThesaurus(*this);
2394 return createGuiToc(*this);
2396 return createGuiHyperlink(*this);
2397 if (name == "vspace")
2398 return createGuiVSpace(*this);
2400 return createGuiWrap(*this);
2401 if (name == "listings")
2402 return createGuiListings(*this);
2408 } // namespace frontend
2411 #include "GuiView_moc.cpp"