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.
337 settings.remove("views");
347 void GuiView::saveLayout() const
350 settings.beginGroup("views");
351 settings.beginGroup(QString::number(id_));
353 settings.setValue("pos", pos());
354 settings.setValue("size", size());
356 settings.setValue("geometry", saveGeometry());
358 settings.setValue("layout", saveState(0));
359 settings.setValue("icon_size", iconSize());
363 bool GuiView::restoreLayout()
366 settings.beginGroup("views");
367 settings.beginGroup(QString::number(id_));
368 QString const icon_key = "icon_size";
369 if (!settings.contains(icon_key))
372 setIconSize(settings.value(icon_key).toSize());
374 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
375 QSize size = settings.value("size", QSize(690, 510)).toSize();
379 if (!restoreGeometry(settings.value("geometry").toByteArray()))
380 setGeometry(50, 50, 690, 510);
382 // Make sure layout is correctly oriented.
383 setLayoutDirection(qApp->layoutDirection());
385 // Allow the toc and view-source dock widget to be restored if needed.
387 if ((tmp = findOrBuild("toc", true)))
389 if ((tmp = findOrBuild("view-source", true)))
392 if (!restoreState(settings.value("layout").toByteArray(), 0))
399 GuiToolbar * GuiView::toolbar(string const & name)
401 ToolbarMap::iterator it = d.toolbars_.find(name);
402 if (it != d.toolbars_.end())
405 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
406 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
411 void GuiView::constructToolbars()
413 ToolbarMap::iterator it = d.toolbars_.begin();
414 for (; it != d.toolbars_.end(); ++it)
419 // extracts the toolbars from the backend
420 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
421 Toolbars::Infos::iterator end = guiApp->toolbars().end();
422 for (; cit != end; ++cit)
423 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
427 void GuiView::initToolbars()
429 // extracts the toolbars from the backend
430 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
431 Toolbars::Infos::iterator end = guiApp->toolbars().end();
432 for (; cit != end; ++cit) {
433 GuiToolbar * tb = toolbar(cit->name);
436 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
438 tb->setVisible(false);
439 tb->setVisibility(visibility);
441 if (visibility & Toolbars::TOP) {
443 addToolBarBreak(Qt::TopToolBarArea);
444 addToolBar(Qt::TopToolBarArea, tb);
447 if (visibility & Toolbars::BOTTOM) {
448 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
449 #if (QT_VERSION >= 0x040202)
450 addToolBarBreak(Qt::BottomToolBarArea);
452 addToolBar(Qt::BottomToolBarArea, tb);
455 if (visibility & Toolbars::LEFT) {
456 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
457 #if (QT_VERSION >= 0x040202)
458 addToolBarBreak(Qt::LeftToolBarArea);
460 addToolBar(Qt::LeftToolBarArea, tb);
463 if (visibility & Toolbars::RIGHT) {
464 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
465 #if (QT_VERSION >= 0x040202)
466 addToolBarBreak(Qt::RightToolBarArea);
468 addToolBar(Qt::RightToolBarArea, tb);
471 if (visibility & Toolbars::ON)
472 tb->setVisible(true);
477 TocModels & GuiView::tocModels()
479 return d.toc_models_;
483 void GuiView::setFocus()
485 // Make sure LyXFunc points to the correct view.
486 theLyXFunc().setLyXView(this);
487 if (d.current_work_area_)
488 d.current_work_area_->setFocus();
494 QMenu * GuiView::createPopupMenu()
496 return d.toolBarPopup(this);
500 void GuiView::showEvent(QShowEvent * e)
502 LYXERR(Debug::GUI, "Passed Geometry "
503 << size().height() << "x" << size().width()
504 << "+" << pos().x() << "+" << pos().y());
506 if (d.splitter_->count() == 0)
507 // No work area, switch to the background widget.
510 QMainWindow::showEvent(e);
514 void GuiView::closeEvent(QCloseEvent * close_event)
518 // it can happen that this event arrives without selecting the view,
519 // e.g. when clicking the close button on a background window.
520 theLyXFunc().setLyXView(this);
521 guiApp->setCurrentView(this);
523 while (Buffer * b = buffer()) {
525 // This is a child document, just close the tab after saving
526 // but keep the file loaded.
527 if (!saveBuffer(*b)) {
529 close_event->ignore();
532 removeWorkArea(d.current_work_area_);
536 QList<int> const ids = guiApp->viewIds();
537 for (int i = 0; i != ids.size(); ++i) {
540 if (guiApp->view(ids[i]).workArea(*b)) {
541 // FIXME 1: should we put an alert box here that the buffer
542 // is viewed elsewhere?
543 // FIXME 2: should we try to save this buffer in any case?
546 // This buffer is also opened in another view, so
547 // close the associated work area...
548 removeWorkArea(d.current_work_area_);
549 // ... but don't close the buffer.
554 if (b && !closeBuffer(*b, true)) {
556 close_event->ignore();
561 // Make sure that nothing will use this close to be closed View.
562 guiApp->unregisterView(this);
564 if (isFullScreen()) {
565 // Switch off fullscreen before closing.
570 // Make sure the timer time out will not trigger a statusbar update.
571 d.statusbar_timer_.stop();
573 // Saving fullscreen requires additional tweaks in the toolbar code.
574 // It wouldn't also work under linux natively.
575 if (lyxrc.allow_geometry_session) {
576 // Save this window geometry and layout.
578 // Then the toolbar private states.
579 ToolbarMap::iterator end = d.toolbars_.end();
580 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
581 it->second->saveSession();
582 // Now take care of all other dialogs:
583 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
584 for (; it!= d.dialogs_.end(); ++it)
585 it->second->saveSession();
588 close_event->accept();
592 void GuiView::dragEnterEvent(QDragEnterEvent * event)
594 if (event->mimeData()->hasUrls())
596 /// \todo Ask lyx-devel is this is enough:
597 /// if (event->mimeData()->hasFormat("text/plain"))
598 /// event->acceptProposedAction();
602 void GuiView::dropEvent(QDropEvent* event)
604 QList<QUrl> files = event->mimeData()->urls();
608 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
609 for (int i = 0; i != files.size(); ++i) {
610 string const file = os::internal_path(fromqstr(
611 files.at(i).toLocalFile()));
613 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
618 void GuiView::message(docstring const & str)
620 if (ForkedProcess::iAmAChild())
623 statusBar()->showMessage(toqstr(str));
624 d.statusbar_timer_.stop();
625 d.statusbar_timer_.start(3000);
629 void GuiView::smallSizedIcons()
631 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
635 void GuiView::normalSizedIcons()
637 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
641 void GuiView::bigSizedIcons()
643 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
647 void GuiView::clearMessage()
651 theLyXFunc().setLyXView(this);
652 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
653 d.statusbar_timer_.stop();
657 void GuiView::updateWindowTitle(GuiWorkArea * wa)
659 if (wa != d.current_work_area_)
661 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
662 setWindowIconText(wa->windowIconText());
666 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
669 disconnectBufferView();
670 connectBufferView(wa->bufferView());
671 connectBuffer(wa->bufferView().buffer());
672 d.current_work_area_ = wa;
673 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
674 this, SLOT(updateWindowTitle(GuiWorkArea *)));
675 updateWindowTitle(wa);
679 // The document settings needs to be reinitialised.
680 updateDialog("document", "");
682 // Buffer-dependent dialogs must be updated. This is done here because
683 // some dialogs require buffer()->text.
688 void GuiView::on_lastWorkAreaRemoved()
691 // We already are in a close event. Nothing more to do.
694 if (d.splitter_->count() > 1)
695 // We have a splitter so don't close anything.
698 // Reset and updates the dialogs.
699 d.toc_models_.reset(0);
700 updateDialog("document", "");
703 resetWindowTitleAndIconText();
705 if (lyxrc.open_buffers_in_tabs)
706 // Nothing more to do, the window should stay open.
709 if (guiApp->viewIds().size() > 1) {
715 // On Mac we also close the last window because the application stay
716 // resident in memory. On other platforms we don't close the last
717 // window because this would quit the application.
723 void GuiView::updateStatusBar()
725 // let the user see the explicit message
726 if (d.statusbar_timer_.isActive())
729 theLyXFunc().setLyXView(this);
730 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
734 bool GuiView::hasFocus() const
736 return qApp->activeWindow() == this;
740 bool GuiView::event(QEvent * e)
744 // Useful debug code:
745 //case QEvent::ActivationChange:
746 //case QEvent::WindowDeactivate:
747 //case QEvent::Paint:
748 //case QEvent::Enter:
749 //case QEvent::Leave:
750 //case QEvent::HoverEnter:
751 //case QEvent::HoverLeave:
752 //case QEvent::HoverMove:
753 //case QEvent::StatusTip:
754 //case QEvent::DragEnter:
755 //case QEvent::DragLeave:
759 case QEvent::WindowActivate: {
760 if (this == guiApp->currentView()) {
762 return QMainWindow::event(e);
764 guiApp->setCurrentView(this);
765 theLyXFunc().setLyXView(this);
766 if (d.current_work_area_) {
767 BufferView & bv = d.current_work_area_->bufferView();
768 connectBufferView(bv);
769 connectBuffer(bv.buffer());
770 // The document structure, name and dialogs might have
771 // changed in another view.
773 // The document settings needs to be reinitialised.
774 updateDialog("document", "");
777 resetWindowTitleAndIconText();
780 return QMainWindow::event(e);
783 case QEvent::ShortcutOverride: {
787 if (isFullScreen() && menuBar()->isHidden()) {
788 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
789 // FIXME: we should also try to detect special LyX shortcut such as
790 // Alt-P and Alt-M. Right now there is a hack in
791 // GuiWorkArea::processKeySym() that hides again the menubar for
793 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
795 return QMainWindow::event(e);
799 if (d.current_work_area_)
800 // Nothing special to do.
801 return QMainWindow::event(e);
803 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
804 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
806 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
807 || ke->key() == Qt::Key_Backtab)
808 return QMainWindow::event(e);
810 // Allow processing of shortcuts that are allowed even when no Buffer
812 theLyXFunc().setLyXView(this);
814 setKeySymbol(&sym, ke);
815 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
821 return QMainWindow::event(e);
825 void GuiView::resetWindowTitleAndIconText()
827 setWindowTitle(qt_("LyX"));
828 setWindowIconText(qt_("LyX"));
831 bool GuiView::focusNextPrevChild(bool /*next*/)
838 void GuiView::setBusy(bool busy)
840 if (d.current_work_area_) {
841 d.current_work_area_->setUpdatesEnabled(!busy);
843 d.current_work_area_->stopBlinkingCursor();
845 d.current_work_area_->startBlinkingCursor();
849 QApplication::setOverrideCursor(Qt::WaitCursor);
851 QApplication::restoreOverrideCursor();
855 GuiWorkArea * GuiView::workArea(Buffer & buffer)
857 if (TabWorkArea * twa = d.currentTabWorkArea())
858 return twa->workArea(buffer);
863 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
865 // Automatically create a TabWorkArea if there are none yet.
866 TabWorkArea * tab_widget = d.splitter_->count()
867 ? d.currentTabWorkArea() : addTabWorkArea();
868 return tab_widget->addWorkArea(buffer, *this);
872 TabWorkArea * GuiView::addTabWorkArea()
874 TabWorkArea * twa = new TabWorkArea;
875 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
876 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
877 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
878 this, SLOT(on_lastWorkAreaRemoved()));
880 d.splitter_->addWidget(twa);
881 d.stack_widget_->setCurrentWidget(d.splitter_);
886 GuiWorkArea const * GuiView::currentWorkArea() const
888 return d.current_work_area_;
892 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
895 d.current_work_area_ = wa;
896 for (int i = 0; i != d.splitter_->count(); ++i) {
897 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
903 void GuiView::removeWorkArea(GuiWorkArea * wa)
906 if (wa == d.current_work_area_) {
908 disconnectBufferView();
909 d.current_work_area_ = 0;
912 for (int i = 0; i != d.splitter_->count(); ++i) {
913 TabWorkArea * twa = d.tabWorkArea(i);
914 if (!twa->removeWorkArea(wa))
915 // Not found in this tab group.
918 // We found and removed the GuiWorkArea.
920 // No more WorkAreas in this tab group, so delete it.
925 if (d.current_work_area_)
926 // This means that we are not closing the current GuiWorkArea;
929 // Switch to the next GuiWorkArea in the found TabWorkArea.
930 d.current_work_area_ = twa->currentWorkArea();
934 if (d.splitter_->count() == 0)
935 // No more work area, switch to the background widget.
940 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
946 void GuiView::updateLayoutList()
949 d.layout_->updateContents(false);
953 void GuiView::updateToolbars()
955 ToolbarMap::iterator end = d.toolbars_.end();
956 if (d.current_work_area_) {
958 d.current_work_area_->bufferView().cursor().inMathed();
960 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
962 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
963 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
964 bool const mathmacrotemplate =
965 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
967 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
968 it->second->update(math, table, review, mathmacrotemplate);
970 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
971 it->second->update(false, false, false, false);
975 Buffer * GuiView::buffer()
977 if (d.current_work_area_)
978 return &d.current_work_area_->bufferView().buffer();
983 Buffer const * GuiView::buffer() const
985 if (d.current_work_area_)
986 return &d.current_work_area_->bufferView().buffer();
991 void GuiView::setBuffer(Buffer * newBuffer)
993 LASSERT(newBuffer, return);
996 GuiWorkArea * wa = workArea(*newBuffer);
998 updateLabels(*newBuffer->masterBuffer());
999 wa = addWorkArea(*newBuffer);
1001 //Disconnect the old buffer...there's no new one.
1004 connectBuffer(*newBuffer);
1005 connectBufferView(wa->bufferView());
1006 setCurrentWorkArea(wa);
1012 void GuiView::connectBuffer(Buffer & buf)
1014 buf.setGuiDelegate(this);
1018 void GuiView::disconnectBuffer()
1020 if (d.current_work_area_)
1021 d.current_work_area_->bufferView().setGuiDelegate(0);
1025 void GuiView::connectBufferView(BufferView & bv)
1027 bv.setGuiDelegate(this);
1031 void GuiView::disconnectBufferView()
1033 if (d.current_work_area_)
1034 d.current_work_area_->bufferView().setGuiDelegate(0);
1038 void GuiView::errors(string const & error_type)
1040 ErrorList & el = buffer()->errorList(error_type);
1042 showDialog("errorlist", error_type);
1046 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1048 d.toc_models_.updateItem(toqstr(type), dit);
1052 void GuiView::structureChanged()
1054 d.toc_models_.reset(view());
1055 // Navigator needs more than a simple update in this case. It needs to be
1057 updateDialog("toc", "");
1061 void GuiView::updateDialog(string const & name, string const & data)
1063 if (!isDialogVisible(name))
1066 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1067 if (it == d.dialogs_.end())
1070 Dialog * const dialog = it->second.get();
1071 if (dialog->isVisibleView())
1072 dialog->initialiseParams(data);
1076 BufferView * GuiView::view()
1078 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1082 void GuiView::autoSave()
1084 LYXERR(Debug::INFO, "Running autoSave()");
1087 view()->buffer().autoSave();
1091 void GuiView::resetAutosaveTimers()
1094 d.autosave_timeout_.restart();
1098 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1101 Buffer * buf = buffer();
1103 /* In LyX/Mac, when a dialog is open, the menus of the
1104 application can still be accessed without giving focus to
1105 the main window. In this case, we want to disable the menu
1106 entries that are buffer-related.
1108 Note that this code is not perfect, as bug 1941 attests:
1109 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1111 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1114 switch(cmd.action) {
1115 case LFUN_BUFFER_WRITE:
1116 enable = buf && (buf->isUnnamed() || !buf->isClean());
1119 case LFUN_BUFFER_WRITE_AS:
1123 case LFUN_SPLIT_VIEW:
1124 if (cmd.getArg(0) == "vertical")
1125 enable = buf && (d.splitter_->count() == 1 ||
1126 d.splitter_->orientation() == Qt::Vertical);
1128 enable = buf && (d.splitter_->count() == 1 ||
1129 d.splitter_->orientation() == Qt::Horizontal);
1132 case LFUN_CLOSE_TAB_GROUP:
1133 enable = d.currentTabWorkArea();
1136 case LFUN_TOOLBAR_TOGGLE:
1137 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1138 flag.setOnOff(t->isVisible());
1141 case LFUN_UI_TOGGLE:
1142 flag.setOnOff(isFullScreen());
1145 case LFUN_DIALOG_TOGGLE:
1146 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1147 // fall through to set "enable"
1148 case LFUN_DIALOG_SHOW: {
1149 string const name = cmd.getArg(0);
1151 enable = name == "aboutlyx"
1152 || name == "file" //FIXME: should be removed.
1154 || name == "texinfo";
1155 else if (name == "print")
1156 enable = buf->isExportable("dvi")
1157 && lyxrc.print_command != "none";
1158 else if (name == "character") {
1162 InsetCode ic = view()->cursor().inset().lyxCode();
1163 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1166 else if (name == "symbols") {
1167 if (!view() || view()->cursor().inMathed())
1170 InsetCode ic = view()->cursor().inset().lyxCode();
1171 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1174 else if (name == "latexlog")
1175 enable = FileName(buf->logName()).isReadableFile();
1176 else if (name == "spellchecker")
1177 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1178 enable = !buf->isReadonly();
1182 else if (name == "vclog")
1183 enable = buf->lyxvc().inUse();
1187 case LFUN_DIALOG_UPDATE: {
1188 string const name = cmd.getArg(0);
1190 enable = name == "prefs";
1194 case LFUN_INSET_APPLY: {
1195 string const name = cmd.getArg(0);
1196 Inset * inset = getOpenInset(name);
1198 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1200 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1201 // Every inset is supposed to handle this
1202 LASSERT(false, break);
1206 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1207 flag |= lyx::getStatus(fr);
1209 enable = flag.enabled();
1213 case LFUN_COMPLETION_INLINE:
1214 if (!d.current_work_area_
1215 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1219 case LFUN_COMPLETION_POPUP:
1220 if (!d.current_work_area_
1221 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1225 case LFUN_COMPLETION_COMPLETE:
1226 if (!d.current_work_area_
1227 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1231 case LFUN_COMPLETION_ACCEPT:
1232 case LFUN_COMPLETION_CANCEL:
1233 if (!d.current_work_area_
1234 || (!d.current_work_area_->completer().popupVisible()
1235 && !d.current_work_area_->completer().inlineVisible()))
1244 flag.setEnabled(false);
1250 static FileName selectTemplateFile()
1252 FileDialog dlg(qt_("Select template file"));
1253 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1254 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1256 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1257 QStringList(qt_("LyX Documents (*.lyx)")));
1259 if (result.first == FileDialog::Later)
1261 if (result.second.isEmpty())
1263 return FileName(fromqstr(result.second));
1267 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1271 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1274 message(_("Document not loaded."));
1279 setBuffer(newBuffer);
1281 // scroll to the position when the file was last closed
1282 if (lyxrc.use_lastfilepos) {
1283 LastFilePosSection::FilePos filepos =
1284 theSession().lastFilePos().load(filename);
1285 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1289 theSession().lastFiles().add(filename);
1296 void GuiView::openDocument(string const & fname)
1298 string initpath = lyxrc.document_path;
1301 string const trypath = buffer()->filePath();
1302 // If directory is writeable, use this as default.
1303 if (FileName(trypath).isDirWritable())
1309 if (fname.empty()) {
1310 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1311 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1312 dlg.setButton2(qt_("Examples|#E#e"),
1313 toqstr(addPath(package().system_support().absFilename(), "examples")));
1315 QStringList filter(qt_("LyX Documents (*.lyx)"));
1316 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1317 << qt_("LyX-1.4.x Documents (*.lyx14)")
1318 << qt_("LyX-1.5.x Documents (*.lyx15)");
1319 FileDialog::Result result =
1320 dlg.open(toqstr(initpath), filter);
1322 if (result.first == FileDialog::Later)
1325 filename = fromqstr(result.second);
1327 // check selected filename
1328 if (filename.empty()) {
1329 message(_("Canceled."));
1335 // get absolute path of file and add ".lyx" to the filename if
1337 FileName const fullname =
1338 fileSearch(string(), filename, "lyx", support::may_not_exist);
1339 if (!fullname.empty())
1340 filename = fullname.absFilename();
1342 if (!fullname.onlyPath().isDirectory()) {
1343 Alert::warning(_("Invalid filename"),
1344 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1345 from_utf8(fullname.absFilename())));
1348 // if the file doesn't exist, let the user create one
1349 if (!fullname.exists()) {
1350 // the user specifically chose this name. Believe him.
1351 Buffer * const b = newFile(filename, string(), true);
1357 docstring const disp_fn = makeDisplayPath(filename);
1358 message(bformat(_("Opening document %1$s..."), disp_fn));
1361 Buffer * buf = loadDocument(fullname);
1366 buf->errors("Parse");
1367 str2 = bformat(_("Document %1$s opened."), disp_fn);
1368 if (buf->lyxvc().inUse())
1369 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1370 " " + _("Version control detected.");
1372 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1377 // FIXME: clean that
1378 static bool import(GuiView * lv, FileName const & filename,
1379 string const & format, ErrorList & errorList)
1381 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1383 string loader_format;
1384 vector<string> loaders = theConverters().loaders();
1385 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1386 for (vector<string>::const_iterator it = loaders.begin();
1387 it != loaders.end(); ++it) {
1388 if (!theConverters().isReachable(format, *it))
1391 string const tofile =
1392 support::changeExtension(filename.absFilename(),
1393 formats.extension(*it));
1394 if (!theConverters().convert(0, filename, FileName(tofile),
1395 filename, format, *it, errorList))
1397 loader_format = *it;
1400 if (loader_format.empty()) {
1401 frontend::Alert::error(_("Couldn't import file"),
1402 bformat(_("No information for importing the format %1$s."),
1403 formats.prettyName(format)));
1407 loader_format = format;
1409 if (loader_format == "lyx") {
1410 Buffer * buf = lv->loadDocument(lyxfile);
1415 buf->errors("Parse");
1417 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1421 bool as_paragraphs = loader_format == "textparagraph";
1422 string filename2 = (loader_format == format) ? filename.absFilename()
1423 : support::changeExtension(filename.absFilename(),
1424 formats.extension(loader_format));
1425 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1426 theLyXFunc().setLyXView(lv);
1427 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1434 void GuiView::importDocument(string const & argument)
1437 string filename = split(argument, format, ' ');
1439 LYXERR(Debug::INFO, format << " file: " << filename);
1441 // need user interaction
1442 if (filename.empty()) {
1443 string initpath = lyxrc.document_path;
1445 Buffer const * buf = buffer();
1447 string const trypath = buf->filePath();
1448 // If directory is writeable, use this as default.
1449 if (FileName(trypath).isDirWritable())
1453 docstring const text = bformat(_("Select %1$s file to import"),
1454 formats.prettyName(format));
1456 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1457 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1458 dlg.setButton2(qt_("Examples|#E#e"),
1459 toqstr(addPath(package().system_support().absFilename(), "examples")));
1461 docstring filter = formats.prettyName(format);
1464 filter += from_utf8(formats.extension(format));
1467 FileDialog::Result result =
1468 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1470 if (result.first == FileDialog::Later)
1473 filename = fromqstr(result.second);
1475 // check selected filename
1476 if (filename.empty())
1477 message(_("Canceled."));
1480 if (filename.empty())
1483 // get absolute path of file
1484 FileName const fullname(support::makeAbsPath(filename));
1486 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1488 // Check if the document already is open
1489 Buffer * buf = theBufferList().getBuffer(lyxfile);
1492 if (!closeBuffer()) {
1493 message(_("Canceled."));
1498 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1500 // if the file exists already, and we didn't do
1501 // -i lyx thefile.lyx, warn
1502 if (lyxfile.exists() && fullname != lyxfile) {
1504 docstring text = bformat(_("The document %1$s already exists.\n\n"
1505 "Do you want to overwrite that document?"), displaypath);
1506 int const ret = Alert::prompt(_("Overwrite document?"),
1507 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1510 message(_("Canceled."));
1515 message(bformat(_("Importing %1$s..."), displaypath));
1516 ErrorList errorList;
1517 if (import(this, fullname, format, errorList))
1518 message(_("imported."));
1520 message(_("file not imported!"));
1522 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1526 void GuiView::newDocument(string const & filename, bool from_template)
1528 FileName initpath(lyxrc.document_path);
1529 Buffer * buf = buffer();
1531 FileName const trypath(buf->filePath());
1532 // If directory is writeable, use this as default.
1533 if (trypath.isDirWritable())
1537 string templatefile = from_template ?
1538 selectTemplateFile().absFilename() : string();
1540 if (filename.empty())
1541 b = newUnnamedFile(templatefile, initpath);
1543 b = newFile(filename, templatefile, true);
1547 // Ensure the cursor is correctly positionned on screen.
1548 view()->showCursor();
1552 void GuiView::insertLyXFile(docstring const & fname)
1554 BufferView * bv = view();
1559 FileName filename(to_utf8(fname));
1561 if (!filename.empty()) {
1562 bv->insertLyXFile(filename);
1566 // Launch a file browser
1568 string initpath = lyxrc.document_path;
1569 string const trypath = bv->buffer().filePath();
1570 // If directory is writeable, use this as default.
1571 if (FileName(trypath).isDirWritable())
1575 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1576 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1577 dlg.setButton2(qt_("Examples|#E#e"),
1578 toqstr(addPath(package().system_support().absFilename(),
1581 FileDialog::Result result = dlg.open(toqstr(initpath),
1582 QStringList(qt_("LyX Documents (*.lyx)")));
1584 if (result.first == FileDialog::Later)
1588 filename.set(fromqstr(result.second));
1590 // check selected filename
1591 if (filename.empty()) {
1592 // emit message signal.
1593 message(_("Canceled."));
1597 bv->insertLyXFile(filename);
1601 void GuiView::insertPlaintextFile(docstring const & fname,
1604 BufferView * bv = view();
1609 FileName filename(to_utf8(fname));
1611 if (!filename.empty()) {
1612 bv->insertPlaintextFile(filename, asParagraph);
1616 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1617 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1619 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1622 if (result.first == FileDialog::Later)
1626 filename.set(fromqstr(result.second));
1628 // check selected filename
1629 if (filename.empty()) {
1630 // emit message signal.
1631 message(_("Canceled."));
1635 bv->insertPlaintextFile(filename, asParagraph);
1639 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1641 FileName fname = b.fileName();
1642 FileName const oldname = fname;
1644 if (!newname.empty()) {
1646 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1648 // Switch to this Buffer.
1651 /// No argument? Ask user through dialog.
1653 FileDialog dlg(qt_("Choose a filename to save document as"),
1654 LFUN_BUFFER_WRITE_AS);
1655 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1656 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1658 if (!isLyXFilename(fname.absFilename()))
1659 fname.changeExtension(".lyx");
1661 FileDialog::Result result =
1662 dlg.save(toqstr(fname.onlyPath().absFilename()),
1663 QStringList(qt_("LyX Documents (*.lyx)")),
1664 toqstr(fname.onlyFileName()));
1666 if (result.first == FileDialog::Later)
1669 fname.set(fromqstr(result.second));
1674 if (!isLyXFilename(fname.absFilename()))
1675 fname.changeExtension(".lyx");
1678 if (FileName(fname).exists()) {
1679 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1680 docstring text = bformat(_("The document %1$s already "
1681 "exists.\n\nDo you want to "
1682 "overwrite that document?"),
1684 int const ret = Alert::prompt(_("Overwrite document?"),
1685 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1688 case 1: return renameBuffer(b, docstring());
1689 case 2: return false;
1693 // Ok, change the name of the buffer
1694 b.setFileName(fname.absFilename());
1696 bool unnamed = b.isUnnamed();
1697 b.setUnnamed(false);
1698 b.saveCheckSum(fname);
1700 if (!saveBuffer(b)) {
1701 b.setFileName(oldname.absFilename());
1702 b.setUnnamed(unnamed);
1703 b.saveCheckSum(oldname);
1711 bool GuiView::saveBuffer(Buffer & b)
1714 return renameBuffer(b, docstring());
1717 theSession().lastFiles().add(b.fileName());
1721 // Switch to this Buffer.
1724 // FIXME: we don't tell the user *WHY* the save failed !!
1725 docstring const file = makeDisplayPath(b.absFileName(), 30);
1726 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1727 "Do you want to rename the document and "
1728 "try again?"), file);
1729 int const ret = Alert::prompt(_("Rename and save?"),
1730 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1733 if (!renameBuffer(b, docstring()))
1742 return saveBuffer(b);
1746 bool GuiView::closeBuffer()
1748 Buffer * buf = buffer();
1749 return buf && closeBuffer(*buf);
1753 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1755 // goto bookmark to update bookmark pit.
1756 //FIXME: we should update only the bookmarks related to this buffer!
1757 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1758 theLyXFunc().gotoBookmark(i+1, false, false);
1760 if (buf.isClean() || buf.paragraphs().empty()) {
1761 if (buf.masterBuffer() == &buf && tolastopened)
1762 theSession().lastOpened().add(buf.fileName());
1763 theBufferList().release(&buf);
1766 // Switch to this Buffer.
1771 if (buf.isUnnamed())
1772 file = from_utf8(buf.fileName().onlyFileName());
1774 file = buf.fileName().displayName(30);
1776 // Bring this window to top before asking questions.
1780 docstring const text = bformat(_("The document %1$s has unsaved changes."
1781 "\n\nDo you want to save the document or discard the changes?"), file);
1782 int const ret = Alert::prompt(_("Save changed document?"),
1783 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1787 if (!saveBuffer(buf))
1791 // if we crash after this we could
1792 // have no autosave file but I guess
1793 // this is really improbable (Jug)
1794 removeAutosaveFile(buf.absFileName());
1800 // save file names to .lyx/session
1801 // if master/slave are both open, do not save slave since it
1802 // will be automatically loaded when the master is loaded
1803 if (buf.masterBuffer() == &buf && tolastopened)
1804 theSession().lastOpened().add(buf.fileName());
1807 // Don't close child documents.
1808 removeWorkArea(d.current_work_area_);
1810 theBufferList().release(&buf);
1816 bool GuiView::dispatch(FuncRequest const & cmd)
1818 BufferView * bv = view();
1819 // By default we won't need any update.
1821 bv->cursor().updateFlags(Update::None);
1822 bool dispatched = true;
1824 switch(cmd.action) {
1825 case LFUN_BUFFER_IMPORT:
1826 importDocument(to_utf8(cmd.argument()));
1829 case LFUN_BUFFER_SWITCH:
1830 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1833 case LFUN_BUFFER_NEXT:
1834 setBuffer(theBufferList().next(buffer()));
1837 case LFUN_BUFFER_PREVIOUS:
1838 setBuffer(theBufferList().previous(buffer()));
1841 case LFUN_COMMAND_EXECUTE: {
1842 bool const show_it = cmd.argument() != "off";
1843 // FIXME: this is a hack, "minibuffer" should not be
1845 if (GuiToolbar * t = toolbar("minibuffer")) {
1846 t->setVisible(show_it);
1847 if (show_it && t->commandBuffer())
1848 t->commandBuffer()->setFocus();
1852 case LFUN_DROP_LAYOUTS_CHOICE:
1854 d.layout_->showPopup();
1857 case LFUN_MENU_OPEN:
1858 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1859 menu->exec(QCursor::pos());
1862 case LFUN_FILE_INSERT:
1863 insertLyXFile(cmd.argument());
1865 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1866 insertPlaintextFile(cmd.argument(), true);
1869 case LFUN_FILE_INSERT_PLAINTEXT:
1870 insertPlaintextFile(cmd.argument(), false);
1873 case LFUN_BUFFER_WRITE:
1875 saveBuffer(bv->buffer());
1878 case LFUN_BUFFER_WRITE_AS:
1880 renameBuffer(bv->buffer(), cmd.argument());
1883 case LFUN_BUFFER_WRITE_ALL: {
1884 Buffer * first = theBufferList().first();
1887 message(_("Saving all documents..."));
1888 // We cannot use a for loop as the buffer list cycles.
1891 if (!b->isClean()) {
1893 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1895 b = theBufferList().next(b);
1896 } while (b != first);
1897 message(_("All documents saved."));
1901 case LFUN_TOOLBAR_TOGGLE: {
1902 string const name = cmd.getArg(0);
1903 if (GuiToolbar * t = toolbar(name))
1908 case LFUN_DIALOG_UPDATE: {
1909 string const name = to_utf8(cmd.argument());
1910 // Can only update a dialog connected to an existing inset
1911 Inset * inset = getOpenInset(name);
1913 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1914 inset->dispatch(view()->cursor(), fr);
1915 } else if (name == "paragraph") {
1916 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1917 } else if (name == "prefs") {
1918 updateDialog(name, string());
1923 case LFUN_DIALOG_TOGGLE: {
1924 if (isDialogVisible(cmd.getArg(0)))
1925 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1927 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1931 case LFUN_DIALOG_DISCONNECT_INSET:
1932 disconnectDialog(to_utf8(cmd.argument()));
1935 case LFUN_DIALOG_HIDE: {
1936 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1940 case LFUN_DIALOG_SHOW: {
1941 string const name = cmd.getArg(0);
1942 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1944 if (name == "character") {
1945 data = freefont2string();
1947 showDialog("character", data);
1948 } else if (name == "latexlog") {
1949 Buffer::LogType type;
1950 string const logfile = buffer()->logName(&type);
1952 case Buffer::latexlog:
1955 case Buffer::buildlog:
1959 data += Lexer::quoteString(logfile);
1960 showDialog("log", data);
1961 } else if (name == "vclog") {
1962 string const data = "vc " +
1963 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1964 showDialog("log", data);
1965 } else if (name == "symbols") {
1966 data = bv->cursor().getEncoding()->name();
1968 showDialog("symbols", data);
1970 showDialog(name, data);
1974 case LFUN_INSET_APPLY: {
1975 string const name = cmd.getArg(0);
1976 Inset * inset = getOpenInset(name);
1978 // put cursor in front of inset.
1979 if (!view()->setCursorFromInset(inset))
1980 LASSERT(false, break);
1982 // useful if we are called from a dialog.
1983 view()->cursor().beginUndoGroup();
1984 view()->cursor().recordUndo();
1985 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1986 inset->dispatch(view()->cursor(), fr);
1987 view()->cursor().endUndoGroup();
1989 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1995 case LFUN_UI_TOGGLE:
1997 // Make sure the keyboard focus stays in the work area.
2001 case LFUN_SPLIT_VIEW:
2002 if (Buffer * buf = buffer()) {
2003 string const orientation = cmd.getArg(0);
2004 d.splitter_->setOrientation(orientation == "vertical"
2005 ? Qt::Vertical : Qt::Horizontal);
2006 TabWorkArea * twa = addTabWorkArea();
2007 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2008 setCurrentWorkArea(wa);
2012 case LFUN_CLOSE_TAB_GROUP:
2013 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2015 twa = d.currentTabWorkArea();
2016 // Switch to the next GuiWorkArea in the found TabWorkArea.
2018 d.current_work_area_ = twa->currentWorkArea();
2019 // Make sure the work area is up to date.
2020 twa->setCurrentWorkArea(d.current_work_area_);
2022 d.current_work_area_ = 0;
2024 if (d.splitter_->count() == 0)
2025 // No more work area, switch to the background widget.
2030 case LFUN_COMPLETION_INLINE:
2031 if (d.current_work_area_)
2032 d.current_work_area_->completer().showInline();
2035 case LFUN_COMPLETION_POPUP:
2036 if (d.current_work_area_)
2037 d.current_work_area_->completer().showPopup();
2041 case LFUN_COMPLETION_COMPLETE:
2042 if (d.current_work_area_)
2043 d.current_work_area_->completer().tab();
2046 case LFUN_COMPLETION_CANCEL:
2047 if (d.current_work_area_) {
2048 if (d.current_work_area_->completer().popupVisible())
2049 d.current_work_area_->completer().hidePopup();
2051 d.current_work_area_->completer().hideInline();
2055 case LFUN_COMPLETION_ACCEPT:
2056 if (d.current_work_area_)
2057 d.current_work_area_->completer().activate();
2066 // Part of automatic menu appearance feature.
2067 if (isFullScreen()) {
2068 if (menuBar()->isVisible())
2070 if (statusBar()->isVisible())
2071 statusBar()->hide();
2078 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2080 string const arg = cmd.getArg(0);
2081 if (arg == "scrollbar") {
2082 // hide() is of no help
2083 if (d.current_work_area_->verticalScrollBarPolicy() ==
2084 Qt::ScrollBarAlwaysOff)
2086 d.current_work_area_->setVerticalScrollBarPolicy(
2087 Qt::ScrollBarAsNeeded);
2089 d.current_work_area_->setVerticalScrollBarPolicy(
2090 Qt::ScrollBarAlwaysOff);
2093 if (arg == "statusbar") {
2094 statusBar()->setVisible(!statusBar()->isVisible());
2097 if (arg == "menubar") {
2098 menuBar()->setVisible(!menuBar()->isVisible());
2101 #if QT_VERSION >= 0x040300
2102 if (arg == "frame") {
2104 getContentsMargins(&l, &t, &r, &b);
2105 //are the frames in default state?
2106 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2108 setContentsMargins(-2, -2, -2, -2);
2110 setContentsMargins(0, 0, 0, 0);
2115 if (arg == "fullscreen") {
2120 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2124 void GuiView::toggleFullScreen()
2126 if (isFullScreen()) {
2127 for (int i = 0; i != d.splitter_->count(); ++i)
2128 d.tabWorkArea(i)->setFullScreen(false);
2129 #if QT_VERSION >= 0x040300
2130 setContentsMargins(0, 0, 0, 0);
2132 setWindowState(windowState() ^ Qt::WindowFullScreen);
2135 statusBar()->show();
2137 for (int i = 0; i != d.splitter_->count(); ++i)
2138 d.tabWorkArea(i)->setFullScreen(true);
2139 #if QT_VERSION >= 0x040300
2140 setContentsMargins(-2, -2, -2, -2);
2143 setWindowState(windowState() ^ Qt::WindowFullScreen);
2144 statusBar()->hide();
2146 if (lyxrc.full_screen_toolbars) {
2147 ToolbarMap::iterator end = d.toolbars_.end();
2148 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2155 Buffer const * GuiView::updateInset(Inset const * inset)
2157 if (!d.current_work_area_)
2161 d.current_work_area_->scheduleRedraw();
2163 return &d.current_work_area_->bufferView().buffer();
2167 void GuiView::restartCursor()
2169 /* When we move around, or type, it's nice to be able to see
2170 * the cursor immediately after the keypress.
2172 if (d.current_work_area_)
2173 d.current_work_area_->startBlinkingCursor();
2175 // Take this occasion to update the other GUI elements.
2181 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2183 if (d.current_work_area_)
2184 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2189 // This list should be kept in sync with the list of insets in
2190 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2191 // dialog should have the same name as the inset.
2192 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2193 // docs in LyXAction.cpp.
2195 char const * const dialognames[] = {
2196 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2197 "citation", "document", "errorlist", "ert", "external", "file",
2198 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2199 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2200 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2202 #ifdef HAVE_LIBAIKSAURUS
2206 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2208 char const * const * const end_dialognames =
2209 dialognames + (sizeof(dialognames) / sizeof(char *));
2213 cmpCStr(char const * name) : name_(name) {}
2214 bool operator()(char const * other) {
2215 return strcmp(other, name_) == 0;
2222 bool isValidName(string const & name)
2224 return find_if(dialognames, end_dialognames,
2225 cmpCStr(name.c_str())) != end_dialognames;
2231 void GuiView::resetDialogs()
2233 // Make sure that no LFUN uses any LyXView.
2234 theLyXFunc().setLyXView(0);
2237 constructToolbars();
2238 guiApp->menus().fillMenuBar(menuBar(), this, true);
2240 d.layout_->updateContents(true);
2241 // Now update controls with current buffer.
2242 theLyXFunc().setLyXView(this);
2248 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2250 if (!isValidName(name))
2253 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2255 if (it != d.dialogs_.end())
2256 return it->second.get();
2258 Dialog * dialog = build(name);
2259 d.dialogs_[name].reset(dialog);
2260 if (lyxrc.allow_geometry_session)
2261 dialog->restoreSession();
2268 void GuiView::showDialog(string const & name, string const & data,
2276 Dialog * dialog = findOrBuild(name, false);
2278 dialog->showData(data);
2280 d.open_insets_[name] = inset;
2283 catch (ExceptionMessage const & ex) {
2291 bool GuiView::isDialogVisible(string const & name) const
2293 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2294 if (it == d.dialogs_.end())
2296 return it->second.get()->isVisibleView();
2300 void GuiView::hideDialog(string const & name, Inset * inset)
2302 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2303 if (it == d.dialogs_.end())
2306 if (inset && inset != getOpenInset(name))
2309 Dialog * const dialog = it->second.get();
2310 if (dialog->isVisibleView())
2312 d.open_insets_[name] = 0;
2316 void GuiView::disconnectDialog(string const & name)
2318 if (!isValidName(name))
2321 if (d.open_insets_.find(name) != d.open_insets_.end())
2322 d.open_insets_[name] = 0;
2326 Inset * GuiView::getOpenInset(string const & name) const
2328 if (!isValidName(name))
2331 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2332 return it == d.open_insets_.end() ? 0 : it->second;
2336 void GuiView::hideAll() const
2338 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2339 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2341 for(; it != end; ++it)
2342 it->second->hideView();
2346 void GuiView::updateDialogs()
2348 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2349 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2351 for(; it != end; ++it) {
2352 Dialog * dialog = it->second.get();
2353 if (dialog && dialog->isVisibleView())
2354 dialog->checkStatus();
2361 // will be replaced by a proper factory...
2362 Dialog * createGuiAbout(GuiView & lv);
2363 Dialog * createGuiBibitem(GuiView & lv);
2364 Dialog * createGuiBibtex(GuiView & lv);
2365 Dialog * createGuiBox(GuiView & lv);
2366 Dialog * createGuiBranch(GuiView & lv);
2367 Dialog * createGuiChanges(GuiView & lv);
2368 Dialog * createGuiCharacter(GuiView & lv);
2369 Dialog * createGuiCitation(GuiView & lv);
2370 Dialog * createGuiDelimiter(GuiView & lv);
2371 Dialog * createGuiDocument(GuiView & lv);
2372 Dialog * createGuiErrorList(GuiView & lv);
2373 Dialog * createGuiERT(GuiView & lv);
2374 Dialog * createGuiExternal(GuiView & lv);
2375 Dialog * createGuiFloat(GuiView & lv);
2376 Dialog * createGuiGraphics(GuiView & lv);
2377 Dialog * createGuiHSpace(GuiView & lv);
2378 Dialog * createGuiInclude(GuiView & lv);
2379 Dialog * createGuiInfo(GuiView & lv);
2380 Dialog * createGuiLabel(GuiView & lv);
2381 Dialog * createGuiListings(GuiView & lv);
2382 Dialog * createGuiLog(GuiView & lv);
2383 Dialog * createGuiMathMatrix(GuiView & lv);
2384 Dialog * createGuiNomenclature(GuiView & lv);
2385 Dialog * createGuiNote(GuiView & lv);
2386 Dialog * createGuiParagraph(GuiView & lv);
2387 Dialog * createGuiPreferences(GuiView & lv);
2388 Dialog * createGuiPrint(GuiView & lv);
2389 Dialog * createGuiRef(GuiView & lv);
2390 Dialog * createGuiSearch(GuiView & lv);
2391 Dialog * createGuiSendTo(GuiView & lv);
2392 Dialog * createGuiShowFile(GuiView & lv);
2393 Dialog * createGuiSpellchecker(GuiView & lv);
2394 Dialog * createGuiSymbols(GuiView & lv);
2395 Dialog * createGuiTabularCreate(GuiView & lv);
2396 Dialog * createGuiTabular(GuiView & lv);
2397 Dialog * createGuiTexInfo(GuiView & lv);
2398 Dialog * createGuiToc(GuiView & lv);
2399 Dialog * createGuiThesaurus(GuiView & lv);
2400 Dialog * createGuiHyperlink(GuiView & lv);
2401 Dialog * createGuiVSpace(GuiView & lv);
2402 Dialog * createGuiViewSource(GuiView & lv);
2403 Dialog * createGuiWrap(GuiView & lv);
2406 Dialog * GuiView::build(string const & name)
2408 LASSERT(isValidName(name), return 0);
2410 if (name == "aboutlyx")
2411 return createGuiAbout(*this);
2412 if (name == "bibitem")
2413 return createGuiBibitem(*this);
2414 if (name == "bibtex")
2415 return createGuiBibtex(*this);
2417 return createGuiBox(*this);
2418 if (name == "branch")
2419 return createGuiBranch(*this);
2420 if (name == "changes")
2421 return createGuiChanges(*this);
2422 if (name == "character")
2423 return createGuiCharacter(*this);
2424 if (name == "citation")
2425 return createGuiCitation(*this);
2426 if (name == "document")
2427 return createGuiDocument(*this);
2428 if (name == "errorlist")
2429 return createGuiErrorList(*this);
2431 return createGuiERT(*this);
2432 if (name == "external")
2433 return createGuiExternal(*this);
2435 return createGuiShowFile(*this);
2436 if (name == "findreplace")
2437 return createGuiSearch(*this);
2438 if (name == "float")
2439 return createGuiFloat(*this);
2440 if (name == "graphics")
2441 return createGuiGraphics(*this);
2442 if (name == "include")
2443 return createGuiInclude(*this);
2445 return createGuiInfo(*this);
2446 if (name == "nomenclature")
2447 return createGuiNomenclature(*this);
2448 if (name == "label")
2449 return createGuiLabel(*this);
2451 return createGuiLog(*this);
2452 if (name == "view-source")
2453 return createGuiViewSource(*this);
2454 if (name == "mathdelimiter")
2455 return createGuiDelimiter(*this);
2456 if (name == "mathmatrix")
2457 return createGuiMathMatrix(*this);
2459 return createGuiNote(*this);
2460 if (name == "paragraph")
2461 return createGuiParagraph(*this);
2462 if (name == "prefs")
2463 return createGuiPreferences(*this);
2464 if (name == "print")
2465 return createGuiPrint(*this);
2467 return createGuiRef(*this);
2468 if (name == "sendto")
2469 return createGuiSendTo(*this);
2470 if (name == "space")
2471 return createGuiHSpace(*this);
2472 if (name == "spellchecker")
2473 return createGuiSpellchecker(*this);
2474 if (name == "symbols")
2475 return createGuiSymbols(*this);
2476 if (name == "tabular")
2477 return createGuiTabular(*this);
2478 if (name == "tabularcreate")
2479 return createGuiTabularCreate(*this);
2480 if (name == "texinfo")
2481 return createGuiTexInfo(*this);
2482 #ifdef HAVE_LIBAIKSAURUS
2483 if (name == "thesaurus")
2484 return createGuiThesaurus(*this);
2487 return createGuiToc(*this);
2489 return createGuiHyperlink(*this);
2490 if (name == "vspace")
2491 return createGuiVSpace(*this);
2493 return createGuiWrap(*this);
2494 if (name == "listings")
2495 return createGuiListings(*this);
2501 } // namespace frontend
2504 #include "GuiView_moc.cpp"