3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "FileDialog.h"
20 #include "GuiApplication.h"
21 #include "GuiCommandBuffer.h"
22 #include "GuiCompleter.h"
23 #include "GuiWorkArea.h"
24 #include "GuiKeySymbol.h"
25 #include "GuiToolbar.h"
29 #include "qt_helpers.h"
31 #include "frontends/alert.h"
33 #include "buffer_funcs.h"
35 #include "BufferList.h"
36 #include "BufferParams.h"
37 #include "BufferView.h"
38 #include "Converter.h"
41 #include "ErrorList.h"
43 #include "FuncStatus.h"
44 #include "FuncRequest.h"
52 #include "Paragraph.h"
53 #include "TextClass.h"
58 #include "support/lassert.h"
59 #include "support/debug.h"
60 #include "support/ExceptionMessage.h"
61 #include "support/FileName.h"
62 #include "support/filetools.h"
63 #include "support/gettext.h"
64 #include "support/ForkedCalls.h"
65 #include "support/lstrings.h"
66 #include "support/os.h"
67 #include "support/Package.h"
68 #include "support/Timeout.h"
71 #include <QApplication>
72 #include <QCloseEvent>
74 #include <QDesktopWidget>
75 #include <QDragEnterEvent>
83 #include <QPushButton>
87 #include <QStackedWidget>
94 #include <boost/bind.hpp>
96 #ifdef HAVE_SYS_TIME_H
97 # include <sys/time.h>
104 using namespace lyx::support;
111 class BackgroundWidget : public QWidget
116 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
117 /// The text to be written on top of the pixmap
118 QString const text = lyx_version ?
119 qt_("version ") + lyx_version : qt_("unknown version");
120 splash_ = QPixmap(":/images/banner.png");
122 QPainter pain(&splash_);
123 pain.setPen(QColor(0, 0, 0));
125 // The font used to display the version info
126 font.setStyleHint(QFont::SansSerif);
127 font.setWeight(QFont::Bold);
128 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
130 pain.drawText(190, 225, 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_);
145 /// Toolbar store providing access to individual toolbars by name.
146 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
148 typedef boost::shared_ptr<Dialog> DialogPtr;
153 struct GuiView::GuiViewPrivate
156 : current_work_area_(0), layout_(0), autosave_timeout_(5000),
159 // hardcode here the platform specific icon size
160 smallIconSize = 14; // scaling problems
161 normalIconSize = 20; // ok, default
162 bigIconSize = 26; // better for some math icons
164 splitter_ = new QSplitter;
165 bg_widget_ = new BackgroundWidget;
166 stack_widget_ = new QStackedWidget;
167 stack_widget_->addWidget(bg_widget_);
168 stack_widget_->addWidget(splitter_);
176 delete stack_widget_;
179 QMenu * toolBarPopup(GuiView * parent)
181 // FIXME: translation
182 QMenu * menu = new QMenu(parent);
183 QActionGroup * iconSizeGroup = new QActionGroup(parent);
185 QAction * smallIcons = new QAction(iconSizeGroup);
186 smallIcons->setText(qt_("Small-sized icons"));
187 smallIcons->setCheckable(true);
188 QObject::connect(smallIcons, SIGNAL(triggered()),
189 parent, SLOT(smallSizedIcons()));
190 menu->addAction(smallIcons);
192 QAction * normalIcons = new QAction(iconSizeGroup);
193 normalIcons->setText(qt_("Normal-sized icons"));
194 normalIcons->setCheckable(true);
195 QObject::connect(normalIcons, SIGNAL(triggered()),
196 parent, SLOT(normalSizedIcons()));
197 menu->addAction(normalIcons);
199 QAction * bigIcons = new QAction(iconSizeGroup);
200 bigIcons->setText(qt_("Big-sized icons"));
201 bigIcons->setCheckable(true);
202 QObject::connect(bigIcons, SIGNAL(triggered()),
203 parent, SLOT(bigSizedIcons()));
204 menu->addAction(bigIcons);
206 unsigned int cur = parent->iconSize().width();
207 if ( cur == parent->d.smallIconSize)
208 smallIcons->setChecked(true);
209 else if (cur == parent->d.normalIconSize)
210 normalIcons->setChecked(true);
211 else if (cur == parent->d.bigIconSize)
212 bigIcons->setChecked(true);
219 stack_widget_->setCurrentWidget(bg_widget_);
220 bg_widget_->setUpdatesEnabled(true);
223 TabWorkArea * tabWorkArea(int i)
225 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
228 TabWorkArea * currentTabWorkArea()
230 if (splitter_->count() == 1)
231 // The first TabWorkArea is always the first one, if any.
232 return tabWorkArea(0);
234 for (int i = 0; i != splitter_->count(); ++i) {
235 TabWorkArea * twa = tabWorkArea(i);
236 if (current_work_area_ == twa->currentWorkArea())
240 // None has the focus so we just take the first one.
241 return tabWorkArea(0);
245 GuiWorkArea * current_work_area_;
246 QSplitter * splitter_;
247 QStackedWidget * stack_widget_;
248 BackgroundWidget * bg_widget_;
250 ToolbarMap toolbars_;
251 /// The main layout box.
253 * \warning Don't Delete! The layout box is actually owned by
254 * whichever toolbar contains it. All the GuiView class needs is a
255 * means of accessing it.
257 * FIXME: replace that with a proper model so that we are not limited
258 * to only one dialog.
260 GuiLayoutBox * layout_;
263 map<string, Inset *> open_insets_;
266 map<string, DialogPtr> dialogs_;
268 unsigned int smallIconSize;
269 unsigned int normalIconSize;
270 unsigned int bigIconSize;
272 QTimer statusbar_timer_;
273 /// auto-saving of buffers
274 Timeout autosave_timeout_;
275 /// flag against a race condition due to multiclicks, see bug #1119
279 TocModels toc_models_;
283 GuiView::GuiView(int id)
284 : d(*new GuiViewPrivate), id_(id), closing_(false)
286 // GuiToolbars *must* be initialised before the menu bar.
289 // set ourself as the current view. This is needed for the menu bar
290 // filling, at least for the static special menu item on Mac. Otherwise
291 // they are greyed out.
292 theLyXFunc().setLyXView(this);
294 // Fill up the menu bar.
295 guiApp->menus().fillMenuBar(menuBar(), this, true);
297 setCentralWidget(d.stack_widget_);
299 // Start autosave timer
300 if (lyxrc.autosave) {
301 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
302 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
303 d.autosave_timeout_.start();
305 connect(&d.statusbar_timer_, SIGNAL(timeout()),
306 this, SLOT(clearMessage()));
308 // We don't want to keep the window in memory if it is closed.
309 setAttribute(Qt::WA_DeleteOnClose, true);
311 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
312 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
313 // since the icon is provided in the application bundle.
314 setWindowIcon(QPixmap(":/images/lyx.png"));
318 setAcceptDrops(true);
320 statusBar()->setSizeGripEnabled(true);
322 // Forbid too small unresizable window because it can happen
323 // with some window manager under X11.
324 setMinimumSize(300, 200);
326 if (lyxrc.allow_geometry_session) {
327 // Now take care of session management.
332 // No session handling, default to a sane size.
333 setGeometry(50, 50, 690, 510);
335 // This enables to clear session data if any.
347 void GuiView::saveLayout() const
350 QString const key = "view-" + QString::number(id_);
352 settings.setValue(key + "/pos", pos());
353 settings.setValue(key + "/size", size());
355 settings.setValue(key + "/geometry", saveGeometry());
357 settings.setValue(key + "/layout", saveState(0));
358 settings.setValue(key + "/icon_size", iconSize());
362 bool GuiView::restoreLayout()
365 QString const key = "view-" + QString::number(id_);
366 QString const icon_key = key + "/icon_size";
367 if (!settings.contains(icon_key))
370 setIconSize(settings.value(icon_key).toSize());
372 QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
373 QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
377 if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
378 setGeometry(50, 50, 690, 510);
380 // Allow the toc and view-source dock widget to be restored if needed.
381 findOrBuild("toc", true);
382 findOrBuild("view-source", true);
383 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
390 GuiToolbar * GuiView::toolbar(string const & name)
392 ToolbarMap::iterator it = d.toolbars_.find(name);
393 if (it != d.toolbars_.end())
396 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
397 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
402 void GuiView::constructToolbars()
404 ToolbarMap::iterator it = d.toolbars_.begin();
405 for (; it != d.toolbars_.end(); ++it)
410 // extracts the toolbars from the backend
411 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
412 Toolbars::Infos::iterator end = guiApp->toolbars().end();
413 for (; cit != end; ++cit)
414 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
418 void GuiView::initToolbars()
420 // extracts the toolbars from the backend
421 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
422 Toolbars::Infos::iterator end = guiApp->toolbars().end();
423 for (; cit != end; ++cit) {
424 GuiToolbar * tb = toolbar(cit->name);
427 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
429 tb->setVisible(false);
430 tb->setVisibility(visibility);
432 if (visibility & Toolbars::TOP) {
434 addToolBarBreak(Qt::TopToolBarArea);
435 addToolBar(Qt::TopToolBarArea, tb);
438 if (visibility & Toolbars::BOTTOM) {
439 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
440 #if (QT_VERSION >= 0x040202)
441 addToolBarBreak(Qt::BottomToolBarArea);
443 addToolBar(Qt::BottomToolBarArea, tb);
446 if (visibility & Toolbars::LEFT) {
447 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
448 #if (QT_VERSION >= 0x040202)
449 addToolBarBreak(Qt::LeftToolBarArea);
451 addToolBar(Qt::LeftToolBarArea, tb);
454 if (visibility & Toolbars::RIGHT) {
455 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
456 #if (QT_VERSION >= 0x040202)
457 addToolBarBreak(Qt::RightToolBarArea);
459 addToolBar(Qt::RightToolBarArea, tb);
462 if (visibility & Toolbars::ON)
463 tb->setVisible(true);
468 TocModels & GuiView::tocModels()
470 return d.toc_models_;
474 void GuiView::setFocus()
476 // Make sure LyXFunc points to the correct view.
477 theLyXFunc().setLyXView(this);
478 if (d.current_work_area_)
479 d.current_work_area_->setFocus();
485 QMenu * GuiView::createPopupMenu()
487 return d.toolBarPopup(this);
491 void GuiView::showEvent(QShowEvent * e)
493 LYXERR(Debug::GUI, "Passed Geometry "
494 << size().height() << "x" << size().width()
495 << "+" << pos().x() << "+" << pos().y());
497 if (d.splitter_->count() == 0)
498 // No work area, switch to the background widget.
501 QMainWindow::showEvent(e);
505 void GuiView::closeEvent(QCloseEvent * close_event)
509 // it can happen that this event arrives without selecting the view,
510 // e.g. when clicking the close button on a background window.
511 theLyXFunc().setLyXView(this);
513 while (Buffer * b = buffer()) {
515 // This is a child document, just close the tab after saving
516 // but keep the file loaded.
517 if (!saveBuffer(*b)) {
519 close_event->ignore();
522 removeWorkArea(d.current_work_area_);
526 QList<int> const ids = guiApp->viewIds();
527 for (int i = 0; i != ids.size(); ++i) {
530 if (guiApp->view(ids[i]).workArea(*b)) {
531 // FIXME 1: should we put an alert box here that the buffer
532 // is viewed elsewhere?
533 // FIXME 2: should we try to save this buffer in any case?
536 // This buffer is also opened in another view, so
537 // close the associated work area...
538 removeWorkArea(d.current_work_area_);
539 // ... but don't close the buffer.
544 if (b && !closeBuffer(*b, true)) {
546 close_event->ignore();
551 // Make sure that nothing will use this close to be closed View.
552 guiApp->unregisterView(this);
554 if (isFullScreen()) {
555 // Switch off fullscreen before closing.
560 // Make sure the timer time out will not trigger a statusbar update.
561 d.statusbar_timer_.stop();
563 // Saving fullscreen requires additional tweaks in the toolbar code.
564 // It wouldn't also work under linux natively.
565 if (lyxrc.allow_geometry_session) {
566 // Save this window geometry and layout.
568 // Then the toolbar private states.
569 ToolbarMap::iterator end = d.toolbars_.end();
570 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
571 it->second->saveSession();
572 // Now take care of all other dialogs:
573 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
574 for (; it!= d.dialogs_.end(); ++it)
575 it->second->saveSession();
578 close_event->accept();
582 void GuiView::dragEnterEvent(QDragEnterEvent * event)
584 if (event->mimeData()->hasUrls())
586 /// \todo Ask lyx-devel is this is enough:
587 /// if (event->mimeData()->hasFormat("text/plain"))
588 /// event->acceptProposedAction();
592 void GuiView::dropEvent(QDropEvent* event)
594 QList<QUrl> files = event->mimeData()->urls();
598 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
599 for (int i = 0; i != files.size(); ++i) {
600 string const file = os::internal_path(fromqstr(
601 files.at(i).toLocalFile()));
603 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
608 void GuiView::message(docstring const & str)
610 if (ForkedProcess::iAmAChild())
613 statusBar()->showMessage(toqstr(str));
614 d.statusbar_timer_.stop();
615 d.statusbar_timer_.start(3000);
619 void GuiView::smallSizedIcons()
621 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
625 void GuiView::normalSizedIcons()
627 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
631 void GuiView::bigSizedIcons()
633 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
637 void GuiView::clearMessage()
641 theLyXFunc().setLyXView(this);
642 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
643 d.statusbar_timer_.stop();
647 void GuiView::updateWindowTitle(GuiWorkArea * wa)
649 if (wa != d.current_work_area_)
651 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
652 setWindowIconText(wa->windowIconText());
656 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
659 disconnectBufferView();
660 connectBufferView(wa->bufferView());
661 connectBuffer(wa->bufferView().buffer());
662 d.current_work_area_ = wa;
663 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
664 this, SLOT(updateWindowTitle(GuiWorkArea *)));
665 updateWindowTitle(wa);
669 // The document settings needs to be reinitialised.
670 updateDialog("document", "");
672 // Buffer-dependent dialogs must be updated. This is done here because
673 // some dialogs require buffer()->text.
678 void GuiView::on_lastWorkAreaRemoved()
681 // We already are in a close event. Nothing more to do.
684 if (d.splitter_->count() > 1)
685 // We have a splitter so don't close anything.
688 // Reset and updates the dialogs.
689 d.toc_models_.reset(0);
690 updateDialog("document", "");
693 resetWindowTitleAndIconText();
695 if (lyxrc.open_buffers_in_tabs)
696 // Nothing more to do, the window should stay open.
699 if (guiApp->viewIds().size() > 1) {
705 // On Mac we also close the last window because the application stay
706 // resident in memory. On other platforms we don't close the last
707 // window because this would quit the application.
713 void GuiView::updateStatusBar()
715 // let the user see the explicit message
716 if (d.statusbar_timer_.isActive())
719 theLyXFunc().setLyXView(this);
720 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
724 bool GuiView::hasFocus() const
726 return qApp->activeWindow() == this;
730 bool GuiView::event(QEvent * e)
734 // Useful debug code:
735 //case QEvent::ActivationChange:
736 //case QEvent::WindowDeactivate:
737 //case QEvent::Paint:
738 //case QEvent::Enter:
739 //case QEvent::Leave:
740 //case QEvent::HoverEnter:
741 //case QEvent::HoverLeave:
742 //case QEvent::HoverMove:
743 //case QEvent::StatusTip:
744 //case QEvent::DragEnter:
745 //case QEvent::DragLeave:
749 case QEvent::WindowActivate: {
750 if (this == guiApp->currentView()) {
752 return QMainWindow::event(e);
754 guiApp->setCurrentView(this);
755 theLyXFunc().setLyXView(this);
756 if (d.current_work_area_) {
757 BufferView & bv = d.current_work_area_->bufferView();
758 connectBufferView(bv);
759 connectBuffer(bv.buffer());
760 // The document structure, name and dialogs might have
761 // changed in another view.
763 // The document settings needs to be reinitialised.
764 updateDialog("document", "");
767 resetWindowTitleAndIconText();
770 return QMainWindow::event(e);
773 case QEvent::ShortcutOverride: {
777 if (isFullScreen() && menuBar()->isHidden()) {
778 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
779 // FIXME: we should also try to detect special LyX shortcut such as
780 // Alt-P and Alt-M. Right now there is a hack in
781 // GuiWorkArea::processKeySym() that hides again the menubar for
783 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
785 return QMainWindow::event(e);
789 if (d.current_work_area_)
790 // Nothing special to do.
791 return QMainWindow::event(e);
793 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
794 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
796 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
797 || ke->key() == Qt::Key_Backtab)
798 return QMainWindow::event(e);
800 // Allow processing of shortcuts that are allowed even when no Buffer
802 theLyXFunc().setLyXView(this);
804 setKeySymbol(&sym, ke);
805 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
811 return QMainWindow::event(e);
815 void GuiView::resetWindowTitleAndIconText()
817 setWindowTitle(qt_("LyX"));
818 setWindowIconText(qt_("LyX"));
821 bool GuiView::focusNextPrevChild(bool /*next*/)
828 void GuiView::setBusy(bool busy)
830 if (d.current_work_area_) {
831 d.current_work_area_->setUpdatesEnabled(!busy);
833 d.current_work_area_->stopBlinkingCursor();
835 d.current_work_area_->startBlinkingCursor();
839 QApplication::setOverrideCursor(Qt::WaitCursor);
841 QApplication::restoreOverrideCursor();
845 GuiWorkArea * GuiView::workArea(Buffer & buffer)
847 if (TabWorkArea * twa = d.currentTabWorkArea())
848 return twa->workArea(buffer);
853 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
855 // Automatically create a TabWorkArea if there are none yet.
856 TabWorkArea * tab_widget = d.splitter_->count()
857 ? d.currentTabWorkArea() : addTabWorkArea();
858 return tab_widget->addWorkArea(buffer, *this);
862 TabWorkArea * GuiView::addTabWorkArea()
864 TabWorkArea * twa = new TabWorkArea;
865 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
866 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
867 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
868 this, SLOT(on_lastWorkAreaRemoved()));
870 d.splitter_->addWidget(twa);
871 d.stack_widget_->setCurrentWidget(d.splitter_);
876 GuiWorkArea const * GuiView::currentWorkArea() const
878 return d.current_work_area_;
882 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
885 d.current_work_area_ = wa;
886 for (int i = 0; i != d.splitter_->count(); ++i) {
887 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
893 void GuiView::removeWorkArea(GuiWorkArea * wa)
896 if (wa == d.current_work_area_) {
898 disconnectBufferView();
899 d.current_work_area_ = 0;
902 for (int i = 0; i != d.splitter_->count(); ++i) {
903 TabWorkArea * twa = d.tabWorkArea(i);
904 if (!twa->removeWorkArea(wa))
905 // Not found in this tab group.
908 // We found and removed the GuiWorkArea.
910 // No more WorkAreas in this tab group, so delete it.
915 if (d.current_work_area_)
916 // This means that we are not closing the current GuiWorkArea;
919 // Switch to the next GuiWorkArea in the found TabWorkArea.
920 d.current_work_area_ = twa->currentWorkArea();
924 if (d.splitter_->count() == 0)
925 // No more work area, switch to the background widget.
930 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
936 void GuiView::updateLayoutList()
939 d.layout_->updateContents(false);
943 void GuiView::updateToolbars()
945 ToolbarMap::iterator end = d.toolbars_.end();
946 if (d.current_work_area_) {
948 d.current_work_area_->bufferView().cursor().inMathed();
950 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
952 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
953 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
954 bool const mathmacrotemplate =
955 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
957 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
958 it->second->update(math, table, review, mathmacrotemplate);
960 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
961 it->second->update(false, false, false, false);
965 Buffer * GuiView::buffer()
967 if (d.current_work_area_)
968 return &d.current_work_area_->bufferView().buffer();
973 Buffer const * GuiView::buffer() const
975 if (d.current_work_area_)
976 return &d.current_work_area_->bufferView().buffer();
981 void GuiView::setBuffer(Buffer * newBuffer)
983 LASSERT(newBuffer, /**/);
986 GuiWorkArea * wa = workArea(*newBuffer);
988 updateLabels(*newBuffer->masterBuffer());
989 wa = addWorkArea(*newBuffer);
991 //Disconnect the old buffer...there's no new one.
994 connectBuffer(*newBuffer);
995 connectBufferView(wa->bufferView());
996 setCurrentWorkArea(wa);
1002 void GuiView::connectBuffer(Buffer & buf)
1004 buf.setGuiDelegate(this);
1008 void GuiView::disconnectBuffer()
1010 if (d.current_work_area_)
1011 d.current_work_area_->bufferView().setGuiDelegate(0);
1015 void GuiView::connectBufferView(BufferView & bv)
1017 bv.setGuiDelegate(this);
1021 void GuiView::disconnectBufferView()
1023 if (d.current_work_area_)
1024 d.current_work_area_->bufferView().setGuiDelegate(0);
1028 void GuiView::errors(string const & error_type)
1030 ErrorList & el = buffer()->errorList(error_type);
1032 showDialog("errorlist", error_type);
1036 void GuiView::structureChanged()
1038 d.toc_models_.reset(view());
1039 // Navigator needs more than a simple update in this case. It needs to be
1041 updateDialog("toc", "");
1045 void GuiView::updateDialog(string const & name, string const & data)
1047 if (!isDialogVisible(name))
1050 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1051 if (it == d.dialogs_.end())
1054 Dialog * const dialog = it->second.get();
1055 if (dialog->isVisibleView())
1056 dialog->initialiseParams(data);
1060 BufferView * GuiView::view()
1062 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1066 void GuiView::autoSave()
1068 LYXERR(Debug::INFO, "Running autoSave()");
1071 view()->buffer().autoSave();
1075 void GuiView::resetAutosaveTimers()
1078 d.autosave_timeout_.restart();
1082 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1085 Buffer * buf = buffer();
1087 /* In LyX/Mac, when a dialog is open, the menus of the
1088 application can still be accessed without giving focus to
1089 the main window. In this case, we want to disable the menu
1090 entries that are buffer-related.
1092 Note that this code is not perfect, as bug 1941 attests:
1093 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1095 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1098 switch(cmd.action) {
1099 case LFUN_BUFFER_WRITE:
1100 enable = buf && (buf->isUnnamed() || !buf->isClean());
1103 case LFUN_BUFFER_WRITE_AS:
1107 case LFUN_SPLIT_VIEW:
1108 if (cmd.getArg(0) == "vertical")
1109 enable = buf && (d.splitter_->count() == 1 ||
1110 d.splitter_->orientation() == Qt::Vertical);
1112 enable = buf && (d.splitter_->count() == 1 ||
1113 d.splitter_->orientation() == Qt::Horizontal);
1116 case LFUN_CLOSE_TAB_GROUP:
1117 enable = d.currentTabWorkArea();
1120 case LFUN_TOOLBAR_TOGGLE:
1121 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1122 flag.setOnOff(t->isVisible());
1125 case LFUN_UI_TOGGLE:
1126 flag.setOnOff(isFullScreen());
1129 case LFUN_DIALOG_TOGGLE:
1130 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1131 // fall through to set "enable"
1132 case LFUN_DIALOG_SHOW: {
1133 string const name = cmd.getArg(0);
1135 enable = name == "aboutlyx"
1136 || name == "file" //FIXME: should be removed.
1138 || name == "texinfo";
1139 else if (name == "print")
1140 enable = buf->isExportable("dvi")
1141 && lyxrc.print_command != "none";
1142 else if (name == "character") {
1146 InsetCode ic = view()->cursor().inset().lyxCode();
1147 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1150 else if (name == "symbols") {
1151 if (!view() || view()->cursor().inMathed())
1154 InsetCode ic = view()->cursor().inset().lyxCode();
1155 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1158 else if (name == "latexlog")
1159 enable = FileName(buf->logName()).isReadableFile();
1160 else if (name == "spellchecker")
1161 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1162 enable = !buf->isReadonly();
1166 else if (name == "vclog")
1167 enable = buf->lyxvc().inUse();
1171 case LFUN_DIALOG_UPDATE: {
1172 string const name = cmd.getArg(0);
1174 enable = name == "prefs";
1178 case LFUN_INSET_APPLY: {
1179 string const name = cmd.getArg(0);
1180 Inset * inset = getOpenInset(name);
1182 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1184 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1185 // Every inset is supposed to handle this
1186 LASSERT(false, /**/);
1190 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1191 flag |= lyx::getStatus(fr);
1193 enable = flag.enabled();
1197 case LFUN_COMPLETION_INLINE:
1198 if (!d.current_work_area_
1199 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1203 case LFUN_COMPLETION_POPUP:
1204 if (!d.current_work_area_
1205 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1209 case LFUN_COMPLETION_COMPLETE:
1210 if (!d.current_work_area_
1211 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1215 case LFUN_COMPLETION_ACCEPT:
1216 case LFUN_COMPLETION_CANCEL:
1217 if (!d.current_work_area_
1218 || (!d.current_work_area_->completer().popupVisible()
1219 && !d.current_work_area_->completer().inlineVisible()))
1228 flag.setEnabled(false);
1234 static FileName selectTemplateFile()
1236 FileDialog dlg(qt_("Select template file"));
1237 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1238 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1240 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1241 QStringList(qt_("LyX Documents (*.lyx)")));
1243 if (result.first == FileDialog::Later)
1245 if (result.second.isEmpty())
1247 return FileName(fromqstr(result.second));
1251 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1255 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1258 message(_("Document not loaded."));
1263 setBuffer(newBuffer);
1265 // scroll to the position when the file was last closed
1266 if (lyxrc.use_lastfilepos) {
1267 LastFilePosSection::FilePos filepos =
1268 theSession().lastFilePos().load(filename);
1269 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1273 theSession().lastFiles().add(filename);
1280 void GuiView::openDocument(string const & fname)
1282 string initpath = lyxrc.document_path;
1285 string const trypath = buffer()->filePath();
1286 // If directory is writeable, use this as default.
1287 if (FileName(trypath).isDirWritable())
1293 if (fname.empty()) {
1294 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1295 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1296 dlg.setButton2(qt_("Examples|#E#e"),
1297 toqstr(addPath(package().system_support().absFilename(), "examples")));
1299 QStringList filter(qt_("LyX Documents (*.lyx)"));
1300 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1301 << qt_("LyX-1.4.x Documents (*.lyx14)")
1302 << qt_("LyX-1.5.x Documents (*.lyx15)");
1303 FileDialog::Result result =
1304 dlg.open(toqstr(initpath), filter);
1306 if (result.first == FileDialog::Later)
1309 filename = fromqstr(result.second);
1311 // check selected filename
1312 if (filename.empty()) {
1313 message(_("Canceled."));
1319 // get absolute path of file and add ".lyx" to the filename if
1321 FileName const fullname =
1322 fileSearch(string(), filename, "lyx", support::may_not_exist);
1323 if (!fullname.empty())
1324 filename = fullname.absFilename();
1326 if (!fullname.onlyPath().isDirectory()) {
1327 Alert::warning(_("Invalid filename"),
1328 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1329 from_utf8(fullname.absFilename())));
1332 // if the file doesn't exist, let the user create one
1333 if (!fullname.exists()) {
1334 // the user specifically chose this name. Believe him.
1335 Buffer * const b = newFile(filename, string(), true);
1341 docstring const disp_fn = makeDisplayPath(filename);
1342 message(bformat(_("Opening document %1$s..."), disp_fn));
1345 Buffer * buf = loadDocument(fullname);
1350 buf->errors("Parse");
1351 str2 = bformat(_("Document %1$s opened."), disp_fn);
1352 if (buf->lyxvc().inUse())
1353 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1354 " " + _("Version control detected.");
1356 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1361 // FIXME: clean that
1362 static bool import(GuiView * lv, FileName const & filename,
1363 string const & format, ErrorList & errorList)
1365 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1367 string loader_format;
1368 vector<string> loaders = theConverters().loaders();
1369 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1370 for (vector<string>::const_iterator it = loaders.begin();
1371 it != loaders.end(); ++it) {
1372 if (!theConverters().isReachable(format, *it))
1375 string const tofile =
1376 support::changeExtension(filename.absFilename(),
1377 formats.extension(*it));
1378 if (!theConverters().convert(0, filename, FileName(tofile),
1379 filename, format, *it, errorList))
1381 loader_format = *it;
1384 if (loader_format.empty()) {
1385 frontend::Alert::error(_("Couldn't import file"),
1386 bformat(_("No information for importing the format %1$s."),
1387 formats.prettyName(format)));
1391 loader_format = format;
1393 if (loader_format == "lyx") {
1394 Buffer * buf = lv->loadDocument(lyxfile);
1399 buf->errors("Parse");
1401 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1405 bool as_paragraphs = loader_format == "textparagraph";
1406 string filename2 = (loader_format == format) ? filename.absFilename()
1407 : support::changeExtension(filename.absFilename(),
1408 formats.extension(loader_format));
1409 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1410 theLyXFunc().setLyXView(lv);
1411 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1418 void GuiView::importDocument(string const & argument)
1421 string filename = split(argument, format, ' ');
1423 LYXERR(Debug::INFO, format << " file: " << filename);
1425 // need user interaction
1426 if (filename.empty()) {
1427 string initpath = lyxrc.document_path;
1429 Buffer const * buf = buffer();
1431 string const trypath = buf->filePath();
1432 // If directory is writeable, use this as default.
1433 if (FileName(trypath).isDirWritable())
1437 docstring const text = bformat(_("Select %1$s file to import"),
1438 formats.prettyName(format));
1440 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1441 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1442 dlg.setButton2(qt_("Examples|#E#e"),
1443 toqstr(addPath(package().system_support().absFilename(), "examples")));
1445 docstring filter = formats.prettyName(format);
1448 filter += from_utf8(formats.extension(format));
1451 FileDialog::Result result =
1452 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1454 if (result.first == FileDialog::Later)
1457 filename = fromqstr(result.second);
1459 // check selected filename
1460 if (filename.empty())
1461 message(_("Canceled."));
1464 if (filename.empty())
1467 // get absolute path of file
1468 FileName const fullname(support::makeAbsPath(filename));
1470 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1472 // Check if the document already is open
1473 Buffer * buf = theBufferList().getBuffer(lyxfile);
1476 if (!closeBuffer()) {
1477 message(_("Canceled."));
1482 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1484 // if the file exists already, and we didn't do
1485 // -i lyx thefile.lyx, warn
1486 if (lyxfile.exists() && fullname != lyxfile) {
1488 docstring text = bformat(_("The document %1$s already exists.\n\n"
1489 "Do you want to overwrite that document?"), displaypath);
1490 int const ret = Alert::prompt(_("Overwrite document?"),
1491 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1494 message(_("Canceled."));
1499 message(bformat(_("Importing %1$s..."), displaypath));
1500 ErrorList errorList;
1501 if (import(this, fullname, format, errorList))
1502 message(_("imported."));
1504 message(_("file not imported!"));
1506 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1510 void GuiView::newDocument(string const & filename, bool from_template)
1512 FileName initpath(lyxrc.document_path);
1513 Buffer * buf = buffer();
1515 FileName const trypath(buf->filePath());
1516 // If directory is writeable, use this as default.
1517 if (trypath.isDirWritable())
1521 string templatefile = from_template ?
1522 selectTemplateFile().absFilename() : string();
1524 if (filename.empty())
1525 b = newUnnamedFile(templatefile, initpath);
1527 b = newFile(filename, templatefile, true);
1531 // Ensure the cursor is correctly positionned on screen.
1532 view()->showCursor();
1536 void GuiView::insertLyXFile(docstring const & fname)
1538 BufferView * bv = view();
1543 FileName filename(to_utf8(fname));
1545 if (!filename.empty()) {
1546 bv->insertLyXFile(filename);
1550 // Launch a file browser
1552 string initpath = lyxrc.document_path;
1553 string const trypath = bv->buffer().filePath();
1554 // If directory is writeable, use this as default.
1555 if (FileName(trypath).isDirWritable())
1559 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1560 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1561 dlg.setButton2(qt_("Examples|#E#e"),
1562 toqstr(addPath(package().system_support().absFilename(),
1565 FileDialog::Result result = dlg.open(toqstr(initpath),
1566 QStringList(qt_("LyX Documents (*.lyx)")));
1568 if (result.first == FileDialog::Later)
1572 filename.set(fromqstr(result.second));
1574 // check selected filename
1575 if (filename.empty()) {
1576 // emit message signal.
1577 message(_("Canceled."));
1581 bv->insertLyXFile(filename);
1585 void GuiView::insertPlaintextFile(docstring const & fname,
1588 BufferView * bv = view();
1593 FileName filename(to_utf8(fname));
1595 if (!filename.empty()) {
1596 bv->insertPlaintextFile(filename, asParagraph);
1600 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1601 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1603 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1606 if (result.first == FileDialog::Later)
1610 filename.set(fromqstr(result.second));
1612 // check selected filename
1613 if (filename.empty()) {
1614 // emit message signal.
1615 message(_("Canceled."));
1619 bv->insertPlaintextFile(filename, asParagraph);
1623 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1625 FileName fname = b.fileName();
1626 FileName const oldname = fname;
1628 if (!newname.empty()) {
1630 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1632 // Switch to this Buffer.
1635 /// No argument? Ask user through dialog.
1637 FileDialog dlg(qt_("Choose a filename to save document as"),
1638 LFUN_BUFFER_WRITE_AS);
1639 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1640 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1642 if (!isLyXFilename(fname.absFilename()))
1643 fname.changeExtension(".lyx");
1645 FileDialog::Result result =
1646 dlg.save(toqstr(fname.onlyPath().absFilename()),
1647 QStringList(qt_("LyX Documents (*.lyx)")),
1648 toqstr(fname.onlyFileName()));
1650 if (result.first == FileDialog::Later)
1653 fname.set(fromqstr(result.second));
1658 if (!isLyXFilename(fname.absFilename()))
1659 fname.changeExtension(".lyx");
1662 if (FileName(fname).exists()) {
1663 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1664 docstring text = bformat(_("The document %1$s already "
1665 "exists.\n\nDo you want to "
1666 "overwrite that document?"),
1668 int const ret = Alert::prompt(_("Overwrite document?"),
1669 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1672 case 1: return renameBuffer(b, docstring());
1673 case 2: return false;
1677 // Ok, change the name of the buffer
1678 b.setFileName(fname.absFilename());
1680 bool unnamed = b.isUnnamed();
1681 b.setUnnamed(false);
1682 b.saveCheckSum(fname);
1684 if (!saveBuffer(b)) {
1685 b.setFileName(oldname.absFilename());
1686 b.setUnnamed(unnamed);
1687 b.saveCheckSum(oldname);
1695 bool GuiView::saveBuffer(Buffer & b)
1698 return renameBuffer(b, docstring());
1701 theSession().lastFiles().add(b.fileName());
1705 // Switch to this Buffer.
1708 // FIXME: we don't tell the user *WHY* the save failed !!
1709 docstring const file = makeDisplayPath(b.absFileName(), 30);
1710 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1711 "Do you want to rename the document and "
1712 "try again?"), file);
1713 int const ret = Alert::prompt(_("Rename and save?"),
1714 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1717 if (!renameBuffer(b, docstring()))
1726 return saveBuffer(b);
1730 bool GuiView::closeBuffer()
1732 Buffer * buf = buffer();
1733 return buf && closeBuffer(*buf);
1737 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1739 // goto bookmark to update bookmark pit.
1740 //FIXME: we should update only the bookmarks related to this buffer!
1741 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1742 theLyXFunc().gotoBookmark(i+1, false, false);
1744 if (buf.isClean() || buf.paragraphs().empty()) {
1745 if (buf.masterBuffer() == &buf && tolastopened)
1746 theSession().lastOpened().add(buf.fileName());
1747 theBufferList().release(&buf);
1750 // Switch to this Buffer.
1755 if (buf.isUnnamed())
1756 file = from_utf8(buf.fileName().onlyFileName());
1758 file = buf.fileName().displayName(30);
1760 // Bring this window to top before asking questions.
1764 docstring const text = bformat(_("The document %1$s has unsaved changes."
1765 "\n\nDo you want to save the document or discard the changes?"), file);
1766 int const ret = Alert::prompt(_("Save changed document?"),
1767 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1771 if (!saveBuffer(buf))
1775 // if we crash after this we could
1776 // have no autosave file but I guess
1777 // this is really improbable (Jug)
1778 removeAutosaveFile(buf.absFileName());
1784 // save file names to .lyx/session
1785 // if master/slave are both open, do not save slave since it
1786 // will be automatically loaded when the master is loaded
1787 if (buf.masterBuffer() == &buf && tolastopened)
1788 theSession().lastOpened().add(buf.fileName());
1791 // Don't close child documents.
1792 removeWorkArea(d.current_work_area_);
1794 theBufferList().release(&buf);
1800 bool GuiView::dispatch(FuncRequest const & cmd)
1802 BufferView * bv = view();
1803 // By default we won't need any update.
1805 bv->cursor().updateFlags(Update::None);
1806 bool dispatched = true;
1808 switch(cmd.action) {
1809 case LFUN_BUFFER_IMPORT:
1810 importDocument(to_utf8(cmd.argument()));
1813 case LFUN_BUFFER_SWITCH:
1814 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1817 case LFUN_BUFFER_NEXT:
1818 setBuffer(theBufferList().next(buffer()));
1821 case LFUN_BUFFER_PREVIOUS:
1822 setBuffer(theBufferList().previous(buffer()));
1825 case LFUN_COMMAND_EXECUTE: {
1826 bool const show_it = cmd.argument() != "off";
1827 // FIXME: this is a hack, "minibuffer" should not be
1829 if (GuiToolbar * t = toolbar("minibuffer")) {
1830 t->setVisible(show_it);
1831 if (show_it && t->commandBuffer())
1832 t->commandBuffer()->setFocus();
1836 case LFUN_DROP_LAYOUTS_CHOICE:
1838 d.layout_->showPopup();
1841 case LFUN_MENU_OPEN:
1842 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1843 menu->exec(QCursor::pos());
1846 case LFUN_FILE_INSERT:
1847 insertLyXFile(cmd.argument());
1849 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1850 insertPlaintextFile(cmd.argument(), true);
1853 case LFUN_FILE_INSERT_PLAINTEXT:
1854 insertPlaintextFile(cmd.argument(), false);
1857 case LFUN_BUFFER_WRITE:
1859 saveBuffer(bv->buffer());
1862 case LFUN_BUFFER_WRITE_AS:
1864 renameBuffer(bv->buffer(), cmd.argument());
1867 case LFUN_BUFFER_WRITE_ALL: {
1868 Buffer * first = theBufferList().first();
1871 message(_("Saving all documents..."));
1872 // We cannot use a for loop as the buffer list cycles.
1875 if (!b->isClean()) {
1877 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1879 b = theBufferList().next(b);
1880 } while (b != first);
1881 message(_("All documents saved."));
1885 case LFUN_TOOLBAR_TOGGLE: {
1886 string const name = cmd.getArg(0);
1887 if (GuiToolbar * t = toolbar(name))
1892 case LFUN_DIALOG_UPDATE: {
1893 string const name = to_utf8(cmd.argument());
1894 // Can only update a dialog connected to an existing inset
1895 Inset * inset = getOpenInset(name);
1897 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1898 inset->dispatch(view()->cursor(), fr);
1899 } else if (name == "paragraph") {
1900 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1901 } else if (name == "prefs") {
1902 updateDialog(name, string());
1907 case LFUN_DIALOG_TOGGLE: {
1908 if (isDialogVisible(cmd.getArg(0)))
1909 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1911 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1915 case LFUN_DIALOG_DISCONNECT_INSET:
1916 disconnectDialog(to_utf8(cmd.argument()));
1919 case LFUN_DIALOG_HIDE: {
1920 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1924 case LFUN_DIALOG_SHOW: {
1925 string const name = cmd.getArg(0);
1926 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1928 if (name == "character") {
1929 data = freefont2string();
1931 showDialog("character", data);
1932 } else if (name == "latexlog") {
1933 Buffer::LogType type;
1934 string const logfile = buffer()->logName(&type);
1936 case Buffer::latexlog:
1939 case Buffer::buildlog:
1943 data += Lexer::quoteString(logfile);
1944 showDialog("log", data);
1945 } else if (name == "vclog") {
1946 string const data = "vc " +
1947 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1948 showDialog("log", data);
1949 } else if (name == "symbols") {
1950 data = bv->cursor().getEncoding()->name();
1952 showDialog("symbols", data);
1954 showDialog(name, data);
1958 case LFUN_INSET_APPLY: {
1959 view()->cursor().recordUndoFullDocument();
1960 string const name = cmd.getArg(0);
1961 Inset * inset = getOpenInset(name);
1963 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1964 inset->dispatch(view()->cursor(), fr);
1966 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1972 case LFUN_UI_TOGGLE:
1974 // Make sure the keyboard focus stays in the work area.
1978 case LFUN_SPLIT_VIEW:
1979 if (Buffer * buf = buffer()) {
1980 string const orientation = cmd.getArg(0);
1981 d.splitter_->setOrientation(orientation == "vertical"
1982 ? Qt::Vertical : Qt::Horizontal);
1983 TabWorkArea * twa = addTabWorkArea();
1984 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1985 setCurrentWorkArea(wa);
1989 case LFUN_CLOSE_TAB_GROUP:
1990 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1992 twa = d.currentTabWorkArea();
1993 // Switch to the next GuiWorkArea in the found TabWorkArea.
1995 d.current_work_area_ = twa->currentWorkArea();
1996 // Make sure the work area is up to date.
1997 twa->setCurrentWorkArea(d.current_work_area_);
1999 d.current_work_area_ = 0;
2001 if (d.splitter_->count() == 0)
2002 // No more work area, switch to the background widget.
2007 case LFUN_COMPLETION_INLINE:
2008 if (d.current_work_area_)
2009 d.current_work_area_->completer().showInline();
2012 case LFUN_COMPLETION_POPUP:
2013 if (d.current_work_area_)
2014 d.current_work_area_->completer().showPopup();
2018 case LFUN_COMPLETION_COMPLETE:
2019 if (d.current_work_area_)
2020 d.current_work_area_->completer().tab();
2023 case LFUN_COMPLETION_CANCEL:
2024 if (d.current_work_area_) {
2025 if (d.current_work_area_->completer().popupVisible())
2026 d.current_work_area_->completer().hidePopup();
2028 d.current_work_area_->completer().hideInline();
2032 case LFUN_COMPLETION_ACCEPT:
2033 if (d.current_work_area_)
2034 d.current_work_area_->completer().activate();
2043 // Part of automatic menu appearance feature.
2044 if (isFullScreen()) {
2045 if (menuBar()->isVisible())
2047 if (statusBar()->isVisible())
2048 statusBar()->hide();
2055 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2057 string const arg = cmd.getArg(0);
2058 if (arg == "scrollbar") {
2059 // hide() is of no help
2060 if (d.current_work_area_->verticalScrollBarPolicy() ==
2061 Qt::ScrollBarAlwaysOff)
2063 d.current_work_area_->setVerticalScrollBarPolicy(
2064 Qt::ScrollBarAsNeeded);
2066 d.current_work_area_->setVerticalScrollBarPolicy(
2067 Qt::ScrollBarAlwaysOff);
2070 if (arg == "statusbar") {
2071 statusBar()->setVisible(!statusBar()->isVisible());
2074 if (arg == "menubar") {
2075 menuBar()->setVisible(!menuBar()->isVisible());
2078 #if QT_VERSION >= 0x040300
2079 if (arg == "frame") {
2081 getContentsMargins(&l, &t, &r, &b);
2082 //are the frames in default state?
2083 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2085 setContentsMargins(-2, -2, -2, -2);
2087 setContentsMargins(0, 0, 0, 0);
2092 if (arg == "fullscreen") {
2097 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2101 void GuiView::toggleFullScreen()
2103 if (isFullScreen()) {
2104 for (int i = 0; i != d.splitter_->count(); ++i)
2105 d.tabWorkArea(i)->setFullScreen(false);
2106 #if QT_VERSION >= 0x040300
2107 setContentsMargins(0, 0, 0, 0);
2109 setWindowState(windowState() ^ Qt::WindowFullScreen);
2112 statusBar()->show();
2114 for (int i = 0; i != d.splitter_->count(); ++i)
2115 d.tabWorkArea(i)->setFullScreen(true);
2116 #if QT_VERSION >= 0x040300
2117 setContentsMargins(-2, -2, -2, -2);
2120 setWindowState(windowState() ^ Qt::WindowFullScreen);
2121 statusBar()->hide();
2123 if (lyxrc.full_screen_toolbars) {
2124 ToolbarMap::iterator end = d.toolbars_.end();
2125 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2132 Buffer const * GuiView::updateInset(Inset const * inset)
2134 if (!d.current_work_area_)
2138 d.current_work_area_->scheduleRedraw();
2140 return &d.current_work_area_->bufferView().buffer();
2144 void GuiView::restartCursor()
2146 /* When we move around, or type, it's nice to be able to see
2147 * the cursor immediately after the keypress.
2149 if (d.current_work_area_)
2150 d.current_work_area_->startBlinkingCursor();
2152 // Take this occasion to update the other GUI elements.
2158 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2160 if (d.current_work_area_)
2161 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2166 // This list should be kept in sync with the list of insets in
2167 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2168 // dialog should have the same name as the inset.
2169 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2170 // docs in LyXAction.cpp.
2172 char const * const dialognames[] = {
2173 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2174 "citation", "document", "errorlist", "ert", "external", "file",
2175 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2176 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2177 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2179 #ifdef HAVE_LIBAIKSAURUS
2183 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2185 char const * const * const end_dialognames =
2186 dialognames + (sizeof(dialognames) / sizeof(char *));
2190 cmpCStr(char const * name) : name_(name) {}
2191 bool operator()(char const * other) {
2192 return strcmp(other, name_) == 0;
2199 bool isValidName(string const & name)
2201 return find_if(dialognames, end_dialognames,
2202 cmpCStr(name.c_str())) != end_dialognames;
2208 void GuiView::resetDialogs()
2210 // Make sure that no LFUN uses any LyXView.
2211 theLyXFunc().setLyXView(0);
2214 constructToolbars();
2215 guiApp->menus().fillMenuBar(menuBar(), this, true);
2217 d.layout_->updateContents(true);
2218 // Now update controls with current buffer.
2219 theLyXFunc().setLyXView(this);
2225 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2227 if (!isValidName(name))
2230 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2232 if (it != d.dialogs_.end())
2233 return it->second.get();
2235 Dialog * dialog = build(name);
2236 d.dialogs_[name].reset(dialog);
2237 if (lyxrc.allow_geometry_session)
2238 dialog->restoreSession();
2245 void GuiView::showDialog(string const & name, string const & data,
2253 Dialog * dialog = findOrBuild(name, false);
2255 dialog->showData(data);
2257 d.open_insets_[name] = inset;
2260 catch (ExceptionMessage const & ex) {
2268 bool GuiView::isDialogVisible(string const & name) const
2270 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2271 if (it == d.dialogs_.end())
2273 return it->second.get()->isVisibleView();
2277 void GuiView::hideDialog(string const & name, Inset * inset)
2279 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2280 if (it == d.dialogs_.end())
2283 if (inset && inset != getOpenInset(name))
2286 Dialog * const dialog = it->second.get();
2287 if (dialog->isVisibleView())
2289 d.open_insets_[name] = 0;
2293 void GuiView::disconnectDialog(string const & name)
2295 if (!isValidName(name))
2298 if (d.open_insets_.find(name) != d.open_insets_.end())
2299 d.open_insets_[name] = 0;
2303 Inset * GuiView::getOpenInset(string const & name) const
2305 if (!isValidName(name))
2308 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2309 return it == d.open_insets_.end() ? 0 : it->second;
2313 void GuiView::hideAll() const
2315 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2316 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2318 for(; it != end; ++it)
2319 it->second->hideView();
2323 void GuiView::updateDialogs()
2325 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2326 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2328 for(; it != end; ++it) {
2329 Dialog * dialog = it->second.get();
2330 if (dialog && dialog->isVisibleView())
2331 dialog->checkStatus();
2338 // will be replaced by a proper factory...
2339 Dialog * createGuiAbout(GuiView & lv);
2340 Dialog * createGuiBibitem(GuiView & lv);
2341 Dialog * createGuiBibtex(GuiView & lv);
2342 Dialog * createGuiBox(GuiView & lv);
2343 Dialog * createGuiBranch(GuiView & lv);
2344 Dialog * createGuiChanges(GuiView & lv);
2345 Dialog * createGuiCharacter(GuiView & lv);
2346 Dialog * createGuiCitation(GuiView & lv);
2347 Dialog * createGuiDelimiter(GuiView & lv);
2348 Dialog * createGuiDocument(GuiView & lv);
2349 Dialog * createGuiErrorList(GuiView & lv);
2350 Dialog * createGuiERT(GuiView & lv);
2351 Dialog * createGuiExternal(GuiView & lv);
2352 Dialog * createGuiFloat(GuiView & lv);
2353 Dialog * createGuiGraphics(GuiView & lv);
2354 Dialog * createGuiHSpace(GuiView & lv);
2355 Dialog * createGuiInclude(GuiView & lv);
2356 Dialog * createGuiInfo(GuiView & lv);
2357 Dialog * createGuiLabel(GuiView & lv);
2358 Dialog * createGuiListings(GuiView & lv);
2359 Dialog * createGuiLog(GuiView & lv);
2360 Dialog * createGuiMathMatrix(GuiView & lv);
2361 Dialog * createGuiNomenclature(GuiView & lv);
2362 Dialog * createGuiNote(GuiView & lv);
2363 Dialog * createGuiParagraph(GuiView & lv);
2364 Dialog * createGuiPreferences(GuiView & lv);
2365 Dialog * createGuiPrint(GuiView & lv);
2366 Dialog * createGuiRef(GuiView & lv);
2367 Dialog * createGuiSearch(GuiView & lv);
2368 Dialog * createGuiSendTo(GuiView & lv);
2369 Dialog * createGuiShowFile(GuiView & lv);
2370 Dialog * createGuiSpellchecker(GuiView & lv);
2371 Dialog * createGuiSymbols(GuiView & lv);
2372 Dialog * createGuiTabularCreate(GuiView & lv);
2373 Dialog * createGuiTabular(GuiView & lv);
2374 Dialog * createGuiTexInfo(GuiView & lv);
2375 Dialog * createGuiToc(GuiView & lv);
2376 Dialog * createGuiThesaurus(GuiView & lv);
2377 Dialog * createGuiHyperlink(GuiView & lv);
2378 Dialog * createGuiVSpace(GuiView & lv);
2379 Dialog * createGuiViewSource(GuiView & lv);
2380 Dialog * createGuiWrap(GuiView & lv);
2383 Dialog * GuiView::build(string const & name)
2385 LASSERT(isValidName(name), /**/);
2387 if (name == "aboutlyx")
2388 return createGuiAbout(*this);
2389 if (name == "bibitem")
2390 return createGuiBibitem(*this);
2391 if (name == "bibtex")
2392 return createGuiBibtex(*this);
2394 return createGuiBox(*this);
2395 if (name == "branch")
2396 return createGuiBranch(*this);
2397 if (name == "changes")
2398 return createGuiChanges(*this);
2399 if (name == "character")
2400 return createGuiCharacter(*this);
2401 if (name == "citation")
2402 return createGuiCitation(*this);
2403 if (name == "document")
2404 return createGuiDocument(*this);
2405 if (name == "errorlist")
2406 return createGuiErrorList(*this);
2408 return createGuiERT(*this);
2409 if (name == "external")
2410 return createGuiExternal(*this);
2412 return createGuiShowFile(*this);
2413 if (name == "findreplace")
2414 return createGuiSearch(*this);
2415 if (name == "float")
2416 return createGuiFloat(*this);
2417 if (name == "graphics")
2418 return createGuiGraphics(*this);
2419 if (name == "include")
2420 return createGuiInclude(*this);
2422 return createGuiInfo(*this);
2423 if (name == "nomenclature")
2424 return createGuiNomenclature(*this);
2425 if (name == "label")
2426 return createGuiLabel(*this);
2428 return createGuiLog(*this);
2429 if (name == "view-source")
2430 return createGuiViewSource(*this);
2431 if (name == "mathdelimiter")
2432 return createGuiDelimiter(*this);
2433 if (name == "mathmatrix")
2434 return createGuiMathMatrix(*this);
2436 return createGuiNote(*this);
2437 if (name == "paragraph")
2438 return createGuiParagraph(*this);
2439 if (name == "prefs")
2440 return createGuiPreferences(*this);
2441 if (name == "print")
2442 return createGuiPrint(*this);
2444 return createGuiRef(*this);
2445 if (name == "sendto")
2446 return createGuiSendTo(*this);
2447 if (name == "space")
2448 return createGuiHSpace(*this);
2449 if (name == "spellchecker")
2450 return createGuiSpellchecker(*this);
2451 if (name == "symbols")
2452 return createGuiSymbols(*this);
2453 if (name == "tabular")
2454 return createGuiTabular(*this);
2455 if (name == "tabularcreate")
2456 return createGuiTabularCreate(*this);
2457 if (name == "texinfo")
2458 return createGuiTexInfo(*this);
2459 #ifdef HAVE_LIBAIKSAURUS
2460 if (name == "thesaurus")
2461 return createGuiThesaurus(*this);
2464 return createGuiToc(*this);
2466 return createGuiHyperlink(*this);
2467 if (name == "vspace")
2468 return createGuiVSpace(*this);
2470 return createGuiWrap(*this);
2471 if (name == "listings")
2472 return createGuiListings(*this);
2478 } // namespace frontend
2481 #include "GuiView_moc.cpp"