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.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
386 QSize iconSize(docstring const & icon_size)
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
395 else if (icon_size == "huge")
397 else if (icon_size == "giant")
398 size = giantIconSize;
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
402 if (size < smallIconSize)
403 size = smallIconSize;
405 return QSize(size, size);
408 QSize iconSize(QString const & icon_size)
410 return iconSize(qstring_to_ucs4(icon_size));
413 string & iconSize(QSize const & qsize)
415 LATTEST(qsize.width() == qsize.height());
417 static string icon_size;
419 unsigned int size = qsize.width();
421 if (size < smallIconSize)
422 size = smallIconSize;
424 if (size == smallIconSize)
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
430 else if (size == hugeIconSize)
432 else if (size == giantIconSize)
435 icon_size = convert<string>(size);
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
452 * \warning Don't Delete! The layout box is actually owned by
453 * whichever toolbar contains it. All the GuiView class needs is a
454 * means of accessing it.
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
462 map<string, DialogPtr> dialogs_;
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
470 QTimer statusbar_timer_;
471 /// auto-saving of buffers
472 Timeout autosave_timeout_;
473 /// flag against a race condition due to multiclicks, see bug #1119
477 TocModels toc_models_;
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
483 string last_export_format;
484 string processing_format;
486 static QSet<Buffer const *> busyBuffers;
487 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
495 // TODO syncFunc/previewFunc: use bind
496 bool asyncBufferProcessing(string const & argument,
497 Buffer const * used_buffer,
498 docstring const & msg,
499 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
503 QVector<GuiWorkArea*> guiWorkAreas();
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
509 GuiView::GuiView(int id)
510 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511 command_execute_(false), minibuffer_focus_(false)
513 connect(this, SIGNAL(bufferViewChanged()),
514 this, SLOT(onBufferViewChanged()));
516 // GuiToolbars *must* be initialised before the menu bar.
517 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
533 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
534 d.autosave_timeout_.start();
536 connect(&d.statusbar_timer_, SIGNAL(timeout()),
537 this, SLOT(clearMessage()));
539 // We don't want to keep the window in memory if it is closed.
540 setAttribute(Qt::WA_DeleteOnClose, true);
542 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
543 // QIcon::fromTheme was introduced in Qt 4.6
544 #if (QT_VERSION >= 0x040600)
545 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
546 // since the icon is provided in the application bundle. We use a themed
547 // version when available and use the bundled one as fallback.
548 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
556 // use tabbed dock area for multiple docks
557 // (such as "source" and "messages")
558 setDockOptions(QMainWindow::ForceTabbedDocks);
561 setAcceptDrops(true);
563 // add busy indicator to statusbar
564 QLabel * busylabel = new QLabel(statusBar());
565 statusBar()->addPermanentWidget(busylabel);
566 search_mode mode = theGuiApp()->imageSearchMode();
567 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
568 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
569 busylabel->setMovie(busyanim);
573 connect(&d.processing_thread_watcher_, SIGNAL(started()),
574 busylabel, SLOT(show()));
575 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
576 busylabel, SLOT(hide()));
578 QFontMetrics const fm(statusBar()->fontMetrics());
579 int const roheight = max(int(d.normalIconSize), fm.height());
580 QSize const rosize(roheight, roheight);
581 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
582 read_only_ = new QLabel(statusBar());
583 read_only_->setPixmap(readonly);
584 read_only_->setScaledContents(true);
585 read_only_->setAlignment(Qt::AlignCenter);
587 statusBar()->addPermanentWidget(read_only_);
589 version_control_ = new QLabel(statusBar());
590 version_control_->setAlignment(Qt::AlignCenter);
591 version_control_->setFrameStyle(QFrame::StyledPanel);
592 version_control_->hide();
593 statusBar()->addPermanentWidget(version_control_);
595 statusBar()->setSizeGripEnabled(true);
598 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
599 SLOT(autoSaveThreadFinished()));
601 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
602 SLOT(processingThreadStarted()));
603 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
604 SLOT(processingThreadFinished()));
606 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
607 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
609 // set custom application bars context menu, e.g. tool bar and menu bar
610 setContextMenuPolicy(Qt::CustomContextMenu);
611 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
612 SLOT(toolBarPopup(const QPoint &)));
614 // Forbid too small unresizable window because it can happen
615 // with some window manager under X11.
616 setMinimumSize(300, 200);
618 if (lyxrc.allow_geometry_session) {
619 // Now take care of session management.
624 // no session handling, default to a sane size.
625 setGeometry(50, 50, 690, 510);
628 // clear session data if any.
630 settings.remove("views");
640 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
642 QVector<GuiWorkArea*> areas;
643 for (int i = 0; i < tabWorkAreaCount(); i++) {
644 TabWorkArea* ta = tabWorkArea(i);
645 for (int u = 0; u < ta->count(); u++) {
646 areas << ta->workArea(u);
652 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
653 string const & format)
655 docstring const fmt = theFormats().prettyName(format);
658 case Buffer::ExportSuccess:
659 msg = bformat(_("Successful export to format: %1$s"), fmt);
661 case Buffer::ExportCancel:
662 msg = _("Document export cancelled.");
664 case Buffer::ExportError:
665 case Buffer::ExportNoPathToFormat:
666 case Buffer::ExportTexPathHasSpaces:
667 case Buffer::ExportConverterError:
668 msg = bformat(_("Error while exporting format: %1$s"), fmt);
670 case Buffer::PreviewSuccess:
671 msg = bformat(_("Successful preview of format: %1$s"), fmt);
673 case Buffer::PreviewError:
674 msg = bformat(_("Error while previewing format: %1$s"), fmt);
681 void GuiView::processingThreadStarted()
686 void GuiView::processingThreadFinished()
688 QFutureWatcher<Buffer::ExportStatus> const * watcher =
689 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
691 Buffer::ExportStatus const status = watcher->result();
692 handleExportStatus(this, status, d.processing_format);
695 BufferView const * const bv = currentBufferView();
696 if (bv && !bv->buffer().errorList("Export").empty()) {
700 errors(d.last_export_format);
704 void GuiView::autoSaveThreadFinished()
706 QFutureWatcher<docstring> const * watcher =
707 static_cast<QFutureWatcher<docstring> const *>(sender());
708 message(watcher->result());
713 void GuiView::saveLayout() const
716 settings.beginGroup("views");
717 settings.beginGroup(QString::number(id_));
718 #if defined(Q_WS_X11) || defined(QPA_XCB)
719 settings.setValue("pos", pos());
720 settings.setValue("size", size());
722 settings.setValue("geometry", saveGeometry());
724 settings.setValue("layout", saveState(0));
725 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
729 void GuiView::saveUISettings() const
731 // Save the toolbar private states
732 ToolbarMap::iterator end = d.toolbars_.end();
733 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
734 it->second->saveSession();
735 // Now take care of all other dialogs
736 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
737 for (; it!= d.dialogs_.end(); ++it)
738 it->second->saveSession();
742 bool GuiView::restoreLayout()
745 settings.beginGroup("views");
746 settings.beginGroup(QString::number(id_));
747 QString const icon_key = "icon_size";
748 if (!settings.contains(icon_key))
751 //code below is skipped when when ~/.config/LyX is (re)created
752 setIconSize(d.iconSize(settings.value(icon_key).toString()));
754 #if defined(Q_WS_X11) || defined(QPA_XCB)
755 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
756 QSize size = settings.value("size", QSize(690, 510)).toSize();
760 // Work-around for bug #6034: the window ends up in an undetermined
761 // state when trying to restore a maximized window when it is
762 // already maximized.
763 if (!(windowState() & Qt::WindowMaximized))
764 if (!restoreGeometry(settings.value("geometry").toByteArray()))
765 setGeometry(50, 50, 690, 510);
767 // Make sure layout is correctly oriented.
768 setLayoutDirection(qApp->layoutDirection());
770 // Allow the toc and view-source dock widget to be restored if needed.
772 if ((dialog = findOrBuild("toc", true)))
773 // see bug 5082. At least setup title and enabled state.
774 // Visibility will be adjusted by restoreState below.
775 dialog->prepareView();
776 if ((dialog = findOrBuild("view-source", true)))
777 dialog->prepareView();
778 if ((dialog = findOrBuild("progress", true)))
779 dialog->prepareView();
781 if (!restoreState(settings.value("layout").toByteArray(), 0))
784 // init the toolbars that have not been restored
785 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
786 Toolbars::Infos::iterator end = guiApp->toolbars().end();
787 for (; cit != end; ++cit) {
788 GuiToolbar * tb = toolbar(cit->name);
789 if (tb && !tb->isRestored())
790 initToolbar(cit->name);
798 GuiToolbar * GuiView::toolbar(string const & name)
800 ToolbarMap::iterator it = d.toolbars_.find(name);
801 if (it != d.toolbars_.end())
804 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
809 void GuiView::constructToolbars()
811 ToolbarMap::iterator it = d.toolbars_.begin();
812 for (; it != d.toolbars_.end(); ++it)
816 // I don't like doing this here, but the standard toolbar
817 // destroys this object when it's destroyed itself (vfr)
818 d.layout_ = new LayoutBox(*this);
819 d.stack_widget_->addWidget(d.layout_);
820 d.layout_->move(0,0);
822 // extracts the toolbars from the backend
823 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
824 Toolbars::Infos::iterator end = guiApp->toolbars().end();
825 for (; cit != end; ++cit)
826 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
830 void GuiView::initToolbars()
832 // extracts the toolbars from the backend
833 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
834 Toolbars::Infos::iterator end = guiApp->toolbars().end();
835 for (; cit != end; ++cit)
836 initToolbar(cit->name);
840 void GuiView::initToolbar(string const & name)
842 GuiToolbar * tb = toolbar(name);
845 int const visibility = guiApp->toolbars().defaultVisibility(name);
846 bool newline = !(visibility & Toolbars::SAMEROW);
847 tb->setVisible(false);
848 tb->setVisibility(visibility);
850 if (visibility & Toolbars::TOP) {
852 addToolBarBreak(Qt::TopToolBarArea);
853 addToolBar(Qt::TopToolBarArea, tb);
856 if (visibility & Toolbars::BOTTOM) {
858 addToolBarBreak(Qt::BottomToolBarArea);
859 addToolBar(Qt::BottomToolBarArea, tb);
862 if (visibility & Toolbars::LEFT) {
864 addToolBarBreak(Qt::LeftToolBarArea);
865 addToolBar(Qt::LeftToolBarArea, tb);
868 if (visibility & Toolbars::RIGHT) {
870 addToolBarBreak(Qt::RightToolBarArea);
871 addToolBar(Qt::RightToolBarArea, tb);
874 if (visibility & Toolbars::ON)
875 tb->setVisible(true);
879 TocModels & GuiView::tocModels()
881 return d.toc_models_;
885 void GuiView::setFocus()
887 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
888 QMainWindow::setFocus();
892 bool GuiView::hasFocus() const
894 if (currentWorkArea())
895 return currentWorkArea()->hasFocus();
896 if (currentMainWorkArea())
897 return currentMainWorkArea()->hasFocus();
898 return d.bg_widget_->hasFocus();
902 void GuiView::focusInEvent(QFocusEvent * e)
904 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
905 QMainWindow::focusInEvent(e);
906 // Make sure guiApp points to the correct view.
907 guiApp->setCurrentView(this);
908 if (currentWorkArea())
909 currentWorkArea()->setFocus();
910 else if (currentMainWorkArea())
911 currentMainWorkArea()->setFocus();
913 d.bg_widget_->setFocus();
917 void GuiView::showEvent(QShowEvent * e)
919 LYXERR(Debug::GUI, "Passed Geometry "
920 << size().height() << "x" << size().width()
921 << "+" << pos().x() << "+" << pos().y());
923 if (d.splitter_->count() == 0)
924 // No work area, switch to the background widget.
928 QMainWindow::showEvent(e);
932 bool GuiView::closeScheduled()
939 bool GuiView::prepareAllBuffersForLogout()
941 Buffer * first = theBufferList().first();
945 // First, iterate over all buffers and ask the users if unsaved
946 // changes should be saved.
947 // We cannot use a for loop as the buffer list cycles.
950 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
952 b = theBufferList().next(b);
953 } while (b != first);
955 // Next, save session state
956 // When a view/window was closed before without quitting LyX, there
957 // are already entries in the lastOpened list.
958 theSession().lastOpened().clear();
965 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
966 ** is responsibility of the container (e.g., dialog)
968 void GuiView::closeEvent(QCloseEvent * close_event)
970 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
972 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
973 Alert::warning(_("Exit LyX"),
974 _("LyX could not be closed because documents are being processed by LyX."));
975 close_event->setAccepted(false);
979 // If the user pressed the x (so we didn't call closeView
980 // programmatically), we want to clear all existing entries.
982 theSession().lastOpened().clear();
987 // it can happen that this event arrives without selecting the view,
988 // e.g. when clicking the close button on a background window.
990 if (!closeWorkAreaAll()) {
992 close_event->ignore();
996 // Make sure that nothing will use this to be closed View.
997 guiApp->unregisterView(this);
999 if (isFullScreen()) {
1000 // Switch off fullscreen before closing.
1005 // Make sure the timer time out will not trigger a statusbar update.
1006 d.statusbar_timer_.stop();
1008 // Saving fullscreen requires additional tweaks in the toolbar code.
1009 // It wouldn't also work under linux natively.
1010 if (lyxrc.allow_geometry_session) {
1015 close_event->accept();
1019 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1021 if (event->mimeData()->hasUrls())
1023 /// \todo Ask lyx-devel is this is enough:
1024 /// if (event->mimeData()->hasFormat("text/plain"))
1025 /// event->acceptProposedAction();
1029 void GuiView::dropEvent(QDropEvent * event)
1031 QList<QUrl> files = event->mimeData()->urls();
1032 if (files.isEmpty())
1035 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1036 for (int i = 0; i != files.size(); ++i) {
1037 string const file = os::internal_path(fromqstr(
1038 files.at(i).toLocalFile()));
1042 string const ext = support::getExtension(file);
1043 vector<const Format *> found_formats;
1045 // Find all formats that have the correct extension.
1046 vector<const Format *> const & import_formats
1047 = theConverters().importableFormats();
1048 vector<const Format *>::const_iterator it = import_formats.begin();
1049 for (; it != import_formats.end(); ++it)
1050 if ((*it)->hasExtension(ext))
1051 found_formats.push_back(*it);
1054 if (found_formats.size() >= 1) {
1055 if (found_formats.size() > 1) {
1056 //FIXME: show a dialog to choose the correct importable format
1057 LYXERR(Debug::FILES,
1058 "Multiple importable formats found, selecting first");
1060 string const arg = found_formats[0]->name() + " " + file;
1061 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1064 //FIXME: do we have to explicitly check whether it's a lyx file?
1065 LYXERR(Debug::FILES,
1066 "No formats found, trying to open it as a lyx file");
1067 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1069 // add the functions to the queue
1070 guiApp->addToFuncRequestQueue(cmd);
1073 // now process the collected functions. We perform the events
1074 // asynchronously. This prevents potential problems in case the
1075 // BufferView is closed within an event.
1076 guiApp->processFuncRequestQueueAsync();
1080 void GuiView::message(docstring const & str)
1082 if (ForkedProcess::iAmAChild())
1085 // call is moved to GUI-thread by GuiProgress
1086 d.progress_->appendMessage(toqstr(str));
1090 void GuiView::clearMessageText()
1092 message(docstring());
1096 void GuiView::updateStatusBarMessage(QString const & str)
1098 statusBar()->showMessage(str);
1099 d.statusbar_timer_.stop();
1100 d.statusbar_timer_.start(3000);
1104 void GuiView::clearMessage()
1106 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1107 // the hasFocus function mostly returns false, even if the focus is on
1108 // a workarea in this view.
1112 d.statusbar_timer_.stop();
1116 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1118 if (wa != d.current_work_area_
1119 || wa->bufferView().buffer().isInternal())
1121 Buffer const & buf = wa->bufferView().buffer();
1122 // Set the windows title
1123 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1124 if (buf.notifiesExternalModification()) {
1125 title = bformat(_("%1$s (modified externally)"), title);
1126 // If the external modification status has changed, then maybe the status of
1127 // buffer-save has changed too.
1131 title += from_ascii(" - LyX");
1133 setWindowTitle(toqstr(title));
1134 // Sets the path for the window: this is used by OSX to
1135 // allow a context click on the title bar showing a menu
1136 // with the path up to the file
1137 setWindowFilePath(toqstr(buf.absFileName()));
1138 // Tell Qt whether the current document is changed
1139 setWindowModified(!buf.isClean());
1141 if (buf.hasReadonlyFlag())
1146 if (buf.lyxvc().inUse()) {
1147 version_control_->show();
1148 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1150 version_control_->hide();
1154 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1156 if (d.current_work_area_)
1157 // disconnect the current work area from all slots
1158 QObject::disconnect(d.current_work_area_, 0, this, 0);
1160 disconnectBufferView();
1161 connectBufferView(wa->bufferView());
1162 connectBuffer(wa->bufferView().buffer());
1163 d.current_work_area_ = wa;
1164 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1165 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1166 QObject::connect(wa, SIGNAL(busy(bool)),
1167 this, SLOT(setBusy(bool)));
1168 // connection of a signal to a signal
1169 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1170 this, SIGNAL(bufferViewChanged()));
1171 Q_EMIT updateWindowTitle(wa);
1172 Q_EMIT bufferViewChanged();
1176 void GuiView::onBufferViewChanged()
1179 // Buffer-dependent dialogs must be updated. This is done here because
1180 // some dialogs require buffer()->text.
1185 void GuiView::on_lastWorkAreaRemoved()
1188 // We already are in a close event. Nothing more to do.
1191 if (d.splitter_->count() > 1)
1192 // We have a splitter so don't close anything.
1195 // Reset and updates the dialogs.
1196 Q_EMIT bufferViewChanged();
1201 if (lyxrc.open_buffers_in_tabs)
1202 // Nothing more to do, the window should stay open.
1205 if (guiApp->viewIds().size() > 1) {
1211 // On Mac we also close the last window because the application stay
1212 // resident in memory. On other platforms we don't close the last
1213 // window because this would quit the application.
1219 void GuiView::updateStatusBar()
1221 // let the user see the explicit message
1222 if (d.statusbar_timer_.isActive())
1229 void GuiView::showMessage()
1233 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1234 if (msg.isEmpty()) {
1235 BufferView const * bv = currentBufferView();
1237 msg = toqstr(bv->cursor().currentState());
1239 msg = qt_("Welcome to LyX!");
1241 statusBar()->showMessage(msg);
1245 bool GuiView::event(QEvent * e)
1249 // Useful debug code:
1250 //case QEvent::ActivationChange:
1251 //case QEvent::WindowDeactivate:
1252 //case QEvent::Paint:
1253 //case QEvent::Enter:
1254 //case QEvent::Leave:
1255 //case QEvent::HoverEnter:
1256 //case QEvent::HoverLeave:
1257 //case QEvent::HoverMove:
1258 //case QEvent::StatusTip:
1259 //case QEvent::DragEnter:
1260 //case QEvent::DragLeave:
1261 //case QEvent::Drop:
1264 case QEvent::WindowActivate: {
1265 GuiView * old_view = guiApp->currentView();
1266 if (this == old_view) {
1268 return QMainWindow::event(e);
1270 if (old_view && old_view->currentBufferView()) {
1271 // save current selection to the selection buffer to allow
1272 // middle-button paste in this window.
1273 cap::saveSelection(old_view->currentBufferView()->cursor());
1275 guiApp->setCurrentView(this);
1276 if (d.current_work_area_)
1277 on_currentWorkAreaChanged(d.current_work_area_);
1281 return QMainWindow::event(e);
1284 case QEvent::ShortcutOverride: {
1286 if (isFullScreen() && menuBar()->isHidden()) {
1287 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1288 // FIXME: we should also try to detect special LyX shortcut such as
1289 // Alt-P and Alt-M. Right now there is a hack in
1290 // GuiWorkArea::processKeySym() that hides again the menubar for
1292 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1294 return QMainWindow::event(e);
1297 return QMainWindow::event(e);
1301 return QMainWindow::event(e);
1305 void GuiView::resetWindowTitle()
1307 setWindowTitle(qt_("LyX"));
1310 bool GuiView::focusNextPrevChild(bool /*next*/)
1317 bool GuiView::busy() const
1323 void GuiView::setBusy(bool busy)
1325 bool const busy_before = busy_ > 0;
1326 busy ? ++busy_ : --busy_;
1327 if ((busy_ > 0) == busy_before)
1328 // busy state didn't change
1332 QApplication::setOverrideCursor(Qt::WaitCursor);
1335 QApplication::restoreOverrideCursor();
1340 void GuiView::resetCommandExecute()
1342 command_execute_ = false;
1347 double GuiView::pixelRatio() const
1349 #if QT_VERSION >= 0x050000
1350 return qt_scale_factor * devicePixelRatio();
1357 GuiWorkArea * GuiView::workArea(int index)
1359 if (TabWorkArea * twa = d.currentTabWorkArea())
1360 if (index < twa->count())
1361 return twa->workArea(index);
1366 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1368 if (currentWorkArea()
1369 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1370 return currentWorkArea();
1371 if (TabWorkArea * twa = d.currentTabWorkArea())
1372 return twa->workArea(buffer);
1377 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1379 // Automatically create a TabWorkArea if there are none yet.
1380 TabWorkArea * tab_widget = d.splitter_->count()
1381 ? d.currentTabWorkArea() : addTabWorkArea();
1382 return tab_widget->addWorkArea(buffer, *this);
1386 TabWorkArea * GuiView::addTabWorkArea()
1388 TabWorkArea * twa = new TabWorkArea;
1389 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1390 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1391 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1392 this, SLOT(on_lastWorkAreaRemoved()));
1394 d.splitter_->addWidget(twa);
1395 d.stack_widget_->setCurrentWidget(d.splitter_);
1400 GuiWorkArea const * GuiView::currentWorkArea() const
1402 return d.current_work_area_;
1406 GuiWorkArea * GuiView::currentWorkArea()
1408 return d.current_work_area_;
1412 GuiWorkArea const * GuiView::currentMainWorkArea() const
1414 if (!d.currentTabWorkArea())
1416 return d.currentTabWorkArea()->currentWorkArea();
1420 GuiWorkArea * GuiView::currentMainWorkArea()
1422 if (!d.currentTabWorkArea())
1424 return d.currentTabWorkArea()->currentWorkArea();
1428 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1430 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1432 d.current_work_area_ = 0;
1434 Q_EMIT bufferViewChanged();
1438 // FIXME: I've no clue why this is here and why it accesses
1439 // theGuiApp()->currentView, which might be 0 (bug 6464).
1440 // See also 27525 (vfr).
1441 if (theGuiApp()->currentView() == this
1442 && theGuiApp()->currentView()->currentWorkArea() == wa)
1445 if (currentBufferView())
1446 cap::saveSelection(currentBufferView()->cursor());
1448 theGuiApp()->setCurrentView(this);
1449 d.current_work_area_ = wa;
1451 // We need to reset this now, because it will need to be
1452 // right if the tabWorkArea gets reset in the for loop. We
1453 // will change it back if we aren't in that case.
1454 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1455 d.current_main_work_area_ = wa;
1457 for (int i = 0; i != d.splitter_->count(); ++i) {
1458 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1459 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1460 << ", Current main wa: " << currentMainWorkArea());
1465 d.current_main_work_area_ = old_cmwa;
1467 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1468 on_currentWorkAreaChanged(wa);
1469 BufferView & bv = wa->bufferView();
1470 bv.cursor().fixIfBroken();
1472 wa->setUpdatesEnabled(true);
1473 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1477 void GuiView::removeWorkArea(GuiWorkArea * wa)
1479 LASSERT(wa, return);
1480 if (wa == d.current_work_area_) {
1482 disconnectBufferView();
1483 d.current_work_area_ = 0;
1484 d.current_main_work_area_ = 0;
1487 bool found_twa = false;
1488 for (int i = 0; i != d.splitter_->count(); ++i) {
1489 TabWorkArea * twa = d.tabWorkArea(i);
1490 if (twa->removeWorkArea(wa)) {
1491 // Found in this tab group, and deleted the GuiWorkArea.
1493 if (twa->count() != 0) {
1494 if (d.current_work_area_ == 0)
1495 // This means that we are closing the current GuiWorkArea, so
1496 // switch to the next GuiWorkArea in the found TabWorkArea.
1497 setCurrentWorkArea(twa->currentWorkArea());
1499 // No more WorkAreas in this tab group, so delete it.
1506 // It is not a tabbed work area (i.e., the search work area), so it
1507 // should be deleted by other means.
1508 LASSERT(found_twa, return);
1510 if (d.current_work_area_ == 0) {
1511 if (d.splitter_->count() != 0) {
1512 TabWorkArea * twa = d.currentTabWorkArea();
1513 setCurrentWorkArea(twa->currentWorkArea());
1515 // No more work areas, switch to the background widget.
1516 setCurrentWorkArea(0);
1522 LayoutBox * GuiView::getLayoutDialog() const
1528 void GuiView::updateLayoutList()
1531 d.layout_->updateContents(false);
1535 void GuiView::updateToolbars()
1537 ToolbarMap::iterator end = d.toolbars_.end();
1538 if (d.current_work_area_) {
1540 if (d.current_work_area_->bufferView().cursor().inMathed()
1541 && !d.current_work_area_->bufferView().cursor().inRegexped())
1542 context |= Toolbars::MATH;
1543 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1544 context |= Toolbars::TABLE;
1545 if (currentBufferView()->buffer().areChangesPresent()
1546 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1547 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1548 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1549 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1550 context |= Toolbars::REVIEW;
1551 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1552 context |= Toolbars::MATHMACROTEMPLATE;
1553 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1554 context |= Toolbars::IPA;
1555 if (command_execute_)
1556 context |= Toolbars::MINIBUFFER;
1557 if (minibuffer_focus_) {
1558 context |= Toolbars::MINIBUFFER_FOCUS;
1559 minibuffer_focus_ = false;
1562 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1563 it->second->update(context);
1565 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1566 it->second->update();
1570 void GuiView::setBuffer(Buffer * newBuffer)
1572 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1573 LASSERT(newBuffer, return);
1575 GuiWorkArea * wa = workArea(*newBuffer);
1578 newBuffer->masterBuffer()->updateBuffer();
1580 wa = addWorkArea(*newBuffer);
1581 // scroll to the position when the BufferView was last closed
1582 if (lyxrc.use_lastfilepos) {
1583 LastFilePosSection::FilePos filepos =
1584 theSession().lastFilePos().load(newBuffer->fileName());
1585 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1588 //Disconnect the old buffer...there's no new one.
1591 connectBuffer(*newBuffer);
1592 connectBufferView(wa->bufferView());
1593 setCurrentWorkArea(wa);
1597 void GuiView::connectBuffer(Buffer & buf)
1599 buf.setGuiDelegate(this);
1603 void GuiView::disconnectBuffer()
1605 if (d.current_work_area_)
1606 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1610 void GuiView::connectBufferView(BufferView & bv)
1612 bv.setGuiDelegate(this);
1616 void GuiView::disconnectBufferView()
1618 if (d.current_work_area_)
1619 d.current_work_area_->bufferView().setGuiDelegate(0);
1623 void GuiView::errors(string const & error_type, bool from_master)
1625 BufferView const * const bv = currentBufferView();
1629 #if EXPORT_in_THREAD
1630 // We are called with from_master == false by default, so we
1631 // have to figure out whether that is the case or not.
1632 ErrorList & el = bv->buffer().errorList(error_type);
1634 el = bv->buffer().masterBuffer()->errorList(error_type);
1638 ErrorList const & el = from_master ?
1639 bv->buffer().masterBuffer()->errorList(error_type) :
1640 bv->buffer().errorList(error_type);
1646 string data = error_type;
1648 data = "from_master|" + error_type;
1649 showDialog("errorlist", data);
1653 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1655 d.toc_models_.updateItem(toqstr(type), dit);
1659 void GuiView::structureChanged()
1661 // This is called from the Buffer, which has no way to ensure that cursors
1662 // in BufferView remain valid.
1663 if (documentBufferView())
1664 documentBufferView()->cursor().sanitize();
1665 // FIXME: This is slightly expensive, though less than the tocBackend update
1666 // (#9880). This also resets the view in the Toc Widget (#6675).
1667 d.toc_models_.reset(documentBufferView());
1668 // Navigator needs more than a simple update in this case. It needs to be
1670 updateDialog("toc", "");
1674 void GuiView::updateDialog(string const & name, string const & data)
1676 if (!isDialogVisible(name))
1679 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1680 if (it == d.dialogs_.end())
1683 Dialog * const dialog = it->second.get();
1684 if (dialog->isVisibleView())
1685 dialog->initialiseParams(data);
1689 BufferView * GuiView::documentBufferView()
1691 return currentMainWorkArea()
1692 ? ¤tMainWorkArea()->bufferView()
1697 BufferView const * GuiView::documentBufferView() const
1699 return currentMainWorkArea()
1700 ? ¤tMainWorkArea()->bufferView()
1705 BufferView * GuiView::currentBufferView()
1707 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1711 BufferView const * GuiView::currentBufferView() const
1713 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1717 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1718 Buffer const * orig, Buffer * clone)
1720 bool const success = clone->autoSave();
1722 busyBuffers.remove(orig);
1724 ? _("Automatic save done.")
1725 : _("Automatic save failed!");
1729 void GuiView::autoSave()
1731 LYXERR(Debug::INFO, "Running autoSave()");
1733 Buffer * buffer = documentBufferView()
1734 ? &documentBufferView()->buffer() : 0;
1736 resetAutosaveTimers();
1740 GuiViewPrivate::busyBuffers.insert(buffer);
1741 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1742 buffer, buffer->cloneBufferOnly());
1743 d.autosave_watcher_.setFuture(f);
1744 resetAutosaveTimers();
1748 void GuiView::resetAutosaveTimers()
1751 d.autosave_timeout_.restart();
1755 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1758 Buffer * buf = currentBufferView()
1759 ? ¤tBufferView()->buffer() : 0;
1760 Buffer * doc_buffer = documentBufferView()
1761 ? &(documentBufferView()->buffer()) : 0;
1764 /* In LyX/Mac, when a dialog is open, the menus of the
1765 application can still be accessed without giving focus to
1766 the main window. In this case, we want to disable the menu
1767 entries that are buffer-related.
1768 This code must not be used on Linux and Windows, since it
1769 would disable buffer-related entries when hovering over the
1770 menu (see bug #9574).
1772 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1778 // Check whether we need a buffer
1779 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1780 // no, exit directly
1781 flag.message(from_utf8(N_("Command not allowed with"
1782 "out any document open")));
1783 flag.setEnabled(false);
1787 if (cmd.origin() == FuncRequest::TOC) {
1788 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1789 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1790 flag.setEnabled(false);
1794 switch(cmd.action()) {
1795 case LFUN_BUFFER_IMPORT:
1798 case LFUN_MASTER_BUFFER_UPDATE:
1799 case LFUN_MASTER_BUFFER_VIEW:
1801 && (doc_buffer->parent() != 0
1802 || doc_buffer->hasChildren())
1803 && !d.processing_thread_watcher_.isRunning();
1806 case LFUN_BUFFER_UPDATE:
1807 case LFUN_BUFFER_VIEW: {
1808 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1812 string format = to_utf8(cmd.argument());
1813 if (cmd.argument().empty())
1814 format = doc_buffer->params().getDefaultOutputFormat();
1815 enable = doc_buffer->params().isExportable(format, true);
1819 case LFUN_BUFFER_RELOAD:
1820 enable = doc_buffer && !doc_buffer->isUnnamed()
1821 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1824 case LFUN_BUFFER_CHILD_OPEN:
1825 enable = doc_buffer != 0;
1828 case LFUN_BUFFER_WRITE:
1829 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1832 //FIXME: This LFUN should be moved to GuiApplication.
1833 case LFUN_BUFFER_WRITE_ALL: {
1834 // We enable the command only if there are some modified buffers
1835 Buffer * first = theBufferList().first();
1840 // We cannot use a for loop as the buffer list is a cycle.
1842 if (!b->isClean()) {
1846 b = theBufferList().next(b);
1847 } while (b != first);
1851 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1852 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1855 case LFUN_BUFFER_WRITE_AS:
1856 case LFUN_BUFFER_EXPORT_AS:
1857 enable = doc_buffer != 0;
1860 case LFUN_BUFFER_CLOSE:
1861 case LFUN_VIEW_CLOSE:
1862 enable = doc_buffer != 0;
1865 case LFUN_BUFFER_CLOSE_ALL:
1866 enable = theBufferList().last() != theBufferList().first();
1869 case LFUN_VIEW_SPLIT:
1870 if (cmd.getArg(0) == "vertical")
1871 enable = doc_buffer && (d.splitter_->count() == 1 ||
1872 d.splitter_->orientation() == Qt::Vertical);
1874 enable = doc_buffer && (d.splitter_->count() == 1 ||
1875 d.splitter_->orientation() == Qt::Horizontal);
1878 case LFUN_TAB_GROUP_CLOSE:
1879 enable = d.tabWorkAreaCount() > 1;
1882 case LFUN_TOOLBAR_TOGGLE: {
1883 string const name = cmd.getArg(0);
1884 if (GuiToolbar * t = toolbar(name))
1885 flag.setOnOff(t->isVisible());
1888 docstring const msg =
1889 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1895 case LFUN_ICON_SIZE:
1896 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1899 case LFUN_DROP_LAYOUTS_CHOICE:
1903 case LFUN_UI_TOGGLE:
1904 flag.setOnOff(isFullScreen());
1907 case LFUN_DIALOG_DISCONNECT_INSET:
1910 case LFUN_DIALOG_HIDE:
1911 // FIXME: should we check if the dialog is shown?
1914 case LFUN_DIALOG_TOGGLE:
1915 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1916 // fall through to set "enable"
1917 case LFUN_DIALOG_SHOW: {
1918 string const name = cmd.getArg(0);
1920 enable = name == "aboutlyx"
1921 || name == "file" //FIXME: should be removed.
1923 || name == "texinfo"
1924 || name == "progress"
1925 || name == "compare";
1926 else if (name == "character" || name == "symbols"
1927 || name == "mathdelimiter" || name == "mathmatrix") {
1928 if (!buf || buf->isReadonly())
1931 Cursor const & cur = currentBufferView()->cursor();
1932 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1935 else if (name == "latexlog")
1936 enable = FileName(doc_buffer->logName()).isReadableFile();
1937 else if (name == "spellchecker")
1938 enable = theSpellChecker()
1939 && !doc_buffer->isReadonly()
1940 && !doc_buffer->text().empty();
1941 else if (name == "vclog")
1942 enable = doc_buffer->lyxvc().inUse();
1946 case LFUN_DIALOG_UPDATE: {
1947 string const name = cmd.getArg(0);
1949 enable = name == "prefs";
1953 case LFUN_COMMAND_EXECUTE:
1955 case LFUN_MENU_OPEN:
1956 // Nothing to check.
1959 case LFUN_COMPLETION_INLINE:
1960 if (!d.current_work_area_
1961 || !d.current_work_area_->completer().inlinePossible(
1962 currentBufferView()->cursor()))
1966 case LFUN_COMPLETION_POPUP:
1967 if (!d.current_work_area_
1968 || !d.current_work_area_->completer().popupPossible(
1969 currentBufferView()->cursor()))
1974 if (!d.current_work_area_
1975 || !d.current_work_area_->completer().inlinePossible(
1976 currentBufferView()->cursor()))
1980 case LFUN_COMPLETION_ACCEPT:
1981 if (!d.current_work_area_
1982 || (!d.current_work_area_->completer().popupVisible()
1983 && !d.current_work_area_->completer().inlineVisible()
1984 && !d.current_work_area_->completer().completionAvailable()))
1988 case LFUN_COMPLETION_CANCEL:
1989 if (!d.current_work_area_
1990 || (!d.current_work_area_->completer().popupVisible()
1991 && !d.current_work_area_->completer().inlineVisible()))
1995 case LFUN_BUFFER_ZOOM_OUT:
1996 case LFUN_BUFFER_ZOOM_IN: {
1997 // only diff between these two is that the default for ZOOM_OUT
1999 bool const neg_zoom =
2000 convert<int>(cmd.argument()) < 0 ||
2001 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2002 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2003 docstring const msg =
2004 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2008 enable = doc_buffer;
2011 case LFUN_BUFFER_MOVE_NEXT:
2012 case LFUN_BUFFER_MOVE_PREVIOUS:
2013 // we do not cycle when moving
2014 case LFUN_BUFFER_NEXT:
2015 case LFUN_BUFFER_PREVIOUS:
2016 // because we cycle, it doesn't matter whether on first or last
2017 enable = (d.currentTabWorkArea()->count() > 1);
2019 case LFUN_BUFFER_SWITCH:
2020 // toggle on the current buffer, but do not toggle off
2021 // the other ones (is that a good idea?)
2023 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2024 flag.setOnOff(true);
2027 case LFUN_VC_REGISTER:
2028 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2030 case LFUN_VC_RENAME:
2031 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2034 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2036 case LFUN_VC_CHECK_IN:
2037 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2039 case LFUN_VC_CHECK_OUT:
2040 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2042 case LFUN_VC_LOCKING_TOGGLE:
2043 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2044 && doc_buffer->lyxvc().lockingToggleEnabled();
2045 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2047 case LFUN_VC_REVERT:
2048 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2049 && !doc_buffer->hasReadonlyFlag();
2051 case LFUN_VC_UNDO_LAST:
2052 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2054 case LFUN_VC_REPO_UPDATE:
2055 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2057 case LFUN_VC_COMMAND: {
2058 if (cmd.argument().empty())
2060 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2064 case LFUN_VC_COMPARE:
2065 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2068 case LFUN_SERVER_GOTO_FILE_ROW:
2069 case LFUN_LYX_ACTIVATE:
2071 case LFUN_FORWARD_SEARCH:
2072 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2075 case LFUN_FILE_INSERT_PLAINTEXT:
2076 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2077 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2080 case LFUN_SPELLING_CONTINUOUSLY:
2081 flag.setOnOff(lyxrc.spellcheck_continuously);
2089 flag.setEnabled(false);
2095 static FileName selectTemplateFile()
2097 FileDialog dlg(qt_("Select template file"));
2098 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2099 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2101 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2102 QStringList(qt_("LyX Documents (*.lyx)")));
2104 if (result.first == FileDialog::Later)
2106 if (result.second.isEmpty())
2108 return FileName(fromqstr(result.second));
2112 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2116 Buffer * newBuffer = 0;
2118 newBuffer = checkAndLoadLyXFile(filename);
2119 } catch (ExceptionMessage const & e) {
2126 message(_("Document not loaded."));
2130 setBuffer(newBuffer);
2131 newBuffer->errors("Parse");
2134 theSession().lastFiles().add(filename);
2140 void GuiView::openDocument(string const & fname)
2142 string initpath = lyxrc.document_path;
2144 if (documentBufferView()) {
2145 string const trypath = documentBufferView()->buffer().filePath();
2146 // If directory is writeable, use this as default.
2147 if (FileName(trypath).isDirWritable())
2153 if (fname.empty()) {
2154 FileDialog dlg(qt_("Select document to open"));
2155 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2156 dlg.setButton2(qt_("Examples|#E#e"),
2157 toqstr(addPath(package().system_support().absFileName(), "examples")));
2159 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2160 FileDialog::Result result =
2161 dlg.open(toqstr(initpath), filter);
2163 if (result.first == FileDialog::Later)
2166 filename = fromqstr(result.second);
2168 // check selected filename
2169 if (filename.empty()) {
2170 message(_("Canceled."));
2176 // get absolute path of file and add ".lyx" to the filename if
2178 FileName const fullname =
2179 fileSearch(string(), filename, "lyx", support::may_not_exist);
2180 if (!fullname.empty())
2181 filename = fullname.absFileName();
2183 if (!fullname.onlyPath().isDirectory()) {
2184 Alert::warning(_("Invalid filename"),
2185 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2186 from_utf8(fullname.absFileName())));
2190 // if the file doesn't exist and isn't already open (bug 6645),
2191 // let the user create one
2192 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2193 !LyXVC::file_not_found_hook(fullname)) {
2194 // the user specifically chose this name. Believe him.
2195 Buffer * const b = newFile(filename, string(), true);
2201 docstring const disp_fn = makeDisplayPath(filename);
2202 message(bformat(_("Opening document %1$s..."), disp_fn));
2205 Buffer * buf = loadDocument(fullname);
2207 str2 = bformat(_("Document %1$s opened."), disp_fn);
2208 if (buf->lyxvc().inUse())
2209 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2210 " " + _("Version control detected.");
2212 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2217 // FIXME: clean that
2218 static bool import(GuiView * lv, FileName const & filename,
2219 string const & format, ErrorList & errorList)
2221 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2223 string loader_format;
2224 vector<string> loaders = theConverters().loaders();
2225 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2226 vector<string>::const_iterator it = loaders.begin();
2227 vector<string>::const_iterator en = loaders.end();
2228 for (; it != en; ++it) {
2229 if (!theConverters().isReachable(format, *it))
2232 string const tofile =
2233 support::changeExtension(filename.absFileName(),
2234 theFormats().extension(*it));
2235 if (!theConverters().convert(0, filename, FileName(tofile),
2236 filename, format, *it, errorList))
2238 loader_format = *it;
2241 if (loader_format.empty()) {
2242 frontend::Alert::error(_("Couldn't import file"),
2243 bformat(_("No information for importing the format %1$s."),
2244 theFormats().prettyName(format)));
2248 loader_format = format;
2250 if (loader_format == "lyx") {
2251 Buffer * buf = lv->loadDocument(lyxfile);
2255 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2259 bool as_paragraphs = loader_format == "textparagraph";
2260 string filename2 = (loader_format == format) ? filename.absFileName()
2261 : support::changeExtension(filename.absFileName(),
2262 theFormats().extension(loader_format));
2263 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2265 guiApp->setCurrentView(lv);
2266 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2273 void GuiView::importDocument(string const & argument)
2276 string filename = split(argument, format, ' ');
2278 LYXERR(Debug::INFO, format << " file: " << filename);
2280 // need user interaction
2281 if (filename.empty()) {
2282 string initpath = lyxrc.document_path;
2283 if (documentBufferView()) {
2284 string const trypath = documentBufferView()->buffer().filePath();
2285 // If directory is writeable, use this as default.
2286 if (FileName(trypath).isDirWritable())
2290 docstring const text = bformat(_("Select %1$s file to import"),
2291 theFormats().prettyName(format));
2293 FileDialog dlg(toqstr(text));
2294 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2295 dlg.setButton2(qt_("Examples|#E#e"),
2296 toqstr(addPath(package().system_support().absFileName(), "examples")));
2298 docstring filter = theFormats().prettyName(format);
2301 filter += from_utf8(theFormats().extensions(format));
2304 FileDialog::Result result =
2305 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2307 if (result.first == FileDialog::Later)
2310 filename = fromqstr(result.second);
2312 // check selected filename
2313 if (filename.empty())
2314 message(_("Canceled."));
2317 if (filename.empty())
2320 // get absolute path of file
2321 FileName const fullname(support::makeAbsPath(filename));
2323 // Can happen if the user entered a path into the dialog
2325 if (fullname.onlyFileName().empty()) {
2326 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2327 "Aborting import."),
2328 from_utf8(fullname.absFileName()));
2329 frontend::Alert::error(_("File name error"), msg);
2330 message(_("Canceled."));
2335 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2337 // Check if the document already is open
2338 Buffer * buf = theBufferList().getBuffer(lyxfile);
2341 if (!closeBuffer()) {
2342 message(_("Canceled."));
2347 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2349 // if the file exists already, and we didn't do
2350 // -i lyx thefile.lyx, warn
2351 if (lyxfile.exists() && fullname != lyxfile) {
2353 docstring text = bformat(_("The document %1$s already exists.\n\n"
2354 "Do you want to overwrite that document?"), displaypath);
2355 int const ret = Alert::prompt(_("Overwrite document?"),
2356 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2359 message(_("Canceled."));
2364 message(bformat(_("Importing %1$s..."), displaypath));
2365 ErrorList errorList;
2366 if (import(this, fullname, format, errorList))
2367 message(_("imported."));
2369 message(_("file not imported!"));
2371 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2375 void GuiView::newDocument(string const & filename, bool from_template)
2377 FileName initpath(lyxrc.document_path);
2378 if (documentBufferView()) {
2379 FileName const trypath(documentBufferView()->buffer().filePath());
2380 // If directory is writeable, use this as default.
2381 if (trypath.isDirWritable())
2385 string templatefile;
2386 if (from_template) {
2387 templatefile = selectTemplateFile().absFileName();
2388 if (templatefile.empty())
2393 if (filename.empty())
2394 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2396 b = newFile(filename, templatefile, true);
2401 // If no new document could be created, it is unsure
2402 // whether there is a valid BufferView.
2403 if (currentBufferView())
2404 // Ensure the cursor is correctly positioned on screen.
2405 currentBufferView()->showCursor();
2409 void GuiView::insertLyXFile(docstring const & fname)
2411 BufferView * bv = documentBufferView();
2416 FileName filename(to_utf8(fname));
2417 if (filename.empty()) {
2418 // Launch a file browser
2420 string initpath = lyxrc.document_path;
2421 string const trypath = bv->buffer().filePath();
2422 // If directory is writeable, use this as default.
2423 if (FileName(trypath).isDirWritable())
2427 FileDialog dlg(qt_("Select LyX document to insert"));
2428 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2429 dlg.setButton2(qt_("Examples|#E#e"),
2430 toqstr(addPath(package().system_support().absFileName(),
2433 FileDialog::Result result = dlg.open(toqstr(initpath),
2434 QStringList(qt_("LyX Documents (*.lyx)")));
2436 if (result.first == FileDialog::Later)
2440 filename.set(fromqstr(result.second));
2442 // check selected filename
2443 if (filename.empty()) {
2444 // emit message signal.
2445 message(_("Canceled."));
2450 bv->insertLyXFile(filename);
2451 bv->buffer().errors("Parse");
2455 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2457 FileName fname = b.fileName();
2458 FileName const oldname = fname;
2460 if (!newname.empty()) {
2462 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2464 // Switch to this Buffer.
2467 // No argument? Ask user through dialog.
2469 FileDialog dlg(qt_("Choose a filename to save document as"));
2470 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2471 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2473 if (!isLyXFileName(fname.absFileName()))
2474 fname.changeExtension(".lyx");
2476 FileDialog::Result result =
2477 dlg.save(toqstr(fname.onlyPath().absFileName()),
2478 QStringList(qt_("LyX Documents (*.lyx)")),
2479 toqstr(fname.onlyFileName()));
2481 if (result.first == FileDialog::Later)
2484 fname.set(fromqstr(result.second));
2489 if (!isLyXFileName(fname.absFileName()))
2490 fname.changeExtension(".lyx");
2493 // fname is now the new Buffer location.
2495 // if there is already a Buffer open with this name, we do not want
2496 // to have another one. (the second test makes sure we're not just
2497 // trying to overwrite ourselves, which is fine.)
2498 if (theBufferList().exists(fname) && fname != oldname
2499 && theBufferList().getBuffer(fname) != &b) {
2500 docstring const text =
2501 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2502 "Please close it before attempting to overwrite it.\n"
2503 "Do you want to choose a new filename?"),
2504 from_utf8(fname.absFileName()));
2505 int const ret = Alert::prompt(_("Chosen File Already Open"),
2506 text, 0, 1, _("&Rename"), _("&Cancel"));
2508 case 0: return renameBuffer(b, docstring(), kind);
2509 case 1: return false;
2514 bool const existsLocal = fname.exists();
2515 bool const existsInVC = LyXVC::fileInVC(fname);
2516 if (existsLocal || existsInVC) {
2517 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2518 if (kind != LV_WRITE_AS && existsInVC) {
2519 // renaming to a name that is already in VC
2521 docstring text = bformat(_("The document %1$s "
2522 "is already registered.\n\n"
2523 "Do you want to choose a new name?"),
2525 docstring const title = (kind == LV_VC_RENAME) ?
2526 _("Rename document?") : _("Copy document?");
2527 docstring const button = (kind == LV_VC_RENAME) ?
2528 _("&Rename") : _("&Copy");
2529 int const ret = Alert::prompt(title, text, 0, 1,
2530 button, _("&Cancel"));
2532 case 0: return renameBuffer(b, docstring(), kind);
2533 case 1: return false;
2538 docstring text = bformat(_("The document %1$s "
2539 "already exists.\n\n"
2540 "Do you want to overwrite that document?"),
2542 int const ret = Alert::prompt(_("Overwrite document?"),
2543 text, 0, 2, _("&Overwrite"),
2544 _("&Rename"), _("&Cancel"));
2547 case 1: return renameBuffer(b, docstring(), kind);
2548 case 2: return false;
2554 case LV_VC_RENAME: {
2555 string msg = b.lyxvc().rename(fname);
2558 message(from_utf8(msg));
2562 string msg = b.lyxvc().copy(fname);
2565 message(from_utf8(msg));
2571 // LyXVC created the file already in case of LV_VC_RENAME or
2572 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2573 // relative paths of included stuff right if we moved e.g. from
2574 // /a/b.lyx to /a/c/b.lyx.
2576 bool const saved = saveBuffer(b, fname);
2583 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2585 FileName fname = b.fileName();
2587 FileDialog dlg(qt_("Choose a filename to export the document as"));
2588 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2591 QString const anyformat = qt_("Guess from extension (*.*)");
2594 vector<Format const *> export_formats;
2595 for (Format const & f : theFormats())
2596 if (f.documentFormat())
2597 export_formats.push_back(&f);
2598 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2599 map<QString, string> fmap;
2602 for (Format const * f : export_formats) {
2603 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2604 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2606 from_ascii(f->extension())));
2607 types << loc_filter;
2608 fmap[loc_filter] = f->name();
2609 if (from_ascii(f->name()) == iformat) {
2610 filter = loc_filter;
2611 ext = f->extension();
2614 string ofname = fname.onlyFileName();
2616 ofname = support::changeExtension(ofname, ext);
2617 FileDialog::Result result =
2618 dlg.save(toqstr(fname.onlyPath().absFileName()),
2622 if (result.first != FileDialog::Chosen)
2626 fname.set(fromqstr(result.second));
2627 if (filter == anyformat)
2628 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2630 fmt_name = fmap[filter];
2631 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2632 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2634 if (fmt_name.empty() || fname.empty())
2637 // fname is now the new Buffer location.
2638 if (FileName(fname).exists()) {
2639 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2640 docstring text = bformat(_("The document %1$s already "
2641 "exists.\n\nDo you want to "
2642 "overwrite that document?"),
2644 int const ret = Alert::prompt(_("Overwrite document?"),
2645 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2648 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2649 case 2: return false;
2653 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2656 return dr.dispatched();
2660 bool GuiView::saveBuffer(Buffer & b)
2662 return saveBuffer(b, FileName());
2666 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2668 if (workArea(b) && workArea(b)->inDialogMode())
2671 if (fn.empty() && b.isUnnamed())
2672 return renameBuffer(b, docstring());
2674 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2676 theSession().lastFiles().add(b.fileName());
2680 // Switch to this Buffer.
2683 // FIXME: we don't tell the user *WHY* the save failed !!
2684 docstring const file = makeDisplayPath(b.absFileName(), 30);
2685 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2686 "Do you want to rename the document and "
2687 "try again?"), file);
2688 int const ret = Alert::prompt(_("Rename and save?"),
2689 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2692 if (!renameBuffer(b, docstring()))
2701 return saveBuffer(b, fn);
2705 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2707 return closeWorkArea(wa, false);
2711 // We only want to close the buffer if it is not visible in other workareas
2712 // of the same view, nor in other views, and if this is not a child
2713 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2715 Buffer & buf = wa->bufferView().buffer();
2717 bool last_wa = d.countWorkAreasOf(buf) == 1
2718 && !inOtherView(buf) && !buf.parent();
2720 bool close_buffer = last_wa;
2723 if (lyxrc.close_buffer_with_last_view == "yes")
2725 else if (lyxrc.close_buffer_with_last_view == "no")
2726 close_buffer = false;
2729 if (buf.isUnnamed())
2730 file = from_utf8(buf.fileName().onlyFileName());
2732 file = buf.fileName().displayName(30);
2733 docstring const text = bformat(
2734 _("Last view on document %1$s is being closed.\n"
2735 "Would you like to close or hide the document?\n"
2737 "Hidden documents can be displayed back through\n"
2738 "the menu: View->Hidden->...\n"
2740 "To remove this question, set your preference in:\n"
2741 " Tools->Preferences->Look&Feel->UserInterface\n"
2743 int ret = Alert::prompt(_("Close or hide document?"),
2744 text, 0, 1, _("&Close"), _("&Hide"));
2745 close_buffer = (ret == 0);
2749 return closeWorkArea(wa, close_buffer);
2753 bool GuiView::closeBuffer()
2755 GuiWorkArea * wa = currentMainWorkArea();
2756 // coverity complained about this
2757 // it seems unnecessary, but perhaps is worth the check
2758 LASSERT(wa, return false);
2760 setCurrentWorkArea(wa);
2761 Buffer & buf = wa->bufferView().buffer();
2762 return closeWorkArea(wa, !buf.parent());
2766 void GuiView::writeSession() const {
2767 GuiWorkArea const * active_wa = currentMainWorkArea();
2768 for (int i = 0; i < d.splitter_->count(); ++i) {
2769 TabWorkArea * twa = d.tabWorkArea(i);
2770 for (int j = 0; j < twa->count(); ++j) {
2771 GuiWorkArea * wa = twa->workArea(j);
2772 Buffer & buf = wa->bufferView().buffer();
2773 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2779 bool GuiView::closeBufferAll()
2781 // Close the workareas in all other views
2782 QList<int> const ids = guiApp->viewIds();
2783 for (int i = 0; i != ids.size(); ++i) {
2784 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2788 // Close our own workareas
2789 if (!closeWorkAreaAll())
2792 // Now close the hidden buffers. We prevent hidden buffers from being
2793 // dirty, so we can just close them.
2794 theBufferList().closeAll();
2799 bool GuiView::closeWorkAreaAll()
2801 setCurrentWorkArea(currentMainWorkArea());
2803 // We might be in a situation that there is still a tabWorkArea, but
2804 // there are no tabs anymore. This can happen when we get here after a
2805 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2806 // many TabWorkArea's have no documents anymore.
2809 // We have to call count() each time, because it can happen that
2810 // more than one splitter will disappear in one iteration (bug 5998).
2811 while (d.splitter_->count() > empty_twa) {
2812 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2814 if (twa->count() == 0)
2817 setCurrentWorkArea(twa->currentWorkArea());
2818 if (!closeTabWorkArea(twa))
2826 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2831 Buffer & buf = wa->bufferView().buffer();
2833 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2834 Alert::warning(_("Close document"),
2835 _("Document could not be closed because it is being processed by LyX."));
2840 return closeBuffer(buf);
2842 if (!inMultiTabs(wa))
2843 if (!saveBufferIfNeeded(buf, true))
2851 bool GuiView::closeBuffer(Buffer & buf)
2853 // If we are in a close_event all children will be closed in some time,
2854 // so no need to do it here. This will ensure that the children end up
2855 // in the session file in the correct order. If we close the master
2856 // buffer, we can close or release the child buffers here too.
2857 bool success = true;
2859 ListOfBuffers clist = buf.getChildren();
2860 ListOfBuffers::const_iterator it = clist.begin();
2861 ListOfBuffers::const_iterator const bend = clist.end();
2862 for (; it != bend; ++it) {
2863 Buffer * child_buf = *it;
2864 if (theBufferList().isOthersChild(&buf, child_buf)) {
2865 child_buf->setParent(0);
2869 // FIXME: should we look in other tabworkareas?
2870 // ANSWER: I don't think so. I've tested, and if the child is
2871 // open in some other window, it closes without a problem.
2872 GuiWorkArea * child_wa = workArea(*child_buf);
2874 success = closeWorkArea(child_wa, true);
2878 // In this case the child buffer is open but hidden.
2879 // It therefore should not (MUST NOT) be dirty!
2880 LATTEST(child_buf->isClean());
2881 theBufferList().release(child_buf);
2886 // goto bookmark to update bookmark pit.
2887 // FIXME: we should update only the bookmarks related to this buffer!
2888 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2889 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2890 guiApp->gotoBookmark(i+1, false, false);
2892 if (saveBufferIfNeeded(buf, false)) {
2893 buf.removeAutosaveFile();
2894 theBufferList().release(&buf);
2898 // open all children again to avoid a crash because of dangling
2899 // pointers (bug 6603)
2905 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2907 while (twa == d.currentTabWorkArea()) {
2908 twa->setCurrentIndex(twa->count() - 1);
2910 GuiWorkArea * wa = twa->currentWorkArea();
2911 Buffer & b = wa->bufferView().buffer();
2913 // We only want to close the buffer if the same buffer is not visible
2914 // in another view, and if this is not a child and if we are closing
2915 // a view (not a tabgroup).
2916 bool const close_buffer =
2917 !inOtherView(b) && !b.parent() && closing_;
2919 if (!closeWorkArea(wa, close_buffer))
2926 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2928 if (buf.isClean() || buf.paragraphs().empty())
2931 // Switch to this Buffer.
2936 if (buf.isUnnamed())
2937 file = from_utf8(buf.fileName().onlyFileName());
2939 file = buf.fileName().displayName(30);
2941 // Bring this window to top before asking questions.
2946 if (hiding && buf.isUnnamed()) {
2947 docstring const text = bformat(_("The document %1$s has not been "
2948 "saved yet.\n\nDo you want to save "
2949 "the document?"), file);
2950 ret = Alert::prompt(_("Save new document?"),
2951 text, 0, 1, _("&Save"), _("&Cancel"));
2955 docstring const text = bformat(_("The document %1$s has unsaved changes."
2956 "\n\nDo you want to save the document or discard the changes?"), file);
2957 ret = Alert::prompt(_("Save changed document?"),
2958 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2963 if (!saveBuffer(buf))
2967 // If we crash after this we could have no autosave file
2968 // but I guess this is really improbable (Jug).
2969 // Sometimes improbable things happen:
2970 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2971 // buf.removeAutosaveFile();
2973 // revert all changes
2984 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2986 Buffer & buf = wa->bufferView().buffer();
2988 for (int i = 0; i != d.splitter_->count(); ++i) {
2989 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2990 if (wa_ && wa_ != wa)
2993 return inOtherView(buf);
2997 bool GuiView::inOtherView(Buffer & buf)
2999 QList<int> const ids = guiApp->viewIds();
3001 for (int i = 0; i != ids.size(); ++i) {
3005 if (guiApp->view(ids[i]).workArea(buf))
3012 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3014 if (!documentBufferView())
3017 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3018 Buffer * const curbuf = &documentBufferView()->buffer();
3019 int nwa = twa->count();
3020 for (int i = 0; i < nwa; ++i) {
3021 if (&workArea(i)->bufferView().buffer() == curbuf) {
3023 if (np == NEXTBUFFER)
3024 next_index = (i == nwa - 1 ? 0 : i + 1);
3026 next_index = (i == 0 ? nwa - 1 : i - 1);
3028 twa->moveTab(i, next_index);
3030 setBuffer(&workArea(next_index)->bufferView().buffer());
3038 /// make sure the document is saved
3039 static bool ensureBufferClean(Buffer * buffer)
3041 LASSERT(buffer, return false);
3042 if (buffer->isClean() && !buffer->isUnnamed())
3045 docstring const file = buffer->fileName().displayName(30);
3048 if (!buffer->isUnnamed()) {
3049 text = bformat(_("The document %1$s has unsaved "
3050 "changes.\n\nDo you want to save "
3051 "the document?"), file);
3052 title = _("Save changed document?");
3055 text = bformat(_("The document %1$s has not been "
3056 "saved yet.\n\nDo you want to save "
3057 "the document?"), file);
3058 title = _("Save new document?");
3060 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3063 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3065 return buffer->isClean() && !buffer->isUnnamed();
3069 bool GuiView::reloadBuffer(Buffer & buf)
3071 Buffer::ReadStatus status = buf.reload();
3072 return status == Buffer::ReadSuccess;
3076 void GuiView::checkExternallyModifiedBuffers()
3078 BufferList::iterator bit = theBufferList().begin();
3079 BufferList::iterator const bend = theBufferList().end();
3080 for (; bit != bend; ++bit) {
3081 Buffer * buf = *bit;
3082 if (buf->fileName().exists() && buf->isChecksumModified()) {
3083 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3084 " Reload now? Any local changes will be lost."),
3085 from_utf8(buf->absFileName()));
3086 int const ret = Alert::prompt(_("Reload externally changed document?"),
3087 text, 0, 1, _("&Reload"), _("&Cancel"));
3095 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3097 Buffer * buffer = documentBufferView()
3098 ? &(documentBufferView()->buffer()) : 0;
3100 switch (cmd.action()) {
3101 case LFUN_VC_REGISTER:
3102 if (!buffer || !ensureBufferClean(buffer))
3104 if (!buffer->lyxvc().inUse()) {
3105 if (buffer->lyxvc().registrer()) {
3106 reloadBuffer(*buffer);
3107 dr.clearMessageUpdate();
3112 case LFUN_VC_RENAME:
3113 case LFUN_VC_COPY: {
3114 if (!buffer || !ensureBufferClean(buffer))
3116 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3117 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3118 // Some changes are not yet committed.
3119 // We test here and not in getStatus(), since
3120 // this test is expensive.
3122 LyXVC::CommandResult ret =
3123 buffer->lyxvc().checkIn(log);
3125 if (ret == LyXVC::ErrorCommand ||
3126 ret == LyXVC::VCSuccess)
3127 reloadBuffer(*buffer);
3128 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3129 frontend::Alert::error(
3130 _("Revision control error."),
3131 _("Document could not be checked in."));
3135 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3136 LV_VC_RENAME : LV_VC_COPY;
3137 renameBuffer(*buffer, cmd.argument(), kind);
3142 case LFUN_VC_CHECK_IN:
3143 if (!buffer || !ensureBufferClean(buffer))
3145 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3147 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3149 // Only skip reloading if the checkin was cancelled or
3150 // an error occurred before the real checkin VCS command
3151 // was executed, since the VCS might have changed the
3152 // file even if it could not checkin successfully.
3153 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3154 reloadBuffer(*buffer);
3158 case LFUN_VC_CHECK_OUT:
3159 if (!buffer || !ensureBufferClean(buffer))
3161 if (buffer->lyxvc().inUse()) {
3162 dr.setMessage(buffer->lyxvc().checkOut());
3163 reloadBuffer(*buffer);
3167 case LFUN_VC_LOCKING_TOGGLE:
3168 LASSERT(buffer, return);
3169 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3171 if (buffer->lyxvc().inUse()) {
3172 string res = buffer->lyxvc().lockingToggle();
3174 frontend::Alert::error(_("Revision control error."),
3175 _("Error when setting the locking property."));
3178 reloadBuffer(*buffer);
3183 case LFUN_VC_REVERT:
3184 LASSERT(buffer, return);
3185 if (buffer->lyxvc().revert()) {
3186 reloadBuffer(*buffer);
3187 dr.clearMessageUpdate();
3191 case LFUN_VC_UNDO_LAST:
3192 LASSERT(buffer, return);
3193 buffer->lyxvc().undoLast();
3194 reloadBuffer(*buffer);
3195 dr.clearMessageUpdate();
3198 case LFUN_VC_REPO_UPDATE:
3199 LASSERT(buffer, return);
3200 if (ensureBufferClean(buffer)) {
3201 dr.setMessage(buffer->lyxvc().repoUpdate());
3202 checkExternallyModifiedBuffers();
3206 case LFUN_VC_COMMAND: {
3207 string flag = cmd.getArg(0);
3208 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3211 if (contains(flag, 'M')) {
3212 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3215 string path = cmd.getArg(1);
3216 if (contains(path, "$$p") && buffer)
3217 path = subst(path, "$$p", buffer->filePath());
3218 LYXERR(Debug::LYXVC, "Directory: " << path);
3220 if (!pp.isReadableDirectory()) {
3221 lyxerr << _("Directory is not accessible.") << endl;
3224 support::PathChanger p(pp);
3226 string command = cmd.getArg(2);
3227 if (command.empty())
3230 command = subst(command, "$$i", buffer->absFileName());
3231 command = subst(command, "$$p", buffer->filePath());
3233 command = subst(command, "$$m", to_utf8(message));
3234 LYXERR(Debug::LYXVC, "Command: " << command);
3236 one.startscript(Systemcall::Wait, command);
3240 if (contains(flag, 'I'))
3241 buffer->markDirty();
3242 if (contains(flag, 'R'))
3243 reloadBuffer(*buffer);
3248 case LFUN_VC_COMPARE: {
3249 if (cmd.argument().empty()) {
3250 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3254 string rev1 = cmd.getArg(0);
3258 // it seems safe to assume we have a buffer
3259 // coverity[FORWARD_NULL]
3260 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3263 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3264 f2 = buffer->absFileName();
3266 string rev2 = cmd.getArg(1);
3270 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3274 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3275 f1 << "\n" << f2 << "\n" );
3276 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3277 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3287 void GuiView::openChildDocument(string const & fname)
3289 LASSERT(documentBufferView(), return);
3290 Buffer & buffer = documentBufferView()->buffer();
3291 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3292 documentBufferView()->saveBookmark(false);
3294 if (theBufferList().exists(filename)) {
3295 child = theBufferList().getBuffer(filename);
3298 message(bformat(_("Opening child document %1$s..."),
3299 makeDisplayPath(filename.absFileName())));
3300 child = loadDocument(filename, false);
3302 // Set the parent name of the child document.
3303 // This makes insertion of citations and references in the child work,
3304 // when the target is in the parent or another child document.
3306 child->setParent(&buffer);
3310 bool GuiView::goToFileRow(string const & argument)
3314 size_t i = argument.find_last_of(' ');
3315 if (i != string::npos) {
3316 file_name = os::internal_path(trim(argument.substr(0, i)));
3317 istringstream is(argument.substr(i + 1));
3322 if (i == string::npos) {
3323 LYXERR0("Wrong argument: " << argument);
3327 string const abstmp = package().temp_dir().absFileName();
3328 string const realtmp = package().temp_dir().realPath();
3329 // We have to use os::path_prefix_is() here, instead of
3330 // simply prefixIs(), because the file name comes from
3331 // an external application and may need case adjustment.
3332 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3333 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3334 // Needed by inverse dvi search. If it is a file
3335 // in tmpdir, call the apropriated function.
3336 // If tmpdir is a symlink, we may have the real
3337 // path passed back, so we correct for that.
3338 if (!prefixIs(file_name, abstmp))
3339 file_name = subst(file_name, realtmp, abstmp);
3340 buf = theBufferList().getBufferFromTmp(file_name);
3342 // Must replace extension of the file to be .lyx
3343 // and get full path
3344 FileName const s = fileSearch(string(),
3345 support::changeExtension(file_name, ".lyx"), "lyx");
3346 // Either change buffer or load the file
3347 if (theBufferList().exists(s))
3348 buf = theBufferList().getBuffer(s);
3349 else if (s.exists()) {
3350 buf = loadDocument(s);
3355 _("File does not exist: %1$s"),
3356 makeDisplayPath(file_name)));
3362 _("No buffer for file: %1$s."),
3363 makeDisplayPath(file_name))
3368 bool success = documentBufferView()->setCursorFromRow(row);
3370 LYXERR(Debug::LATEX,
3371 "setCursorFromRow: invalid position for row " << row);
3372 frontend::Alert::error(_("Inverse Search Failed"),
3373 _("Invalid position requested by inverse search.\n"
3374 "You may need to update the viewed document."));
3380 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3382 QMenu * menu = new QMenu;
3383 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3384 menu->exec(QCursor::pos());
3389 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3391 Buffer::ExportStatus const status = func(format);
3393 // the cloning operation will have produced a clone of the entire set of
3394 // documents, starting from the master. so we must delete those.
3395 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3397 busyBuffers.remove(orig);
3402 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3404 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3405 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3409 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3411 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3412 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3416 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3418 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3419 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3423 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3424 string const & argument,
3425 Buffer const * used_buffer,
3426 docstring const & msg,
3427 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3428 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3429 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3434 string format = argument;
3436 format = used_buffer->params().getDefaultOutputFormat();
3437 processing_format = format;
3439 progress_->clearMessages();
3442 #if EXPORT_in_THREAD
3443 GuiViewPrivate::busyBuffers.insert(used_buffer);
3444 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3445 if (!cloned_buffer) {
3446 Alert::error(_("Export Error"),
3447 _("Error cloning the Buffer."));
3450 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3455 setPreviewFuture(f);
3456 last_export_format = used_buffer->params().bufferFormat();
3459 // We are asynchronous, so we don't know here anything about the success
3462 Buffer::ExportStatus status;
3464 status = (used_buffer->*syncFunc)(format, true);
3465 } else if (previewFunc) {
3466 status = (used_buffer->*previewFunc)(format);
3469 handleExportStatus(gv_, status, format);
3471 return (status == Buffer::ExportSuccess
3472 || status == Buffer::PreviewSuccess);
3476 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3478 BufferView * bv = currentBufferView();
3479 LASSERT(bv, return);
3481 // Let the current BufferView dispatch its own actions.
3482 bv->dispatch(cmd, dr);
3483 if (dr.dispatched())
3486 // Try with the document BufferView dispatch if any.
3487 BufferView * doc_bv = documentBufferView();
3488 if (doc_bv && doc_bv != bv) {
3489 doc_bv->dispatch(cmd, dr);
3490 if (dr.dispatched())
3494 // Then let the current Cursor dispatch its own actions.
3495 bv->cursor().dispatch(cmd);
3497 // update completion. We do it here and not in
3498 // processKeySym to avoid another redraw just for a
3499 // changed inline completion
3500 if (cmd.origin() == FuncRequest::KEYBOARD) {
3501 if (cmd.action() == LFUN_SELF_INSERT
3502 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3503 updateCompletion(bv->cursor(), true, true);
3504 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3505 updateCompletion(bv->cursor(), false, true);
3507 updateCompletion(bv->cursor(), false, false);
3510 dr = bv->cursor().result();
3514 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3516 BufferView * bv = currentBufferView();
3517 // By default we won't need any update.
3518 dr.screenUpdate(Update::None);
3519 // assume cmd will be dispatched
3520 dr.dispatched(true);
3522 Buffer * doc_buffer = documentBufferView()
3523 ? &(documentBufferView()->buffer()) : 0;
3525 if (cmd.origin() == FuncRequest::TOC) {
3526 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3527 // FIXME: do we need to pass a DispatchResult object here?
3528 toc->doDispatch(bv->cursor(), cmd);
3532 string const argument = to_utf8(cmd.argument());
3534 switch(cmd.action()) {
3535 case LFUN_BUFFER_CHILD_OPEN:
3536 openChildDocument(to_utf8(cmd.argument()));
3539 case LFUN_BUFFER_IMPORT:
3540 importDocument(to_utf8(cmd.argument()));
3543 case LFUN_BUFFER_EXPORT: {
3546 // GCC only sees strfwd.h when building merged
3547 if (::lyx::operator==(cmd.argument(), "custom")) {
3548 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3552 string const dest = cmd.getArg(1);
3553 FileName target_dir;
3554 if (!dest.empty() && FileName::isAbsolute(dest))
3555 target_dir = FileName(support::onlyPath(dest));
3557 target_dir = doc_buffer->fileName().onlyPath();
3559 string const format = (argument.empty() || argument == "default") ?
3560 doc_buffer->params().getDefaultOutputFormat() : argument;
3562 if ((dest.empty() && doc_buffer->isUnnamed())
3563 || !target_dir.isDirWritable()) {
3564 exportBufferAs(*doc_buffer, from_utf8(format));
3567 /* TODO/Review: Is it a problem to also export the children?
3568 See the update_unincluded flag */
3569 d.asyncBufferProcessing(format,
3572 &GuiViewPrivate::exportAndDestroy,
3575 // TODO Inform user about success
3579 case LFUN_BUFFER_EXPORT_AS: {
3580 LASSERT(doc_buffer, break);
3581 docstring f = cmd.argument();
3583 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3584 exportBufferAs(*doc_buffer, f);
3588 case LFUN_BUFFER_UPDATE: {
3589 d.asyncBufferProcessing(argument,
3592 &GuiViewPrivate::compileAndDestroy,
3597 case LFUN_BUFFER_VIEW: {
3598 d.asyncBufferProcessing(argument,
3600 _("Previewing ..."),
3601 &GuiViewPrivate::previewAndDestroy,
3606 case LFUN_MASTER_BUFFER_UPDATE: {
3607 d.asyncBufferProcessing(argument,
3608 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3610 &GuiViewPrivate::compileAndDestroy,
3615 case LFUN_MASTER_BUFFER_VIEW: {
3616 d.asyncBufferProcessing(argument,
3617 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3619 &GuiViewPrivate::previewAndDestroy,
3620 0, &Buffer::preview);
3623 case LFUN_BUFFER_SWITCH: {
3624 string const file_name = to_utf8(cmd.argument());
3625 if (!FileName::isAbsolute(file_name)) {
3627 dr.setMessage(_("Absolute filename expected."));
3631 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3634 dr.setMessage(_("Document not loaded"));
3638 // Do we open or switch to the buffer in this view ?
3639 if (workArea(*buffer)
3640 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3645 // Look for the buffer in other views
3646 QList<int> const ids = guiApp->viewIds();
3648 for (; i != ids.size(); ++i) {
3649 GuiView & gv = guiApp->view(ids[i]);
3650 if (gv.workArea(*buffer)) {
3652 gv.activateWindow();
3654 gv.setBuffer(buffer);
3659 // If necessary, open a new window as a last resort
3660 if (i == ids.size()) {
3661 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3667 case LFUN_BUFFER_NEXT:
3668 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3671 case LFUN_BUFFER_MOVE_NEXT:
3672 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3675 case LFUN_BUFFER_PREVIOUS:
3676 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3679 case LFUN_BUFFER_MOVE_PREVIOUS:
3680 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3683 case LFUN_COMMAND_EXECUTE: {
3684 command_execute_ = true;
3685 minibuffer_focus_ = true;
3688 case LFUN_DROP_LAYOUTS_CHOICE:
3689 d.layout_->showPopup();
3692 case LFUN_MENU_OPEN:
3693 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3694 menu->exec(QCursor::pos());
3697 case LFUN_FILE_INSERT:
3698 insertLyXFile(cmd.argument());
3701 case LFUN_FILE_INSERT_PLAINTEXT:
3702 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3703 string const fname = to_utf8(cmd.argument());
3704 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3705 dr.setMessage(_("Absolute filename expected."));
3709 FileName filename(fname);
3710 if (fname.empty()) {
3711 FileDialog dlg(qt_("Select file to insert"));
3713 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3714 QStringList(qt_("All Files (*)")));
3716 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3717 dr.setMessage(_("Canceled."));
3721 filename.set(fromqstr(result.second));
3725 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3726 bv->dispatch(new_cmd, dr);
3731 case LFUN_BUFFER_RELOAD: {
3732 LASSERT(doc_buffer, break);
3735 if (!doc_buffer->isClean()) {
3736 docstring const file =
3737 makeDisplayPath(doc_buffer->absFileName(), 20);
3738 docstring text = doc_buffer->notifiesExternalModification() ?
3739 _("Any changes will be lost. "
3740 "Are you sure you want to load the version on disk "
3741 "of the document %1$s?")
3742 : _("Any changes will be lost. "
3743 "Are you sure you want to revert to the saved version "
3744 "of the document %1$s?");
3745 ret = Alert::prompt(_("Revert to file on disk?"),
3746 bformat(text, file), 1, 1, _("&Revert"), _("&Cancel"));
3750 doc_buffer->markClean();
3751 reloadBuffer(*doc_buffer);
3752 dr.forceBufferUpdate();
3757 case LFUN_BUFFER_WRITE:
3758 LASSERT(doc_buffer, break);
3759 saveBuffer(*doc_buffer);
3762 case LFUN_BUFFER_WRITE_AS:
3763 LASSERT(doc_buffer, break);
3764 renameBuffer(*doc_buffer, cmd.argument());
3767 case LFUN_BUFFER_WRITE_ALL: {
3768 Buffer * first = theBufferList().first();
3771 message(_("Saving all documents..."));
3772 // We cannot use a for loop as the buffer list cycles.
3775 if (!b->isClean()) {
3777 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3779 b = theBufferList().next(b);
3780 } while (b != first);
3781 dr.setMessage(_("All documents saved."));
3785 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3786 LASSERT(doc_buffer, break);
3787 doc_buffer->clearExternalModification();
3790 case LFUN_BUFFER_CLOSE:
3794 case LFUN_BUFFER_CLOSE_ALL:
3798 case LFUN_TOOLBAR_TOGGLE: {
3799 string const name = cmd.getArg(0);
3800 if (GuiToolbar * t = toolbar(name))
3805 case LFUN_ICON_SIZE: {
3806 QSize size = d.iconSize(cmd.argument());
3808 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3809 size.width(), size.height()));
3813 case LFUN_DIALOG_UPDATE: {
3814 string const name = to_utf8(cmd.argument());
3815 if (name == "prefs" || name == "document")
3816 updateDialog(name, string());
3817 else if (name == "paragraph")
3818 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3819 else if (currentBufferView()) {
3820 Inset * inset = currentBufferView()->editedInset(name);
3821 // Can only update a dialog connected to an existing inset
3823 // FIXME: get rid of this indirection; GuiView ask the inset
3824 // if he is kind enough to update itself...
3825 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3826 //FIXME: pass DispatchResult here?
3827 inset->dispatch(currentBufferView()->cursor(), fr);
3833 case LFUN_DIALOG_TOGGLE: {
3834 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3835 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3836 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3840 case LFUN_DIALOG_DISCONNECT_INSET:
3841 disconnectDialog(to_utf8(cmd.argument()));
3844 case LFUN_DIALOG_HIDE: {
3845 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3849 case LFUN_DIALOG_SHOW: {
3850 string const name = cmd.getArg(0);
3851 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3853 if (name == "character") {
3854 data = freefont2string();
3856 showDialog("character", data);
3857 } else if (name == "latexlog") {
3858 Buffer::LogType type;
3859 string const logfile = doc_buffer->logName(&type);
3861 case Buffer::latexlog:
3864 case Buffer::buildlog:
3868 data += Lexer::quoteString(logfile);
3869 showDialog("log", data);
3870 } else if (name == "vclog") {
3871 string const data = "vc " +
3872 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3873 showDialog("log", data);
3874 } else if (name == "symbols") {
3875 data = bv->cursor().getEncoding()->name();
3877 showDialog("symbols", data);
3879 } else if (name == "prefs" && isFullScreen()) {
3880 lfunUiToggle("fullscreen");
3881 showDialog("prefs", data);
3883 showDialog(name, data);
3888 dr.setMessage(cmd.argument());
3891 case LFUN_UI_TOGGLE: {
3892 string arg = cmd.getArg(0);
3893 if (!lfunUiToggle(arg)) {
3894 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3895 dr.setMessage(bformat(msg, from_utf8(arg)));
3897 // Make sure the keyboard focus stays in the work area.
3902 case LFUN_VIEW_SPLIT: {
3903 LASSERT(doc_buffer, break);
3904 string const orientation = cmd.getArg(0);
3905 d.splitter_->setOrientation(orientation == "vertical"
3906 ? Qt::Vertical : Qt::Horizontal);
3907 TabWorkArea * twa = addTabWorkArea();
3908 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3909 setCurrentWorkArea(wa);
3912 case LFUN_TAB_GROUP_CLOSE:
3913 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3914 closeTabWorkArea(twa);
3915 d.current_work_area_ = 0;
3916 twa = d.currentTabWorkArea();
3917 // Switch to the next GuiWorkArea in the found TabWorkArea.
3919 // Make sure the work area is up to date.
3920 setCurrentWorkArea(twa->currentWorkArea());
3922 setCurrentWorkArea(0);
3927 case LFUN_VIEW_CLOSE:
3928 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3929 closeWorkArea(twa->currentWorkArea());
3930 d.current_work_area_ = 0;
3931 twa = d.currentTabWorkArea();
3932 // Switch to the next GuiWorkArea in the found TabWorkArea.
3934 // Make sure the work area is up to date.
3935 setCurrentWorkArea(twa->currentWorkArea());
3937 setCurrentWorkArea(0);
3942 case LFUN_COMPLETION_INLINE:
3943 if (d.current_work_area_)
3944 d.current_work_area_->completer().showInline();
3947 case LFUN_COMPLETION_POPUP:
3948 if (d.current_work_area_)
3949 d.current_work_area_->completer().showPopup();
3954 if (d.current_work_area_)
3955 d.current_work_area_->completer().tab();
3958 case LFUN_COMPLETION_CANCEL:
3959 if (d.current_work_area_) {
3960 if (d.current_work_area_->completer().popupVisible())
3961 d.current_work_area_->completer().hidePopup();
3963 d.current_work_area_->completer().hideInline();
3967 case LFUN_COMPLETION_ACCEPT:
3968 if (d.current_work_area_)
3969 d.current_work_area_->completer().activate();
3972 case LFUN_BUFFER_ZOOM_IN:
3973 case LFUN_BUFFER_ZOOM_OUT: {
3974 // use a signed temp to avoid overflow
3975 int zoom = lyxrc.zoom;
3976 if (cmd.argument().empty()) {
3977 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3982 zoom += convert<int>(cmd.argument());
3984 if (zoom < static_cast<int>(zoom_min_))
3988 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3990 // The global QPixmapCache is used in GuiPainter to cache text
3991 // painting so we must reset it.
3992 QPixmapCache::clear();
3993 guiApp->fontLoader().update();
3994 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3998 case LFUN_VC_REGISTER:
3999 case LFUN_VC_RENAME:
4001 case LFUN_VC_CHECK_IN:
4002 case LFUN_VC_CHECK_OUT:
4003 case LFUN_VC_REPO_UPDATE:
4004 case LFUN_VC_LOCKING_TOGGLE:
4005 case LFUN_VC_REVERT:
4006 case LFUN_VC_UNDO_LAST:
4007 case LFUN_VC_COMMAND:
4008 case LFUN_VC_COMPARE:
4009 dispatchVC(cmd, dr);
4012 case LFUN_SERVER_GOTO_FILE_ROW:
4013 if(goToFileRow(to_utf8(cmd.argument())))
4014 dr.screenUpdate(Update::Force | Update::FitCursor);
4017 case LFUN_LYX_ACTIVATE:
4021 case LFUN_FORWARD_SEARCH: {
4022 // it seems safe to assume we have a document buffer, since
4023 // getStatus wants one.
4024 // coverity[FORWARD_NULL]
4025 Buffer const * doc_master = doc_buffer->masterBuffer();
4026 FileName const path(doc_master->temppath());
4027 string const texname = doc_master->isChild(doc_buffer)
4028 ? DocFileName(changeExtension(
4029 doc_buffer->absFileName(),
4030 "tex")).mangledFileName()
4031 : doc_buffer->latexName();
4032 string const fulltexname =
4033 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4034 string const mastername =
4035 removeExtension(doc_master->latexName());
4036 FileName const dviname(addName(path.absFileName(),
4037 addExtension(mastername, "dvi")));
4038 FileName const pdfname(addName(path.absFileName(),
4039 addExtension(mastername, "pdf")));
4040 bool const have_dvi = dviname.exists();
4041 bool const have_pdf = pdfname.exists();
4042 if (!have_dvi && !have_pdf) {
4043 dr.setMessage(_("Please, preview the document first."));
4046 string outname = dviname.onlyFileName();
4047 string command = lyxrc.forward_search_dvi;
4048 if (!have_dvi || (have_pdf &&
4049 pdfname.lastModified() > dviname.lastModified())) {
4050 outname = pdfname.onlyFileName();
4051 command = lyxrc.forward_search_pdf;
4054 DocIterator cur = bv->cursor();
4055 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4056 LYXERR(Debug::ACTION, "Forward search: row:" << row
4058 if (row == -1 || command.empty()) {
4059 dr.setMessage(_("Couldn't proceed."));
4062 string texrow = convert<string>(row);
4064 command = subst(command, "$$n", texrow);
4065 command = subst(command, "$$f", fulltexname);
4066 command = subst(command, "$$t", texname);
4067 command = subst(command, "$$o", outname);
4069 PathChanger p(path);
4071 one.startscript(Systemcall::DontWait, command);
4075 case LFUN_SPELLING_CONTINUOUSLY:
4076 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4077 dr.screenUpdate(Update::Force);
4081 // The LFUN must be for one of BufferView, Buffer or Cursor;
4083 dispatchToBufferView(cmd, dr);
4087 // Part of automatic menu appearance feature.
4088 if (isFullScreen()) {
4089 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4093 // Need to update bv because many LFUNs here might have destroyed it
4094 bv = currentBufferView();
4096 // Clear non-empty selections
4097 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4099 Cursor & cur = bv->cursor();
4100 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4101 cur.clearSelection();
4107 bool GuiView::lfunUiToggle(string const & ui_component)
4109 if (ui_component == "scrollbar") {
4110 // hide() is of no help
4111 if (d.current_work_area_->verticalScrollBarPolicy() ==
4112 Qt::ScrollBarAlwaysOff)
4114 d.current_work_area_->setVerticalScrollBarPolicy(
4115 Qt::ScrollBarAsNeeded);
4117 d.current_work_area_->setVerticalScrollBarPolicy(
4118 Qt::ScrollBarAlwaysOff);
4119 } else if (ui_component == "statusbar") {
4120 statusBar()->setVisible(!statusBar()->isVisible());
4121 } else if (ui_component == "menubar") {
4122 menuBar()->setVisible(!menuBar()->isVisible());
4124 if (ui_component == "frame") {
4126 getContentsMargins(&l, &t, &r, &b);
4127 //are the frames in default state?
4128 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4130 setContentsMargins(-2, -2, -2, -2);
4132 setContentsMargins(0, 0, 0, 0);
4135 if (ui_component == "fullscreen") {
4143 void GuiView::toggleFullScreen()
4145 if (isFullScreen()) {
4146 for (int i = 0; i != d.splitter_->count(); ++i)
4147 d.tabWorkArea(i)->setFullScreen(false);
4148 setContentsMargins(0, 0, 0, 0);
4149 setWindowState(windowState() ^ Qt::WindowFullScreen);
4152 statusBar()->show();
4155 hideDialogs("prefs", 0);
4156 for (int i = 0; i != d.splitter_->count(); ++i)
4157 d.tabWorkArea(i)->setFullScreen(true);
4158 setContentsMargins(-2, -2, -2, -2);
4160 setWindowState(windowState() ^ Qt::WindowFullScreen);
4161 if (lyxrc.full_screen_statusbar)
4162 statusBar()->hide();
4163 if (lyxrc.full_screen_menubar)
4165 if (lyxrc.full_screen_toolbars) {
4166 ToolbarMap::iterator end = d.toolbars_.end();
4167 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4172 // give dialogs like the TOC a chance to adapt
4177 Buffer const * GuiView::updateInset(Inset const * inset)
4182 Buffer const * inset_buffer = &(inset->buffer());
4184 for (int i = 0; i != d.splitter_->count(); ++i) {
4185 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4188 Buffer const * buffer = &(wa->bufferView().buffer());
4189 if (inset_buffer == buffer)
4190 wa->scheduleRedraw();
4192 return inset_buffer;
4196 void GuiView::restartCursor()
4198 /* When we move around, or type, it's nice to be able to see
4199 * the cursor immediately after the keypress.
4201 if (d.current_work_area_)
4202 d.current_work_area_->startBlinkingCursor();
4204 // Take this occasion to update the other GUI elements.
4210 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4212 if (d.current_work_area_)
4213 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4218 // This list should be kept in sync with the list of insets in
4219 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4220 // dialog should have the same name as the inset.
4221 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4222 // docs in LyXAction.cpp.
4224 char const * const dialognames[] = {
4226 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4227 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4228 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4229 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4230 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4231 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4232 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4233 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4235 char const * const * const end_dialognames =
4236 dialognames + (sizeof(dialognames) / sizeof(char *));
4240 cmpCStr(char const * name) : name_(name) {}
4241 bool operator()(char const * other) {
4242 return strcmp(other, name_) == 0;
4249 bool isValidName(string const & name)
4251 return find_if(dialognames, end_dialognames,
4252 cmpCStr(name.c_str())) != end_dialognames;
4258 void GuiView::resetDialogs()
4260 // Make sure that no LFUN uses any GuiView.
4261 guiApp->setCurrentView(0);
4265 constructToolbars();
4266 guiApp->menus().fillMenuBar(menuBar(), this, false);
4267 d.layout_->updateContents(true);
4268 // Now update controls with current buffer.
4269 guiApp->setCurrentView(this);
4275 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4277 if (!isValidName(name))
4280 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4282 if (it != d.dialogs_.end()) {
4284 it->second->hideView();
4285 return it->second.get();
4288 Dialog * dialog = build(name);
4289 d.dialogs_[name].reset(dialog);
4290 if (lyxrc.allow_geometry_session)
4291 dialog->restoreSession();
4298 void GuiView::showDialog(string const & name, string const & data,
4301 triggerShowDialog(toqstr(name), toqstr(data), inset);
4305 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4311 const string name = fromqstr(qname);
4312 const string data = fromqstr(qdata);
4316 Dialog * dialog = findOrBuild(name, false);
4318 bool const visible = dialog->isVisibleView();
4319 dialog->showData(data);
4320 if (inset && currentBufferView())
4321 currentBufferView()->editInset(name, inset);
4322 // We only set the focus to the new dialog if it was not yet
4323 // visible in order not to change the existing previous behaviour
4325 // activateWindow is needed for floating dockviews
4326 dialog->asQWidget()->raise();
4327 dialog->asQWidget()->activateWindow();
4328 dialog->asQWidget()->setFocus();
4332 catch (ExceptionMessage const & ex) {
4340 bool GuiView::isDialogVisible(string const & name) const
4342 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4343 if (it == d.dialogs_.end())
4345 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4349 void GuiView::hideDialog(string const & name, Inset * inset)
4351 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4352 if (it == d.dialogs_.end())
4356 if (!currentBufferView())
4358 if (inset != currentBufferView()->editedInset(name))
4362 Dialog * const dialog = it->second.get();
4363 if (dialog->isVisibleView())
4365 if (currentBufferView())
4366 currentBufferView()->editInset(name, 0);
4370 void GuiView::disconnectDialog(string const & name)
4372 if (!isValidName(name))
4374 if (currentBufferView())
4375 currentBufferView()->editInset(name, 0);
4379 void GuiView::hideAll() const
4381 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4382 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4384 for(; it != end; ++it)
4385 it->second->hideView();
4389 void GuiView::updateDialogs()
4391 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4392 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4394 for(; it != end; ++it) {
4395 Dialog * dialog = it->second.get();
4397 if (dialog->needBufferOpen() && !documentBufferView())
4398 hideDialog(fromqstr(dialog->name()), 0);
4399 else if (dialog->isVisibleView())
4400 dialog->checkStatus();
4407 Dialog * createDialog(GuiView & lv, string const & name);
4409 // will be replaced by a proper factory...
4410 Dialog * createGuiAbout(GuiView & lv);
4411 Dialog * createGuiBibtex(GuiView & lv);
4412 Dialog * createGuiChanges(GuiView & lv);
4413 Dialog * createGuiCharacter(GuiView & lv);
4414 Dialog * createGuiCitation(GuiView & lv);
4415 Dialog * createGuiCompare(GuiView & lv);
4416 Dialog * createGuiCompareHistory(GuiView & lv);
4417 Dialog * createGuiDelimiter(GuiView & lv);
4418 Dialog * createGuiDocument(GuiView & lv);
4419 Dialog * createGuiErrorList(GuiView & lv);
4420 Dialog * createGuiExternal(GuiView & lv);
4421 Dialog * createGuiGraphics(GuiView & lv);
4422 Dialog * createGuiInclude(GuiView & lv);
4423 Dialog * createGuiIndex(GuiView & lv);
4424 Dialog * createGuiListings(GuiView & lv);
4425 Dialog * createGuiLog(GuiView & lv);
4426 Dialog * createGuiMathMatrix(GuiView & lv);
4427 Dialog * createGuiNote(GuiView & lv);
4428 Dialog * createGuiParagraph(GuiView & lv);
4429 Dialog * createGuiPhantom(GuiView & lv);
4430 Dialog * createGuiPreferences(GuiView & lv);
4431 Dialog * createGuiPrint(GuiView & lv);
4432 Dialog * createGuiPrintindex(GuiView & lv);
4433 Dialog * createGuiRef(GuiView & lv);
4434 Dialog * createGuiSearch(GuiView & lv);
4435 Dialog * createGuiSearchAdv(GuiView & lv);
4436 Dialog * createGuiSendTo(GuiView & lv);
4437 Dialog * createGuiShowFile(GuiView & lv);
4438 Dialog * createGuiSpellchecker(GuiView & lv);
4439 Dialog * createGuiSymbols(GuiView & lv);
4440 Dialog * createGuiTabularCreate(GuiView & lv);
4441 Dialog * createGuiTexInfo(GuiView & lv);
4442 Dialog * createGuiToc(GuiView & lv);
4443 Dialog * createGuiThesaurus(GuiView & lv);
4444 Dialog * createGuiViewSource(GuiView & lv);
4445 Dialog * createGuiWrap(GuiView & lv);
4446 Dialog * createGuiProgressView(GuiView & lv);
4450 Dialog * GuiView::build(string const & name)
4452 LASSERT(isValidName(name), return 0);
4454 Dialog * dialog = createDialog(*this, name);
4458 if (name == "aboutlyx")
4459 return createGuiAbout(*this);
4460 if (name == "bibtex")
4461 return createGuiBibtex(*this);
4462 if (name == "changes")
4463 return createGuiChanges(*this);
4464 if (name == "character")
4465 return createGuiCharacter(*this);
4466 if (name == "citation")
4467 return createGuiCitation(*this);
4468 if (name == "compare")
4469 return createGuiCompare(*this);
4470 if (name == "comparehistory")
4471 return createGuiCompareHistory(*this);
4472 if (name == "document")
4473 return createGuiDocument(*this);
4474 if (name == "errorlist")
4475 return createGuiErrorList(*this);
4476 if (name == "external")
4477 return createGuiExternal(*this);
4479 return createGuiShowFile(*this);
4480 if (name == "findreplace")
4481 return createGuiSearch(*this);
4482 if (name == "findreplaceadv")
4483 return createGuiSearchAdv(*this);
4484 if (name == "graphics")
4485 return createGuiGraphics(*this);
4486 if (name == "include")
4487 return createGuiInclude(*this);
4488 if (name == "index")
4489 return createGuiIndex(*this);
4490 if (name == "index_print")
4491 return createGuiPrintindex(*this);
4492 if (name == "listings")
4493 return createGuiListings(*this);
4495 return createGuiLog(*this);
4496 if (name == "mathdelimiter")
4497 return createGuiDelimiter(*this);
4498 if (name == "mathmatrix")
4499 return createGuiMathMatrix(*this);
4501 return createGuiNote(*this);
4502 if (name == "paragraph")
4503 return createGuiParagraph(*this);
4504 if (name == "phantom")
4505 return createGuiPhantom(*this);
4506 if (name == "prefs")
4507 return createGuiPreferences(*this);
4509 return createGuiRef(*this);
4510 if (name == "sendto")
4511 return createGuiSendTo(*this);
4512 if (name == "spellchecker")
4513 return createGuiSpellchecker(*this);
4514 if (name == "symbols")
4515 return createGuiSymbols(*this);
4516 if (name == "tabularcreate")
4517 return createGuiTabularCreate(*this);
4518 if (name == "texinfo")
4519 return createGuiTexInfo(*this);
4520 if (name == "thesaurus")
4521 return createGuiThesaurus(*this);
4523 return createGuiToc(*this);
4524 if (name == "view-source")
4525 return createGuiViewSource(*this);
4527 return createGuiWrap(*this);
4528 if (name == "progress")
4529 return createGuiProgressView(*this);
4535 } // namespace frontend
4538 #include "moc_GuiView.cpp"