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)))
384 if ((tmp = findOrBuild("view-source", true)))
387 if (!restoreState(settings.value(key + "/layout").toByteArray(), 0))
394 GuiToolbar * GuiView::toolbar(string const & name)
396 ToolbarMap::iterator it = d.toolbars_.find(name);
397 if (it != d.toolbars_.end())
400 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
401 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
406 void GuiView::constructToolbars()
408 ToolbarMap::iterator it = d.toolbars_.begin();
409 for (; it != d.toolbars_.end(); ++it)
414 // extracts the toolbars from the backend
415 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
416 Toolbars::Infos::iterator end = guiApp->toolbars().end();
417 for (; cit != end; ++cit)
418 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
422 void GuiView::initToolbars()
424 // extracts the toolbars from the backend
425 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
426 Toolbars::Infos::iterator end = guiApp->toolbars().end();
427 for (; cit != end; ++cit) {
428 GuiToolbar * tb = toolbar(cit->name);
431 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
433 tb->setVisible(false);
434 tb->setVisibility(visibility);
436 if (visibility & Toolbars::TOP) {
438 addToolBarBreak(Qt::TopToolBarArea);
439 addToolBar(Qt::TopToolBarArea, tb);
442 if (visibility & Toolbars::BOTTOM) {
443 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
444 #if (QT_VERSION >= 0x040202)
445 addToolBarBreak(Qt::BottomToolBarArea);
447 addToolBar(Qt::BottomToolBarArea, tb);
450 if (visibility & Toolbars::LEFT) {
451 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
452 #if (QT_VERSION >= 0x040202)
453 addToolBarBreak(Qt::LeftToolBarArea);
455 addToolBar(Qt::LeftToolBarArea, tb);
458 if (visibility & Toolbars::RIGHT) {
459 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
460 #if (QT_VERSION >= 0x040202)
461 addToolBarBreak(Qt::RightToolBarArea);
463 addToolBar(Qt::RightToolBarArea, tb);
466 if (visibility & Toolbars::ON)
467 tb->setVisible(true);
472 TocModels & GuiView::tocModels()
474 return d.toc_models_;
478 void GuiView::setFocus()
480 // Make sure LyXFunc points to the correct view.
481 theLyXFunc().setLyXView(this);
482 if (d.current_work_area_)
483 d.current_work_area_->setFocus();
489 QMenu * GuiView::createPopupMenu()
491 return d.toolBarPopup(this);
495 void GuiView::showEvent(QShowEvent * e)
497 LYXERR(Debug::GUI, "Passed Geometry "
498 << size().height() << "x" << size().width()
499 << "+" << pos().x() << "+" << pos().y());
501 if (d.splitter_->count() == 0)
502 // No work area, switch to the background widget.
505 QMainWindow::showEvent(e);
509 void GuiView::closeEvent(QCloseEvent * close_event)
513 // it can happen that this event arrives without selecting the view,
514 // e.g. when clicking the close button on a background window.
515 theLyXFunc().setLyXView(this);
517 while (Buffer * b = buffer()) {
519 // This is a child document, just close the tab after saving
520 // but keep the file loaded.
521 if (!saveBuffer(*b)) {
523 close_event->ignore();
526 removeWorkArea(d.current_work_area_);
530 QList<int> const ids = guiApp->viewIds();
531 for (int i = 0; i != ids.size(); ++i) {
534 if (guiApp->view(ids[i]).workArea(*b)) {
535 // FIXME 1: should we put an alert box here that the buffer
536 // is viewed elsewhere?
537 // FIXME 2: should we try to save this buffer in any case?
540 // This buffer is also opened in another view, so
541 // close the associated work area...
542 removeWorkArea(d.current_work_area_);
543 // ... but don't close the buffer.
548 if (b && !closeBuffer(*b, true)) {
550 close_event->ignore();
555 // Make sure that nothing will use this close to be closed View.
556 guiApp->unregisterView(this);
558 if (isFullScreen()) {
559 // Switch off fullscreen before closing.
564 // Make sure the timer time out will not trigger a statusbar update.
565 d.statusbar_timer_.stop();
567 // Saving fullscreen requires additional tweaks in the toolbar code.
568 // It wouldn't also work under linux natively.
569 if (lyxrc.allow_geometry_session) {
570 // Save this window geometry and layout.
572 // Then the toolbar private states.
573 ToolbarMap::iterator end = d.toolbars_.end();
574 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
575 it->second->saveSession();
576 // Now take care of all other dialogs:
577 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
578 for (; it!= d.dialogs_.end(); ++it)
579 it->second->saveSession();
582 close_event->accept();
586 void GuiView::dragEnterEvent(QDragEnterEvent * event)
588 if (event->mimeData()->hasUrls())
590 /// \todo Ask lyx-devel is this is enough:
591 /// if (event->mimeData()->hasFormat("text/plain"))
592 /// event->acceptProposedAction();
596 void GuiView::dropEvent(QDropEvent* event)
598 QList<QUrl> files = event->mimeData()->urls();
602 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
603 for (int i = 0; i != files.size(); ++i) {
604 string const file = os::internal_path(fromqstr(
605 files.at(i).toLocalFile()));
607 lyx::dispatch(FuncRequest(LFUN_FILE_OPEN, file));
612 void GuiView::message(docstring const & str)
614 if (ForkedProcess::iAmAChild())
617 statusBar()->showMessage(toqstr(str));
618 d.statusbar_timer_.stop();
619 d.statusbar_timer_.start(3000);
623 void GuiView::smallSizedIcons()
625 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
629 void GuiView::normalSizedIcons()
631 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
635 void GuiView::bigSizedIcons()
637 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
641 void GuiView::clearMessage()
645 theLyXFunc().setLyXView(this);
646 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
647 d.statusbar_timer_.stop();
651 void GuiView::updateWindowTitle(GuiWorkArea * wa)
653 if (wa != d.current_work_area_)
655 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
656 setWindowIconText(wa->windowIconText());
660 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
663 disconnectBufferView();
664 connectBufferView(wa->bufferView());
665 connectBuffer(wa->bufferView().buffer());
666 d.current_work_area_ = wa;
667 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
668 this, SLOT(updateWindowTitle(GuiWorkArea *)));
669 updateWindowTitle(wa);
673 // The document settings needs to be reinitialised.
674 updateDialog("document", "");
676 // Buffer-dependent dialogs must be updated. This is done here because
677 // some dialogs require buffer()->text.
682 void GuiView::on_lastWorkAreaRemoved()
685 // We already are in a close event. Nothing more to do.
688 if (d.splitter_->count() > 1)
689 // We have a splitter so don't close anything.
692 // Reset and updates the dialogs.
693 d.toc_models_.reset(0);
694 updateDialog("document", "");
697 resetWindowTitleAndIconText();
699 if (lyxrc.open_buffers_in_tabs)
700 // Nothing more to do, the window should stay open.
703 if (guiApp->viewIds().size() > 1) {
709 // On Mac we also close the last window because the application stay
710 // resident in memory. On other platforms we don't close the last
711 // window because this would quit the application.
717 void GuiView::updateStatusBar()
719 // let the user see the explicit message
720 if (d.statusbar_timer_.isActive())
723 theLyXFunc().setLyXView(this);
724 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
728 bool GuiView::hasFocus() const
730 return qApp->activeWindow() == this;
734 bool GuiView::event(QEvent * e)
738 // Useful debug code:
739 //case QEvent::ActivationChange:
740 //case QEvent::WindowDeactivate:
741 //case QEvent::Paint:
742 //case QEvent::Enter:
743 //case QEvent::Leave:
744 //case QEvent::HoverEnter:
745 //case QEvent::HoverLeave:
746 //case QEvent::HoverMove:
747 //case QEvent::StatusTip:
748 //case QEvent::DragEnter:
749 //case QEvent::DragLeave:
753 case QEvent::WindowActivate: {
754 if (this == guiApp->currentView()) {
756 return QMainWindow::event(e);
758 guiApp->setCurrentView(this);
759 theLyXFunc().setLyXView(this);
760 if (d.current_work_area_) {
761 BufferView & bv = d.current_work_area_->bufferView();
762 connectBufferView(bv);
763 connectBuffer(bv.buffer());
764 // The document structure, name and dialogs might have
765 // changed in another view.
767 // The document settings needs to be reinitialised.
768 updateDialog("document", "");
771 resetWindowTitleAndIconText();
774 return QMainWindow::event(e);
777 case QEvent::ShortcutOverride: {
781 if (isFullScreen() && menuBar()->isHidden()) {
782 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
783 // FIXME: we should also try to detect special LyX shortcut such as
784 // Alt-P and Alt-M. Right now there is a hack in
785 // GuiWorkArea::processKeySym() that hides again the menubar for
787 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt)
789 return QMainWindow::event(e);
793 if (d.current_work_area_)
794 // Nothing special to do.
795 return QMainWindow::event(e);
797 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
798 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
800 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
801 || ke->key() == Qt::Key_Backtab)
802 return QMainWindow::event(e);
804 // Allow processing of shortcuts that are allowed even when no Buffer
806 theLyXFunc().setLyXView(this);
808 setKeySymbol(&sym, ke);
809 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
815 return QMainWindow::event(e);
819 void GuiView::resetWindowTitleAndIconText()
821 setWindowTitle(qt_("LyX"));
822 setWindowIconText(qt_("LyX"));
825 bool GuiView::focusNextPrevChild(bool /*next*/)
832 void GuiView::setBusy(bool busy)
834 if (d.current_work_area_) {
835 d.current_work_area_->setUpdatesEnabled(!busy);
837 d.current_work_area_->stopBlinkingCursor();
839 d.current_work_area_->startBlinkingCursor();
843 QApplication::setOverrideCursor(Qt::WaitCursor);
845 QApplication::restoreOverrideCursor();
849 GuiWorkArea * GuiView::workArea(Buffer & buffer)
851 if (TabWorkArea * twa = d.currentTabWorkArea())
852 return twa->workArea(buffer);
857 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
859 // Automatically create a TabWorkArea if there are none yet.
860 TabWorkArea * tab_widget = d.splitter_->count()
861 ? d.currentTabWorkArea() : addTabWorkArea();
862 return tab_widget->addWorkArea(buffer, *this);
866 TabWorkArea * GuiView::addTabWorkArea()
868 TabWorkArea * twa = new TabWorkArea;
869 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
870 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
871 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
872 this, SLOT(on_lastWorkAreaRemoved()));
874 d.splitter_->addWidget(twa);
875 d.stack_widget_->setCurrentWidget(d.splitter_);
880 GuiWorkArea const * GuiView::currentWorkArea() const
882 return d.current_work_area_;
886 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
889 d.current_work_area_ = wa;
890 for (int i = 0; i != d.splitter_->count(); ++i) {
891 if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
897 void GuiView::removeWorkArea(GuiWorkArea * wa)
900 if (wa == d.current_work_area_) {
902 disconnectBufferView();
903 d.current_work_area_ = 0;
906 for (int i = 0; i != d.splitter_->count(); ++i) {
907 TabWorkArea * twa = d.tabWorkArea(i);
908 if (!twa->removeWorkArea(wa))
909 // Not found in this tab group.
912 // We found and removed the GuiWorkArea.
914 // No more WorkAreas in this tab group, so delete it.
919 if (d.current_work_area_)
920 // This means that we are not closing the current GuiWorkArea;
923 // Switch to the next GuiWorkArea in the found TabWorkArea.
924 d.current_work_area_ = twa->currentWorkArea();
928 if (d.splitter_->count() == 0)
929 // No more work area, switch to the background widget.
934 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
940 void GuiView::updateLayoutList()
943 d.layout_->updateContents(false);
947 void GuiView::updateToolbars()
949 ToolbarMap::iterator end = d.toolbars_.end();
950 if (d.current_work_area_) {
952 d.current_work_area_->bufferView().cursor().inMathed();
954 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
956 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
957 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
958 bool const mathmacrotemplate =
959 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
961 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
962 it->second->update(math, table, review, mathmacrotemplate);
964 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
965 it->second->update(false, false, false, false);
969 Buffer * GuiView::buffer()
971 if (d.current_work_area_)
972 return &d.current_work_area_->bufferView().buffer();
977 Buffer const * GuiView::buffer() const
979 if (d.current_work_area_)
980 return &d.current_work_area_->bufferView().buffer();
985 void GuiView::setBuffer(Buffer * newBuffer)
987 LASSERT(newBuffer, /**/);
990 GuiWorkArea * wa = workArea(*newBuffer);
992 updateLabels(*newBuffer->masterBuffer());
993 wa = addWorkArea(*newBuffer);
995 //Disconnect the old buffer...there's no new one.
998 connectBuffer(*newBuffer);
999 connectBufferView(wa->bufferView());
1000 setCurrentWorkArea(wa);
1006 void GuiView::connectBuffer(Buffer & buf)
1008 buf.setGuiDelegate(this);
1012 void GuiView::disconnectBuffer()
1014 if (d.current_work_area_)
1015 d.current_work_area_->bufferView().setGuiDelegate(0);
1019 void GuiView::connectBufferView(BufferView & bv)
1021 bv.setGuiDelegate(this);
1025 void GuiView::disconnectBufferView()
1027 if (d.current_work_area_)
1028 d.current_work_area_->bufferView().setGuiDelegate(0);
1032 void GuiView::errors(string const & error_type)
1034 ErrorList & el = buffer()->errorList(error_type);
1036 showDialog("errorlist", error_type);
1040 void GuiView::structureChanged()
1042 d.toc_models_.reset(view());
1043 // Navigator needs more than a simple update in this case. It needs to be
1045 updateDialog("toc", "");
1049 void GuiView::updateDialog(string const & name, string const & data)
1051 if (!isDialogVisible(name))
1054 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1055 if (it == d.dialogs_.end())
1058 Dialog * const dialog = it->second.get();
1059 if (dialog->isVisibleView())
1060 dialog->initialiseParams(data);
1064 BufferView * GuiView::view()
1066 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1070 void GuiView::autoSave()
1072 LYXERR(Debug::INFO, "Running autoSave()");
1075 view()->buffer().autoSave();
1079 void GuiView::resetAutosaveTimers()
1082 d.autosave_timeout_.restart();
1086 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1089 Buffer * buf = buffer();
1091 /* In LyX/Mac, when a dialog is open, the menus of the
1092 application can still be accessed without giving focus to
1093 the main window. In this case, we want to disable the menu
1094 entries that are buffer-related.
1096 Note that this code is not perfect, as bug 1941 attests:
1097 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1099 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1102 switch(cmd.action) {
1103 case LFUN_BUFFER_WRITE:
1104 enable = buf && (buf->isUnnamed() || !buf->isClean());
1107 case LFUN_BUFFER_WRITE_AS:
1111 case LFUN_SPLIT_VIEW:
1112 if (cmd.getArg(0) == "vertical")
1113 enable = buf && (d.splitter_->count() == 1 ||
1114 d.splitter_->orientation() == Qt::Vertical);
1116 enable = buf && (d.splitter_->count() == 1 ||
1117 d.splitter_->orientation() == Qt::Horizontal);
1120 case LFUN_CLOSE_TAB_GROUP:
1121 enable = d.currentTabWorkArea();
1124 case LFUN_TOOLBAR_TOGGLE:
1125 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1126 flag.setOnOff(t->isVisible());
1129 case LFUN_UI_TOGGLE:
1130 flag.setOnOff(isFullScreen());
1133 case LFUN_DIALOG_TOGGLE:
1134 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1135 // fall through to set "enable"
1136 case LFUN_DIALOG_SHOW: {
1137 string const name = cmd.getArg(0);
1139 enable = name == "aboutlyx"
1140 || name == "file" //FIXME: should be removed.
1142 || name == "texinfo";
1143 else if (name == "print")
1144 enable = buf->isExportable("dvi")
1145 && lyxrc.print_command != "none";
1146 else if (name == "character") {
1150 InsetCode ic = view()->cursor().inset().lyxCode();
1151 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1154 else if (name == "symbols") {
1155 if (!view() || view()->cursor().inMathed())
1158 InsetCode ic = view()->cursor().inset().lyxCode();
1159 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1162 else if (name == "latexlog")
1163 enable = FileName(buf->logName()).isReadableFile();
1164 else if (name == "spellchecker")
1165 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
1166 enable = !buf->isReadonly();
1170 else if (name == "vclog")
1171 enable = buf->lyxvc().inUse();
1175 case LFUN_DIALOG_UPDATE: {
1176 string const name = cmd.getArg(0);
1178 enable = name == "prefs";
1182 case LFUN_INSET_APPLY: {
1183 string const name = cmd.getArg(0);
1184 Inset * inset = getOpenInset(name);
1186 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1188 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1189 // Every inset is supposed to handle this
1190 LASSERT(false, /**/);
1194 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1195 flag |= lyx::getStatus(fr);
1197 enable = flag.enabled();
1201 case LFUN_COMPLETION_INLINE:
1202 if (!d.current_work_area_
1203 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1207 case LFUN_COMPLETION_POPUP:
1208 if (!d.current_work_area_
1209 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1213 case LFUN_COMPLETION_COMPLETE:
1214 if (!d.current_work_area_
1215 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1219 case LFUN_COMPLETION_ACCEPT:
1220 case LFUN_COMPLETION_CANCEL:
1221 if (!d.current_work_area_
1222 || (!d.current_work_area_->completer().popupVisible()
1223 && !d.current_work_area_->completer().inlineVisible()))
1232 flag.setEnabled(false);
1238 static FileName selectTemplateFile()
1240 FileDialog dlg(qt_("Select template file"));
1241 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1242 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1244 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1245 QStringList(qt_("LyX Documents (*.lyx)")));
1247 if (result.first == FileDialog::Later)
1249 if (result.second.isEmpty())
1251 return FileName(fromqstr(result.second));
1255 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1259 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1262 message(_("Document not loaded."));
1267 setBuffer(newBuffer);
1269 // scroll to the position when the file was last closed
1270 if (lyxrc.use_lastfilepos) {
1271 LastFilePosSection::FilePos filepos =
1272 theSession().lastFilePos().load(filename);
1273 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1277 theSession().lastFiles().add(filename);
1284 void GuiView::openDocument(string const & fname)
1286 string initpath = lyxrc.document_path;
1289 string const trypath = buffer()->filePath();
1290 // If directory is writeable, use this as default.
1291 if (FileName(trypath).isDirWritable())
1297 if (fname.empty()) {
1298 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1299 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1300 dlg.setButton2(qt_("Examples|#E#e"),
1301 toqstr(addPath(package().system_support().absFilename(), "examples")));
1303 QStringList filter(qt_("LyX Documents (*.lyx)"));
1304 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1305 << qt_("LyX-1.4.x Documents (*.lyx14)")
1306 << qt_("LyX-1.5.x Documents (*.lyx15)");
1307 FileDialog::Result result =
1308 dlg.open(toqstr(initpath), filter);
1310 if (result.first == FileDialog::Later)
1313 filename = fromqstr(result.second);
1315 // check selected filename
1316 if (filename.empty()) {
1317 message(_("Canceled."));
1323 // get absolute path of file and add ".lyx" to the filename if
1325 FileName const fullname =
1326 fileSearch(string(), filename, "lyx", support::may_not_exist);
1327 if (!fullname.empty())
1328 filename = fullname.absFilename();
1330 if (!fullname.onlyPath().isDirectory()) {
1331 Alert::warning(_("Invalid filename"),
1332 bformat(_("The directory in the given path\n%1$s\ndoes not exists."),
1333 from_utf8(fullname.absFilename())));
1336 // if the file doesn't exist, let the user create one
1337 if (!fullname.exists()) {
1338 // the user specifically chose this name. Believe him.
1339 Buffer * const b = newFile(filename, string(), true);
1345 docstring const disp_fn = makeDisplayPath(filename);
1346 message(bformat(_("Opening document %1$s..."), disp_fn));
1349 Buffer * buf = loadDocument(fullname);
1354 buf->errors("Parse");
1355 str2 = bformat(_("Document %1$s opened."), disp_fn);
1356 if (buf->lyxvc().inUse())
1357 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1358 " " + _("Version control detected.");
1360 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1365 // FIXME: clean that
1366 static bool import(GuiView * lv, FileName const & filename,
1367 string const & format, ErrorList & errorList)
1369 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1371 string loader_format;
1372 vector<string> loaders = theConverters().loaders();
1373 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1374 for (vector<string>::const_iterator it = loaders.begin();
1375 it != loaders.end(); ++it) {
1376 if (!theConverters().isReachable(format, *it))
1379 string const tofile =
1380 support::changeExtension(filename.absFilename(),
1381 formats.extension(*it));
1382 if (!theConverters().convert(0, filename, FileName(tofile),
1383 filename, format, *it, errorList))
1385 loader_format = *it;
1388 if (loader_format.empty()) {
1389 frontend::Alert::error(_("Couldn't import file"),
1390 bformat(_("No information for importing the format %1$s."),
1391 formats.prettyName(format)));
1395 loader_format = format;
1397 if (loader_format == "lyx") {
1398 Buffer * buf = lv->loadDocument(lyxfile);
1403 buf->errors("Parse");
1405 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1409 bool as_paragraphs = loader_format == "textparagraph";
1410 string filename2 = (loader_format == format) ? filename.absFilename()
1411 : support::changeExtension(filename.absFilename(),
1412 formats.extension(loader_format));
1413 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1414 theLyXFunc().setLyXView(lv);
1415 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1422 void GuiView::importDocument(string const & argument)
1425 string filename = split(argument, format, ' ');
1427 LYXERR(Debug::INFO, format << " file: " << filename);
1429 // need user interaction
1430 if (filename.empty()) {
1431 string initpath = lyxrc.document_path;
1433 Buffer const * buf = buffer();
1435 string const trypath = buf->filePath();
1436 // If directory is writeable, use this as default.
1437 if (FileName(trypath).isDirWritable())
1441 docstring const text = bformat(_("Select %1$s file to import"),
1442 formats.prettyName(format));
1444 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1445 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1446 dlg.setButton2(qt_("Examples|#E#e"),
1447 toqstr(addPath(package().system_support().absFilename(), "examples")));
1449 docstring filter = formats.prettyName(format);
1452 filter += from_utf8(formats.extension(format));
1455 FileDialog::Result result =
1456 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1458 if (result.first == FileDialog::Later)
1461 filename = fromqstr(result.second);
1463 // check selected filename
1464 if (filename.empty())
1465 message(_("Canceled."));
1468 if (filename.empty())
1471 // get absolute path of file
1472 FileName const fullname(support::makeAbsPath(filename));
1474 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1476 // Check if the document already is open
1477 Buffer * buf = theBufferList().getBuffer(lyxfile);
1480 if (!closeBuffer()) {
1481 message(_("Canceled."));
1486 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1488 // if the file exists already, and we didn't do
1489 // -i lyx thefile.lyx, warn
1490 if (lyxfile.exists() && fullname != lyxfile) {
1492 docstring text = bformat(_("The document %1$s already exists.\n\n"
1493 "Do you want to overwrite that document?"), displaypath);
1494 int const ret = Alert::prompt(_("Overwrite document?"),
1495 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1498 message(_("Canceled."));
1503 message(bformat(_("Importing %1$s..."), displaypath));
1504 ErrorList errorList;
1505 if (import(this, fullname, format, errorList))
1506 message(_("imported."));
1508 message(_("file not imported!"));
1510 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1514 void GuiView::newDocument(string const & filename, bool from_template)
1516 FileName initpath(lyxrc.document_path);
1517 Buffer * buf = buffer();
1519 FileName const trypath(buf->filePath());
1520 // If directory is writeable, use this as default.
1521 if (trypath.isDirWritable())
1525 string templatefile = from_template ?
1526 selectTemplateFile().absFilename() : string();
1528 if (filename.empty())
1529 b = newUnnamedFile(templatefile, initpath);
1531 b = newFile(filename, templatefile, true);
1535 // Ensure the cursor is correctly positionned on screen.
1536 view()->showCursor();
1540 void GuiView::insertLyXFile(docstring const & fname)
1542 BufferView * bv = view();
1547 FileName filename(to_utf8(fname));
1549 if (!filename.empty()) {
1550 bv->insertLyXFile(filename);
1554 // Launch a file browser
1556 string initpath = lyxrc.document_path;
1557 string const trypath = bv->buffer().filePath();
1558 // If directory is writeable, use this as default.
1559 if (FileName(trypath).isDirWritable())
1563 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1564 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1565 dlg.setButton2(qt_("Examples|#E#e"),
1566 toqstr(addPath(package().system_support().absFilename(),
1569 FileDialog::Result result = dlg.open(toqstr(initpath),
1570 QStringList(qt_("LyX Documents (*.lyx)")));
1572 if (result.first == FileDialog::Later)
1576 filename.set(fromqstr(result.second));
1578 // check selected filename
1579 if (filename.empty()) {
1580 // emit message signal.
1581 message(_("Canceled."));
1585 bv->insertLyXFile(filename);
1589 void GuiView::insertPlaintextFile(docstring const & fname,
1592 BufferView * bv = view();
1597 FileName filename(to_utf8(fname));
1599 if (!filename.empty()) {
1600 bv->insertPlaintextFile(filename, asParagraph);
1604 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1605 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1607 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1610 if (result.first == FileDialog::Later)
1614 filename.set(fromqstr(result.second));
1616 // check selected filename
1617 if (filename.empty()) {
1618 // emit message signal.
1619 message(_("Canceled."));
1623 bv->insertPlaintextFile(filename, asParagraph);
1627 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1629 FileName fname = b.fileName();
1630 FileName const oldname = fname;
1632 if (!newname.empty()) {
1634 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1636 // Switch to this Buffer.
1639 /// No argument? Ask user through dialog.
1641 FileDialog dlg(qt_("Choose a filename to save document as"),
1642 LFUN_BUFFER_WRITE_AS);
1643 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1644 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1646 if (!isLyXFilename(fname.absFilename()))
1647 fname.changeExtension(".lyx");
1649 FileDialog::Result result =
1650 dlg.save(toqstr(fname.onlyPath().absFilename()),
1651 QStringList(qt_("LyX Documents (*.lyx)")),
1652 toqstr(fname.onlyFileName()));
1654 if (result.first == FileDialog::Later)
1657 fname.set(fromqstr(result.second));
1662 if (!isLyXFilename(fname.absFilename()))
1663 fname.changeExtension(".lyx");
1666 if (FileName(fname).exists()) {
1667 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1668 docstring text = bformat(_("The document %1$s already "
1669 "exists.\n\nDo you want to "
1670 "overwrite that document?"),
1672 int const ret = Alert::prompt(_("Overwrite document?"),
1673 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1676 case 1: return renameBuffer(b, docstring());
1677 case 2: return false;
1681 // Ok, change the name of the buffer
1682 b.setFileName(fname.absFilename());
1684 bool unnamed = b.isUnnamed();
1685 b.setUnnamed(false);
1686 b.saveCheckSum(fname);
1688 if (!saveBuffer(b)) {
1689 b.setFileName(oldname.absFilename());
1690 b.setUnnamed(unnamed);
1691 b.saveCheckSum(oldname);
1699 bool GuiView::saveBuffer(Buffer & b)
1702 return renameBuffer(b, docstring());
1705 theSession().lastFiles().add(b.fileName());
1709 // Switch to this Buffer.
1712 // FIXME: we don't tell the user *WHY* the save failed !!
1713 docstring const file = makeDisplayPath(b.absFileName(), 30);
1714 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1715 "Do you want to rename the document and "
1716 "try again?"), file);
1717 int const ret = Alert::prompt(_("Rename and save?"),
1718 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1721 if (!renameBuffer(b, docstring()))
1730 return saveBuffer(b);
1734 bool GuiView::closeBuffer()
1736 Buffer * buf = buffer();
1737 return buf && closeBuffer(*buf);
1741 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1743 // goto bookmark to update bookmark pit.
1744 //FIXME: we should update only the bookmarks related to this buffer!
1745 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1746 theLyXFunc().gotoBookmark(i+1, false, false);
1748 if (buf.isClean() || buf.paragraphs().empty()) {
1749 if (buf.masterBuffer() == &buf && tolastopened)
1750 theSession().lastOpened().add(buf.fileName());
1751 theBufferList().release(&buf);
1754 // Switch to this Buffer.
1759 if (buf.isUnnamed())
1760 file = from_utf8(buf.fileName().onlyFileName());
1762 file = buf.fileName().displayName(30);
1764 // Bring this window to top before asking questions.
1768 docstring const text = bformat(_("The document %1$s has unsaved changes."
1769 "\n\nDo you want to save the document or discard the changes?"), file);
1770 int const ret = Alert::prompt(_("Save changed document?"),
1771 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1775 if (!saveBuffer(buf))
1779 // if we crash after this we could
1780 // have no autosave file but I guess
1781 // this is really improbable (Jug)
1782 removeAutosaveFile(buf.absFileName());
1788 // save file names to .lyx/session
1789 // if master/slave are both open, do not save slave since it
1790 // will be automatically loaded when the master is loaded
1791 if (buf.masterBuffer() == &buf && tolastopened)
1792 theSession().lastOpened().add(buf.fileName());
1795 // Don't close child documents.
1796 removeWorkArea(d.current_work_area_);
1798 theBufferList().release(&buf);
1804 bool GuiView::dispatch(FuncRequest const & cmd)
1806 BufferView * bv = view();
1807 // By default we won't need any update.
1809 bv->cursor().updateFlags(Update::None);
1810 bool dispatched = true;
1812 switch(cmd.action) {
1813 case LFUN_BUFFER_IMPORT:
1814 importDocument(to_utf8(cmd.argument()));
1817 case LFUN_BUFFER_SWITCH:
1818 setBuffer(theBufferList().getBuffer(FileName(to_utf8(cmd.argument()))));
1821 case LFUN_BUFFER_NEXT:
1822 setBuffer(theBufferList().next(buffer()));
1825 case LFUN_BUFFER_PREVIOUS:
1826 setBuffer(theBufferList().previous(buffer()));
1829 case LFUN_COMMAND_EXECUTE: {
1830 bool const show_it = cmd.argument() != "off";
1831 // FIXME: this is a hack, "minibuffer" should not be
1833 if (GuiToolbar * t = toolbar("minibuffer")) {
1834 t->setVisible(show_it);
1835 if (show_it && t->commandBuffer())
1836 t->commandBuffer()->setFocus();
1840 case LFUN_DROP_LAYOUTS_CHOICE:
1842 d.layout_->showPopup();
1845 case LFUN_MENU_OPEN:
1846 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
1847 menu->exec(QCursor::pos());
1850 case LFUN_FILE_INSERT:
1851 insertLyXFile(cmd.argument());
1853 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1854 insertPlaintextFile(cmd.argument(), true);
1857 case LFUN_FILE_INSERT_PLAINTEXT:
1858 insertPlaintextFile(cmd.argument(), false);
1861 case LFUN_BUFFER_WRITE:
1863 saveBuffer(bv->buffer());
1866 case LFUN_BUFFER_WRITE_AS:
1868 renameBuffer(bv->buffer(), cmd.argument());
1871 case LFUN_BUFFER_WRITE_ALL: {
1872 Buffer * first = theBufferList().first();
1875 message(_("Saving all documents..."));
1876 // We cannot use a for loop as the buffer list cycles.
1879 if (!b->isClean()) {
1881 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
1883 b = theBufferList().next(b);
1884 } while (b != first);
1885 message(_("All documents saved."));
1889 case LFUN_TOOLBAR_TOGGLE: {
1890 string const name = cmd.getArg(0);
1891 if (GuiToolbar * t = toolbar(name))
1896 case LFUN_DIALOG_UPDATE: {
1897 string const name = to_utf8(cmd.argument());
1898 // Can only update a dialog connected to an existing inset
1899 Inset * inset = getOpenInset(name);
1901 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1902 inset->dispatch(view()->cursor(), fr);
1903 } else if (name == "paragraph") {
1904 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1905 } else if (name == "prefs") {
1906 updateDialog(name, string());
1911 case LFUN_DIALOG_TOGGLE: {
1912 if (isDialogVisible(cmd.getArg(0)))
1913 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
1915 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
1919 case LFUN_DIALOG_DISCONNECT_INSET:
1920 disconnectDialog(to_utf8(cmd.argument()));
1923 case LFUN_DIALOG_HIDE: {
1924 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
1928 case LFUN_DIALOG_SHOW: {
1929 string const name = cmd.getArg(0);
1930 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1932 if (name == "character") {
1933 data = freefont2string();
1935 showDialog("character", data);
1936 } else if (name == "latexlog") {
1937 Buffer::LogType type;
1938 string const logfile = buffer()->logName(&type);
1940 case Buffer::latexlog:
1943 case Buffer::buildlog:
1947 data += Lexer::quoteString(logfile);
1948 showDialog("log", data);
1949 } else if (name == "vclog") {
1950 string const data = "vc " +
1951 Lexer::quoteString(buffer()->lyxvc().getLogFile());
1952 showDialog("log", data);
1953 } else if (name == "symbols") {
1954 data = bv->cursor().getEncoding()->name();
1956 showDialog("symbols", data);
1958 showDialog(name, data);
1962 case LFUN_INSET_APPLY: {
1963 string const name = cmd.getArg(0);
1964 Inset * inset = getOpenInset(name);
1966 // put cursor in front of inset.
1967 if (!view()->setCursorFromInset(inset))
1968 LASSERT(false, /**/);
1970 view()->cursor().recordUndo();
1971 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1972 inset->dispatch(view()->cursor(), fr);
1974 view()->cursor().recordUndo();
1975 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1981 case LFUN_UI_TOGGLE:
1983 // Make sure the keyboard focus stays in the work area.
1987 case LFUN_SPLIT_VIEW:
1988 if (Buffer * buf = buffer()) {
1989 string const orientation = cmd.getArg(0);
1990 d.splitter_->setOrientation(orientation == "vertical"
1991 ? Qt::Vertical : Qt::Horizontal);
1992 TabWorkArea * twa = addTabWorkArea();
1993 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
1994 setCurrentWorkArea(wa);
1998 case LFUN_CLOSE_TAB_GROUP:
1999 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2001 twa = d.currentTabWorkArea();
2002 // Switch to the next GuiWorkArea in the found TabWorkArea.
2004 d.current_work_area_ = twa->currentWorkArea();
2005 // Make sure the work area is up to date.
2006 twa->setCurrentWorkArea(d.current_work_area_);
2008 d.current_work_area_ = 0;
2010 if (d.splitter_->count() == 0)
2011 // No more work area, switch to the background widget.
2016 case LFUN_COMPLETION_INLINE:
2017 if (d.current_work_area_)
2018 d.current_work_area_->completer().showInline();
2021 case LFUN_COMPLETION_POPUP:
2022 if (d.current_work_area_)
2023 d.current_work_area_->completer().showPopup();
2027 case LFUN_COMPLETION_COMPLETE:
2028 if (d.current_work_area_)
2029 d.current_work_area_->completer().tab();
2032 case LFUN_COMPLETION_CANCEL:
2033 if (d.current_work_area_) {
2034 if (d.current_work_area_->completer().popupVisible())
2035 d.current_work_area_->completer().hidePopup();
2037 d.current_work_area_->completer().hideInline();
2041 case LFUN_COMPLETION_ACCEPT:
2042 if (d.current_work_area_)
2043 d.current_work_area_->completer().activate();
2052 // Part of automatic menu appearance feature.
2053 if (isFullScreen()) {
2054 if (menuBar()->isVisible())
2056 if (statusBar()->isVisible())
2057 statusBar()->hide();
2064 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2066 string const arg = cmd.getArg(0);
2067 if (arg == "scrollbar") {
2068 // hide() is of no help
2069 if (d.current_work_area_->verticalScrollBarPolicy() ==
2070 Qt::ScrollBarAlwaysOff)
2072 d.current_work_area_->setVerticalScrollBarPolicy(
2073 Qt::ScrollBarAsNeeded);
2075 d.current_work_area_->setVerticalScrollBarPolicy(
2076 Qt::ScrollBarAlwaysOff);
2079 if (arg == "statusbar") {
2080 statusBar()->setVisible(!statusBar()->isVisible());
2083 if (arg == "menubar") {
2084 menuBar()->setVisible(!menuBar()->isVisible());
2087 #if QT_VERSION >= 0x040300
2088 if (arg == "frame") {
2090 getContentsMargins(&l, &t, &r, &b);
2091 //are the frames in default state?
2092 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2094 setContentsMargins(-2, -2, -2, -2);
2096 setContentsMargins(0, 0, 0, 0);
2101 if (arg == "fullscreen") {
2106 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2110 void GuiView::toggleFullScreen()
2112 if (isFullScreen()) {
2113 for (int i = 0; i != d.splitter_->count(); ++i)
2114 d.tabWorkArea(i)->setFullScreen(false);
2115 #if QT_VERSION >= 0x040300
2116 setContentsMargins(0, 0, 0, 0);
2118 setWindowState(windowState() ^ Qt::WindowFullScreen);
2121 statusBar()->show();
2123 for (int i = 0; i != d.splitter_->count(); ++i)
2124 d.tabWorkArea(i)->setFullScreen(true);
2125 #if QT_VERSION >= 0x040300
2126 setContentsMargins(-2, -2, -2, -2);
2129 setWindowState(windowState() ^ Qt::WindowFullScreen);
2130 statusBar()->hide();
2132 if (lyxrc.full_screen_toolbars) {
2133 ToolbarMap::iterator end = d.toolbars_.end();
2134 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2141 Buffer const * GuiView::updateInset(Inset const * inset)
2143 if (!d.current_work_area_)
2147 d.current_work_area_->scheduleRedraw();
2149 return &d.current_work_area_->bufferView().buffer();
2153 void GuiView::restartCursor()
2155 /* When we move around, or type, it's nice to be able to see
2156 * the cursor immediately after the keypress.
2158 if (d.current_work_area_)
2159 d.current_work_area_->startBlinkingCursor();
2161 // Take this occasion to update the other GUI elements.
2167 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2169 if (d.current_work_area_)
2170 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2175 // This list should be kept in sync with the list of insets in
2176 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2177 // dialog should have the same name as the inset.
2178 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2179 // docs in LyXAction.cpp.
2181 char const * const dialognames[] = {
2182 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2183 "citation", "document", "errorlist", "ert", "external", "file",
2184 "findreplace", "float", "graphics", "include", "index", "info", "nomenclature", "label", "log",
2185 "mathdelimiter", "mathmatrix", "note", "paragraph", "prefs", "print",
2186 "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
2188 #ifdef HAVE_LIBAIKSAURUS
2192 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings" };
2194 char const * const * const end_dialognames =
2195 dialognames + (sizeof(dialognames) / sizeof(char *));
2199 cmpCStr(char const * name) : name_(name) {}
2200 bool operator()(char const * other) {
2201 return strcmp(other, name_) == 0;
2208 bool isValidName(string const & name)
2210 return find_if(dialognames, end_dialognames,
2211 cmpCStr(name.c_str())) != end_dialognames;
2217 void GuiView::resetDialogs()
2219 // Make sure that no LFUN uses any LyXView.
2220 theLyXFunc().setLyXView(0);
2223 constructToolbars();
2224 guiApp->menus().fillMenuBar(menuBar(), this, true);
2226 d.layout_->updateContents(true);
2227 // Now update controls with current buffer.
2228 theLyXFunc().setLyXView(this);
2234 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2236 if (!isValidName(name))
2239 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2241 if (it != d.dialogs_.end())
2242 return it->second.get();
2244 Dialog * dialog = build(name);
2245 d.dialogs_[name].reset(dialog);
2246 if (lyxrc.allow_geometry_session)
2247 dialog->restoreSession();
2254 void GuiView::showDialog(string const & name, string const & data,
2262 Dialog * dialog = findOrBuild(name, false);
2264 dialog->showData(data);
2266 d.open_insets_[name] = inset;
2269 catch (ExceptionMessage const & ex) {
2277 bool GuiView::isDialogVisible(string const & name) const
2279 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2280 if (it == d.dialogs_.end())
2282 return it->second.get()->isVisibleView();
2286 void GuiView::hideDialog(string const & name, Inset * inset)
2288 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2289 if (it == d.dialogs_.end())
2292 if (inset && inset != getOpenInset(name))
2295 Dialog * const dialog = it->second.get();
2296 if (dialog->isVisibleView())
2298 d.open_insets_[name] = 0;
2302 void GuiView::disconnectDialog(string const & name)
2304 if (!isValidName(name))
2307 if (d.open_insets_.find(name) != d.open_insets_.end())
2308 d.open_insets_[name] = 0;
2312 Inset * GuiView::getOpenInset(string const & name) const
2314 if (!isValidName(name))
2317 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2318 return it == d.open_insets_.end() ? 0 : it->second;
2322 void GuiView::hideAll() const
2324 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2325 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2327 for(; it != end; ++it)
2328 it->second->hideView();
2332 void GuiView::updateDialogs()
2334 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2335 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2337 for(; it != end; ++it) {
2338 Dialog * dialog = it->second.get();
2339 if (dialog && dialog->isVisibleView())
2340 dialog->checkStatus();
2347 // will be replaced by a proper factory...
2348 Dialog * createGuiAbout(GuiView & lv);
2349 Dialog * createGuiBibitem(GuiView & lv);
2350 Dialog * createGuiBibtex(GuiView & lv);
2351 Dialog * createGuiBox(GuiView & lv);
2352 Dialog * createGuiBranch(GuiView & lv);
2353 Dialog * createGuiChanges(GuiView & lv);
2354 Dialog * createGuiCharacter(GuiView & lv);
2355 Dialog * createGuiCitation(GuiView & lv);
2356 Dialog * createGuiDelimiter(GuiView & lv);
2357 Dialog * createGuiDocument(GuiView & lv);
2358 Dialog * createGuiErrorList(GuiView & lv);
2359 Dialog * createGuiERT(GuiView & lv);
2360 Dialog * createGuiExternal(GuiView & lv);
2361 Dialog * createGuiFloat(GuiView & lv);
2362 Dialog * createGuiGraphics(GuiView & lv);
2363 Dialog * createGuiHSpace(GuiView & lv);
2364 Dialog * createGuiInclude(GuiView & lv);
2365 Dialog * createGuiInfo(GuiView & lv);
2366 Dialog * createGuiLabel(GuiView & lv);
2367 Dialog * createGuiListings(GuiView & lv);
2368 Dialog * createGuiLog(GuiView & lv);
2369 Dialog * createGuiMathMatrix(GuiView & lv);
2370 Dialog * createGuiNomenclature(GuiView & lv);
2371 Dialog * createGuiNote(GuiView & lv);
2372 Dialog * createGuiParagraph(GuiView & lv);
2373 Dialog * createGuiPreferences(GuiView & lv);
2374 Dialog * createGuiPrint(GuiView & lv);
2375 Dialog * createGuiRef(GuiView & lv);
2376 Dialog * createGuiSearch(GuiView & lv);
2377 Dialog * createGuiSendTo(GuiView & lv);
2378 Dialog * createGuiShowFile(GuiView & lv);
2379 Dialog * createGuiSpellchecker(GuiView & lv);
2380 Dialog * createGuiSymbols(GuiView & lv);
2381 Dialog * createGuiTabularCreate(GuiView & lv);
2382 Dialog * createGuiTabular(GuiView & lv);
2383 Dialog * createGuiTexInfo(GuiView & lv);
2384 Dialog * createGuiToc(GuiView & lv);
2385 Dialog * createGuiThesaurus(GuiView & lv);
2386 Dialog * createGuiHyperlink(GuiView & lv);
2387 Dialog * createGuiVSpace(GuiView & lv);
2388 Dialog * createGuiViewSource(GuiView & lv);
2389 Dialog * createGuiWrap(GuiView & lv);
2392 Dialog * GuiView::build(string const & name)
2394 LASSERT(isValidName(name), /**/);
2396 if (name == "aboutlyx")
2397 return createGuiAbout(*this);
2398 if (name == "bibitem")
2399 return createGuiBibitem(*this);
2400 if (name == "bibtex")
2401 return createGuiBibtex(*this);
2403 return createGuiBox(*this);
2404 if (name == "branch")
2405 return createGuiBranch(*this);
2406 if (name == "changes")
2407 return createGuiChanges(*this);
2408 if (name == "character")
2409 return createGuiCharacter(*this);
2410 if (name == "citation")
2411 return createGuiCitation(*this);
2412 if (name == "document")
2413 return createGuiDocument(*this);
2414 if (name == "errorlist")
2415 return createGuiErrorList(*this);
2417 return createGuiERT(*this);
2418 if (name == "external")
2419 return createGuiExternal(*this);
2421 return createGuiShowFile(*this);
2422 if (name == "findreplace")
2423 return createGuiSearch(*this);
2424 if (name == "float")
2425 return createGuiFloat(*this);
2426 if (name == "graphics")
2427 return createGuiGraphics(*this);
2428 if (name == "include")
2429 return createGuiInclude(*this);
2431 return createGuiInfo(*this);
2432 if (name == "nomenclature")
2433 return createGuiNomenclature(*this);
2434 if (name == "label")
2435 return createGuiLabel(*this);
2437 return createGuiLog(*this);
2438 if (name == "view-source")
2439 return createGuiViewSource(*this);
2440 if (name == "mathdelimiter")
2441 return createGuiDelimiter(*this);
2442 if (name == "mathmatrix")
2443 return createGuiMathMatrix(*this);
2445 return createGuiNote(*this);
2446 if (name == "paragraph")
2447 return createGuiParagraph(*this);
2448 if (name == "prefs")
2449 return createGuiPreferences(*this);
2450 if (name == "print")
2451 return createGuiPrint(*this);
2453 return createGuiRef(*this);
2454 if (name == "sendto")
2455 return createGuiSendTo(*this);
2456 if (name == "space")
2457 return createGuiHSpace(*this);
2458 if (name == "spellchecker")
2459 return createGuiSpellchecker(*this);
2460 if (name == "symbols")
2461 return createGuiSymbols(*this);
2462 if (name == "tabular")
2463 return createGuiTabular(*this);
2464 if (name == "tabularcreate")
2465 return createGuiTabularCreate(*this);
2466 if (name == "texinfo")
2467 return createGuiTexInfo(*this);
2468 #ifdef HAVE_LIBAIKSAURUS
2469 if (name == "thesaurus")
2470 return createGuiThesaurus(*this);
2473 return createGuiToc(*this);
2475 return createGuiHyperlink(*this);
2476 if (name == "vspace")
2477 return createGuiVSpace(*this);
2479 return createGuiWrap(*this);
2480 if (name == "listings")
2481 return createGuiListings(*this);
2487 } // namespace frontend
2490 #include "GuiView_moc.cpp"