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 "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiWorkArea.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
31 #include "qt_helpers.h"
33 #include "frontends/alert.h"
35 #include "buffer_funcs.h"
37 #include "BufferList.h"
38 #include "BufferParams.h"
39 #include "BufferView.h"
40 #include "Converter.h"
43 #include "ErrorList.h"
45 #include "FuncStatus.h"
46 #include "FuncRequest.h"
54 #include "Paragraph.h"
55 #include "TextClass.h"
60 #include "support/convert.h"
61 #include "support/debug.h"
62 #include "support/ExceptionMessage.h"
63 #include "support/FileName.h"
64 #include "support/filetools.h"
65 #include "support/gettext.h"
66 #include "support/ForkedCalls.h"
67 #include "support/lassert.h"
68 #include "support/lstrings.h"
69 #include "support/os.h"
70 #include "support/Package.h"
71 #include "support/Timeout.h"
74 #include <QApplication>
75 #include <QCloseEvent>
77 #include <QDesktopWidget>
78 #include <QDragEnterEvent>
85 #include <QPixmapCache>
87 #include <QPushButton>
91 #include <QStackedWidget>
98 #include <boost/bind.hpp>
100 #ifdef HAVE_SYS_TIME_H
101 # include <sys/time.h>
108 using namespace lyx::support;
115 class BackgroundWidget : public QWidget
120 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
121 /// The text to be written on top of the pixmap
122 QString const text = lyx_version ?
123 qt_("version ") + lyx_version : qt_("unknown version");
124 splash_ = getPixmap("images/", "banner", "png");
126 QPainter pain(&splash_);
127 pain.setPen(QColor(0, 0, 0));
129 // The font used to display the version info
130 font.setStyleHint(QFont::SansSerif);
131 font.setWeight(QFont::Bold);
132 font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
134 pain.drawText(260, 15, text);
137 void paintEvent(QPaintEvent *)
139 int x = (width() - splash_.width()) / 2;
140 int y = (height() - splash_.height()) / 2;
142 pain.drawPixmap(x, y, splash_);
150 /// Toolbar store providing access to individual toolbars by name.
151 typedef std::map<std::string, GuiToolbar *> ToolbarMap;
153 typedef boost::shared_ptr<Dialog> DialogPtr;
158 struct GuiView::GuiViewPrivate
161 : current_work_area_(0), current_main_work_area_(0),
162 layout_(0), autosave_timeout_(5000),
165 // hardcode here the platform specific icon size
166 smallIconSize = 14; // scaling problems
167 normalIconSize = 20; // ok, default
168 bigIconSize = 26; // better for some math icons
170 splitter_ = new QSplitter;
171 bg_widget_ = new BackgroundWidget;
172 stack_widget_ = new QStackedWidget;
173 stack_widget_->addWidget(bg_widget_);
174 stack_widget_->addWidget(splitter_);
182 delete stack_widget_;
185 QMenu * toolBarPopup(GuiView * parent)
187 // FIXME: translation
188 QMenu * menu = new QMenu(parent);
189 QActionGroup * iconSizeGroup = new QActionGroup(parent);
191 QAction * smallIcons = new QAction(iconSizeGroup);
192 smallIcons->setText(qt_("Small-sized icons"));
193 smallIcons->setCheckable(true);
194 QObject::connect(smallIcons, SIGNAL(triggered()),
195 parent, SLOT(smallSizedIcons()));
196 menu->addAction(smallIcons);
198 QAction * normalIcons = new QAction(iconSizeGroup);
199 normalIcons->setText(qt_("Normal-sized icons"));
200 normalIcons->setCheckable(true);
201 QObject::connect(normalIcons, SIGNAL(triggered()),
202 parent, SLOT(normalSizedIcons()));
203 menu->addAction(normalIcons);
205 QAction * bigIcons = new QAction(iconSizeGroup);
206 bigIcons->setText(qt_("Big-sized icons"));
207 bigIcons->setCheckable(true);
208 QObject::connect(bigIcons, SIGNAL(triggered()),
209 parent, SLOT(bigSizedIcons()));
210 menu->addAction(bigIcons);
212 unsigned int cur = parent->iconSize().width();
213 if ( cur == parent->d.smallIconSize)
214 smallIcons->setChecked(true);
215 else if (cur == parent->d.normalIconSize)
216 normalIcons->setChecked(true);
217 else if (cur == parent->d.bigIconSize)
218 bigIcons->setChecked(true);
225 stack_widget_->setCurrentWidget(bg_widget_);
226 bg_widget_->setUpdatesEnabled(true);
229 TabWorkArea * tabWorkArea(int i)
231 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
234 TabWorkArea * currentTabWorkArea()
236 if (splitter_->count() == 1)
237 // The first TabWorkArea is always the first one, if any.
238 return tabWorkArea(0);
240 for (int i = 0; i != splitter_->count(); ++i) {
241 TabWorkArea * twa = tabWorkArea(i);
242 if (current_main_work_area_ == twa->currentWorkArea())
246 // None has the focus so we just take the first one.
247 return tabWorkArea(0);
251 GuiWorkArea * current_work_area_;
252 GuiWorkArea * current_main_work_area_;
253 QSplitter * splitter_;
254 QStackedWidget * stack_widget_;
255 BackgroundWidget * bg_widget_;
257 ToolbarMap toolbars_;
258 /// The main layout box.
260 * \warning Don't Delete! The layout box is actually owned by
261 * whichever toolbar contains it. All the GuiView class needs is a
262 * means of accessing it.
264 * FIXME: replace that with a proper model so that we are not limited
265 * to only one dialog.
267 GuiLayoutBox * layout_;
270 map<string, Inset *> open_insets_;
273 map<string, DialogPtr> dialogs_;
275 unsigned int smallIconSize;
276 unsigned int normalIconSize;
277 unsigned int bigIconSize;
279 QTimer statusbar_timer_;
280 /// auto-saving of buffers
281 Timeout autosave_timeout_;
282 /// flag against a race condition due to multiclicks, see bug #1119
286 TocModels toc_models_;
290 GuiView::GuiView(int id)
291 : d(*new GuiViewPrivate), id_(id), closing_(false)
293 // GuiToolbars *must* be initialised before the menu bar.
294 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
297 // set ourself as the current view. This is needed for the menu bar
298 // filling, at least for the static special menu item on Mac. Otherwise
299 // they are greyed out.
300 theLyXFunc().setLyXView(this);
302 // Fill up the menu bar.
303 guiApp->menus().fillMenuBar(menuBar(), this, true);
305 setCentralWidget(d.stack_widget_);
307 // Start autosave timer
308 if (lyxrc.autosave) {
309 d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
310 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
311 d.autosave_timeout_.start();
313 connect(&d.statusbar_timer_, SIGNAL(timeout()),
314 this, SLOT(clearMessage()));
316 // We don't want to keep the window in memory if it is closed.
317 setAttribute(Qt::WA_DeleteOnClose, true);
319 #if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
320 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
321 // since the icon is provided in the application bundle.
322 setWindowIcon(getPixmap("images/", "lyx", "png"));
326 setAcceptDrops(true);
328 statusBar()->setSizeGripEnabled(true);
330 // Forbid too small unresizable window because it can happen
331 // with some window manager under X11.
332 setMinimumSize(300, 200);
334 if (lyxrc.allow_geometry_session) {
335 // Now take care of session management.
340 // no session handling, default to a sane size.
341 setGeometry(50, 50, 690, 510);
344 // clear session data if any.
346 settings.remove("views");
356 void GuiView::saveLayout() const
359 settings.beginGroup("views");
360 settings.beginGroup(QString::number(id_));
362 settings.setValue("pos", pos());
363 settings.setValue("size", size());
365 settings.setValue("geometry", saveGeometry());
367 settings.setValue("layout", saveState(0));
368 settings.setValue("icon_size", iconSize());
372 bool GuiView::restoreLayout()
375 settings.beginGroup("views");
376 settings.beginGroup(QString::number(id_));
377 QString const icon_key = "icon_size";
378 if (!settings.contains(icon_key))
381 setIconSize(settings.value(icon_key).toSize());
383 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
384 QSize size = settings.value("size", QSize(690, 510)).toSize();
388 if (!restoreGeometry(settings.value("geometry").toByteArray()))
389 setGeometry(50, 50, 690, 510);
391 // Make sure layout is correctly oriented.
392 setLayoutDirection(qApp->layoutDirection());
394 // Allow the toc and view-source dock widget to be restored if needed.
396 if ((d = findOrBuild("toc", true)))
399 if ((d = findOrBuild("view-source", true)))
402 if (!restoreState(settings.value("layout").toByteArray(), 0))
409 GuiToolbar * GuiView::toolbar(string const & name)
411 ToolbarMap::iterator it = d.toolbars_.find(name);
412 if (it != d.toolbars_.end())
415 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
416 message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
421 void GuiView::constructToolbars()
423 ToolbarMap::iterator it = d.toolbars_.begin();
424 for (; it != d.toolbars_.end(); ++it)
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 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
437 void GuiView::initToolbars()
439 // extracts the toolbars from the backend
440 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
441 Toolbars::Infos::iterator end = guiApp->toolbars().end();
442 for (; cit != end; ++cit) {
443 GuiToolbar * tb = toolbar(cit->name);
446 int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
448 tb->setVisible(false);
449 tb->setVisibility(visibility);
451 if (visibility & Toolbars::TOP) {
453 addToolBarBreak(Qt::TopToolBarArea);
454 addToolBar(Qt::TopToolBarArea, tb);
457 if (visibility & Toolbars::BOTTOM) {
458 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
459 #if (QT_VERSION >= 0x040202)
460 addToolBarBreak(Qt::BottomToolBarArea);
462 addToolBar(Qt::BottomToolBarArea, tb);
465 if (visibility & Toolbars::LEFT) {
466 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
467 #if (QT_VERSION >= 0x040202)
468 addToolBarBreak(Qt::LeftToolBarArea);
470 addToolBar(Qt::LeftToolBarArea, tb);
473 if (visibility & Toolbars::RIGHT) {
474 // Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
475 #if (QT_VERSION >= 0x040202)
476 addToolBarBreak(Qt::RightToolBarArea);
478 addToolBar(Qt::RightToolBarArea, tb);
481 if (visibility & Toolbars::ON)
482 tb->setVisible(true);
487 TocModels & GuiView::tocModels()
489 return d.toc_models_;
493 void GuiView::setFocus()
495 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
496 // Make sure LyXFunc points to the correct view.
497 guiApp->setCurrentView(this);
498 theLyXFunc().setLyXView(this);
499 QMainWindow::setFocus();
500 if (d.current_work_area_)
501 d.current_work_area_->setFocus();
505 QMenu * GuiView::createPopupMenu()
507 return d.toolBarPopup(this);
511 void GuiView::showEvent(QShowEvent * e)
513 LYXERR(Debug::GUI, "Passed Geometry "
514 << size().height() << "x" << size().width()
515 << "+" << pos().x() << "+" << pos().y());
517 if (d.splitter_->count() == 0)
518 // No work area, switch to the background widget.
521 QMainWindow::showEvent(e);
525 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
526 ** is responsibility of the container (e.g., dialog)
528 void GuiView::closeEvent(QCloseEvent * close_event)
530 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
533 // it can happen that this event arrives without selecting the view,
534 // e.g. when clicking the close button on a background window.
536 setCurrentWorkArea(currentMainWorkArea());
538 int splitter_count = d.splitter_->count();
539 for (; splitter_count > 0; --splitter_count) {
540 TabWorkArea * twa = d.tabWorkArea(0);
542 int twa_count = twa->count();
543 for (; twa_count > 0; --twa_count) {
544 twa->setCurrentIndex(0);
546 GuiWorkArea * wa = twa->currentWorkArea();
547 Buffer * b = &wa->bufferView().buffer();
549 // This is a child document, just close the tab
550 // after saving but keep the file loaded.
551 if (!closeBuffer(*b, true)) {
553 close_event->ignore();
559 vector<Buffer *> clist = b->getChildren();
560 for (vector<Buffer *>::const_iterator it = clist.begin();
561 it != clist.end(); ++it) {
562 if ((*it)->isClean())
565 // If a child is dirty, do not close
566 // without user intervention
567 if (!closeBuffer(*c, false)) {
569 close_event->ignore();
574 QList<int> const ids = guiApp->viewIds();
575 for (int i = 0; i != ids.size(); ++i) {
578 if (guiApp->view(ids[i]).workArea(*b)) {
579 // FIXME 1: should we put an alert box here that the buffer
580 // is viewed elsewhere?
581 // FIXME 2: should we try to save this buffer in any case?
584 // This buffer is also opened in another view, so
585 // close the associated work area...
587 // ... but don't close the buffer.
592 // closeBuffer() needs buffer workArea still alive and set as currrent one, and destroys it
593 if (b && !closeBuffer(*b, true)) {
595 close_event->ignore();
600 // Make sure that nothing will use this close to be closed View.
601 guiApp->unregisterView(this);
603 if (isFullScreen()) {
604 // Switch off fullscreen before closing.
609 // Make sure the timer time out will not trigger a statusbar update.
610 d.statusbar_timer_.stop();
612 // Saving fullscreen requires additional tweaks in the toolbar code.
613 // It wouldn't also work under linux natively.
614 if (lyxrc.allow_geometry_session) {
615 // Save this window geometry and layout.
617 // Then the toolbar private states.
618 ToolbarMap::iterator end = d.toolbars_.end();
619 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
620 it->second->saveSession();
621 // Now take care of all other dialogs:
622 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
623 for (; it!= d.dialogs_.end(); ++it)
624 it->second->saveSession();
627 close_event->accept();
631 void GuiView::dragEnterEvent(QDragEnterEvent * event)
633 if (event->mimeData()->hasUrls())
635 /// \todo Ask lyx-devel is this is enough:
636 /// if (event->mimeData()->hasFormat("text/plain"))
637 /// event->acceptProposedAction();
641 void GuiView::dropEvent(QDropEvent * event)
643 QList<QUrl> files = event->mimeData()->urls();
647 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
648 for (int i = 0; i != files.size(); ++i) {
649 string const file = os::internal_path(fromqstr(
650 files.at(i).toLocalFile()));
652 // Asynchronously post the event. DropEvent usually come
653 // from the BufferView. But reloading a file might close
654 // the BufferView from within its own event handler.
655 guiApp->dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, file));
662 void GuiView::message(docstring const & str)
664 if (ForkedProcess::iAmAChild())
667 statusBar()->showMessage(toqstr(str));
668 d.statusbar_timer_.stop();
669 d.statusbar_timer_.start(3000);
673 void GuiView::smallSizedIcons()
675 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
679 void GuiView::normalSizedIcons()
681 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
685 void GuiView::bigSizedIcons()
687 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
691 void GuiView::clearMessage()
695 theLyXFunc().setLyXView(this);
696 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
697 d.statusbar_timer_.stop();
701 void GuiView::updateWindowTitle(GuiWorkArea * wa)
703 if (wa != d.current_work_area_)
705 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
706 setWindowIconText(wa->windowIconText());
710 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
713 disconnectBufferView();
714 connectBufferView(wa->bufferView());
715 connectBuffer(wa->bufferView().buffer());
716 d.current_work_area_ = wa;
717 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
718 this, SLOT(updateWindowTitle(GuiWorkArea *)));
719 updateWindowTitle(wa);
723 // The document settings needs to be reinitialised.
724 updateDialog("document", "");
726 // Buffer-dependent dialogs must be updated. This is done here because
727 // some dialogs require buffer()->text.
732 void GuiView::on_lastWorkAreaRemoved()
735 // We already are in a close event. Nothing more to do.
738 if (d.splitter_->count() > 1)
739 // We have a splitter so don't close anything.
742 // Reset and updates the dialogs.
743 d.toc_models_.reset(0);
744 updateDialog("document", "");
747 resetWindowTitleAndIconText();
749 if (lyxrc.open_buffers_in_tabs)
750 // Nothing more to do, the window should stay open.
753 if (guiApp->viewIds().size() > 1) {
759 // On Mac we also close the last window because the application stay
760 // resident in memory. On other platforms we don't close the last
761 // window because this would quit the application.
767 void GuiView::updateStatusBar()
769 // let the user see the explicit message
770 if (d.statusbar_timer_.isActive())
773 theLyXFunc().setLyXView(this);
774 statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
778 bool GuiView::hasFocus() const
780 return qApp->activeWindow() == this;
784 bool GuiView::event(QEvent * e)
788 // Useful debug code:
789 //case QEvent::ActivationChange:
790 //case QEvent::WindowDeactivate:
791 //case QEvent::Paint:
792 //case QEvent::Enter:
793 //case QEvent::Leave:
794 //case QEvent::HoverEnter:
795 //case QEvent::HoverLeave:
796 //case QEvent::HoverMove:
797 //case QEvent::StatusTip:
798 //case QEvent::DragEnter:
799 //case QEvent::DragLeave:
803 case QEvent::WindowActivate: {
804 if (this == guiApp->currentView()) {
806 return QMainWindow::event(e);
808 guiApp->setCurrentView(this);
809 theLyXFunc().setLyXView(this);
810 if (d.current_work_area_) {
811 BufferView & bv = d.current_work_area_->bufferView();
812 connectBufferView(bv);
813 connectBuffer(bv.buffer());
814 // The document structure, name and dialogs might have
815 // changed in another view.
817 // The document settings needs to be reinitialised.
818 updateDialog("document", "");
821 resetWindowTitleAndIconText();
824 return QMainWindow::event(e);
827 case QEvent::ShortcutOverride: {
830 #if (!defined Q_WS_X11) || (QT_VERSION >= 0x040500)
831 if (isFullScreen() && menuBar()->isHidden()) {
832 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
833 // FIXME: we should also try to detect special LyX shortcut such as
834 // Alt-P and Alt-M. Right now there is a hack in
835 // GuiWorkArea::processKeySym() that hides again the menubar for
837 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
839 return QMainWindow::event(e);
844 if (d.current_work_area_)
845 // Nothing special to do.
846 return QMainWindow::event(e);
848 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
849 // Let Qt handle menu access and the Tab keys to navigate keys to navigate
851 if (ke->modifiers() & Qt::AltModifier || ke->key() == Qt::Key_Tab
852 || ke->key() == Qt::Key_Backtab)
853 return QMainWindow::event(e);
855 // Allow processing of shortcuts that are allowed even when no Buffer
857 theLyXFunc().setLyXView(this);
859 setKeySymbol(&sym, ke);
860 theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
866 return QMainWindow::event(e);
870 void GuiView::resetWindowTitleAndIconText()
872 setWindowTitle(qt_("LyX"));
873 setWindowIconText(qt_("LyX"));
876 bool GuiView::focusNextPrevChild(bool /*next*/)
883 void GuiView::setBusy(bool busy)
885 if (d.current_work_area_) {
886 d.current_work_area_->setUpdatesEnabled(!busy);
888 d.current_work_area_->stopBlinkingCursor();
890 d.current_work_area_->startBlinkingCursor();
894 QApplication::setOverrideCursor(Qt::WaitCursor);
896 QApplication::restoreOverrideCursor();
900 GuiWorkArea * GuiView::workArea(Buffer & buffer)
902 if (currentWorkArea()
903 && ¤tWorkArea()->bufferView().buffer() == &buffer)
904 return (GuiWorkArea *) currentWorkArea();
905 if (TabWorkArea * twa = d.currentTabWorkArea())
906 return twa->workArea(buffer);
911 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
913 // Automatically create a TabWorkArea if there are none yet.
914 TabWorkArea * tab_widget = d.splitter_->count()
915 ? d.currentTabWorkArea() : addTabWorkArea();
916 return tab_widget->addWorkArea(buffer, *this);
920 TabWorkArea * GuiView::addTabWorkArea()
922 TabWorkArea * twa = new TabWorkArea;
923 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
924 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
925 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
926 this, SLOT(on_lastWorkAreaRemoved()));
928 d.splitter_->addWidget(twa);
929 d.stack_widget_->setCurrentWidget(d.splitter_);
934 GuiWorkArea const * GuiView::currentWorkArea() const
936 return d.current_work_area_;
940 GuiWorkArea * GuiView::currentWorkArea()
942 return d.current_work_area_;
946 GuiWorkArea const * GuiView::currentMainWorkArea() const
948 if (d.currentTabWorkArea() == NULL)
950 return d.currentTabWorkArea()->currentWorkArea();
954 GuiWorkArea * GuiView::currentMainWorkArea()
956 if (d.currentTabWorkArea() == NULL)
958 return d.currentTabWorkArea()->currentWorkArea();
962 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
964 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
966 d.current_work_area_ = NULL;
970 GuiWorkArea * old_gwa = theGuiApp()->currentView()->currentWorkArea();
974 theGuiApp()->setCurrentView(this);
975 d.current_work_area_ = wa;
976 for (int i = 0; i != d.splitter_->count(); ++i) {
977 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
978 //if (d.current_main_work_area_)
979 // d.current_main_work_area_->setFrameStyle(QFrame::NoFrame);
980 d.current_main_work_area_ = wa;
981 //d.current_main_work_area_->setFrameStyle(QFrame::Box | QFrame::Plain);
982 //d.current_main_work_area_->setLineWidth(2);
983 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
987 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
988 on_currentWorkAreaChanged(wa);
989 BufferView & bv = wa->bufferView();
990 bv.cursor().fixIfBroken();
992 wa->setUpdatesEnabled(true);
993 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
997 void GuiView::removeWorkArea(GuiWorkArea * wa)
1000 if (wa == d.current_work_area_) {
1002 disconnectBufferView();
1003 d.current_work_area_ = 0;
1004 d.current_main_work_area_ = 0;
1007 bool found_twa = false;
1008 for (int i = 0; i != d.splitter_->count(); ++i) {
1009 TabWorkArea * twa = d.tabWorkArea(i);
1010 if (twa->removeWorkArea(wa)) {
1011 // Found in this tab group, and deleted the GuiWorkArea.
1013 if (twa->count() != 0) {
1014 if (d.current_work_area_ == 0)
1015 // This means that we are closing the current GuiWorkArea, so
1016 // switch to the next GuiWorkArea in the found TabWorkArea.
1017 setCurrentWorkArea(twa->currentWorkArea());
1019 // No more WorkAreas in this tab group, so delete it.
1026 // It is not a tabbed work area (i.e., the search work area), so it
1027 // should be deleted by other means.
1028 LASSERT(found_twa, /* */);
1030 if (d.current_work_area_ == 0) {
1031 if (d.splitter_->count() != 0) {
1032 TabWorkArea * twa = d.currentTabWorkArea();
1033 setCurrentWorkArea(twa->currentWorkArea());
1035 // No more work areas, switch to the background widget.
1036 setCurrentWorkArea(0);
1042 void GuiView::setLayoutDialog(GuiLayoutBox * layout)
1048 void GuiView::updateLayoutList()
1051 d.layout_->updateContents(false);
1055 void GuiView::updateToolbars()
1057 ToolbarMap::iterator end = d.toolbars_.end();
1058 if (d.current_work_area_) {
1060 d.current_work_area_->bufferView().cursor().inMathed();
1062 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1064 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1065 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
1066 bool const mathmacrotemplate =
1067 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1069 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1070 it->second->update(math, table, review, mathmacrotemplate);
1072 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1073 it->second->update(false, false, false, false);
1077 Buffer * GuiView::buffer()
1079 if (d.current_work_area_)
1080 return &d.current_work_area_->bufferView().buffer();
1085 Buffer const * GuiView::buffer() const
1087 if (d.current_work_area_)
1088 return &d.current_work_area_->bufferView().buffer();
1093 void GuiView::setBuffer(Buffer * newBuffer)
1095 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << std::endl);
1096 LASSERT(newBuffer, return);
1099 GuiWorkArea * wa = workArea(*newBuffer);
1101 newBuffer->masterBuffer()->updateLabels();
1102 wa = addWorkArea(*newBuffer);
1104 //Disconnect the old buffer...there's no new one.
1107 connectBuffer(*newBuffer);
1108 connectBufferView(wa->bufferView());
1109 setCurrentWorkArea(wa);
1115 void GuiView::connectBuffer(Buffer & buf)
1117 buf.setGuiDelegate(this);
1121 void GuiView::disconnectBuffer()
1123 if (d.current_work_area_)
1124 d.current_work_area_->bufferView().setGuiDelegate(0);
1128 void GuiView::connectBufferView(BufferView & bv)
1130 bv.setGuiDelegate(this);
1134 void GuiView::disconnectBufferView()
1136 if (d.current_work_area_)
1137 d.current_work_area_->bufferView().setGuiDelegate(0);
1141 void GuiView::errors(string const & error_type)
1143 ErrorList & el = buffer()->errorList(error_type);
1145 showDialog("errorlist", error_type);
1149 void GuiView::updateTocItem(std::string const & type, DocIterator const & dit)
1151 d.toc_models_.updateItem(toqstr(type), dit);
1155 void GuiView::structureChanged()
1157 d.toc_models_.reset(view());
1158 // Navigator needs more than a simple update in this case. It needs to be
1160 updateDialog("toc", "");
1164 void GuiView::updateDialog(string const & name, string const & data)
1166 if (!isDialogVisible(name))
1169 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1170 if (it == d.dialogs_.end())
1173 Dialog * const dialog = it->second.get();
1174 if (dialog->isVisibleView())
1175 dialog->initialiseParams(data);
1179 BufferView * GuiView::view()
1181 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1185 void GuiView::autoSave()
1187 LYXERR(Debug::INFO, "Running autoSave()");
1190 view()->buffer().autoSave();
1194 void GuiView::resetAutosaveTimers()
1197 d.autosave_timeout_.restart();
1201 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1204 Buffer * buf = buffer();
1206 /* In LyX/Mac, when a dialog is open, the menus of the
1207 application can still be accessed without giving focus to
1208 the main window. In this case, we want to disable the menu
1209 entries that are buffer-related.
1211 Note that this code is not perfect, as bug 1941 attests:
1212 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1214 if (cmd.origin == FuncRequest::MENU && !hasFocus())
1217 if (cmd.origin == FuncRequest::TOC) {
1218 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1220 if (toc->getStatus(view()->cursor(), cmd, fs))
1223 flag.setEnabled(false);
1227 switch(cmd.action) {
1228 case LFUN_BUFFER_WRITE:
1229 enable = buf && (buf->isUnnamed() || !buf->isClean());
1232 case LFUN_BUFFER_WRITE_AS:
1236 case LFUN_SPLIT_VIEW:
1237 if (cmd.getArg(0) == "vertical")
1238 enable = buf && (d.splitter_->count() == 1 ||
1239 d.splitter_->orientation() == Qt::Vertical);
1241 enable = buf && (d.splitter_->count() == 1 ||
1242 d.splitter_->orientation() == Qt::Horizontal);
1245 case LFUN_CLOSE_TAB_GROUP:
1246 enable = d.currentTabWorkArea();
1249 case LFUN_TOOLBAR_TOGGLE:
1250 if (GuiToolbar * t = toolbar(cmd.getArg(0)))
1251 flag.setOnOff(t->isVisible());
1254 case LFUN_UI_TOGGLE:
1255 flag.setOnOff(isFullScreen());
1258 case LFUN_DIALOG_TOGGLE:
1259 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1260 // fall through to set "enable"
1261 case LFUN_DIALOG_SHOW: {
1262 string const name = cmd.getArg(0);
1264 enable = name == "aboutlyx"
1265 || name == "file" //FIXME: should be removed.
1267 || name == "texinfo";
1268 else if (name == "print")
1269 enable = buf->isExportable("dvi")
1270 && lyxrc.print_command != "none";
1271 else if (name == "character") {
1275 InsetCode ic = view()->cursor().inset().lyxCode();
1276 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1279 else if (name == "symbols") {
1280 if (!view() || view()->cursor().inMathed())
1283 InsetCode ic = view()->cursor().inset().lyxCode();
1284 enable = ic != ERT_CODE && ic != LISTINGS_CODE;
1287 else if (name == "latexlog")
1288 enable = FileName(buf->logName()).isReadableFile();
1289 else if (name == "spellchecker")
1290 #if defined (USE_ASPELL)
1291 enable = !buf->isReadonly();
1295 else if (name == "vclog")
1296 enable = buf->lyxvc().inUse();
1300 case LFUN_DIALOG_UPDATE: {
1301 string const name = cmd.getArg(0);
1303 enable = name == "prefs";
1307 case LFUN_INSET_APPLY: {
1308 string const name = cmd.getArg(0);
1309 Inset * inset = getOpenInset(name);
1311 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
1313 if (!inset->getStatus(view()->cursor(), fr, fs)) {
1314 // Every inset is supposed to handle this
1315 LASSERT(false, break);
1319 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
1320 flag |= lyx::getStatus(fr);
1322 enable = flag.enabled();
1326 case LFUN_COMPLETION_INLINE:
1327 if (!d.current_work_area_
1328 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1332 case LFUN_COMPLETION_POPUP:
1333 if (!d.current_work_area_
1334 || !d.current_work_area_->completer().popupPossible(view()->cursor()))
1338 case LFUN_COMPLETION_COMPLETE:
1339 if (!d.current_work_area_
1340 || !d.current_work_area_->completer().inlinePossible(view()->cursor()))
1344 case LFUN_COMPLETION_ACCEPT:
1345 if (!d.current_work_area_
1346 || (!d.current_work_area_->completer().popupVisible()
1347 && !d.current_work_area_->completer().inlineVisible()
1348 && !d.current_work_area_->completer().completionAvailable()))
1352 case LFUN_COMPLETION_CANCEL:
1353 if (!d.current_work_area_
1354 || (!d.current_work_area_->completer().popupVisible()
1355 && !d.current_work_area_->completer().inlineVisible()))
1359 case LFUN_BUFFER_ZOOM_OUT:
1360 enable = buf && lyxrc.zoom > 10;
1363 case LFUN_BUFFER_ZOOM_IN:
1372 flag.setEnabled(false);
1378 static FileName selectTemplateFile()
1380 FileDialog dlg(qt_("Select template file"));
1381 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1382 dlg.setButton1(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1384 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
1385 QStringList(qt_("LyX Documents (*.lyx)")));
1387 if (result.first == FileDialog::Later)
1389 if (result.second.isEmpty())
1391 return FileName(fromqstr(result.second));
1395 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
1399 Buffer * newBuffer = checkAndLoadLyXFile(filename);
1402 message(_("Document not loaded."));
1407 setBuffer(newBuffer);
1409 // scroll to the position when the file was last closed
1410 if (lyxrc.use_lastfilepos) {
1411 LastFilePosSection::FilePos filepos =
1412 theSession().lastFilePos().load(filename);
1413 view()->moveToPosition(filepos.pit, filepos.pos, 0, 0);
1417 theSession().lastFiles().add(filename);
1424 void GuiView::openDocument(string const & fname)
1426 string initpath = lyxrc.document_path;
1429 string const trypath = buffer()->filePath();
1430 // If directory is writeable, use this as default.
1431 if (FileName(trypath).isDirWritable())
1437 if (fname.empty()) {
1438 FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
1439 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1440 dlg.setButton2(qt_("Examples|#E#e"),
1441 toqstr(addPath(package().system_support().absFilename(), "examples")));
1443 QStringList filter(qt_("LyX Documents (*.lyx)"));
1444 filter << qt_("LyX-1.3.x Documents (*.lyx13)")
1445 << qt_("LyX-1.4.x Documents (*.lyx14)")
1446 << qt_("LyX-1.5.x Documents (*.lyx15)")
1447 << qt_("LyX-1.6.x Documents (*.lyx16)");
1448 FileDialog::Result result =
1449 dlg.open(toqstr(initpath), filter);
1451 if (result.first == FileDialog::Later)
1454 filename = fromqstr(result.second);
1456 // check selected filename
1457 if (filename.empty()) {
1458 message(_("Canceled."));
1464 // get absolute path of file and add ".lyx" to the filename if
1466 FileName const fullname =
1467 fileSearch(string(), filename, "lyx", support::may_not_exist);
1468 if (!fullname.empty())
1469 filename = fullname.absFilename();
1471 if (!fullname.onlyPath().isDirectory()) {
1472 Alert::warning(_("Invalid filename"),
1473 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
1474 from_utf8(fullname.absFilename())));
1477 // if the file doesn't exist, let the user create one
1478 if (!fullname.exists()) {
1479 // the user specifically chose this name. Believe him.
1480 Buffer * const b = newFile(filename, string(), true);
1486 docstring const disp_fn = makeDisplayPath(filename);
1487 message(bformat(_("Opening document %1$s..."), disp_fn));
1490 Buffer * buf = loadDocument(fullname);
1492 buf->updateLabels();
1494 buf->errors("Parse");
1495 str2 = bformat(_("Document %1$s opened."), disp_fn);
1496 if (buf->lyxvc().inUse())
1497 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
1498 " " + _("Version control detected.");
1500 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1505 // FIXME: clean that
1506 static bool import(GuiView * lv, FileName const & filename,
1507 string const & format, ErrorList & errorList)
1509 FileName const lyxfile(support::changeExtension(filename.absFilename(), ".lyx"));
1511 string loader_format;
1512 vector<string> loaders = theConverters().loaders();
1513 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
1514 for (vector<string>::const_iterator it = loaders.begin();
1515 it != loaders.end(); ++it) {
1516 if (!theConverters().isReachable(format, *it))
1519 string const tofile =
1520 support::changeExtension(filename.absFilename(),
1521 formats.extension(*it));
1522 if (!theConverters().convert(0, filename, FileName(tofile),
1523 filename, format, *it, errorList))
1525 loader_format = *it;
1528 if (loader_format.empty()) {
1529 frontend::Alert::error(_("Couldn't import file"),
1530 bformat(_("No information for importing the format %1$s."),
1531 formats.prettyName(format)));
1535 loader_format = format;
1537 if (loader_format == "lyx") {
1538 Buffer * buf = lv->loadDocument(lyxfile);
1541 buf->updateLabels();
1543 buf->errors("Parse");
1545 Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
1549 bool as_paragraphs = loader_format == "textparagraph";
1550 string filename2 = (loader_format == format) ? filename.absFilename()
1551 : support::changeExtension(filename.absFilename(),
1552 formats.extension(loader_format));
1553 lv->view()->insertPlaintextFile(FileName(filename2), as_paragraphs);
1554 theLyXFunc().setLyXView(lv);
1555 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1562 void GuiView::importDocument(string const & argument)
1565 string filename = split(argument, format, ' ');
1567 LYXERR(Debug::INFO, format << " file: " << filename);
1569 // need user interaction
1570 if (filename.empty()) {
1571 string initpath = lyxrc.document_path;
1573 Buffer const * buf = buffer();
1575 string const trypath = buf->filePath();
1576 // If directory is writeable, use this as default.
1577 if (FileName(trypath).isDirWritable())
1581 docstring const text = bformat(_("Select %1$s file to import"),
1582 formats.prettyName(format));
1584 FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
1585 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1586 dlg.setButton2(qt_("Examples|#E#e"),
1587 toqstr(addPath(package().system_support().absFilename(), "examples")));
1589 docstring filter = formats.prettyName(format);
1592 filter += from_utf8(formats.extension(format));
1595 FileDialog::Result result =
1596 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
1598 if (result.first == FileDialog::Later)
1601 filename = fromqstr(result.second);
1603 // check selected filename
1604 if (filename.empty())
1605 message(_("Canceled."));
1608 if (filename.empty())
1611 // get absolute path of file
1612 FileName const fullname(support::makeAbsPath(filename));
1614 FileName const lyxfile(support::changeExtension(fullname.absFilename(), ".lyx"));
1616 // Check if the document already is open
1617 Buffer * buf = theBufferList().getBuffer(lyxfile);
1620 if (!closeBuffer()) {
1621 message(_("Canceled."));
1626 docstring const displaypath = makeDisplayPath(lyxfile.absFilename(), 30);
1628 // if the file exists already, and we didn't do
1629 // -i lyx thefile.lyx, warn
1630 if (lyxfile.exists() && fullname != lyxfile) {
1632 docstring text = bformat(_("The document %1$s already exists.\n\n"
1633 "Do you want to overwrite that document?"), displaypath);
1634 int const ret = Alert::prompt(_("Overwrite document?"),
1635 text, 0, 1, _("&Overwrite"), _("&Cancel"));
1638 message(_("Canceled."));
1643 message(bformat(_("Importing %1$s..."), displaypath));
1644 ErrorList errorList;
1645 if (import(this, fullname, format, errorList))
1646 message(_("imported."));
1648 message(_("file not imported!"));
1650 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1654 void GuiView::newDocument(string const & filename, bool from_template)
1656 FileName initpath(lyxrc.document_path);
1657 Buffer * buf = buffer();
1659 FileName const trypath(buf->filePath());
1660 // If directory is writeable, use this as default.
1661 if (trypath.isDirWritable())
1665 string templatefile;
1666 if (from_template) {
1667 templatefile = selectTemplateFile().absFilename();
1668 if (templatefile.empty())
1673 if (filename.empty())
1674 b = newUnnamedFile(templatefile, initpath);
1676 b = newFile(filename, templatefile, true);
1681 // If no new document could be created, it is unsure
1682 // whether there is a valid BufferView.
1684 // Ensure the cursor is correctly positioned on screen.
1685 view()->showCursor();
1689 void GuiView::insertLyXFile(docstring const & fname)
1691 BufferView * bv = view();
1696 FileName filename(to_utf8(fname));
1698 if (!filename.empty()) {
1699 bv->insertLyXFile(filename);
1703 // Launch a file browser
1705 string initpath = lyxrc.document_path;
1706 string const trypath = bv->buffer().filePath();
1707 // If directory is writeable, use this as default.
1708 if (FileName(trypath).isDirWritable())
1712 FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
1713 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1714 dlg.setButton2(qt_("Examples|#E#e"),
1715 toqstr(addPath(package().system_support().absFilename(),
1718 FileDialog::Result result = dlg.open(toqstr(initpath),
1719 QStringList(qt_("LyX Documents (*.lyx)")));
1721 if (result.first == FileDialog::Later)
1725 filename.set(fromqstr(result.second));
1727 // check selected filename
1728 if (filename.empty()) {
1729 // emit message signal.
1730 message(_("Canceled."));
1734 bv->insertLyXFile(filename);
1738 void GuiView::insertPlaintextFile(docstring const & fname,
1741 BufferView * bv = view();
1746 FileName filename(to_utf8(fname));
1748 if (!filename.empty()) {
1749 bv->insertPlaintextFile(filename, asParagraph);
1753 FileDialog dlg(qt_("Select file to insert"), (asParagraph ?
1754 LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
1756 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
1757 QStringList(qt_("All Files (*)")));
1759 if (result.first == FileDialog::Later)
1763 filename.set(fromqstr(result.second));
1765 // check selected filename
1766 if (filename.empty()) {
1767 // emit message signal.
1768 message(_("Canceled."));
1772 bv->insertPlaintextFile(filename, asParagraph);
1776 bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
1778 FileName fname = b.fileName();
1779 FileName const oldname = fname;
1781 if (!newname.empty()) {
1783 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
1785 // Switch to this Buffer.
1788 // No argument? Ask user through dialog.
1790 FileDialog dlg(qt_("Choose a filename to save document as"),
1791 LFUN_BUFFER_WRITE_AS);
1792 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
1793 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
1795 if (!isLyXFilename(fname.absFilename()))
1796 fname.changeExtension(".lyx");
1798 FileDialog::Result result =
1799 dlg.save(toqstr(fname.onlyPath().absFilename()),
1800 QStringList(qt_("LyX Documents (*.lyx)")),
1801 toqstr(fname.onlyFileName()));
1803 if (result.first == FileDialog::Later)
1806 fname.set(fromqstr(result.second));
1811 if (!isLyXFilename(fname.absFilename()))
1812 fname.changeExtension(".lyx");
1815 if (FileName(fname).exists()) {
1816 docstring const file = makeDisplayPath(fname.absFilename(), 30);
1817 docstring text = bformat(_("The document %1$s already "
1818 "exists.\n\nDo you want to "
1819 "overwrite that document?"),
1821 int const ret = Alert::prompt(_("Overwrite document?"),
1822 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
1825 case 1: return renameBuffer(b, docstring());
1826 case 2: return false;
1830 FileName oldauto = b.getAutosaveFilename();
1832 // Ok, change the name of the buffer
1833 b.setFileName(fname.absFilename());
1835 bool unnamed = b.isUnnamed();
1836 b.setUnnamed(false);
1837 b.saveCheckSum(fname);
1839 // bring the autosave file with us, just in case.
1840 b.moveAutosaveFile(oldauto);
1842 if (!saveBuffer(b)) {
1843 oldauto = b.getAutosaveFilename();
1844 b.setFileName(oldname.absFilename());
1845 b.setUnnamed(unnamed);
1846 b.saveCheckSum(oldname);
1847 b.moveAutosaveFile(oldauto);
1855 bool GuiView::saveBuffer(Buffer & b)
1857 if (workArea(b) && workArea(b)->inDialogMode())
1861 return renameBuffer(b, docstring());
1864 theSession().lastFiles().add(b.fileName());
1868 // Switch to this Buffer.
1871 // FIXME: we don't tell the user *WHY* the save failed !!
1872 docstring const file = makeDisplayPath(b.absFileName(), 30);
1873 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
1874 "Do you want to rename the document and "
1875 "try again?"), file);
1876 int const ret = Alert::prompt(_("Rename and save?"),
1877 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
1880 if (!renameBuffer(b, docstring()))
1889 return saveBuffer(b);
1893 bool GuiView::closeBuffer()
1895 Buffer * buf = buffer();
1896 return buf && closeBuffer(*buf);
1900 bool GuiView::closeBuffer(Buffer & buf, bool tolastopened)
1902 // goto bookmark to update bookmark pit.
1903 //FIXME: we should update only the bookmarks related to this buffer!
1904 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
1905 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
1906 theLyXFunc().gotoBookmark(i+1, false, false);
1908 if (buf.isClean() || buf.paragraphs().empty()) {
1909 // save in sessions if requested
1910 // do not save childs if their master
1911 // is opened as well
1913 theSession().lastOpened().add(buf.fileName());
1915 // Don't close child documents.
1916 removeWorkArea(currentMainWorkArea());
1918 theBufferList().release(&buf);
1921 // Switch to this Buffer.
1926 if (buf.isUnnamed())
1927 file = from_utf8(buf.fileName().onlyFileName());
1929 file = buf.fileName().displayName(30);
1931 // Bring this window to top before asking questions.
1935 docstring const text = bformat(_("The document %1$s has unsaved changes."
1936 "\n\nDo you want to save the document or discard the changes?"), file);
1937 int const ret = Alert::prompt(_("Save changed document?"),
1938 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
1942 if (!saveBuffer(buf))
1946 // if we crash after this we could
1947 // have no autosave file but I guess
1948 // this is really improbable (Jug)
1949 buf.removeAutosaveFile();
1955 // save file names to .lyx/session
1957 theSession().lastOpened().add(buf.fileName());
1960 // Don't close child documents.
1961 removeWorkArea(currentMainWorkArea());
1963 theBufferList().release(&buf);
1969 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
1971 Buffer * const curbuf = buffer();
1972 Buffer * nextbuf = curbuf;
1974 if (np == NEXTBUFFER)
1975 nextbuf = theBufferList().next(nextbuf);
1977 nextbuf = theBufferList().previous(nextbuf);
1978 if (nextbuf == curbuf)
1984 if (workArea(*nextbuf))
1991 bool GuiView::dispatch(FuncRequest const & cmd)
1993 BufferView * bv = view();
1994 // By default we won't need any update.
1996 bv->cursor().updateFlags(Update::None);
1997 bool dispatched = true;
1999 if (cmd.origin == FuncRequest::TOC) {
2000 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
2001 toc->doDispatch(bv->cursor(), cmd);
2005 switch(cmd.action) {
2006 case LFUN_BUFFER_IMPORT:
2007 importDocument(to_utf8(cmd.argument()));
2010 case LFUN_BUFFER_SWITCH: {
2012 theBufferList().getBuffer(FileName(to_utf8(cmd.argument())));
2016 bv->cursor().message(_("Document not loaded"));
2020 case LFUN_BUFFER_NEXT:
2021 gotoNextOrPreviousBuffer(NEXTBUFFER);
2024 case LFUN_BUFFER_PREVIOUS:
2025 gotoNextOrPreviousBuffer(PREVBUFFER);
2028 case LFUN_COMMAND_EXECUTE: {
2029 bool const show_it = cmd.argument() != "off";
2030 // FIXME: this is a hack, "minibuffer" should not be
2032 if (GuiToolbar * t = toolbar("minibuffer")) {
2033 t->setVisible(show_it);
2034 if (show_it && t->commandBuffer())
2035 t->commandBuffer()->setFocus();
2039 case LFUN_DROP_LAYOUTS_CHOICE:
2041 d.layout_->showPopup();
2044 case LFUN_MENU_OPEN:
2045 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
2046 menu->exec(QCursor::pos());
2049 case LFUN_FILE_INSERT:
2050 insertLyXFile(cmd.argument());
2052 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2053 insertPlaintextFile(cmd.argument(), true);
2056 case LFUN_FILE_INSERT_PLAINTEXT:
2057 insertPlaintextFile(cmd.argument(), false);
2060 case LFUN_BUFFER_WRITE:
2062 saveBuffer(bv->buffer());
2065 case LFUN_BUFFER_WRITE_AS:
2067 renameBuffer(bv->buffer(), cmd.argument());
2070 case LFUN_BUFFER_WRITE_ALL: {
2071 Buffer * first = theBufferList().first();
2074 message(_("Saving all documents..."));
2075 // We cannot use a for loop as the buffer list cycles.
2078 if (!b->isClean()) {
2080 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
2082 b = theBufferList().next(b);
2083 } while (b != first);
2084 message(_("All documents saved."));
2088 case LFUN_TOOLBAR_TOGGLE: {
2089 string const name = cmd.getArg(0);
2090 if (GuiToolbar * t = toolbar(name))
2095 case LFUN_DIALOG_UPDATE: {
2096 string const name = to_utf8(cmd.argument());
2097 // Can only update a dialog connected to an existing inset
2098 Inset * inset = getOpenInset(name);
2100 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
2101 inset->dispatch(view()->cursor(), fr);
2102 } else if (name == "paragraph") {
2103 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
2104 } else if (name == "prefs" || name == "document") {
2105 updateDialog(name, string());
2110 case LFUN_DIALOG_TOGGLE: {
2111 if (isDialogVisible(cmd.getArg(0)))
2112 dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
2114 dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
2118 case LFUN_DIALOG_DISCONNECT_INSET:
2119 disconnectDialog(to_utf8(cmd.argument()));
2122 case LFUN_DIALOG_HIDE: {
2123 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
2127 case LFUN_DIALOG_SHOW: {
2128 string const name = cmd.getArg(0);
2129 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
2131 if (name == "character") {
2132 data = freefont2string();
2134 showDialog("character", data);
2135 } else if (name == "latexlog") {
2136 Buffer::LogType type;
2137 string const logfile = buffer()->logName(&type);
2139 case Buffer::latexlog:
2142 case Buffer::buildlog:
2146 data += Lexer::quoteString(logfile);
2147 showDialog("log", data);
2148 } else if (name == "vclog") {
2149 string const data = "vc " +
2150 Lexer::quoteString(buffer()->lyxvc().getLogFile());
2151 showDialog("log", data);
2152 } else if (name == "symbols") {
2153 data = bv->cursor().getEncoding()->name();
2155 showDialog("symbols", data);
2157 } else if (name == "prefs" && isFullScreen()) {
2158 FuncRequest fr(LFUN_INSET_INSERT, "fullscreen");
2160 showDialog("prefs", data);
2162 showDialog(name, data);
2166 case LFUN_INSET_APPLY: {
2167 string const name = cmd.getArg(0);
2168 Inset * inset = getOpenInset(name);
2170 // put cursor in front of inset.
2171 if (!view()->setCursorFromInset(inset)) {
2172 LASSERT(false, break);
2175 // useful if we are called from a dialog.
2176 view()->cursor().beginUndoGroup();
2177 view()->cursor().recordUndo();
2178 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
2179 inset->dispatch(view()->cursor(), fr);
2180 view()->cursor().endUndoGroup();
2182 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
2188 case LFUN_UI_TOGGLE:
2190 // Make sure the keyboard focus stays in the work area.
2194 case LFUN_SPLIT_VIEW:
2195 if (Buffer * buf = buffer()) {
2196 string const orientation = cmd.getArg(0);
2197 d.splitter_->setOrientation(orientation == "vertical"
2198 ? Qt::Vertical : Qt::Horizontal);
2199 TabWorkArea * twa = addTabWorkArea();
2200 GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
2201 setCurrentWorkArea(wa);
2205 case LFUN_CLOSE_TAB_GROUP:
2206 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2208 twa = d.currentTabWorkArea();
2209 // Switch to the next GuiWorkArea in the found TabWorkArea.
2211 // Make sure the work area is up to date.
2212 setCurrentWorkArea(twa->currentWorkArea());
2214 setCurrentWorkArea(0);
2219 case LFUN_COMPLETION_INLINE:
2220 if (d.current_work_area_)
2221 d.current_work_area_->completer().showInline();
2224 case LFUN_COMPLETION_POPUP:
2225 if (d.current_work_area_)
2226 d.current_work_area_->completer().showPopup();
2230 case LFUN_COMPLETION_COMPLETE:
2231 if (d.current_work_area_)
2232 d.current_work_area_->completer().tab();
2235 case LFUN_COMPLETION_CANCEL:
2236 if (d.current_work_area_) {
2237 if (d.current_work_area_->completer().popupVisible())
2238 d.current_work_area_->completer().hidePopup();
2240 d.current_work_area_->completer().hideInline();
2244 case LFUN_COMPLETION_ACCEPT:
2245 if (d.current_work_area_)
2246 d.current_work_area_->completer().activate();
2249 case LFUN_BUFFER_ZOOM_IN:
2250 case LFUN_BUFFER_ZOOM_OUT:
2251 if (cmd.argument().empty()) {
2252 if (cmd.action == LFUN_BUFFER_ZOOM_IN)
2257 lyxrc.zoom += convert<int>(cmd.argument());
2259 if (lyxrc.zoom < 10)
2262 // The global QPixmapCache is used in GuiPainter to cache text
2263 // painting so we must reset it.
2264 QPixmapCache::clear();
2265 guiApp->fontLoader().update();
2266 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
2274 // Part of automatic menu appearance feature.
2275 if (isFullScreen()) {
2276 if (menuBar()->isVisible())
2278 if (statusBar()->isVisible())
2279 statusBar()->hide();
2286 void GuiView::lfunUiToggle(FuncRequest const & cmd)
2288 string const arg = cmd.getArg(0);
2289 if (arg == "scrollbar") {
2290 // hide() is of no help
2291 if (d.current_work_area_->verticalScrollBarPolicy() ==
2292 Qt::ScrollBarAlwaysOff)
2294 d.current_work_area_->setVerticalScrollBarPolicy(
2295 Qt::ScrollBarAsNeeded);
2297 d.current_work_area_->setVerticalScrollBarPolicy(
2298 Qt::ScrollBarAlwaysOff);
2301 if (arg == "statusbar") {
2302 statusBar()->setVisible(!statusBar()->isVisible());
2305 if (arg == "menubar") {
2306 menuBar()->setVisible(!menuBar()->isVisible());
2309 #if QT_VERSION >= 0x040300
2310 if (arg == "frame") {
2312 getContentsMargins(&l, &t, &r, &b);
2313 //are the frames in default state?
2314 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
2316 setContentsMargins(-2, -2, -2, -2);
2318 setContentsMargins(0, 0, 0, 0);
2323 if (arg == "fullscreen") {
2328 message(bformat("LFUN_UI_TOGGLE " + _("%1$s unknown command!"), from_utf8(arg)));
2332 void GuiView::toggleFullScreen()
2334 if (isFullScreen()) {
2335 for (int i = 0; i != d.splitter_->count(); ++i)
2336 d.tabWorkArea(i)->setFullScreen(false);
2337 #if QT_VERSION >= 0x040300
2338 setContentsMargins(0, 0, 0, 0);
2340 setWindowState(windowState() ^ Qt::WindowFullScreen);
2343 statusBar()->show();
2346 hideDialogs("prefs", 0);
2347 for (int i = 0; i != d.splitter_->count(); ++i)
2348 d.tabWorkArea(i)->setFullScreen(true);
2349 #if QT_VERSION >= 0x040300
2350 setContentsMargins(-2, -2, -2, -2);
2353 setWindowState(windowState() ^ Qt::WindowFullScreen);
2354 statusBar()->hide();
2356 if (lyxrc.full_screen_toolbars) {
2357 ToolbarMap::iterator end = d.toolbars_.end();
2358 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
2363 // give dialogs like the TOC a chance to adapt
2368 Buffer const * GuiView::updateInset(Inset const * inset)
2370 if (!d.current_work_area_)
2374 d.current_work_area_->scheduleRedraw();
2376 return &d.current_work_area_->bufferView().buffer();
2380 void GuiView::restartCursor()
2382 /* When we move around, or type, it's nice to be able to see
2383 * the cursor immediately after the keypress.
2385 if (d.current_work_area_)
2386 d.current_work_area_->startBlinkingCursor();
2388 // Take this occasion to update the other GUI elements.
2394 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
2396 if (d.current_work_area_)
2397 d.current_work_area_->completer().updateVisibility(cur, start, keep);
2402 // This list should be kept in sync with the list of insets in
2403 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
2404 // dialog should have the same name as the inset.
2405 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
2406 // docs in LyXAction.cpp.
2408 char const * const dialognames[] = {
2409 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
2410 "citation", "document", "errorlist", "ert", "external", "file", "findreplace",
2411 "float", "graphics", "include", "index", "index_print", "info", "nomenclature",
2412 "label", "log", "mathdelimiter", "mathmatrix", "mathspace", "note", "paragraph",
2413 "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker",
2414 "symbols", "tabular", "tabularcreate",
2416 #ifdef HAVE_LIBAIKSAURUS
2420 "texinfo", "toc", "href", "view-source", "vspace", "wrap", "listings", "findreplaceadv" };
2422 char const * const * const end_dialognames =
2423 dialognames + (sizeof(dialognames) / sizeof(char *));
2427 cmpCStr(char const * name) : name_(name) {}
2428 bool operator()(char const * other) {
2429 return strcmp(other, name_) == 0;
2436 bool isValidName(string const & name)
2438 return find_if(dialognames, end_dialognames,
2439 cmpCStr(name.c_str())) != end_dialognames;
2445 void GuiView::resetDialogs()
2447 // Make sure that no LFUN uses any LyXView.
2448 theLyXFunc().setLyXView(0);
2451 constructToolbars();
2452 guiApp->menus().fillMenuBar(menuBar(), this, false);
2454 d.layout_->updateContents(true);
2455 // Now update controls with current buffer.
2456 theLyXFunc().setLyXView(this);
2462 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
2464 if (!isValidName(name))
2467 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
2469 if (it != d.dialogs_.end()) {
2471 it->second->hideView();
2472 return it->second.get();
2475 Dialog * dialog = build(name);
2476 d.dialogs_[name].reset(dialog);
2477 if (lyxrc.allow_geometry_session)
2478 dialog->restoreSession();
2485 void GuiView::showDialog(string const & name, string const & data,
2493 Dialog * dialog = findOrBuild(name, false);
2495 dialog->showData(data);
2497 d.open_insets_[name] = inset;
2500 catch (ExceptionMessage const & ex) {
2508 bool GuiView::isDialogVisible(string const & name) const
2510 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2511 if (it == d.dialogs_.end())
2513 return it->second.get()->isVisibleView();
2517 void GuiView::hideDialog(string const & name, Inset * inset)
2519 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
2520 if (it == d.dialogs_.end())
2523 if (inset && inset != getOpenInset(name))
2526 Dialog * const dialog = it->second.get();
2527 if (dialog->isVisibleView())
2529 d.open_insets_[name] = 0;
2533 void GuiView::disconnectDialog(string const & name)
2535 if (!isValidName(name))
2538 if (d.open_insets_.find(name) != d.open_insets_.end())
2539 d.open_insets_[name] = 0;
2543 Inset * GuiView::getOpenInset(string const & name) const
2545 if (!isValidName(name))
2548 map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
2549 return it == d.open_insets_.end() ? 0 : it->second;
2553 void GuiView::hideAll() const
2555 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2556 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2558 for(; it != end; ++it)
2559 it->second->hideView();
2563 void GuiView::updateDialogs()
2565 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
2566 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
2568 for(; it != end; ++it) {
2569 Dialog * dialog = it->second.get();
2570 if (dialog && dialog->isVisibleView())
2571 dialog->checkStatus();
2578 // will be replaced by a proper factory...
2579 Dialog * createGuiAbout(GuiView & lv);
2580 Dialog * createGuiBibitem(GuiView & lv);
2581 Dialog * createGuiBibtex(GuiView & lv);
2582 Dialog * createGuiBox(GuiView & lv);
2583 Dialog * createGuiBranch(GuiView & lv);
2584 Dialog * createGuiChanges(GuiView & lv);
2585 Dialog * createGuiCharacter(GuiView & lv);
2586 Dialog * createGuiCitation(GuiView & lv);
2587 Dialog * createGuiDelimiter(GuiView & lv);
2588 Dialog * createGuiDocument(GuiView & lv);
2589 Dialog * createGuiErrorList(GuiView & lv);
2590 Dialog * createGuiERT(GuiView & lv);
2591 Dialog * createGuiExternal(GuiView & lv);
2592 Dialog * createGuiFloat(GuiView & lv);
2593 Dialog * createGuiGraphics(GuiView & lv);
2594 Dialog * createGuiInclude(GuiView & lv);
2595 Dialog * createGuiIndex(GuiView & lv);
2596 Dialog * createGuiInfo(GuiView & lv);
2597 Dialog * createGuiLabel(GuiView & lv);
2598 Dialog * createGuiListings(GuiView & lv);
2599 Dialog * createGuiLog(GuiView & lv);
2600 Dialog * createGuiMathHSpace(GuiView & lv);
2601 Dialog * createGuiMathMatrix(GuiView & lv);
2602 Dialog * createGuiNomenclature(GuiView & lv);
2603 Dialog * createGuiNote(GuiView & lv);
2604 Dialog * createGuiParagraph(GuiView & lv);
2605 Dialog * createGuiPhantom(GuiView & lv);
2606 Dialog * createGuiPreferences(GuiView & lv);
2607 Dialog * createGuiPrint(GuiView & lv);
2608 Dialog * createGuiPrintindex(GuiView & lv);
2609 Dialog * createGuiRef(GuiView & lv);
2610 Dialog * createGuiSearch(GuiView & lv);
2611 Dialog * createGuiSearchAdv(GuiView & lv);
2612 Dialog * createGuiSendTo(GuiView & lv);
2613 Dialog * createGuiShowFile(GuiView & lv);
2614 Dialog * createGuiSpellchecker(GuiView & lv);
2615 Dialog * createGuiSymbols(GuiView & lv);
2616 Dialog * createGuiTabularCreate(GuiView & lv);
2617 Dialog * createGuiTabular(GuiView & lv);
2618 Dialog * createGuiTexInfo(GuiView & lv);
2619 Dialog * createGuiTextHSpace(GuiView & lv);
2620 Dialog * createGuiToc(GuiView & lv);
2621 Dialog * createGuiThesaurus(GuiView & lv);
2622 Dialog * createGuiHyperlink(GuiView & lv);
2623 Dialog * createGuiVSpace(GuiView & lv);
2624 Dialog * createGuiViewSource(GuiView & lv);
2625 Dialog * createGuiWrap(GuiView & lv);
2628 Dialog * GuiView::build(string const & name)
2630 LASSERT(isValidName(name), return 0);
2632 if (name == "aboutlyx")
2633 return createGuiAbout(*this);
2634 if (name == "bibitem")
2635 return createGuiBibitem(*this);
2636 if (name == "bibtex")
2637 return createGuiBibtex(*this);
2639 return createGuiBox(*this);
2640 if (name == "branch")
2641 return createGuiBranch(*this);
2642 if (name == "changes")
2643 return createGuiChanges(*this);
2644 if (name == "character")
2645 return createGuiCharacter(*this);
2646 if (name == "citation")
2647 return createGuiCitation(*this);
2648 if (name == "document")
2649 return createGuiDocument(*this);
2650 if (name == "errorlist")
2651 return createGuiErrorList(*this);
2653 return createGuiERT(*this);
2654 if (name == "external")
2655 return createGuiExternal(*this);
2657 return createGuiShowFile(*this);
2658 if (name == "findreplace")
2659 return createGuiSearch(*this);
2660 if (name == "findreplaceadv")
2661 return createGuiSearchAdv(*this);
2662 if (name == "float")
2663 return createGuiFloat(*this);
2664 if (name == "graphics")
2665 return createGuiGraphics(*this);
2666 if (name == "include")
2667 return createGuiInclude(*this);
2668 if (name == "index")
2669 return createGuiIndex(*this);
2671 return createGuiInfo(*this);
2672 if (name == "nomenclature")
2673 return createGuiNomenclature(*this);
2674 if (name == "label")
2675 return createGuiLabel(*this);
2677 return createGuiLog(*this);
2678 if (name == "mathdelimiter")
2679 return createGuiDelimiter(*this);
2680 if (name == "mathspace")
2681 return createGuiMathHSpace(*this);
2682 if (name == "mathmatrix")
2683 return createGuiMathMatrix(*this);
2685 return createGuiNote(*this);
2686 if (name == "paragraph")
2687 return createGuiParagraph(*this);
2688 if (name == "phantom")
2689 return createGuiPhantom(*this);
2690 if (name == "prefs")
2691 return createGuiPreferences(*this);
2692 if (name == "print")
2693 return createGuiPrint(*this);
2695 return createGuiRef(*this);
2696 if (name == "sendto")
2697 return createGuiSendTo(*this);
2698 if (name == "space")
2699 return createGuiTextHSpace(*this);
2700 if (name == "spellchecker")
2701 return createGuiSpellchecker(*this);
2702 if (name == "symbols")
2703 return createGuiSymbols(*this);
2704 if (name == "tabular")
2705 return createGuiTabular(*this);
2706 if (name == "tabularcreate")
2707 return createGuiTabularCreate(*this);
2708 if (name == "texinfo")
2709 return createGuiTexInfo(*this);
2710 if (name == "view-source")
2711 return createGuiViewSource(*this);
2712 #ifdef HAVE_LIBAIKSAURUS
2713 if (name == "thesaurus")
2714 return createGuiThesaurus(*this);
2717 return createGuiHyperlink(*this);
2718 if (name == "index_print")
2719 return createGuiPrintindex(*this);
2720 if (name == "listings")
2721 return createGuiListings(*this);
2723 return createGuiToc(*this);
2724 if (name == "vspace")
2725 return createGuiVSpace(*this);
2727 return createGuiWrap(*this);
2733 } // namespace frontend
2736 #include "moc_GuiView.cpp"