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.
382 if (tmp = findOrBuild("toc", true)) tmp->showView();
383 if (tmp = findOrBuild("view-source", true)) tmp->showView();
384 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
391 GuiToolbar * GuiView::toolbar(string const & name)
393 ToolbarMap::iterator it = d.toolbars_.find(name);
394 if (it != d.toolbars_.end())
397 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
398 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
403 void GuiView::constructToolbars()
405 ToolbarMap::iterator it = d.toolbars_.begin();
406 for (; it != d.toolbars_.end(); ++it)
411 // extracts the toolbars from the backend
412 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
413 Toolbars::Infos::iterator end = guiApp->toolbars().end();
414 for (; cit != end; ++cit)
415 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
419 void GuiView::initToolbars()
421 // extracts the toolbars from the backend
422 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
423 Toolbars::Infos::iterator end = guiApp->toolbars().end();
424 for (; cit != end; ++cit) {
425 GuiToolbar * tb = toolbar(cit->name);
428 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
430 tb->setVisible(false);
431 tb->setVisibility(visibility);
433 if (visibility & Toolbars::TOP) {
435 addToolBarBreak(Qt::TopToolBarArea);
436 addToolBar(Qt::TopToolBarArea, tb);
439 if (visibility & Toolbars::BOTTOM) {
440 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
441 #if (QT_VERSION >= 0x040202)
442 addToolBarBreak(Qt::BottomToolBarArea);
444 addToolBar(Qt::BottomToolBarArea, tb);
447 if (visibility & Toolbars::LEFT) {
448 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
449 #if (QT_VERSION >= 0x040202)
450 addToolBarBreak(Qt::LeftToolBarArea);
452 addToolBar(Qt::LeftToolBarArea, tb);
455 if (visibility & Toolbars::RIGHT) {
456 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
457 #if (QT_VERSION >= 0x040202)
458 addToolBarBreak(Qt::RightToolBarArea);
460 addToolBar(Qt::RightToolBarArea, tb);
463 if (visibility & Toolbars::ON)
464 tb->setVisible(true);
469 TocModels & GuiView::tocModels()
471 return d.toc_models_;
475 void GuiView::setFocus()
477 // Make sure LyXFunc points to the correct view.
478 theLyXFunc().setLyXView(this);
479 if (d.current_work_area_)
480 d.current_work_area_->setFocus();
486 QMenu * GuiView::createPopupMenu()
488 return d.toolBarPopup(this);
492 void GuiView::showEvent(QShowEvent * e)
494 LYXERR(Debug::GUI, "Passed Geometry "
495 << size().height() << "x" << size().width()
496 << "+" << pos().x() << "+" << pos().y());
498 if (d.splitter_->count() == 0)
499 // No work area, switch to the background widget.
502 QMainWindow::showEvent(e);
506 void GuiView::closeEvent(QCloseEvent * close_event)
510 // it can happen that this event arrives without selecting the view,
511 // e.g. when clicking the close button on a background window.
512 theLyXFunc().setLyXView(this);
514 while (Buffer * b = buffer()) {
516 // This is a child document, just close the tab after saving
517 // but keep the file loaded.
518 if (!saveBuffer(*b)) {
520 close_event->ignore();
523 removeWorkArea(d.current_work_area_);
527 QList<int> const ids = guiApp->viewIds();
528 for (int i = 0; i != ids.size(); ++i) {
531 if (guiApp->view(ids[i]).workArea(*b)) {
532 // FIXME 1: should we put an alert box here that the buffer
533 // is viewed elsewhere?
534 // FIXME 2: should we try to save this buffer in any case?
537 // This buffer is also opened in another view, so
538 // close the associated work area...
539 removeWorkArea(d.current_work_area_);
540 // ... but don't close the buffer.
545 if (b && !closeBuffer(*b, true)) {
547 close_event->ignore();
552 // Make sure that nothing will use this close to be closed View.
553 guiApp->unregisterView(this);
555 if (isFullScreen()) {
556 // Switch off fullscreen before closing.
561 // Make sure the timer time out will not trigger a statusbar update.
562 d.statusbar_timer_.stop();
564 // Saving fullscreen requires additional tweaks in the toolbar code.
565 // It wouldn't also work under linux natively.
566 if (lyxrc.allow_geometry_session) {
567 // Save this window geometry and layout.
569 // Then the toolbar private states.
570 ToolbarMap::iterator end = d.toolbars_.end();
571 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
572 it->second->saveSession();
573 // Now take care of all other dialogs:
574 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
575 for (; it!= d.dialogs_.end(); ++it)
576 it->second->saveSession();
579 close_event->accept();
583 void GuiView::dragEnterEvent(QDragEnterEvent * event)
585 if (event->mimeData()->hasUrls())
587 /// \todo Ask lyx-devel is this is enough:
588 /// if (event->mimeData()->hasFormat("text/plain"))
589 /// event->acceptProposedAction();
593 void GuiView::dropEvent(QDropEvent* event)
595 QList<QUrl> files = event->mimeData()->urls();
599 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
600 for (int i = 0; i != files.size(); ++i) {
601 string const file = os::internal_path(fromqstr(
602 files.at(i).toLocalFile()));
604 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
609 void GuiView::message(docstring const & str)
611 if (ForkedProcess::iAmAChild())
614 statusBar()->showMessage(toqstr(str));
615 d.statusbar_timer_.stop();
616 d.statusbar_timer_.start(3000);
620 void GuiView::smallSizedIcons()
622 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
626 void GuiView::normalSizedIcons()
628 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
632 void GuiView::bigSizedIcons()
634 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
638 void GuiView::clearMessage()
642 theLyXFunc().setLyXView(this);
643 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
644 d.statusbar_timer_.stop();
648 void GuiView::updateWindowTitle(GuiWorkArea * wa)
650 if (wa != d.current_work_area_)
652 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
653 setWindowIconText(wa->windowIconText());
657 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
660 disconnectBufferView();
661 connectBufferView(wa->bufferView());
662 connectBuffer(wa->bufferView().buffer());
663 d.current_work_area_ = wa;
664 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
665 this, SLOT(updateWindowTitle(GuiWorkArea *)));
666 updateWindowTitle(wa);
670 // The document settings needs to be reinitialised.
671 updateDialog("document", "");
673 // Buffer-dependent dialogs must be updated. This is done here because
674 // some dialogs require buffer()->text.
679 void GuiView::on_lastWorkAreaRemoved()
682 // We already are in a close event. Nothing more to do.
685 if (d.splitter_->count() > 1)
686 // We have a splitter so don't close anything.
689 // Reset and updates the dialogs.
690 d.toc_models_.reset(0);
691 updateDialog("document", "");
694 resetWindowTitleAndIconText();
696 if (lyxrc.open_buffers_in_tabs)
697 // Nothing more to do, the window should stay open.
700 if (guiApp->viewIds().size() > 1) {
706 // On Mac we also close the last window because the application stay
707 // resident in memory. On other platforms we don't close the last
708 // window because this would quit the application.
714 void GuiView::updateStatusBar()
716 // let the user see the explicit message
717 if (d.statusbar_timer_.isActive())
720 theLyXFunc().setLyXView(this);
721 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
725 bool GuiView::hasFocus() const
727 return qApp->activeWindow() == this;
731 bool GuiView::event(QEvent * e)
735 // Useful debug code:
736 //case QEvent::ActivationChange:
737 //case QEvent::WindowDeactivate:
738 //case QEvent::Paint:
739 //case QEvent::Enter:
740 //case QEvent::Leave:
741 //case QEvent::HoverEnter:
742 //case QEvent::HoverLeave:
743 //case QEvent::HoverMove:
744 //case QEvent::StatusTip:
745 //case QEvent::DragEnter:
746 //case QEvent::DragLeave:
750 case QEvent::WindowActivate: {
751 if (this == guiApp->currentView()) {
753 return QMainWindow::event(e);
755 guiApp->setCurrentView(this);
756 theLyXFunc().setLyXView(this);
757 if (d.current_work_area_) {
758 BufferView & bv = d.current_work_area_->bufferView();
759 connectBufferView(bv);
760 connectBuffer(bv.buffer());
761 // The document structure, name and dialogs might have
762 // changed in another view.
764 // The document settings needs to be reinitialised.
765 updateDialog("document", "");
768 resetWindowTitleAndIconText();
771 return QMainWindow::event(e);
774 case QEvent::ShortcutOverride: {
778 if (isFullScreen() && menuBar()->isHidden()) {
779 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
780 // FIXME: we should also try to detect special LyX shortcut such as
781 // Alt-P and Alt-M. Right now there is a hack in
782 // GuiWorkArea::processKeySym() that hides again the menubar for
784 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
786 return QMainWindow::event(e);
790 if (d.current_work_area_)
791 // Nothing special to do.
792 return QMainWindow::event(e);
794 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
795 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
797 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
798 || ke->key() == Qt::Key_Backtab)
799 return QMainWindow::event(e);
801 // Allow processing of shortcuts that are allowed even when no Buffer
803 theLyXFunc().setLyXView(this);
805 setKeySymbol(&sym, ke);
806 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
812 return QMainWindow::event(e);
816 void GuiView::resetWindowTitleAndIconText()
818 setWindowTitle(qt_("LyX"));
819 setWindowIconText(qt_("LyX"));
822 bool GuiView::focusNextPrevChild(bool /*next*/)
829 void GuiView::setBusy(bool busy)
831 if (d.current_work_area_) {
832 d.current_work_area_->setUpdatesEnabled(!busy);
834 d.current_work_area_->stopBlinkingCursor();
836 d.current_work_area_->startBlinkingCursor();
840 QApplication::setOverrideCursor(Qt::WaitCursor);
842 QApplication::restoreOverrideCursor();
846 GuiWorkArea * GuiView::workArea(Buffer & buffer)
848 if (TabWorkArea * twa = d.currentTabWorkArea())
849 return twa->workArea(buffer);
854 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
856 // Automatically create a TabWorkArea if there are none yet.
857 TabWorkArea * tab_widget = d.splitter_->count()
858 ? d.currentTabWorkArea() : addTabWorkArea();
859 return tab_widget->addWorkArea(buffer, *this);
863 TabWorkArea * GuiView::addTabWorkArea()
865 TabWorkArea * twa = new TabWorkArea;
866 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
867 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
868 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
869 this, SLOT(on_lastWorkAreaRemoved()));
871 d.splitter_->addWidget(twa);
872 d.stack_widget_->setCurrentWidget(d.splitter_);
877 GuiWorkArea const * GuiView::currentWorkArea() const
879 return d.current_work_area_;
883 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
886 d.current_work_area_ = wa;
887 for (int i = 0; i != d.splitter_->count(); ++i) {
888 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
894 void GuiView::removeWorkArea(GuiWorkArea * wa)
897 if (wa == d.current_work_area_) {
899 disconnectBufferView();
900 d.current_work_area_ = 0;
903 for (int i = 0; i != d.splitter_->count(); ++i) {
904 TabWorkArea * twa = d.tabWorkArea(i);
905 if (!twa->removeWorkArea(wa))
906 // Not found in this tab group.
909 // We found and removed the GuiWorkArea.
911 // No more WorkAreas in this tab group, so delete it.
916 if (d.current_work_area_)
917 // This means that we are not closing the current GuiWorkArea;
920 // Switch to the next GuiWorkArea in the found TabWorkArea.
921 d.current_work_area_ = twa->currentWorkArea();
925 if (d.splitter_->count() == 0)
926 // No more work area, switch to the background widget.
931 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
937 void GuiView::updateLayoutList()
940 d.layout_->updateContents(false);
944 void GuiView::updateToolbars()
946 ToolbarMap::iterator end = d.toolbars_.end();
947 if (d.current_work_area_) {
949 d.current_work_area_->bufferView().cursor().inMathed();
951 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
953 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
954 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
955 bool const mathmacrotemplate =
956 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
958 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
959 it->second->update(math, table, review, mathmacrotemplate);
961 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
962 it->second->update(false, false, false, false);
966 Buffer * GuiView::buffer()
968 if (d.current_work_area_)
969 return &d.current_work_area_->bufferView().buffer();
974 Buffer const * GuiView::buffer() const
976 if (d.current_work_area_)
977 return &d.current_work_area_->bufferView().buffer();
982 void GuiView::setBuffer(Buffer * newBuffer)
984 LASSERT(newBuffer, /**/);
987 GuiWorkArea * wa = workArea(*newBuffer);
989 updateLabels(*newBuffer->masterBuffer());
990 wa = addWorkArea(*newBuffer);
992 //Disconnect the old buffer...there's no new one.
995 connectBuffer(*newBuffer);
996 connectBufferView(wa->bufferView());
997 setCurrentWorkArea(wa);
1003 void GuiView::connectBuffer(Buffer & buf)
1005 buf.setGuiDelegate(this);
1009 void GuiView::disconnectBuffer()
1011 if (d.current_work_area_)
1012 d.current_work_area_->bufferView().setGuiDelegate(0);
1016 void GuiView::connectBufferView(BufferView & bv)
1018 bv.setGuiDelegate(this);
1022 void GuiView::disconnectBufferView()
1024 if (d.current_work_area_)
1025 d.current_work_area_->bufferView().setGuiDelegate(0);
1029 void GuiView::errors(string const & error_type)
1031 ErrorList & el = buffer()->errorList(error_type);
1033 showDialog("errorlist", error_type);
1037 void GuiView::structureChanged()
1039 d.toc_models_.reset(view());
1040 // Navigator needs more than a simple update in this case. It needs to be
1042 updateDialog("toc", "");
1046 void GuiView::updateDialog(string const & name, string const & data)
1048 if (!isDialogVisible(name))
1051 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1052 if (it == d.dialogs_.end())
1055 Dialog * const dialog = it->second.get();
1056 if (dialog->isVisibleView())
1057 dialog->initialiseParams(data);
1061 BufferView * GuiView::view()
1063 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1067 void GuiView::autoSave()
1069 LYXERR(Debug::INFO, "Running autoSave()");
1072 view()->buffer().autoSave();
1076 void GuiView::resetAutosaveTimers()
1079 d.autosave_timeout_.restart();
1083 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1086 Buffer * buf = buffer();
1088 /* In LyX/Mac, when a dialog is open, the menus of the
1089 application can still be accessed without giving focus to
1090 the main window. In this case, we want to disable the menu
1091 entries that are buffer-related.
1093 Note that this code is not perfect, as bug 1941 attests:
1094 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1096 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1099 switch(cmd.action) {
1100 case LFUN_BUFFER_WRITE:
1101 enable = buf && (buf->isUnnamed() || !buf->isClean());
1104 case LFUN_BUFFER_WRITE_AS:
1108 case LFUN_SPLIT_VIEW:
1109 if (cmd.getArg(0) == "vertical")
1110 enable = buf && (d.splitter_->count() == 1 ||
1111 d.splitter_->orientation() == Qt::Vertical);
1113 enable = buf && (d.splitter_->count() == 1 ||
1114 d.splitter_->orientation() == Qt::Horizontal);
1117 case LFUN_CLOSE_TAB_GROUP:
1118 enable = d.currentTabWorkArea();
1121 case LFUN_TOOLBAR_TOGGLE:
1122 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1123 flag.setOnOff(t->isVisible());
1126 case LFUN_UI_TOGGLE:
1127 flag.setOnOff(isFullScreen());
1130 case LFUN_DIALOG_TOGGLE:
1131 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1132 // fall through to set "enable"
1133 case LFUN_DIALOG_SHOW: {
1134 string const name = cmd.getArg(0);
1136 enable = name == "aboutlyx"
1137 || name == "file" //FIXME: should be removed.
1139 || name == "texinfo";
1140 else if (name == "print")
1141 enable = buf->isExportable("dvi")
1142 && lyxrc.print_command != "none";
1143 else if (name == "character") {
1147 InsetCode ic = view()->cursor().inset().lyxCode();
1148 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1151 else if (name == "symbols") {
1152 if (!view() || view()->cursor().inMathed())
1155 InsetCode ic = view()->cursor().inset().lyxCode();
1156 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1159 else if (name == "latexlog")
1160 enable = FileName(buf->logName()).isReadableFile();
1161 else if (name == "spellchecker")
1162 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1163 enable = !buf->isReadonly();
1167 else if (name == "vclog")
1168 enable = buf->lyxvc().inUse();
1172 case LFUN_DIALOG_UPDATE: {
1173 string const name = cmd.getArg(0);
1175 enable = name == "prefs";
1179 case LFUN_INSET_APPLY: {
1180 string const name = cmd.getArg(0);
1181 Inset * inset = getOpenInset(name);
1183 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1185 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1186 // Every inset is supposed to handle this
1187 LASSERT(false, /**/);
1191 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1192 flag |= lyx::getStatus(fr);
1194 enable = flag.enabled();
1198 case LFUN_COMPLETION_INLINE:
1199 if (!d.current_work_area_
1200 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1204 case LFUN_COMPLETION_POPUP:
1205 if (!d.current_work_area_
1206 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1210 case LFUN_COMPLETION_COMPLETE:
1211 if (!d.current_work_area_
1212 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1216 case LFUN_COMPLETION_ACCEPT:
1217 case LFUN_COMPLETION_CANCEL:
1218 if (!d.current_work_area_
1219 || (!d.current_work_area_->completer().popupVisible()
1220 && !d.current_work_area_->completer().inlineVisible()))
1229 flag.setEnabled(false);
1235 static FileName selectTemplateFile()
1237 FileDialog dlg(qt_("Select template file"));
1238 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1239 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1241 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1242 QStringList(qt_("LyX Documents (*.lyx)")));
1244 if (result.first == FileDialog::Later)
1246 if (result.second.isEmpty())
1248 return FileName(fromqstr(result.second));
1252 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1256 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1259 message(_("Document not loaded."));
1264 setBuffer(newBuffer);
1266 // scroll to the position when the file was last closed
1267 if (lyxrc.use_lastfilepos) {
1268 LastFilePosSection::FilePos filepos =
1269 theSession().lastFilePos().load(filename);
1270 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1274 theSession().lastFiles().add(filename);
1281 void GuiView::openDocument(string const & fname)
1283 string initpath = lyxrc.document_path;
1286 string const trypath = buffer()->filePath();
1287 // If directory is writeable, use this as default.
1288 if (FileName(trypath).isDirWritable())
1294 if (fname.empty()) {
1295 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1296 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1297 dlg.setButton2(qt_("Examples|#E#e"),
1298 toqstr(addPath(package().system_support().absFilename(), "examples")));
1300 QStringList filter(qt_("LyX Documents (*.lyx)"));
1301 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1302 << qt_("LyX-1.4.x Documents (*.lyx14)")
1303 << qt_("LyX-1.5.x Documents (*.lyx15)");
1304 FileDialog::Result result =
1305 dlg.open(toqstr(initpath), filter);
1307 if (result.first == FileDialog::Later)
1310 filename = fromqstr(result.second);
1312 // check selected filename
1313 if (filename.empty()) {
1314 message(_("Canceled."));
1320 // get absolute path of file and add ".lyx" to the filename if
1322 FileName const fullname =
1323 fileSearch(string(), filename, "lyx", support::may_not_exist);
1324 if (!fullname.empty())
1325 filename = fullname.absFilename();
1327 if (!fullname.onlyPath().isDirectory()) {
1328 Alert::warning(_("Invalid filename"),
1329 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1330 from_utf8(fullname.absFilename())));
1333 // if the file doesn't exist, let the user create one
1334 if (!fullname.exists()) {
1335 // the user specifically chose this name. Believe him.
1336 Buffer * const b = newFile(filename, string(), true);
1342 docstring const disp_fn = makeDisplayPath(filename);
1343 message(bformat(_("Opening document %1$s..."), disp_fn));
1346 Buffer * buf = loadDocument(fullname);
1351 buf->errors("Parse");
1352 str2 = bformat(_("Document %1$s opened."), disp_fn);
1353 if (buf->lyxvc().inUse())
1354 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1355 " " + _("Version control detected.");
1357 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1362 // FIXME: clean that
1363 static bool import(GuiView * lv, FileName const & filename,
1364 string const & format, ErrorList & errorList)
1366 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1368 string loader_format;
1369 vector<string> loaders = theConverters().loaders();
1370 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1371 for (vector<string>::const_iterator it = loaders.begin();
1372 it != loaders.end(); ++it) {
1373 if (!theConverters().isReachable(format, *it))
1376 string const tofile =
1377 support::changeExtension(filename.absFilename(),
1378 formats.extension(*it));
1379 if (!theConverters().convert(0, filename, FileName(tofile),
1380 filename, format, *it, errorList))
1382 loader_format = *it;
1385 if (loader_format.empty()) {
1386 frontend::Alert::error(_("Couldn't import file"),
1387 bformat(_("No information for importing the format %1$s."),
1388 formats.prettyName(format)));
1392 loader_format = format;
1394 if (loader_format == "lyx") {
1395 Buffer * buf = lv->loadDocument(lyxfile);
1400 buf->errors("Parse");
1402 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1406 bool as_paragraphs = loader_format == "textparagraph";
1407 string filename2 = (loader_format == format) ? filename.absFilename()
1408 : support::changeExtension(filename.absFilename(),
1409 formats.extension(loader_format));
1410 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1411 theLyXFunc().setLyXView(lv);
1412 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1419 void GuiView::importDocument(string const & argument)
1422 string filename = split(argument, format, ' ');
1424 LYXERR(Debug::INFO, format << " file: " << filename);
1426 // need user interaction
1427 if (filename.empty()) {
1428 string initpath = lyxrc.document_path;
1430 Buffer const * buf = buffer();
1432 string const trypath = buf->filePath();
1433 // If directory is writeable, use this as default.
1434 if (FileName(trypath).isDirWritable())
1438 docstring const text = bformat(_("Select %1$s file to import"),
1439 formats.prettyName(format));
1441 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1442 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1443 dlg.setButton2(qt_("Examples|#E#e"),
1444 toqstr(addPath(package().system_support().absFilename(), "examples")));
1446 docstring filter = formats.prettyName(format);
1449 filter += from_utf8(formats.extension(format));
1452 FileDialog::Result result =
1453 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1455 if (result.first == FileDialog::Later)
1458 filename = fromqstr(result.second);
1460 // check selected filename
1461 if (filename.empty())
1462 message(_("Canceled."));
1465 if (filename.empty())
1468 // get absolute path of file
1469 FileName const fullname(support::makeAbsPath(filename));
1471 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1473 // Check if the document already is open
1474 Buffer * buf = theBufferList().getBuffer(lyxfile);
1477 if (!closeBuffer()) {
1478 message(_("Canceled."));
1483 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1485 // if the file exists already, and we didn't do
1486 // -i lyx thefile.lyx, warn
1487 if (lyxfile.exists() && fullname != lyxfile) {
1489 docstring text = bformat(_("The document %1$s already exists.\n\n"
1490 "Do you want to overwrite that document?"), displaypath);
1491 int const ret = Alert::prompt(_("Overwrite document?"),
1492 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1495 message(_("Canceled."));
1500 message(bformat(_("Importing %1$s..."), displaypath));
1501 ErrorList errorList;
1502 if (import(this, fullname, format, errorList))
1503 message(_("imported."));
1505 message(_("file not imported!"));
1507 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1511 void GuiView::newDocument(string const & filename, bool from_template)
1513 FileName initpath(lyxrc.document_path);
1514 Buffer * buf = buffer();
1516 FileName const trypath(buf->filePath());
1517 // If directory is writeable, use this as default.
1518 if (trypath.isDirWritable())
1522 string templatefile = from_template ?
1523 selectTemplateFile().absFilename() : string();
1525 if (filename.empty())
1526 b = newUnnamedFile(templatefile, initpath);
1528 b = newFile(filename, templatefile, true);
1532 // Ensure the cursor is correctly positionned on screen.
1533 view()->showCursor();
1537 void GuiView::insertLyXFile(docstring const & fname)
1539 BufferView * bv = view();
1544 FileName filename(to_utf8(fname));
1546 if (!filename.empty()) {
1547 bv->insertLyXFile(filename);
1551 // Launch a file browser
1553 string initpath = lyxrc.document_path;
1554 string const trypath = bv->buffer().filePath();
1555 // If directory is writeable, use this as default.
1556 if (FileName(trypath).isDirWritable())
1560 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1561 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1562 dlg.setButton2(qt_("Examples|#E#e"),
1563 toqstr(addPath(package().system_support().absFilename(),
1566 FileDialog::Result result = dlg.open(toqstr(initpath),
1567 QStringList(qt_("LyX Documents (*.lyx)")));
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->insertLyXFile(filename);
1586 void GuiView::insertPlaintextFile(docstring const & fname,
1589 BufferView * bv = view();
1594 FileName filename(to_utf8(fname));
1596 if (!filename.empty()) {
1597 bv->insertPlaintextFile(filename, asParagraph);
1601 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1602 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1604 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1607 if (result.first == FileDialog::Later)
1611 filename.set(fromqstr(result.second));
1613 // check selected filename
1614 if (filename.empty()) {
1615 // emit message signal.
1616 message(_("Canceled."));
1620 bv->insertPlaintextFile(filename, asParagraph);
1624 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1626 FileName fname = b.fileName();
1627 FileName const oldname = fname;
1629 if (!newname.empty()) {
1631 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1633 // Switch to this Buffer.
1636 /// No argument? Ask user through dialog.
1638 FileDialog dlg(qt_("Choose a filename to save document as"),
1639 LFUN_BUFFER_WRITE_AS);
1640 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1641 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1643 if (!isLyXFilename(fname.absFilename()))
1644 fname.changeExtension(".lyx");
1646 FileDialog::Result result =
1647 dlg.save(toqstr(fname.onlyPath().absFilename()),
1648 QStringList(qt_("LyX Documents (*.lyx)")),
1649 toqstr(fname.onlyFileName()));
1651 if (result.first == FileDialog::Later)
1654 fname.set(fromqstr(result.second));
1659 if (!isLyXFilename(fname.absFilename()))
1660 fname.changeExtension(".lyx");
1663 if (FileName(fname).exists()) {
1664 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1665 docstring text = bformat(_("The document %1$s already "
1666 "exists.\n\nDo you want to "
1667 "overwrite that document?"),
1669 int const ret = Alert::prompt(_("Overwrite document?"),
1670 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1673 case 1: return renameBuffer(b, docstring());
1674 case 2: return false;
1678 // Ok, change the name of the buffer
1679 b.setFileName(fname.absFilename());
1681 bool unnamed = b.isUnnamed();
1682 b.setUnnamed(false);
1683 b.saveCheckSum(fname);
1685 if (!saveBuffer(b)) {
1686 b.setFileName(oldname.absFilename());
1687 b.setUnnamed(unnamed);
1688 b.saveCheckSum(oldname);
1696 bool GuiView::saveBuffer(Buffer & b)
1699 return renameBuffer(b, docstring());
1702 theSession().lastFiles().add(b.fileName());
1706 // Switch to this Buffer.
1709 // FIXME: we don't tell the user *WHY* the save failed !!
1710 docstring const file = makeDisplayPath(b.absFileName(), 30);
1711 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1712 "Do you want to rename the document and "
1713 "try again?"), file);
1714 int const ret = Alert::prompt(_("Rename and save?"),
1715 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1718 if (!renameBuffer(b, docstring()))
1727 return saveBuffer(b);
1731 bool GuiView::closeBuffer()
1733 Buffer * buf = buffer();
1734 return buf && closeBuffer(*buf);
1738 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1740 // goto bookmark to update bookmark pit.
1741 //FIXME: we should update only the bookmarks related to this buffer!
1742 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1743 theLyXFunc().gotoBookmark(i+1, false, false);
1745 if (buf.isClean() || buf.paragraphs().empty()) {
1746 if (buf.masterBuffer() == &buf && tolastopened)
1747 theSession().lastOpened().add(buf.fileName());
1748 theBufferList().release(&buf);
1751 // Switch to this Buffer.
1756 if (buf.isUnnamed())
1757 file = from_utf8(buf.fileName().onlyFileName());
1759 file = buf.fileName().displayName(30);
1761 // Bring this window to top before asking questions.
1765 docstring const text = bformat(_("The document %1$s has unsaved changes."
1766 "\n\nDo you want to save the document or discard the changes?"), file);
1767 int const ret = Alert::prompt(_("Save changed document?"),
1768 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1772 if (!saveBuffer(buf))
1776 // if we crash after this we could
1777 // have no autosave file but I guess
1778 // this is really improbable (Jug)
1779 removeAutosaveFile(buf.absFileName());
1785 // save file names to .lyx/session
1786 // if master/slave are both open, do not save slave since it
1787 // will be automatically loaded when the master is loaded
1788 if (buf.masterBuffer() == &buf && tolastopened)
1789 theSession().lastOpened().add(buf.fileName());
1792 // Don't close child documents.
1793 removeWorkArea(d.current_work_area_);
1795 theBufferList().release(&buf);
1801 bool GuiView::dispatch(FuncRequest const & cmd)
1803 BufferView * bv = view();
1804 // By default we won't need any update.
1806 bv->cursor().updateFlags(Update::None);
1807 bool dispatched = true;
1809 switch(cmd.action) {
1810 case LFUN_BUFFER_IMPORT:
1811 importDocument(to_utf8(cmd.argument()));
1814 case LFUN_BUFFER_SWITCH:
1815 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1818 case LFUN_BUFFER_NEXT:
1819 setBuffer(theBufferList().next(buffer()));
1822 case LFUN_BUFFER_PREVIOUS:
1823 setBuffer(theBufferList().previous(buffer()));
1826 case LFUN_COMMAND_EXECUTE: {
1827 bool const show_it = cmd.argument() != "off";
1828 // FIXME: this is a hack, "minibuffer" should not be
1830 if (GuiToolbar * t = toolbar("minibuffer")) {
1831 t->setVisible(show_it);
1832 if (show_it && t->commandBuffer())
1833 t->commandBuffer()->setFocus();
1837 case LFUN_DROP_LAYOUTS_CHOICE:
1839 d.layout_->showPopup();
1842 case LFUN_MENU_OPEN:
1843 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1844 menu->exec(QCursor::pos());
1847 case LFUN_FILE_INSERT:
1848 insertLyXFile(cmd.argument());
1850 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1851 insertPlaintextFile(cmd.argument(), true);
1854 case LFUN_FILE_INSERT_PLAINTEXT:
1855 insertPlaintextFile(cmd.argument(), false);
1858 case LFUN_BUFFER_WRITE:
1860 saveBuffer(bv->buffer());
1863 case LFUN_BUFFER_WRITE_AS:
1865 renameBuffer(bv->buffer(), cmd.argument());
1868 case LFUN_BUFFER_WRITE_ALL: {
1869 Buffer * first = theBufferList().first();
1872 message(_("Saving all documents..."));
1873 // We cannot use a for loop as the buffer list cycles.
1876 if (!b->isClean()) {
1878 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1880 b = theBufferList().next(b);
1881 } while (b != first);
1882 message(_("All documents saved."));
1886 case LFUN_TOOLBAR_TOGGLE: {
1887 string const name = cmd.getArg(0);
1888 if (GuiToolbar * t = toolbar(name))
1893 case LFUN_DIALOG_UPDATE: {
1894 string const name = to_utf8(cmd.argument());
1895 // Can only update a dialog connected to an existing inset
1896 Inset * inset = getOpenInset(name);
1898 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1899 inset->dispatch(view()->cursor(), fr);
1900 } else if (name == "paragraph") {
1901 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1902 } else if (name == "prefs") {
1903 updateDialog(name, string());
1908 case LFUN_DIALOG_TOGGLE: {
1909 if (isDialogVisible(cmd.getArg(0)))
1910 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1912 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1916 case LFUN_DIALOG_DISCONNECT_INSET:
1917 disconnectDialog(to_utf8(cmd.argument()));
1920 case LFUN_DIALOG_HIDE: {
1921 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1925 case LFUN_DIALOG_SHOW: {
1926 string const name = cmd.getArg(0);
1927 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1929 if (name == "character") {
1930 data = freefont2string();
1932 showDialog("character", data);
1933 } else if (name == "latexlog") {
1934 Buffer::LogType type;
1935 string const logfile = buffer()->logName(&type);
1937 case Buffer::latexlog:
1940 case Buffer::buildlog:
1944 data += Lexer::quoteString(logfile);
1945 showDialog("log", data);
1946 } else if (name == "vclog") {
1947 string const data = "vc " +
1948 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1949 showDialog("log", data);
1950 } else if (name == "symbols") {
1951 data = bv->cursor().getEncoding()->name();
1953 showDialog("symbols", data);
1955 showDialog(name, data);
1959 case LFUN_INSET_APPLY: {
1960 view()->cursor().recordUndoFullDocument();
1961 string const name = cmd.getArg(0);
1962 Inset * inset = getOpenInset(name);
1964 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1965 inset->dispatch(view()->cursor(), fr);
1967 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1973 case LFUN_UI_TOGGLE:
1975 // Make sure the keyboard focus stays in the work area.
1979 case LFUN_SPLIT_VIEW:
1980 if (Buffer * buf = buffer()) {
1981 string const orientation = cmd.getArg(0);
1982 d.splitter_->setOrientation(orientation == "vertical"
1983 ? Qt::Vertical : Qt::Horizontal);
1984 TabWorkArea * twa = addTabWorkArea();
1985 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1986 setCurrentWorkArea(wa);
1990 case LFUN_CLOSE_TAB_GROUP:
1991 if (TabWorkArea * twa = d.currentTabWorkArea()) {
1993 twa = d.currentTabWorkArea();
1994 // Switch to the next GuiWorkArea in the found TabWorkArea.
1996 d.current_work_area_ = twa->currentWorkArea();
1997 // Make sure the work area is up to date.
1998 twa->setCurrentWorkArea(d.current_work_area_);
2000 d.current_work_area_ = 0;
2002 if (d.splitter_->count() == 0)
2003 // No more work area, switch to the background widget.
2008 case LFUN_COMPLETION_INLINE:
2009 if (d.current_work_area_)
2010 d.current_work_area_->completer().showInline();
2013 case LFUN_COMPLETION_POPUP:
2014 if (d.current_work_area_)
2015 d.current_work_area_->completer().showPopup();
2019 case LFUN_COMPLETION_COMPLETE:
2020 if (d.current_work_area_)
2021 d.current_work_area_->completer().tab();
2024 case LFUN_COMPLETION_CANCEL:
2025 if (d.current_work_area_) {
2026 if (d.current_work_area_->completer().popupVisible())
2027 d.current_work_area_->completer().hidePopup();
2029 d.current_work_area_->completer().hideInline();
2033 case LFUN_COMPLETION_ACCEPT:
2034 if (d.current_work_area_)
2035 d.current_work_area_->completer().activate();
2044 // Part of automatic menu appearance feature.
2045 if (isFullScreen()) {
2046 if (menuBar()->isVisible())
2048 if (statusBar()->isVisible())
2049 statusBar()->hide();
2056 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2058 string const arg = cmd.getArg(0);
2059 if (arg == "scrollbar") {
2060 // hide() is of no help
2061 if (d.current_work_area_->verticalScrollBarPolicy() ==
2062 Qt::ScrollBarAlwaysOff)
2064 d.current_work_area_->setVerticalScrollBarPolicy(
2065 Qt::ScrollBarAsNeeded);
2067 d.current_work_area_->setVerticalScrollBarPolicy(
2068 Qt::ScrollBarAlwaysOff);
2071 if (arg == "statusbar") {
2072 statusBar()->setVisible(!statusBar()->isVisible());
2075 if (arg == "menubar") {
2076 menuBar()->setVisible(!menuBar()->isVisible());
2079 #if QT_VERSION >= 0x040300
2080 if (arg == "frame") {
2082 getContentsMargins(&l, &t, &r, &b);
2083 //are the frames in default state?
2084 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2086 setContentsMargins(-2, -2, -2, -2);
2088 setContentsMargins(0, 0, 0, 0);
2093 if (arg == "fullscreen") {
2098 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2102 void GuiView::toggleFullScreen()
2104 if (isFullScreen()) {
2105 for (int i = 0; i != d.splitter_->count(); ++i)
2106 d.tabWorkArea(i)->setFullScreen(false);
2107 #if QT_VERSION >= 0x040300
2108 setContentsMargins(0, 0, 0, 0);
2110 setWindowState(windowState() ^ Qt::WindowFullScreen);
2113 statusBar()->show();
2115 for (int i = 0; i != d.splitter_->count(); ++i)
2116 d.tabWorkArea(i)->setFullScreen(true);
2117 #if QT_VERSION >= 0x040300
2118 setContentsMargins(-2, -2, -2, -2);
2121 setWindowState(windowState() ^ Qt::WindowFullScreen);
2122 statusBar()->hide();
2124 if (lyxrc.full_screen_toolbars) {
2125 ToolbarMap::iterator end = d.toolbars_.end();
2126 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2133 Buffer const * GuiView::updateInset(Inset const * inset)
2135 if (!d.current_work_area_)
2139 d.current_work_area_->scheduleRedraw();
2141 return &d.current_work_area_->bufferView().buffer();
2145 void GuiView::restartCursor()
2147 /* When we move around, or type, it's nice to be able to see
2148 * the cursor immediately after the keypress.
2150 if (d.current_work_area_)
2151 d.current_work_area_->startBlinkingCursor();
2153 // Take this occasion to update the other GUI elements.
2159 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2161 if (d.current_work_area_)
2162 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2167 // This list should be kept in sync with the list of insets in
2168 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2169 // dialog should have the same name as the inset.
2170 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2171 // docs in LyXAction.cpp.
2173 char const * const dialognames[] = {
2174 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2175 "citation", "document", "errorlist", "ert", "external", "file",
2176 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2177 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2178 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2180 #ifdef HAVE_LIBAIKSAURUS
2184 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2186 char const * const * const end_dialognames =
2187 dialognames + (sizeof(dialognames) / sizeof(char *));
2191 cmpCStr(char const * name) : name_(name) {}
2192 bool operator()(char const * other) {
2193 return strcmp(other, name_) == 0;
2200 bool isValidName(string const & name)
2202 return find_if(dialognames, end_dialognames,
2203 cmpCStr(name.c_str())) != end_dialognames;
2209 void GuiView::resetDialogs()
2211 // Make sure that no LFUN uses any LyXView.
2212 theLyXFunc().setLyXView(0);
2215 constructToolbars();
2216 guiApp->menus().fillMenuBar(menuBar(), this, true);
2218 d.layout_->updateContents(true);
2219 // Now update controls with current buffer.
2220 theLyXFunc().setLyXView(this);
2226 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2228 if (!isValidName(name))
2231 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2233 if (it != d.dialogs_.end())
2234 return it->second.get();
2236 Dialog * dialog = build(name);
2237 d.dialogs_[name].reset(dialog);
2238 if (lyxrc.allow_geometry_session)
2239 dialog->restoreSession();
2246 void GuiView::showDialog(string const & name, string const & data,
2254 Dialog * dialog = findOrBuild(name, false);
2256 dialog->showData(data);
2258 d.open_insets_[name] = inset;
2261 catch (ExceptionMessage const & ex) {
2269 bool GuiView::isDialogVisible(string const & name) const
2271 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2272 if (it == d.dialogs_.end())
2274 return it->second.get()->isVisibleView();
2278 void GuiView::hideDialog(string const & name, Inset * inset)
2280 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2281 if (it == d.dialogs_.end())
2284 if (inset && inset != getOpenInset(name))
2287 Dialog * const dialog = it->second.get();
2288 if (dialog->isVisibleView())
2290 d.open_insets_[name] = 0;
2294 void GuiView::disconnectDialog(string const & name)
2296 if (!isValidName(name))
2299 if (d.open_insets_.find(name) != d.open_insets_.end())
2300 d.open_insets_[name] = 0;
2304 Inset * GuiView::getOpenInset(string const & name) const
2306 if (!isValidName(name))
2309 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2310 return it == d.open_insets_.end() ? 0 : it->second;
2314 void GuiView::hideAll() const
2316 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2317 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2319 for(; it != end; ++it)
2320 it->second->hideView();
2324 void GuiView::updateDialogs()
2326 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2327 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2329 for(; it != end; ++it) {
2330 Dialog * dialog = it->second.get();
2331 if (dialog && dialog->isVisibleView())
2332 dialog->checkStatus();
2339 // will be replaced by a proper factory...
2340 Dialog * createGuiAbout(GuiView & lv);
2341 Dialog * createGuiBibitem(GuiView & lv);
2342 Dialog * createGuiBibtex(GuiView & lv);
2343 Dialog * createGuiBox(GuiView & lv);
2344 Dialog * createGuiBranch(GuiView & lv);
2345 Dialog * createGuiChanges(GuiView & lv);
2346 Dialog * createGuiCharacter(GuiView & lv);
2347 Dialog * createGuiCitation(GuiView & lv);
2348 Dialog * createGuiDelimiter(GuiView & lv);
2349 Dialog * createGuiDocument(GuiView & lv);
2350 Dialog * createGuiErrorList(GuiView & lv);
2351 Dialog * createGuiERT(GuiView & lv);
2352 Dialog * createGuiExternal(GuiView & lv);
2353 Dialog * createGuiFloat(GuiView & lv);
2354 Dialog * createGuiGraphics(GuiView & lv);
2355 Dialog * createGuiHSpace(GuiView & lv);
2356 Dialog * createGuiInclude(GuiView & lv);
2357 Dialog * createGuiInfo(GuiView & lv);
2358 Dialog * createGuiLabel(GuiView & lv);
2359 Dialog * createGuiListings(GuiView & lv);
2360 Dialog * createGuiLog(GuiView & lv);
2361 Dialog * createGuiMathMatrix(GuiView & lv);
2362 Dialog * createGuiNomenclature(GuiView & lv);
2363 Dialog * createGuiNote(GuiView & lv);
2364 Dialog * createGuiParagraph(GuiView & lv);
2365 Dialog * createGuiPreferences(GuiView & lv);
2366 Dialog * createGuiPrint(GuiView & lv);
2367 Dialog * createGuiRef(GuiView & lv);
2368 Dialog * createGuiSearch(GuiView & lv);
2369 Dialog * createGuiSendTo(GuiView & lv);
2370 Dialog * createGuiShowFile(GuiView & lv);
2371 Dialog * createGuiSpellchecker(GuiView & lv);
2372 Dialog * createGuiSymbols(GuiView & lv);
2373 Dialog * createGuiTabularCreate(GuiView & lv);
2374 Dialog * createGuiTabular(GuiView & lv);
2375 Dialog * createGuiTexInfo(GuiView & lv);
2376 Dialog * createGuiToc(GuiView & lv);
2377 Dialog * createGuiThesaurus(GuiView & lv);
2378 Dialog * createGuiHyperlink(GuiView & lv);
2379 Dialog * createGuiVSpace(GuiView & lv);
2380 Dialog * createGuiViewSource(GuiView & lv);
2381 Dialog * createGuiWrap(GuiView & lv);
2384 Dialog * GuiView::build(string const & name)
2386 LASSERT(isValidName(name), /**/);
2388 if (name == "aboutlyx")
2389 return createGuiAbout(*this);
2390 if (name == "bibitem")
2391 return createGuiBibitem(*this);
2392 if (name == "bibtex")
2393 return createGuiBibtex(*this);
2395 return createGuiBox(*this);
2396 if (name == "branch")
2397 return createGuiBranch(*this);
2398 if (name == "changes")
2399 return createGuiChanges(*this);
2400 if (name == "character")
2401 return createGuiCharacter(*this);
2402 if (name == "citation")
2403 return createGuiCitation(*this);
2404 if (name == "document")
2405 return createGuiDocument(*this);
2406 if (name == "errorlist")
2407 return createGuiErrorList(*this);
2409 return createGuiERT(*this);
2410 if (name == "external")
2411 return createGuiExternal(*this);
2413 return createGuiShowFile(*this);
2414 if (name == "findreplace")
2415 return createGuiSearch(*this);
2416 if (name == "float")
2417 return createGuiFloat(*this);
2418 if (name == "graphics")
2419 return createGuiGraphics(*this);
2420 if (name == "include")
2421 return createGuiInclude(*this);
2423 return createGuiInfo(*this);
2424 if (name == "nomenclature")
2425 return createGuiNomenclature(*this);
2426 if (name == "label")
2427 return createGuiLabel(*this);
2429 return createGuiLog(*this);
2430 if (name == "view-source")
2431 return createGuiViewSource(*this);
2432 if (name == "mathdelimiter")
2433 return createGuiDelimiter(*this);
2434 if (name == "mathmatrix")
2435 return createGuiMathMatrix(*this);
2437 return createGuiNote(*this);
2438 if (name == "paragraph")
2439 return createGuiParagraph(*this);
2440 if (name == "prefs")
2441 return createGuiPreferences(*this);
2442 if (name == "print")
2443 return createGuiPrint(*this);
2445 return createGuiRef(*this);
2446 if (name == "sendto")
2447 return createGuiSendTo(*this);
2448 if (name == "space")
2449 return createGuiHSpace(*this);
2450 if (name == "spellchecker")
2451 return createGuiSpellchecker(*this);
2452 if (name == "symbols")
2453 return createGuiSymbols(*this);
2454 if (name == "tabular")
2455 return createGuiTabular(*this);
2456 if (name == "tabularcreate")
2457 return createGuiTabularCreate(*this);
2458 if (name == "texinfo")
2459 return createGuiTexInfo(*this);
2460 #ifdef HAVE_LIBAIKSAURUS
2461 if (name == "thesaurus")
2462 return createGuiThesaurus(*this);
2465 return createGuiToc(*this);
2467 return createGuiHyperlink(*this);
2468 if (name == "vspace")
2469 return createGuiVSpace(*this);
2471 return createGuiWrap(*this);
2472 if (name == "listings")
2473 return createGuiListings(*this);
2479 } // namespace frontend
2482 #include "GuiView_moc.cpp"