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 = formats.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("[*]");
1125 title += from_ascii(" - LyX");
1127 setWindowTitle(toqstr(title));
1128 // Sets the path for the window: this is used by OSX to
1129 // allow a context click on the title bar showing a menu
1130 // with the path up to the file
1131 setWindowFilePath(toqstr(buf.absFileName()));
1132 // Tell Qt whether the current document is changed
1133 setWindowModified(!buf.isClean());
1135 if (buf.isReadonly())
1140 if (buf.lyxvc().inUse()) {
1141 version_control_->show();
1142 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1144 version_control_->hide();
1148 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1150 if (d.current_work_area_)
1151 // disconnect the current work area from all slots
1152 QObject::disconnect(d.current_work_area_, 0, this, 0);
1154 disconnectBufferView();
1155 connectBufferView(wa->bufferView());
1156 connectBuffer(wa->bufferView().buffer());
1157 d.current_work_area_ = wa;
1158 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1159 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1160 QObject::connect(wa, SIGNAL(busy(bool)),
1161 this, SLOT(setBusy(bool)));
1162 // connection of a signal to a signal
1163 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1164 this, SIGNAL(bufferViewChanged()));
1165 Q_EMIT updateWindowTitle(wa);
1166 Q_EMIT bufferViewChanged();
1170 void GuiView::onBufferViewChanged()
1173 // Buffer-dependent dialogs must be updated. This is done here because
1174 // some dialogs require buffer()->text.
1179 void GuiView::on_lastWorkAreaRemoved()
1182 // We already are in a close event. Nothing more to do.
1185 if (d.splitter_->count() > 1)
1186 // We have a splitter so don't close anything.
1189 // Reset and updates the dialogs.
1190 Q_EMIT bufferViewChanged();
1195 if (lyxrc.open_buffers_in_tabs)
1196 // Nothing more to do, the window should stay open.
1199 if (guiApp->viewIds().size() > 1) {
1205 // On Mac we also close the last window because the application stay
1206 // resident in memory. On other platforms we don't close the last
1207 // window because this would quit the application.
1213 void GuiView::updateStatusBar()
1215 // let the user see the explicit message
1216 if (d.statusbar_timer_.isActive())
1223 void GuiView::showMessage()
1227 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1228 if (msg.isEmpty()) {
1229 BufferView const * bv = currentBufferView();
1231 msg = toqstr(bv->cursor().currentState());
1233 msg = qt_("Welcome to LyX!");
1235 statusBar()->showMessage(msg);
1239 bool GuiView::event(QEvent * e)
1243 // Useful debug code:
1244 //case QEvent::ActivationChange:
1245 //case QEvent::WindowDeactivate:
1246 //case QEvent::Paint:
1247 //case QEvent::Enter:
1248 //case QEvent::Leave:
1249 //case QEvent::HoverEnter:
1250 //case QEvent::HoverLeave:
1251 //case QEvent::HoverMove:
1252 //case QEvent::StatusTip:
1253 //case QEvent::DragEnter:
1254 //case QEvent::DragLeave:
1255 //case QEvent::Drop:
1258 case QEvent::WindowActivate: {
1259 GuiView * old_view = guiApp->currentView();
1260 if (this == old_view) {
1262 return QMainWindow::event(e);
1264 if (old_view && old_view->currentBufferView()) {
1265 // save current selection to the selection buffer to allow
1266 // middle-button paste in this window.
1267 cap::saveSelection(old_view->currentBufferView()->cursor());
1269 guiApp->setCurrentView(this);
1270 if (d.current_work_area_)
1271 on_currentWorkAreaChanged(d.current_work_area_);
1275 return QMainWindow::event(e);
1278 case QEvent::ShortcutOverride: {
1280 if (isFullScreen() && menuBar()->isHidden()) {
1281 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1282 // FIXME: we should also try to detect special LyX shortcut such as
1283 // Alt-P and Alt-M. Right now there is a hack in
1284 // GuiWorkArea::processKeySym() that hides again the menubar for
1286 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1288 return QMainWindow::event(e);
1291 return QMainWindow::event(e);
1295 return QMainWindow::event(e);
1299 void GuiView::resetWindowTitle()
1301 setWindowTitle(qt_("LyX"));
1304 bool GuiView::focusNextPrevChild(bool /*next*/)
1311 bool GuiView::busy() const
1317 void GuiView::setBusy(bool busy)
1319 bool const busy_before = busy_ > 0;
1320 busy ? ++busy_ : --busy_;
1321 if ((busy_ > 0) == busy_before)
1322 // busy state didn't change
1326 QApplication::setOverrideCursor(Qt::WaitCursor);
1329 QApplication::restoreOverrideCursor();
1334 void GuiView::resetCommandExecute()
1336 command_execute_ = false;
1341 double GuiView::pixelRatio() const
1343 #if QT_VERSION >= 0x050000
1344 return qt_scale_factor * devicePixelRatio();
1351 GuiWorkArea * GuiView::workArea(int index)
1353 if (TabWorkArea * twa = d.currentTabWorkArea())
1354 if (index < twa->count())
1355 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1360 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1362 if (currentWorkArea()
1363 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1364 return currentWorkArea();
1365 if (TabWorkArea * twa = d.currentTabWorkArea())
1366 return twa->workArea(buffer);
1371 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1373 // Automatically create a TabWorkArea if there are none yet.
1374 TabWorkArea * tab_widget = d.splitter_->count()
1375 ? d.currentTabWorkArea() : addTabWorkArea();
1376 return tab_widget->addWorkArea(buffer, *this);
1380 TabWorkArea * GuiView::addTabWorkArea()
1382 TabWorkArea * twa = new TabWorkArea;
1383 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1384 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1385 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1386 this, SLOT(on_lastWorkAreaRemoved()));
1388 d.splitter_->addWidget(twa);
1389 d.stack_widget_->setCurrentWidget(d.splitter_);
1394 GuiWorkArea const * GuiView::currentWorkArea() const
1396 return d.current_work_area_;
1400 GuiWorkArea * GuiView::currentWorkArea()
1402 return d.current_work_area_;
1406 GuiWorkArea const * GuiView::currentMainWorkArea() const
1408 if (!d.currentTabWorkArea())
1410 return d.currentTabWorkArea()->currentWorkArea();
1414 GuiWorkArea * GuiView::currentMainWorkArea()
1416 if (!d.currentTabWorkArea())
1418 return d.currentTabWorkArea()->currentWorkArea();
1422 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1424 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1426 d.current_work_area_ = 0;
1428 Q_EMIT bufferViewChanged();
1432 // FIXME: I've no clue why this is here and why it accesses
1433 // theGuiApp()->currentView, which might be 0 (bug 6464).
1434 // See also 27525 (vfr).
1435 if (theGuiApp()->currentView() == this
1436 && theGuiApp()->currentView()->currentWorkArea() == wa)
1439 if (currentBufferView())
1440 cap::saveSelection(currentBufferView()->cursor());
1442 theGuiApp()->setCurrentView(this);
1443 d.current_work_area_ = wa;
1445 // We need to reset this now, because it will need to be
1446 // right if the tabWorkArea gets reset in the for loop. We
1447 // will change it back if we aren't in that case.
1448 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1449 d.current_main_work_area_ = wa;
1451 for (int i = 0; i != d.splitter_->count(); ++i) {
1452 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1453 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1454 << ", Current main wa: " << currentMainWorkArea());
1459 d.current_main_work_area_ = old_cmwa;
1461 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1462 on_currentWorkAreaChanged(wa);
1463 BufferView & bv = wa->bufferView();
1464 bv.cursor().fixIfBroken();
1466 wa->setUpdatesEnabled(true);
1467 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1471 void GuiView::removeWorkArea(GuiWorkArea * wa)
1473 LASSERT(wa, return);
1474 if (wa == d.current_work_area_) {
1476 disconnectBufferView();
1477 d.current_work_area_ = 0;
1478 d.current_main_work_area_ = 0;
1481 bool found_twa = false;
1482 for (int i = 0; i != d.splitter_->count(); ++i) {
1483 TabWorkArea * twa = d.tabWorkArea(i);
1484 if (twa->removeWorkArea(wa)) {
1485 // Found in this tab group, and deleted the GuiWorkArea.
1487 if (twa->count() != 0) {
1488 if (d.current_work_area_ == 0)
1489 // This means that we are closing the current GuiWorkArea, so
1490 // switch to the next GuiWorkArea in the found TabWorkArea.
1491 setCurrentWorkArea(twa->currentWorkArea());
1493 // No more WorkAreas in this tab group, so delete it.
1500 // It is not a tabbed work area (i.e., the search work area), so it
1501 // should be deleted by other means.
1502 LASSERT(found_twa, return);
1504 if (d.current_work_area_ == 0) {
1505 if (d.splitter_->count() != 0) {
1506 TabWorkArea * twa = d.currentTabWorkArea();
1507 setCurrentWorkArea(twa->currentWorkArea());
1509 // No more work areas, switch to the background widget.
1510 setCurrentWorkArea(0);
1516 LayoutBox * GuiView::getLayoutDialog() const
1522 void GuiView::updateLayoutList()
1525 d.layout_->updateContents(false);
1529 void GuiView::updateToolbars()
1531 ToolbarMap::iterator end = d.toolbars_.end();
1532 if (d.current_work_area_) {
1534 if (d.current_work_area_->bufferView().cursor().inMathed()
1535 && !d.current_work_area_->bufferView().cursor().inRegexped())
1536 context |= Toolbars::MATH;
1537 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1538 context |= Toolbars::TABLE;
1539 if (currentBufferView()->buffer().areChangesPresent()
1540 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1541 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1542 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1543 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1544 context |= Toolbars::REVIEW;
1545 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1546 context |= Toolbars::MATHMACROTEMPLATE;
1547 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1548 context |= Toolbars::IPA;
1549 if (command_execute_)
1550 context |= Toolbars::MINIBUFFER;
1551 if (minibuffer_focus_) {
1552 context |= Toolbars::MINIBUFFER_FOCUS;
1553 minibuffer_focus_ = false;
1556 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1557 it->second->update(context);
1559 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1560 it->second->update();
1564 void GuiView::setBuffer(Buffer * newBuffer)
1566 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1567 LASSERT(newBuffer, return);
1569 GuiWorkArea * wa = workArea(*newBuffer);
1572 newBuffer->masterBuffer()->updateBuffer();
1574 wa = addWorkArea(*newBuffer);
1575 // scroll to the position when the BufferView was last closed
1576 if (lyxrc.use_lastfilepos) {
1577 LastFilePosSection::FilePos filepos =
1578 theSession().lastFilePos().load(newBuffer->fileName());
1579 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1582 //Disconnect the old buffer...there's no new one.
1585 connectBuffer(*newBuffer);
1586 connectBufferView(wa->bufferView());
1587 setCurrentWorkArea(wa);
1591 void GuiView::connectBuffer(Buffer & buf)
1593 buf.setGuiDelegate(this);
1597 void GuiView::disconnectBuffer()
1599 if (d.current_work_area_)
1600 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1604 void GuiView::connectBufferView(BufferView & bv)
1606 bv.setGuiDelegate(this);
1610 void GuiView::disconnectBufferView()
1612 if (d.current_work_area_)
1613 d.current_work_area_->bufferView().setGuiDelegate(0);
1617 void GuiView::errors(string const & error_type, bool from_master)
1619 BufferView const * const bv = currentBufferView();
1623 #if EXPORT_in_THREAD
1624 // We are called with from_master == false by default, so we
1625 // have to figure out whether that is the case or not.
1626 ErrorList & el = bv->buffer().errorList(error_type);
1628 el = bv->buffer().masterBuffer()->errorList(error_type);
1632 ErrorList const & el = from_master ?
1633 bv->buffer().masterBuffer()->errorList(error_type) :
1634 bv->buffer().errorList(error_type);
1640 string data = error_type;
1642 data = "from_master|" + error_type;
1643 showDialog("errorlist", data);
1647 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1649 d.toc_models_.updateItem(toqstr(type), dit);
1653 void GuiView::structureChanged()
1655 // This is called from the Buffer, which has no way to ensure that cursors
1656 // in BufferView remain valid.
1657 if (documentBufferView())
1658 documentBufferView()->cursor().sanitize();
1659 // FIXME: This is slightly expensive, though less than the tocBackend update
1660 // (#9880). This also resets the view in the Toc Widget (#6675).
1661 d.toc_models_.reset(documentBufferView());
1662 // Navigator needs more than a simple update in this case. It needs to be
1664 updateDialog("toc", "");
1668 void GuiView::updateDialog(string const & name, string const & data)
1670 if (!isDialogVisible(name))
1673 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1674 if (it == d.dialogs_.end())
1677 Dialog * const dialog = it->second.get();
1678 if (dialog->isVisibleView())
1679 dialog->initialiseParams(data);
1683 BufferView * GuiView::documentBufferView()
1685 return currentMainWorkArea()
1686 ? ¤tMainWorkArea()->bufferView()
1691 BufferView const * GuiView::documentBufferView() const
1693 return currentMainWorkArea()
1694 ? ¤tMainWorkArea()->bufferView()
1699 BufferView * GuiView::currentBufferView()
1701 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1705 BufferView const * GuiView::currentBufferView() const
1707 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1711 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1712 Buffer const * orig, Buffer * clone)
1714 bool const success = clone->autoSave();
1716 busyBuffers.remove(orig);
1718 ? _("Automatic save done.")
1719 : _("Automatic save failed!");
1723 void GuiView::autoSave()
1725 LYXERR(Debug::INFO, "Running autoSave()");
1727 Buffer * buffer = documentBufferView()
1728 ? &documentBufferView()->buffer() : 0;
1730 resetAutosaveTimers();
1734 GuiViewPrivate::busyBuffers.insert(buffer);
1735 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1736 buffer, buffer->cloneBufferOnly());
1737 d.autosave_watcher_.setFuture(f);
1738 resetAutosaveTimers();
1742 void GuiView::resetAutosaveTimers()
1745 d.autosave_timeout_.restart();
1749 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1752 Buffer * buf = currentBufferView()
1753 ? ¤tBufferView()->buffer() : 0;
1754 Buffer * doc_buffer = documentBufferView()
1755 ? &(documentBufferView()->buffer()) : 0;
1758 /* In LyX/Mac, when a dialog is open, the menus of the
1759 application can still be accessed without giving focus to
1760 the main window. In this case, we want to disable the menu
1761 entries that are buffer-related.
1762 This code must not be used on Linux and Windows, since it
1763 would disable buffer-related entries when hovering over the
1764 menu (see bug #9574).
1766 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1772 // Check whether we need a buffer
1773 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1774 // no, exit directly
1775 flag.message(from_utf8(N_("Command not allowed with"
1776 "out any document open")));
1777 flag.setEnabled(false);
1781 if (cmd.origin() == FuncRequest::TOC) {
1782 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1783 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1784 flag.setEnabled(false);
1788 switch(cmd.action()) {
1789 case LFUN_BUFFER_IMPORT:
1792 case LFUN_MASTER_BUFFER_UPDATE:
1793 case LFUN_MASTER_BUFFER_VIEW:
1795 && (doc_buffer->parent() != 0
1796 || doc_buffer->hasChildren())
1797 && !d.processing_thread_watcher_.isRunning();
1800 case LFUN_BUFFER_UPDATE:
1801 case LFUN_BUFFER_VIEW: {
1802 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1806 string format = to_utf8(cmd.argument());
1807 if (cmd.argument().empty())
1808 format = doc_buffer->params().getDefaultOutputFormat();
1809 enable = doc_buffer->params().isExportable(format, true);
1813 case LFUN_BUFFER_RELOAD:
1814 enable = doc_buffer && !doc_buffer->isUnnamed()
1815 && doc_buffer->fileName().exists()
1816 && (!doc_buffer->isClean()
1817 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1820 case LFUN_BUFFER_CHILD_OPEN:
1821 enable = doc_buffer != 0;
1824 case LFUN_BUFFER_WRITE:
1825 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1828 //FIXME: This LFUN should be moved to GuiApplication.
1829 case LFUN_BUFFER_WRITE_ALL: {
1830 // We enable the command only if there are some modified buffers
1831 Buffer * first = theBufferList().first();
1836 // We cannot use a for loop as the buffer list is a cycle.
1838 if (!b->isClean()) {
1842 b = theBufferList().next(b);
1843 } while (b != first);
1847 case LFUN_BUFFER_WRITE_AS:
1848 case LFUN_BUFFER_EXPORT_AS:
1849 enable = doc_buffer != 0;
1852 case LFUN_BUFFER_CLOSE:
1853 case LFUN_VIEW_CLOSE:
1854 enable = doc_buffer != 0;
1857 case LFUN_BUFFER_CLOSE_ALL:
1858 enable = theBufferList().last() != theBufferList().first();
1861 case LFUN_VIEW_SPLIT:
1862 if (cmd.getArg(0) == "vertical")
1863 enable = doc_buffer && (d.splitter_->count() == 1 ||
1864 d.splitter_->orientation() == Qt::Vertical);
1866 enable = doc_buffer && (d.splitter_->count() == 1 ||
1867 d.splitter_->orientation() == Qt::Horizontal);
1870 case LFUN_TAB_GROUP_CLOSE:
1871 enable = d.tabWorkAreaCount() > 1;
1874 case LFUN_TOOLBAR_TOGGLE: {
1875 string const name = cmd.getArg(0);
1876 if (GuiToolbar * t = toolbar(name))
1877 flag.setOnOff(t->isVisible());
1880 docstring const msg =
1881 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1887 case LFUN_ICON_SIZE:
1888 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1891 case LFUN_DROP_LAYOUTS_CHOICE:
1895 case LFUN_UI_TOGGLE:
1896 flag.setOnOff(isFullScreen());
1899 case LFUN_DIALOG_DISCONNECT_INSET:
1902 case LFUN_DIALOG_HIDE:
1903 // FIXME: should we check if the dialog is shown?
1906 case LFUN_DIALOG_TOGGLE:
1907 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1908 // fall through to set "enable"
1909 case LFUN_DIALOG_SHOW: {
1910 string const name = cmd.getArg(0);
1912 enable = name == "aboutlyx"
1913 || name == "file" //FIXME: should be removed.
1915 || name == "texinfo"
1916 || name == "progress"
1917 || name == "compare";
1918 else if (name == "character" || name == "symbols"
1919 || name == "mathdelimiter" || name == "mathmatrix") {
1920 if (!buf || buf->isReadonly())
1923 Cursor const & cur = currentBufferView()->cursor();
1924 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1927 else if (name == "latexlog")
1928 enable = FileName(doc_buffer->logName()).isReadableFile();
1929 else if (name == "spellchecker")
1930 enable = theSpellChecker()
1931 && !doc_buffer->isReadonly()
1932 && !doc_buffer->text().empty();
1933 else if (name == "vclog")
1934 enable = doc_buffer->lyxvc().inUse();
1938 case LFUN_DIALOG_UPDATE: {
1939 string const name = cmd.getArg(0);
1941 enable = name == "prefs";
1945 case LFUN_COMMAND_EXECUTE:
1947 case LFUN_MENU_OPEN:
1948 // Nothing to check.
1951 case LFUN_COMPLETION_INLINE:
1952 if (!d.current_work_area_
1953 || !d.current_work_area_->completer().inlinePossible(
1954 currentBufferView()->cursor()))
1958 case LFUN_COMPLETION_POPUP:
1959 if (!d.current_work_area_
1960 || !d.current_work_area_->completer().popupPossible(
1961 currentBufferView()->cursor()))
1966 if (!d.current_work_area_
1967 || !d.current_work_area_->completer().inlinePossible(
1968 currentBufferView()->cursor()))
1972 case LFUN_COMPLETION_ACCEPT:
1973 if (!d.current_work_area_
1974 || (!d.current_work_area_->completer().popupVisible()
1975 && !d.current_work_area_->completer().inlineVisible()
1976 && !d.current_work_area_->completer().completionAvailable()))
1980 case LFUN_COMPLETION_CANCEL:
1981 if (!d.current_work_area_
1982 || (!d.current_work_area_->completer().popupVisible()
1983 && !d.current_work_area_->completer().inlineVisible()))
1987 case LFUN_BUFFER_ZOOM_OUT:
1988 case LFUN_BUFFER_ZOOM_IN: {
1989 // only diff between these two is that the default for ZOOM_OUT
1991 bool const neg_zoom =
1992 convert<int>(cmd.argument()) < 0 ||
1993 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
1994 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
1995 docstring const msg =
1996 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2000 enable = doc_buffer;
2003 case LFUN_BUFFER_MOVE_NEXT:
2004 case LFUN_BUFFER_MOVE_PREVIOUS:
2005 // we do not cycle when moving
2006 case LFUN_BUFFER_NEXT:
2007 case LFUN_BUFFER_PREVIOUS:
2008 // because we cycle, it doesn't matter whether on first or last
2009 enable = (d.currentTabWorkArea()->count() > 1);
2011 case LFUN_BUFFER_SWITCH:
2012 // toggle on the current buffer, but do not toggle off
2013 // the other ones (is that a good idea?)
2015 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2016 flag.setOnOff(true);
2019 case LFUN_VC_REGISTER:
2020 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2022 case LFUN_VC_RENAME:
2023 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2026 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2028 case LFUN_VC_CHECK_IN:
2029 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2031 case LFUN_VC_CHECK_OUT:
2032 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2034 case LFUN_VC_LOCKING_TOGGLE:
2035 enable = doc_buffer && !doc_buffer->isReadonly()
2036 && doc_buffer->lyxvc().lockingToggleEnabled();
2037 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2039 case LFUN_VC_REVERT:
2040 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2042 case LFUN_VC_UNDO_LAST:
2043 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2045 case LFUN_VC_REPO_UPDATE:
2046 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2048 case LFUN_VC_COMMAND: {
2049 if (cmd.argument().empty())
2051 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2055 case LFUN_VC_COMPARE:
2056 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2059 case LFUN_SERVER_GOTO_FILE_ROW:
2060 case LFUN_LYX_ACTIVATE:
2062 case LFUN_FORWARD_SEARCH:
2063 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2066 case LFUN_FILE_INSERT_PLAINTEXT:
2067 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2068 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2071 case LFUN_SPELLING_CONTINUOUSLY:
2072 flag.setOnOff(lyxrc.spellcheck_continuously);
2080 flag.setEnabled(false);
2086 static FileName selectTemplateFile()
2088 FileDialog dlg(qt_("Select template file"));
2089 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2090 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2092 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2093 QStringList(qt_("LyX Documents (*.lyx)")));
2095 if (result.first == FileDialog::Later)
2097 if (result.second.isEmpty())
2099 return FileName(fromqstr(result.second));
2103 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2107 Buffer * newBuffer = 0;
2109 newBuffer = checkAndLoadLyXFile(filename);
2110 } catch (ExceptionMessage const & e) {
2117 message(_("Document not loaded."));
2121 setBuffer(newBuffer);
2122 newBuffer->errors("Parse");
2125 theSession().lastFiles().add(filename);
2131 void GuiView::openDocument(string const & fname)
2133 string initpath = lyxrc.document_path;
2135 if (documentBufferView()) {
2136 string const trypath = documentBufferView()->buffer().filePath();
2137 // If directory is writeable, use this as default.
2138 if (FileName(trypath).isDirWritable())
2144 if (fname.empty()) {
2145 FileDialog dlg(qt_("Select document to open"));
2146 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2147 dlg.setButton2(qt_("Examples|#E#e"),
2148 toqstr(addPath(package().system_support().absFileName(), "examples")));
2150 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2151 FileDialog::Result result =
2152 dlg.open(toqstr(initpath), filter);
2154 if (result.first == FileDialog::Later)
2157 filename = fromqstr(result.second);
2159 // check selected filename
2160 if (filename.empty()) {
2161 message(_("Canceled."));
2167 // get absolute path of file and add ".lyx" to the filename if
2169 FileName const fullname =
2170 fileSearch(string(), filename, "lyx", support::may_not_exist);
2171 if (!fullname.empty())
2172 filename = fullname.absFileName();
2174 if (!fullname.onlyPath().isDirectory()) {
2175 Alert::warning(_("Invalid filename"),
2176 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2177 from_utf8(fullname.absFileName())));
2181 // if the file doesn't exist and isn't already open (bug 6645),
2182 // let the user create one
2183 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2184 !LyXVC::file_not_found_hook(fullname)) {
2185 // the user specifically chose this name. Believe him.
2186 Buffer * const b = newFile(filename, string(), true);
2192 docstring const disp_fn = makeDisplayPath(filename);
2193 message(bformat(_("Opening document %1$s..."), disp_fn));
2196 Buffer * buf = loadDocument(fullname);
2198 str2 = bformat(_("Document %1$s opened."), disp_fn);
2199 if (buf->lyxvc().inUse())
2200 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2201 " " + _("Version control detected.");
2203 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2208 // FIXME: clean that
2209 static bool import(GuiView * lv, FileName const & filename,
2210 string const & format, ErrorList & errorList)
2212 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2214 string loader_format;
2215 vector<string> loaders = theConverters().loaders();
2216 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2217 vector<string>::const_iterator it = loaders.begin();
2218 vector<string>::const_iterator en = loaders.end();
2219 for (; it != en; ++it) {
2220 if (!theConverters().isReachable(format, *it))
2223 string const tofile =
2224 support::changeExtension(filename.absFileName(),
2225 formats.extension(*it));
2226 if (!theConverters().convert(0, filename, FileName(tofile),
2227 filename, format, *it, errorList))
2229 loader_format = *it;
2232 if (loader_format.empty()) {
2233 frontend::Alert::error(_("Couldn't import file"),
2234 bformat(_("No information for importing the format %1$s."),
2235 formats.prettyName(format)));
2239 loader_format = format;
2241 if (loader_format == "lyx") {
2242 Buffer * buf = lv->loadDocument(lyxfile);
2246 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2250 bool as_paragraphs = loader_format == "textparagraph";
2251 string filename2 = (loader_format == format) ? filename.absFileName()
2252 : support::changeExtension(filename.absFileName(),
2253 formats.extension(loader_format));
2254 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2256 guiApp->setCurrentView(lv);
2257 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2264 void GuiView::importDocument(string const & argument)
2267 string filename = split(argument, format, ' ');
2269 LYXERR(Debug::INFO, format << " file: " << filename);
2271 // need user interaction
2272 if (filename.empty()) {
2273 string initpath = lyxrc.document_path;
2274 if (documentBufferView()) {
2275 string const trypath = documentBufferView()->buffer().filePath();
2276 // If directory is writeable, use this as default.
2277 if (FileName(trypath).isDirWritable())
2281 docstring const text = bformat(_("Select %1$s file to import"),
2282 formats.prettyName(format));
2284 FileDialog dlg(toqstr(text));
2285 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2286 dlg.setButton2(qt_("Examples|#E#e"),
2287 toqstr(addPath(package().system_support().absFileName(), "examples")));
2289 docstring filter = formats.prettyName(format);
2292 filter += from_utf8(formats.extensions(format));
2295 FileDialog::Result result =
2296 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2298 if (result.first == FileDialog::Later)
2301 filename = fromqstr(result.second);
2303 // check selected filename
2304 if (filename.empty())
2305 message(_("Canceled."));
2308 if (filename.empty())
2311 // get absolute path of file
2312 FileName const fullname(support::makeAbsPath(filename));
2314 // Can happen if the user entered a path into the dialog
2316 if (fullname.onlyFileName().empty()) {
2317 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2318 "Aborting import."),
2319 from_utf8(fullname.absFileName()));
2320 frontend::Alert::error(_("File name error"), msg);
2321 message(_("Canceled."));
2326 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2328 // Check if the document already is open
2329 Buffer * buf = theBufferList().getBuffer(lyxfile);
2332 if (!closeBuffer()) {
2333 message(_("Canceled."));
2338 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2340 // if the file exists already, and we didn't do
2341 // -i lyx thefile.lyx, warn
2342 if (lyxfile.exists() && fullname != lyxfile) {
2344 docstring text = bformat(_("The document %1$s already exists.\n\n"
2345 "Do you want to overwrite that document?"), displaypath);
2346 int const ret = Alert::prompt(_("Overwrite document?"),
2347 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2350 message(_("Canceled."));
2355 message(bformat(_("Importing %1$s..."), displaypath));
2356 ErrorList errorList;
2357 if (import(this, fullname, format, errorList))
2358 message(_("imported."));
2360 message(_("file not imported!"));
2362 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2366 void GuiView::newDocument(string const & filename, bool from_template)
2368 FileName initpath(lyxrc.document_path);
2369 if (documentBufferView()) {
2370 FileName const trypath(documentBufferView()->buffer().filePath());
2371 // If directory is writeable, use this as default.
2372 if (trypath.isDirWritable())
2376 string templatefile;
2377 if (from_template) {
2378 templatefile = selectTemplateFile().absFileName();
2379 if (templatefile.empty())
2384 if (filename.empty())
2385 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2387 b = newFile(filename, templatefile, true);
2392 // If no new document could be created, it is unsure
2393 // whether there is a valid BufferView.
2394 if (currentBufferView())
2395 // Ensure the cursor is correctly positioned on screen.
2396 currentBufferView()->showCursor();
2400 void GuiView::insertLyXFile(docstring const & fname)
2402 BufferView * bv = documentBufferView();
2407 FileName filename(to_utf8(fname));
2408 if (filename.empty()) {
2409 // Launch a file browser
2411 string initpath = lyxrc.document_path;
2412 string const trypath = bv->buffer().filePath();
2413 // If directory is writeable, use this as default.
2414 if (FileName(trypath).isDirWritable())
2418 FileDialog dlg(qt_("Select LyX document to insert"));
2419 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2420 dlg.setButton2(qt_("Examples|#E#e"),
2421 toqstr(addPath(package().system_support().absFileName(),
2424 FileDialog::Result result = dlg.open(toqstr(initpath),
2425 QStringList(qt_("LyX Documents (*.lyx)")));
2427 if (result.first == FileDialog::Later)
2431 filename.set(fromqstr(result.second));
2433 // check selected filename
2434 if (filename.empty()) {
2435 // emit message signal.
2436 message(_("Canceled."));
2441 bv->insertLyXFile(filename);
2442 bv->buffer().errors("Parse");
2446 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2448 FileName fname = b.fileName();
2449 FileName const oldname = fname;
2451 if (!newname.empty()) {
2453 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2455 // Switch to this Buffer.
2458 // No argument? Ask user through dialog.
2460 FileDialog dlg(qt_("Choose a filename to save document as"));
2461 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2462 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2464 if (!isLyXFileName(fname.absFileName()))
2465 fname.changeExtension(".lyx");
2467 FileDialog::Result result =
2468 dlg.save(toqstr(fname.onlyPath().absFileName()),
2469 QStringList(qt_("LyX Documents (*.lyx)")),
2470 toqstr(fname.onlyFileName()));
2472 if (result.first == FileDialog::Later)
2475 fname.set(fromqstr(result.second));
2480 if (!isLyXFileName(fname.absFileName()))
2481 fname.changeExtension(".lyx");
2484 // fname is now the new Buffer location.
2486 // if there is already a Buffer open with this name, we do not want
2487 // to have another one. (the second test makes sure we're not just
2488 // trying to overwrite ourselves, which is fine.)
2489 if (theBufferList().exists(fname) && fname != oldname
2490 && theBufferList().getBuffer(fname) != &b) {
2491 docstring const text =
2492 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2493 "Please close it before attempting to overwrite it.\n"
2494 "Do you want to choose a new filename?"),
2495 from_utf8(fname.absFileName()));
2496 int const ret = Alert::prompt(_("Chosen File Already Open"),
2497 text, 0, 1, _("&Rename"), _("&Cancel"));
2499 case 0: return renameBuffer(b, docstring(), kind);
2500 case 1: return false;
2505 bool const existsLocal = fname.exists();
2506 bool const existsInVC = LyXVC::fileInVC(fname);
2507 if (existsLocal || existsInVC) {
2508 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2509 if (kind != LV_WRITE_AS && existsInVC) {
2510 // renaming to a name that is already in VC
2512 docstring text = bformat(_("The document %1$s "
2513 "is already registered.\n\n"
2514 "Do you want to choose a new name?"),
2516 docstring const title = (kind == LV_VC_RENAME) ?
2517 _("Rename document?") : _("Copy document?");
2518 docstring const button = (kind == LV_VC_RENAME) ?
2519 _("&Rename") : _("&Copy");
2520 int const ret = Alert::prompt(title, text, 0, 1,
2521 button, _("&Cancel"));
2523 case 0: return renameBuffer(b, docstring(), kind);
2524 case 1: return false;
2529 docstring text = bformat(_("The document %1$s "
2530 "already exists.\n\n"
2531 "Do you want to overwrite that document?"),
2533 int const ret = Alert::prompt(_("Overwrite document?"),
2534 text, 0, 2, _("&Overwrite"),
2535 _("&Rename"), _("&Cancel"));
2538 case 1: return renameBuffer(b, docstring(), kind);
2539 case 2: return false;
2545 case LV_VC_RENAME: {
2546 string msg = b.lyxvc().rename(fname);
2549 message(from_utf8(msg));
2553 string msg = b.lyxvc().copy(fname);
2556 message(from_utf8(msg));
2562 // LyXVC created the file already in case of LV_VC_RENAME or
2563 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2564 // relative paths of included stuff right if we moved e.g. from
2565 // /a/b.lyx to /a/c/b.lyx.
2567 bool const saved = saveBuffer(b, fname);
2574 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2576 FileName fname = b.fileName();
2578 FileDialog dlg(qt_("Choose a filename to export the document as"));
2579 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2582 QString const anyformat = qt_("Guess from extension (*.*)");
2585 vector<Format const *> export_formats;
2586 for (Format const & f : formats)
2587 if (f.documentFormat())
2588 export_formats.push_back(&f);
2589 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2590 map<QString, string> fmap;
2593 for (Format const * f : export_formats) {
2594 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2595 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2597 from_ascii(f->extension())));
2598 types << loc_filter;
2599 fmap[loc_filter] = f->name();
2600 if (from_ascii(f->name()) == iformat) {
2601 filter = loc_filter;
2602 ext = f->extension();
2605 string ofname = fname.onlyFileName();
2607 ofname = support::changeExtension(ofname, ext);
2608 FileDialog::Result result =
2609 dlg.save(toqstr(fname.onlyPath().absFileName()),
2613 if (result.first != FileDialog::Chosen)
2617 fname.set(fromqstr(result.second));
2618 if (filter == anyformat)
2619 fmt_name = formats.getFormatFromExtension(fname.extension());
2621 fmt_name = fmap[filter];
2622 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2623 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2625 if (fmt_name.empty() || fname.empty())
2628 // fname is now the new Buffer location.
2629 if (FileName(fname).exists()) {
2630 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2631 docstring text = bformat(_("The document %1$s already "
2632 "exists.\n\nDo you want to "
2633 "overwrite that document?"),
2635 int const ret = Alert::prompt(_("Overwrite document?"),
2636 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2639 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2640 case 2: return false;
2644 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2647 return dr.dispatched();
2651 bool GuiView::saveBuffer(Buffer & b)
2653 return saveBuffer(b, FileName());
2657 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2659 if (workArea(b) && workArea(b)->inDialogMode())
2662 if (fn.empty() && b.isUnnamed())
2663 return renameBuffer(b, docstring());
2665 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2667 theSession().lastFiles().add(b.fileName());
2671 // Switch to this Buffer.
2674 // FIXME: we don't tell the user *WHY* the save failed !!
2675 docstring const file = makeDisplayPath(b.absFileName(), 30);
2676 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2677 "Do you want to rename the document and "
2678 "try again?"), file);
2679 int const ret = Alert::prompt(_("Rename and save?"),
2680 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2683 if (!renameBuffer(b, docstring()))
2692 return saveBuffer(b, fn);
2696 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2698 return closeWorkArea(wa, false);
2702 // We only want to close the buffer if it is not visible in other workareas
2703 // of the same view, nor in other views, and if this is not a child
2704 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2706 Buffer & buf = wa->bufferView().buffer();
2708 bool last_wa = d.countWorkAreasOf(buf) == 1
2709 && !inOtherView(buf) && !buf.parent();
2711 bool close_buffer = last_wa;
2714 if (lyxrc.close_buffer_with_last_view == "yes")
2716 else if (lyxrc.close_buffer_with_last_view == "no")
2717 close_buffer = false;
2720 if (buf.isUnnamed())
2721 file = from_utf8(buf.fileName().onlyFileName());
2723 file = buf.fileName().displayName(30);
2724 docstring const text = bformat(
2725 _("Last view on document %1$s is being closed.\n"
2726 "Would you like to close or hide the document?\n"
2728 "Hidden documents can be displayed back through\n"
2729 "the menu: View->Hidden->...\n"
2731 "To remove this question, set your preference in:\n"
2732 " Tools->Preferences->Look&Feel->UserInterface\n"
2734 int ret = Alert::prompt(_("Close or hide document?"),
2735 text, 0, 1, _("&Close"), _("&Hide"));
2736 close_buffer = (ret == 0);
2740 return closeWorkArea(wa, close_buffer);
2744 bool GuiView::closeBuffer()
2746 GuiWorkArea * wa = currentMainWorkArea();
2747 // coverity complained about this
2748 // it seems unnecessary, but perhaps is worth the check
2749 LASSERT(wa, return false);
2751 setCurrentWorkArea(wa);
2752 Buffer & buf = wa->bufferView().buffer();
2753 return closeWorkArea(wa, !buf.parent());
2757 void GuiView::writeSession() const {
2758 GuiWorkArea const * active_wa = currentMainWorkArea();
2759 for (int i = 0; i < d.splitter_->count(); ++i) {
2760 TabWorkArea * twa = d.tabWorkArea(i);
2761 for (int j = 0; j < twa->count(); ++j) {
2762 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2763 Buffer & buf = wa->bufferView().buffer();
2764 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2770 bool GuiView::closeBufferAll()
2772 // Close the workareas in all other views
2773 QList<int> const ids = guiApp->viewIds();
2774 for (int i = 0; i != ids.size(); ++i) {
2775 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2779 // Close our own workareas
2780 if (!closeWorkAreaAll())
2783 // Now close the hidden buffers. We prevent hidden buffers from being
2784 // dirty, so we can just close them.
2785 theBufferList().closeAll();
2790 bool GuiView::closeWorkAreaAll()
2792 setCurrentWorkArea(currentMainWorkArea());
2794 // We might be in a situation that there is still a tabWorkArea, but
2795 // there are no tabs anymore. This can happen when we get here after a
2796 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2797 // many TabWorkArea's have no documents anymore.
2800 // We have to call count() each time, because it can happen that
2801 // more than one splitter will disappear in one iteration (bug 5998).
2802 while (d.splitter_->count() > empty_twa) {
2803 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2805 if (twa->count() == 0)
2808 setCurrentWorkArea(twa->currentWorkArea());
2809 if (!closeTabWorkArea(twa))
2817 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2822 Buffer & buf = wa->bufferView().buffer();
2824 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2825 Alert::warning(_("Close document"),
2826 _("Document could not be closed because it is being processed by LyX."));
2831 return closeBuffer(buf);
2833 if (!inMultiTabs(wa))
2834 if (!saveBufferIfNeeded(buf, true))
2842 bool GuiView::closeBuffer(Buffer & buf)
2844 // If we are in a close_event all children will be closed in some time,
2845 // so no need to do it here. This will ensure that the children end up
2846 // in the session file in the correct order. If we close the master
2847 // buffer, we can close or release the child buffers here too.
2848 bool success = true;
2850 ListOfBuffers clist = buf.getChildren();
2851 ListOfBuffers::const_iterator it = clist.begin();
2852 ListOfBuffers::const_iterator const bend = clist.end();
2853 for (; it != bend; ++it) {
2854 Buffer * child_buf = *it;
2855 if (theBufferList().isOthersChild(&buf, child_buf)) {
2856 child_buf->setParent(0);
2860 // FIXME: should we look in other tabworkareas?
2861 // ANSWER: I don't think so. I've tested, and if the child is
2862 // open in some other window, it closes without a problem.
2863 GuiWorkArea * child_wa = workArea(*child_buf);
2865 success = closeWorkArea(child_wa, true);
2869 // In this case the child buffer is open but hidden.
2870 // It therefore should not (MUST NOT) be dirty!
2871 LATTEST(child_buf->isClean());
2872 theBufferList().release(child_buf);
2877 // goto bookmark to update bookmark pit.
2878 // FIXME: we should update only the bookmarks related to this buffer!
2879 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2880 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2881 guiApp->gotoBookmark(i+1, false, false);
2883 if (saveBufferIfNeeded(buf, false)) {
2884 buf.removeAutosaveFile();
2885 theBufferList().release(&buf);
2889 // open all children again to avoid a crash because of dangling
2890 // pointers (bug 6603)
2896 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2898 while (twa == d.currentTabWorkArea()) {
2899 twa->setCurrentIndex(twa->count() - 1);
2901 GuiWorkArea * wa = twa->currentWorkArea();
2902 Buffer & b = wa->bufferView().buffer();
2904 // We only want to close the buffer if the same buffer is not visible
2905 // in another view, and if this is not a child and if we are closing
2906 // a view (not a tabgroup).
2907 bool const close_buffer =
2908 !inOtherView(b) && !b.parent() && closing_;
2910 if (!closeWorkArea(wa, close_buffer))
2917 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2919 if (buf.isClean() || buf.paragraphs().empty())
2922 // Switch to this Buffer.
2927 if (buf.isUnnamed())
2928 file = from_utf8(buf.fileName().onlyFileName());
2930 file = buf.fileName().displayName(30);
2932 // Bring this window to top before asking questions.
2937 if (hiding && buf.isUnnamed()) {
2938 docstring const text = bformat(_("The document %1$s has not been "
2939 "saved yet.\n\nDo you want to save "
2940 "the document?"), file);
2941 ret = Alert::prompt(_("Save new document?"),
2942 text, 0, 1, _("&Save"), _("&Cancel"));
2946 docstring const text = bformat(_("The document %1$s has unsaved changes."
2947 "\n\nDo you want to save the document or discard the changes?"), file);
2948 ret = Alert::prompt(_("Save changed document?"),
2949 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2954 if (!saveBuffer(buf))
2958 // If we crash after this we could have no autosave file
2959 // but I guess this is really improbable (Jug).
2960 // Sometimes improbable things happen:
2961 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2962 // buf.removeAutosaveFile();
2964 // revert all changes
2975 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2977 Buffer & buf = wa->bufferView().buffer();
2979 for (int i = 0; i != d.splitter_->count(); ++i) {
2980 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2981 if (wa_ && wa_ != wa)
2984 return inOtherView(buf);
2988 bool GuiView::inOtherView(Buffer & buf)
2990 QList<int> const ids = guiApp->viewIds();
2992 for (int i = 0; i != ids.size(); ++i) {
2996 if (guiApp->view(ids[i]).workArea(buf))
3003 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3005 if (!documentBufferView())
3008 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3009 Buffer * const curbuf = &documentBufferView()->buffer();
3010 int nwa = twa->count();
3011 for (int i = 0; i < nwa; ++i) {
3012 if (&workArea(i)->bufferView().buffer() == curbuf) {
3014 if (np == NEXTBUFFER)
3015 next_index = (i == nwa - 1 ? 0 : i + 1);
3017 next_index = (i == 0 ? nwa - 1 : i - 1);
3019 twa->moveTab(i, next_index);
3021 setBuffer(&workArea(next_index)->bufferView().buffer());
3029 /// make sure the document is saved
3030 static bool ensureBufferClean(Buffer * buffer)
3032 LASSERT(buffer, return false);
3033 if (buffer->isClean() && !buffer->isUnnamed())
3036 docstring const file = buffer->fileName().displayName(30);
3039 if (!buffer->isUnnamed()) {
3040 text = bformat(_("The document %1$s has unsaved "
3041 "changes.\n\nDo you want to save "
3042 "the document?"), file);
3043 title = _("Save changed document?");
3046 text = bformat(_("The document %1$s has not been "
3047 "saved yet.\n\nDo you want to save "
3048 "the document?"), file);
3049 title = _("Save new document?");
3051 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3054 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3056 return buffer->isClean() && !buffer->isUnnamed();
3060 bool GuiView::reloadBuffer(Buffer & buf)
3062 Buffer::ReadStatus status = buf.reload();
3063 return status == Buffer::ReadSuccess;
3067 void GuiView::checkExternallyModifiedBuffers()
3069 BufferList::iterator bit = theBufferList().begin();
3070 BufferList::iterator const bend = theBufferList().end();
3071 for (; bit != bend; ++bit) {
3072 Buffer * buf = *bit;
3073 if (buf->fileName().exists()
3074 && buf->isExternallyModified(Buffer::checksum_method)) {
3075 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3076 " Reload now? Any local changes will be lost."),
3077 from_utf8(buf->absFileName()));
3078 int const ret = Alert::prompt(_("Reload externally changed document?"),
3079 text, 0, 1, _("&Reload"), _("&Cancel"));
3087 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3089 Buffer * buffer = documentBufferView()
3090 ? &(documentBufferView()->buffer()) : 0;
3092 switch (cmd.action()) {
3093 case LFUN_VC_REGISTER:
3094 if (!buffer || !ensureBufferClean(buffer))
3096 if (!buffer->lyxvc().inUse()) {
3097 if (buffer->lyxvc().registrer()) {
3098 reloadBuffer(*buffer);
3099 dr.clearMessageUpdate();
3104 case LFUN_VC_RENAME:
3105 case LFUN_VC_COPY: {
3106 if (!buffer || !ensureBufferClean(buffer))
3108 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3109 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3110 // Some changes are not yet committed.
3111 // We test here and not in getStatus(), since
3112 // this test is expensive.
3114 LyXVC::CommandResult ret =
3115 buffer->lyxvc().checkIn(log);
3117 if (ret == LyXVC::ErrorCommand ||
3118 ret == LyXVC::VCSuccess)
3119 reloadBuffer(*buffer);
3120 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3121 frontend::Alert::error(
3122 _("Revision control error."),
3123 _("Document could not be checked in."));
3127 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3128 LV_VC_RENAME : LV_VC_COPY;
3129 renameBuffer(*buffer, cmd.argument(), kind);
3134 case LFUN_VC_CHECK_IN:
3135 if (!buffer || !ensureBufferClean(buffer))
3137 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3139 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3141 // Only skip reloading if the checkin was cancelled or
3142 // an error occurred before the real checkin VCS command
3143 // was executed, since the VCS might have changed the
3144 // file even if it could not checkin successfully.
3145 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3146 reloadBuffer(*buffer);
3150 case LFUN_VC_CHECK_OUT:
3151 if (!buffer || !ensureBufferClean(buffer))
3153 if (buffer->lyxvc().inUse()) {
3154 dr.setMessage(buffer->lyxvc().checkOut());
3155 reloadBuffer(*buffer);
3159 case LFUN_VC_LOCKING_TOGGLE:
3160 LASSERT(buffer, return);
3161 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3163 if (buffer->lyxvc().inUse()) {
3164 string res = buffer->lyxvc().lockingToggle();
3166 frontend::Alert::error(_("Revision control error."),
3167 _("Error when setting the locking property."));
3170 reloadBuffer(*buffer);
3175 case LFUN_VC_REVERT:
3176 LASSERT(buffer, return);
3177 if (buffer->lyxvc().revert()) {
3178 reloadBuffer(*buffer);
3179 dr.clearMessageUpdate();
3183 case LFUN_VC_UNDO_LAST:
3184 LASSERT(buffer, return);
3185 buffer->lyxvc().undoLast();
3186 reloadBuffer(*buffer);
3187 dr.clearMessageUpdate();
3190 case LFUN_VC_REPO_UPDATE:
3191 LASSERT(buffer, return);
3192 if (ensureBufferClean(buffer)) {
3193 dr.setMessage(buffer->lyxvc().repoUpdate());
3194 checkExternallyModifiedBuffers();
3198 case LFUN_VC_COMMAND: {
3199 string flag = cmd.getArg(0);
3200 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3203 if (contains(flag, 'M')) {
3204 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3207 string path = cmd.getArg(1);
3208 if (contains(path, "$$p") && buffer)
3209 path = subst(path, "$$p", buffer->filePath());
3210 LYXERR(Debug::LYXVC, "Directory: " << path);
3212 if (!pp.isReadableDirectory()) {
3213 lyxerr << _("Directory is not accessible.") << endl;
3216 support::PathChanger p(pp);
3218 string command = cmd.getArg(2);
3219 if (command.empty())
3222 command = subst(command, "$$i", buffer->absFileName());
3223 command = subst(command, "$$p", buffer->filePath());
3225 command = subst(command, "$$m", to_utf8(message));
3226 LYXERR(Debug::LYXVC, "Command: " << command);
3228 one.startscript(Systemcall::Wait, command);
3232 if (contains(flag, 'I'))
3233 buffer->markDirty();
3234 if (contains(flag, 'R'))
3235 reloadBuffer(*buffer);
3240 case LFUN_VC_COMPARE: {
3241 if (cmd.argument().empty()) {
3242 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3246 string rev1 = cmd.getArg(0);
3250 // it seems safe to assume we have a buffer
3251 // coverity[FORWARD_NULL]
3252 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3255 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3256 f2 = buffer->absFileName();
3258 string rev2 = cmd.getArg(1);
3262 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3266 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3267 f1 << "\n" << f2 << "\n" );
3268 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3269 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3279 void GuiView::openChildDocument(string const & fname)
3281 LASSERT(documentBufferView(), return);
3282 Buffer & buffer = documentBufferView()->buffer();
3283 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3284 documentBufferView()->saveBookmark(false);
3286 if (theBufferList().exists(filename)) {
3287 child = theBufferList().getBuffer(filename);
3290 message(bformat(_("Opening child document %1$s..."),
3291 makeDisplayPath(filename.absFileName())));
3292 child = loadDocument(filename, false);
3294 // Set the parent name of the child document.
3295 // This makes insertion of citations and references in the child work,
3296 // when the target is in the parent or another child document.
3298 child->setParent(&buffer);
3302 bool GuiView::goToFileRow(string const & argument)
3306 size_t i = argument.find_last_of(' ');
3307 if (i != string::npos) {
3308 file_name = os::internal_path(trim(argument.substr(0, i)));
3309 istringstream is(argument.substr(i + 1));
3314 if (i == string::npos) {
3315 LYXERR0("Wrong argument: " << argument);
3319 string const abstmp = package().temp_dir().absFileName();
3320 string const realtmp = package().temp_dir().realPath();
3321 // We have to use os::path_prefix_is() here, instead of
3322 // simply prefixIs(), because the file name comes from
3323 // an external application and may need case adjustment.
3324 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3325 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3326 // Needed by inverse dvi search. If it is a file
3327 // in tmpdir, call the apropriated function.
3328 // If tmpdir is a symlink, we may have the real
3329 // path passed back, so we correct for that.
3330 if (!prefixIs(file_name, abstmp))
3331 file_name = subst(file_name, realtmp, abstmp);
3332 buf = theBufferList().getBufferFromTmp(file_name);
3334 // Must replace extension of the file to be .lyx
3335 // and get full path
3336 FileName const s = fileSearch(string(),
3337 support::changeExtension(file_name, ".lyx"), "lyx");
3338 // Either change buffer or load the file
3339 if (theBufferList().exists(s))
3340 buf = theBufferList().getBuffer(s);
3341 else if (s.exists()) {
3342 buf = loadDocument(s);
3347 _("File does not exist: %1$s"),
3348 makeDisplayPath(file_name)));
3354 _("No buffer for file: %1$s."),
3355 makeDisplayPath(file_name))
3360 bool success = documentBufferView()->setCursorFromRow(row);
3362 LYXERR(Debug::LATEX,
3363 "setCursorFromRow: invalid position for row " << row);
3364 frontend::Alert::error(_("Inverse Search Failed"),
3365 _("Invalid position requested by inverse search.\n"
3366 "You may need to update the viewed document."));
3372 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3374 QMenu * menu = new QMenu;
3375 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3376 menu->exec(QCursor::pos());
3381 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3383 Buffer::ExportStatus const status = func(format);
3385 // the cloning operation will have produced a clone of the entire set of
3386 // documents, starting from the master. so we must delete those.
3387 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3389 busyBuffers.remove(orig);
3394 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3396 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3397 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3401 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3403 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3404 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3408 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3410 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3411 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3415 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3416 string const & argument,
3417 Buffer const * used_buffer,
3418 docstring const & msg,
3419 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3420 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3421 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3426 string format = argument;
3428 format = used_buffer->params().getDefaultOutputFormat();
3429 processing_format = format;
3431 progress_->clearMessages();
3434 #if EXPORT_in_THREAD
3435 GuiViewPrivate::busyBuffers.insert(used_buffer);
3436 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3437 if (!cloned_buffer) {
3438 Alert::error(_("Export Error"),
3439 _("Error cloning the Buffer."));
3442 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3447 setPreviewFuture(f);
3448 last_export_format = used_buffer->params().bufferFormat();
3451 // We are asynchronous, so we don't know here anything about the success
3454 Buffer::ExportStatus status;
3456 status = (used_buffer->*syncFunc)(format, true);
3457 } else if (previewFunc) {
3458 status = (used_buffer->*previewFunc)(format);
3461 handleExportStatus(gv_, status, format);
3463 return (status == Buffer::ExportSuccess
3464 || status == Buffer::PreviewSuccess);
3468 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3470 BufferView * bv = currentBufferView();
3471 LASSERT(bv, return);
3473 // Let the current BufferView dispatch its own actions.
3474 bv->dispatch(cmd, dr);
3475 if (dr.dispatched())
3478 // Try with the document BufferView dispatch if any.
3479 BufferView * doc_bv = documentBufferView();
3480 if (doc_bv && doc_bv != bv) {
3481 doc_bv->dispatch(cmd, dr);
3482 if (dr.dispatched())
3486 // Then let the current Cursor dispatch its own actions.
3487 bv->cursor().dispatch(cmd);
3489 // update completion. We do it here and not in
3490 // processKeySym to avoid another redraw just for a
3491 // changed inline completion
3492 if (cmd.origin() == FuncRequest::KEYBOARD) {
3493 if (cmd.action() == LFUN_SELF_INSERT
3494 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3495 updateCompletion(bv->cursor(), true, true);
3496 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3497 updateCompletion(bv->cursor(), false, true);
3499 updateCompletion(bv->cursor(), false, false);
3502 dr = bv->cursor().result();
3506 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3508 BufferView * bv = currentBufferView();
3509 // By default we won't need any update.
3510 dr.screenUpdate(Update::None);
3511 // assume cmd will be dispatched
3512 dr.dispatched(true);
3514 Buffer * doc_buffer = documentBufferView()
3515 ? &(documentBufferView()->buffer()) : 0;
3517 if (cmd.origin() == FuncRequest::TOC) {
3518 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3519 // FIXME: do we need to pass a DispatchResult object here?
3520 toc->doDispatch(bv->cursor(), cmd);
3524 string const argument = to_utf8(cmd.argument());
3526 switch(cmd.action()) {
3527 case LFUN_BUFFER_CHILD_OPEN:
3528 openChildDocument(to_utf8(cmd.argument()));
3531 case LFUN_BUFFER_IMPORT:
3532 importDocument(to_utf8(cmd.argument()));
3535 case LFUN_BUFFER_EXPORT: {
3538 // GCC only sees strfwd.h when building merged
3539 if (::lyx::operator==(cmd.argument(), "custom")) {
3540 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3544 string const dest = cmd.getArg(1);
3545 FileName target_dir;
3546 if (!dest.empty() && FileName::isAbsolute(dest))
3547 target_dir = FileName(support::onlyPath(dest));
3549 target_dir = doc_buffer->fileName().onlyPath();
3551 string const format = argument.empty() ?
3552 doc_buffer->params().getDefaultOutputFormat() : argument;
3554 if ((dest.empty() && doc_buffer->isUnnamed())
3555 || !target_dir.isDirWritable()) {
3556 exportBufferAs(*doc_buffer, from_utf8(format));
3559 /* TODO/Review: Is it a problem to also export the children?
3560 See the update_unincluded flag */
3561 d.asyncBufferProcessing(format,
3564 &GuiViewPrivate::exportAndDestroy,
3567 // TODO Inform user about success
3571 case LFUN_BUFFER_EXPORT_AS: {
3572 LASSERT(doc_buffer, break);
3573 docstring f = cmd.argument();
3575 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3576 exportBufferAs(*doc_buffer, f);
3580 case LFUN_BUFFER_UPDATE: {
3581 d.asyncBufferProcessing(argument,
3584 &GuiViewPrivate::compileAndDestroy,
3589 case LFUN_BUFFER_VIEW: {
3590 d.asyncBufferProcessing(argument,
3592 _("Previewing ..."),
3593 &GuiViewPrivate::previewAndDestroy,
3598 case LFUN_MASTER_BUFFER_UPDATE: {
3599 d.asyncBufferProcessing(argument,
3600 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3602 &GuiViewPrivate::compileAndDestroy,
3607 case LFUN_MASTER_BUFFER_VIEW: {
3608 d.asyncBufferProcessing(argument,
3609 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3611 &GuiViewPrivate::previewAndDestroy,
3612 0, &Buffer::preview);
3615 case LFUN_BUFFER_SWITCH: {
3616 string const file_name = to_utf8(cmd.argument());
3617 if (!FileName::isAbsolute(file_name)) {
3619 dr.setMessage(_("Absolute filename expected."));
3623 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3626 dr.setMessage(_("Document not loaded"));
3630 // Do we open or switch to the buffer in this view ?
3631 if (workArea(*buffer)
3632 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3637 // Look for the buffer in other views
3638 QList<int> const ids = guiApp->viewIds();
3640 for (; i != ids.size(); ++i) {
3641 GuiView & gv = guiApp->view(ids[i]);
3642 if (gv.workArea(*buffer)) {
3644 gv.activateWindow();
3646 gv.setBuffer(buffer);
3651 // If necessary, open a new window as a last resort
3652 if (i == ids.size()) {
3653 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3659 case LFUN_BUFFER_NEXT:
3660 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3663 case LFUN_BUFFER_MOVE_NEXT:
3664 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3667 case LFUN_BUFFER_PREVIOUS:
3668 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3671 case LFUN_BUFFER_MOVE_PREVIOUS:
3672 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3675 case LFUN_COMMAND_EXECUTE: {
3676 command_execute_ = true;
3677 minibuffer_focus_ = true;
3680 case LFUN_DROP_LAYOUTS_CHOICE:
3681 d.layout_->showPopup();
3684 case LFUN_MENU_OPEN:
3685 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3686 menu->exec(QCursor::pos());
3689 case LFUN_FILE_INSERT:
3690 insertLyXFile(cmd.argument());
3693 case LFUN_FILE_INSERT_PLAINTEXT:
3694 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3695 string const fname = to_utf8(cmd.argument());
3696 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3697 dr.setMessage(_("Absolute filename expected."));
3701 FileName filename(fname);
3702 if (fname.empty()) {
3703 FileDialog dlg(qt_("Select file to insert"));
3705 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3706 QStringList(qt_("All Files (*)")));
3708 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3709 dr.setMessage(_("Canceled."));
3713 filename.set(fromqstr(result.second));
3717 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3718 bv->dispatch(new_cmd, dr);
3723 case LFUN_BUFFER_RELOAD: {
3724 LASSERT(doc_buffer, break);
3727 if (!doc_buffer->isClean()) {
3728 docstring const file =
3729 makeDisplayPath(doc_buffer->absFileName(), 20);
3730 docstring text = bformat(_("Any changes will be lost. "
3731 "Are you sure you want to revert to the saved version "
3732 "of the document %1$s?"), file);
3733 ret = Alert::prompt(_("Revert to saved document?"),
3734 text, 1, 1, _("&Revert"), _("&Cancel"));
3738 doc_buffer->markClean();
3739 reloadBuffer(*doc_buffer);
3740 dr.forceBufferUpdate();
3745 case LFUN_BUFFER_WRITE:
3746 LASSERT(doc_buffer, break);
3747 saveBuffer(*doc_buffer);
3750 case LFUN_BUFFER_WRITE_AS:
3751 LASSERT(doc_buffer, break);
3752 renameBuffer(*doc_buffer, cmd.argument());
3755 case LFUN_BUFFER_WRITE_ALL: {
3756 Buffer * first = theBufferList().first();
3759 message(_("Saving all documents..."));
3760 // We cannot use a for loop as the buffer list cycles.
3763 if (!b->isClean()) {
3765 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3767 b = theBufferList().next(b);
3768 } while (b != first);
3769 dr.setMessage(_("All documents saved."));
3773 case LFUN_BUFFER_CLOSE:
3777 case LFUN_BUFFER_CLOSE_ALL:
3781 case LFUN_TOOLBAR_TOGGLE: {
3782 string const name = cmd.getArg(0);
3783 if (GuiToolbar * t = toolbar(name))
3788 case LFUN_ICON_SIZE: {
3789 QSize size = d.iconSize(cmd.argument());
3791 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3792 size.width(), size.height()));
3796 case LFUN_DIALOG_UPDATE: {
3797 string const name = to_utf8(cmd.argument());
3798 if (name == "prefs" || name == "document")
3799 updateDialog(name, string());
3800 else if (name == "paragraph")
3801 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3802 else if (currentBufferView()) {
3803 Inset * inset = currentBufferView()->editedInset(name);
3804 // Can only update a dialog connected to an existing inset
3806 // FIXME: get rid of this indirection; GuiView ask the inset
3807 // if he is kind enough to update itself...
3808 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3809 //FIXME: pass DispatchResult here?
3810 inset->dispatch(currentBufferView()->cursor(), fr);
3816 case LFUN_DIALOG_TOGGLE: {
3817 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3818 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3819 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3823 case LFUN_DIALOG_DISCONNECT_INSET:
3824 disconnectDialog(to_utf8(cmd.argument()));
3827 case LFUN_DIALOG_HIDE: {
3828 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3832 case LFUN_DIALOG_SHOW: {
3833 string const name = cmd.getArg(0);
3834 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3836 if (name == "character") {
3837 data = freefont2string();
3839 showDialog("character", data);
3840 } else if (name == "latexlog") {
3841 Buffer::LogType type;
3842 string const logfile = doc_buffer->logName(&type);
3844 case Buffer::latexlog:
3847 case Buffer::buildlog:
3851 data += Lexer::quoteString(logfile);
3852 showDialog("log", data);
3853 } else if (name == "vclog") {
3854 string const data = "vc " +
3855 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3856 showDialog("log", data);
3857 } else if (name == "symbols") {
3858 data = bv->cursor().getEncoding()->name();
3860 showDialog("symbols", data);
3862 } else if (name == "prefs" && isFullScreen()) {
3863 lfunUiToggle("fullscreen");
3864 showDialog("prefs", data);
3866 showDialog(name, data);
3871 dr.setMessage(cmd.argument());
3874 case LFUN_UI_TOGGLE: {
3875 string arg = cmd.getArg(0);
3876 if (!lfunUiToggle(arg)) {
3877 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3878 dr.setMessage(bformat(msg, from_utf8(arg)));
3880 // Make sure the keyboard focus stays in the work area.
3885 case LFUN_VIEW_SPLIT: {
3886 LASSERT(doc_buffer, break);
3887 string const orientation = cmd.getArg(0);
3888 d.splitter_->setOrientation(orientation == "vertical"
3889 ? Qt::Vertical : Qt::Horizontal);
3890 TabWorkArea * twa = addTabWorkArea();
3891 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3892 setCurrentWorkArea(wa);
3895 case LFUN_TAB_GROUP_CLOSE:
3896 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3897 closeTabWorkArea(twa);
3898 d.current_work_area_ = 0;
3899 twa = d.currentTabWorkArea();
3900 // Switch to the next GuiWorkArea in the found TabWorkArea.
3902 // Make sure the work area is up to date.
3903 setCurrentWorkArea(twa->currentWorkArea());
3905 setCurrentWorkArea(0);
3910 case LFUN_VIEW_CLOSE:
3911 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3912 closeWorkArea(twa->currentWorkArea());
3913 d.current_work_area_ = 0;
3914 twa = d.currentTabWorkArea();
3915 // Switch to the next GuiWorkArea in the found TabWorkArea.
3917 // Make sure the work area is up to date.
3918 setCurrentWorkArea(twa->currentWorkArea());
3920 setCurrentWorkArea(0);
3925 case LFUN_COMPLETION_INLINE:
3926 if (d.current_work_area_)
3927 d.current_work_area_->completer().showInline();
3930 case LFUN_COMPLETION_POPUP:
3931 if (d.current_work_area_)
3932 d.current_work_area_->completer().showPopup();
3937 if (d.current_work_area_)
3938 d.current_work_area_->completer().tab();
3941 case LFUN_COMPLETION_CANCEL:
3942 if (d.current_work_area_) {
3943 if (d.current_work_area_->completer().popupVisible())
3944 d.current_work_area_->completer().hidePopup();
3946 d.current_work_area_->completer().hideInline();
3950 case LFUN_COMPLETION_ACCEPT:
3951 if (d.current_work_area_)
3952 d.current_work_area_->completer().activate();
3955 case LFUN_BUFFER_ZOOM_IN:
3956 case LFUN_BUFFER_ZOOM_OUT: {
3957 // use a signed temp to avoid overflow
3958 int zoom = lyxrc.zoom;
3959 if (cmd.argument().empty()) {
3960 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3965 zoom += convert<int>(cmd.argument());
3967 if (zoom < static_cast<int>(zoom_min_))
3971 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3973 // The global QPixmapCache is used in GuiPainter to cache text
3974 // painting so we must reset it.
3975 QPixmapCache::clear();
3976 guiApp->fontLoader().update();
3977 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3981 case LFUN_VC_REGISTER:
3982 case LFUN_VC_RENAME:
3984 case LFUN_VC_CHECK_IN:
3985 case LFUN_VC_CHECK_OUT:
3986 case LFUN_VC_REPO_UPDATE:
3987 case LFUN_VC_LOCKING_TOGGLE:
3988 case LFUN_VC_REVERT:
3989 case LFUN_VC_UNDO_LAST:
3990 case LFUN_VC_COMMAND:
3991 case LFUN_VC_COMPARE:
3992 dispatchVC(cmd, dr);
3995 case LFUN_SERVER_GOTO_FILE_ROW:
3996 if(goToFileRow(to_utf8(cmd.argument())))
3997 dr.screenUpdate(Update::Force | Update::FitCursor);
4000 case LFUN_LYX_ACTIVATE:
4004 case LFUN_FORWARD_SEARCH: {
4005 // it seems safe to assume we have a document buffer, since
4006 // getStatus wants one.
4007 // coverity[FORWARD_NULL]
4008 Buffer const * doc_master = doc_buffer->masterBuffer();
4009 FileName const path(doc_master->temppath());
4010 string const texname = doc_master->isChild(doc_buffer)
4011 ? DocFileName(changeExtension(
4012 doc_buffer->absFileName(),
4013 "tex")).mangledFileName()
4014 : doc_buffer->latexName();
4015 string const fulltexname =
4016 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4017 string const mastername =
4018 removeExtension(doc_master->latexName());
4019 FileName const dviname(addName(path.absFileName(),
4020 addExtension(mastername, "dvi")));
4021 FileName const pdfname(addName(path.absFileName(),
4022 addExtension(mastername, "pdf")));
4023 bool const have_dvi = dviname.exists();
4024 bool const have_pdf = pdfname.exists();
4025 if (!have_dvi && !have_pdf) {
4026 dr.setMessage(_("Please, preview the document first."));
4029 string outname = dviname.onlyFileName();
4030 string command = lyxrc.forward_search_dvi;
4031 if (!have_dvi || (have_pdf &&
4032 pdfname.lastModified() > dviname.lastModified())) {
4033 outname = pdfname.onlyFileName();
4034 command = lyxrc.forward_search_pdf;
4037 DocIterator cur = bv->cursor();
4038 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4039 LYXERR(Debug::ACTION, "Forward search: row:" << row
4041 if (row == -1 || command.empty()) {
4042 dr.setMessage(_("Couldn't proceed."));
4045 string texrow = convert<string>(row);
4047 command = subst(command, "$$n", texrow);
4048 command = subst(command, "$$f", fulltexname);
4049 command = subst(command, "$$t", texname);
4050 command = subst(command, "$$o", outname);
4052 PathChanger p(path);
4054 one.startscript(Systemcall::DontWait, command);
4058 case LFUN_SPELLING_CONTINUOUSLY:
4059 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4060 dr.screenUpdate(Update::Force);
4064 // The LFUN must be for one of BufferView, Buffer or Cursor;
4066 dispatchToBufferView(cmd, dr);
4070 // Part of automatic menu appearance feature.
4071 if (isFullScreen()) {
4072 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4076 // Need to update bv because many LFUNs here might have destroyed it
4077 bv = currentBufferView();
4079 // Clear non-empty selections
4080 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4082 Cursor & cur = bv->cursor();
4083 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4084 cur.clearSelection();
4090 bool GuiView::lfunUiToggle(string const & ui_component)
4092 if (ui_component == "scrollbar") {
4093 // hide() is of no help
4094 if (d.current_work_area_->verticalScrollBarPolicy() ==
4095 Qt::ScrollBarAlwaysOff)
4097 d.current_work_area_->setVerticalScrollBarPolicy(
4098 Qt::ScrollBarAsNeeded);
4100 d.current_work_area_->setVerticalScrollBarPolicy(
4101 Qt::ScrollBarAlwaysOff);
4102 } else if (ui_component == "statusbar") {
4103 statusBar()->setVisible(!statusBar()->isVisible());
4104 } else if (ui_component == "menubar") {
4105 menuBar()->setVisible(!menuBar()->isVisible());
4107 if (ui_component == "frame") {
4109 getContentsMargins(&l, &t, &r, &b);
4110 //are the frames in default state?
4111 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4113 setContentsMargins(-2, -2, -2, -2);
4115 setContentsMargins(0, 0, 0, 0);
4118 if (ui_component == "fullscreen") {
4126 void GuiView::toggleFullScreen()
4128 if (isFullScreen()) {
4129 for (int i = 0; i != d.splitter_->count(); ++i)
4130 d.tabWorkArea(i)->setFullScreen(false);
4131 setContentsMargins(0, 0, 0, 0);
4132 setWindowState(windowState() ^ Qt::WindowFullScreen);
4135 statusBar()->show();
4138 hideDialogs("prefs", 0);
4139 for (int i = 0; i != d.splitter_->count(); ++i)
4140 d.tabWorkArea(i)->setFullScreen(true);
4141 setContentsMargins(-2, -2, -2, -2);
4143 setWindowState(windowState() ^ Qt::WindowFullScreen);
4144 if (lyxrc.full_screen_statusbar)
4145 statusBar()->hide();
4146 if (lyxrc.full_screen_menubar)
4148 if (lyxrc.full_screen_toolbars) {
4149 ToolbarMap::iterator end = d.toolbars_.end();
4150 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4155 // give dialogs like the TOC a chance to adapt
4160 Buffer const * GuiView::updateInset(Inset const * inset)
4165 Buffer const * inset_buffer = &(inset->buffer());
4167 for (int i = 0; i != d.splitter_->count(); ++i) {
4168 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4171 Buffer const * buffer = &(wa->bufferView().buffer());
4172 if (inset_buffer == buffer)
4173 wa->scheduleRedraw();
4175 return inset_buffer;
4179 void GuiView::restartCursor()
4181 /* When we move around, or type, it's nice to be able to see
4182 * the cursor immediately after the keypress.
4184 if (d.current_work_area_)
4185 d.current_work_area_->startBlinkingCursor();
4187 // Take this occasion to update the other GUI elements.
4193 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4195 if (d.current_work_area_)
4196 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4201 // This list should be kept in sync with the list of insets in
4202 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4203 // dialog should have the same name as the inset.
4204 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4205 // docs in LyXAction.cpp.
4207 char const * const dialognames[] = {
4209 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4210 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4211 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4212 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4213 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4214 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4215 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4216 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4218 char const * const * const end_dialognames =
4219 dialognames + (sizeof(dialognames) / sizeof(char *));
4223 cmpCStr(char const * name) : name_(name) {}
4224 bool operator()(char const * other) {
4225 return strcmp(other, name_) == 0;
4232 bool isValidName(string const & name)
4234 return find_if(dialognames, end_dialognames,
4235 cmpCStr(name.c_str())) != end_dialognames;
4241 void GuiView::resetDialogs()
4243 // Make sure that no LFUN uses any GuiView.
4244 guiApp->setCurrentView(0);
4248 constructToolbars();
4249 guiApp->menus().fillMenuBar(menuBar(), this, false);
4250 d.layout_->updateContents(true);
4251 // Now update controls with current buffer.
4252 guiApp->setCurrentView(this);
4258 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4260 if (!isValidName(name))
4263 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4265 if (it != d.dialogs_.end()) {
4267 it->second->hideView();
4268 return it->second.get();
4271 Dialog * dialog = build(name);
4272 d.dialogs_[name].reset(dialog);
4273 if (lyxrc.allow_geometry_session)
4274 dialog->restoreSession();
4281 void GuiView::showDialog(string const & name, string const & data,
4284 triggerShowDialog(toqstr(name), toqstr(data), inset);
4288 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4294 const string name = fromqstr(qname);
4295 const string data = fromqstr(qdata);
4299 Dialog * dialog = findOrBuild(name, false);
4301 bool const visible = dialog->isVisibleView();
4302 dialog->showData(data);
4303 if (inset && currentBufferView())
4304 currentBufferView()->editInset(name, inset);
4305 // We only set the focus to the new dialog if it was not yet
4306 // visible in order not to change the existing previous behaviour
4308 // activateWindow is needed for floating dockviews
4309 dialog->asQWidget()->raise();
4310 dialog->asQWidget()->activateWindow();
4311 dialog->asQWidget()->setFocus();
4315 catch (ExceptionMessage const & ex) {
4323 bool GuiView::isDialogVisible(string const & name) const
4325 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4326 if (it == d.dialogs_.end())
4328 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4332 void GuiView::hideDialog(string const & name, Inset * inset)
4334 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4335 if (it == d.dialogs_.end())
4339 if (!currentBufferView())
4341 if (inset != currentBufferView()->editedInset(name))
4345 Dialog * const dialog = it->second.get();
4346 if (dialog->isVisibleView())
4348 if (currentBufferView())
4349 currentBufferView()->editInset(name, 0);
4353 void GuiView::disconnectDialog(string const & name)
4355 if (!isValidName(name))
4357 if (currentBufferView())
4358 currentBufferView()->editInset(name, 0);
4362 void GuiView::hideAll() const
4364 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4365 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4367 for(; it != end; ++it)
4368 it->second->hideView();
4372 void GuiView::updateDialogs()
4374 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4375 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4377 for(; it != end; ++it) {
4378 Dialog * dialog = it->second.get();
4380 if (dialog->needBufferOpen() && !documentBufferView())
4381 hideDialog(fromqstr(dialog->name()), 0);
4382 else if (dialog->isVisibleView())
4383 dialog->checkStatus();
4390 Dialog * createDialog(GuiView & lv, string const & name);
4392 // will be replaced by a proper factory...
4393 Dialog * createGuiAbout(GuiView & lv);
4394 Dialog * createGuiBibtex(GuiView & lv);
4395 Dialog * createGuiChanges(GuiView & lv);
4396 Dialog * createGuiCharacter(GuiView & lv);
4397 Dialog * createGuiCitation(GuiView & lv);
4398 Dialog * createGuiCompare(GuiView & lv);
4399 Dialog * createGuiCompareHistory(GuiView & lv);
4400 Dialog * createGuiDelimiter(GuiView & lv);
4401 Dialog * createGuiDocument(GuiView & lv);
4402 Dialog * createGuiErrorList(GuiView & lv);
4403 Dialog * createGuiExternal(GuiView & lv);
4404 Dialog * createGuiGraphics(GuiView & lv);
4405 Dialog * createGuiInclude(GuiView & lv);
4406 Dialog * createGuiIndex(GuiView & lv);
4407 Dialog * createGuiListings(GuiView & lv);
4408 Dialog * createGuiLog(GuiView & lv);
4409 Dialog * createGuiMathMatrix(GuiView & lv);
4410 Dialog * createGuiNote(GuiView & lv);
4411 Dialog * createGuiParagraph(GuiView & lv);
4412 Dialog * createGuiPhantom(GuiView & lv);
4413 Dialog * createGuiPreferences(GuiView & lv);
4414 Dialog * createGuiPrint(GuiView & lv);
4415 Dialog * createGuiPrintindex(GuiView & lv);
4416 Dialog * createGuiRef(GuiView & lv);
4417 Dialog * createGuiSearch(GuiView & lv);
4418 Dialog * createGuiSearchAdv(GuiView & lv);
4419 Dialog * createGuiSendTo(GuiView & lv);
4420 Dialog * createGuiShowFile(GuiView & lv);
4421 Dialog * createGuiSpellchecker(GuiView & lv);
4422 Dialog * createGuiSymbols(GuiView & lv);
4423 Dialog * createGuiTabularCreate(GuiView & lv);
4424 Dialog * createGuiTexInfo(GuiView & lv);
4425 Dialog * createGuiToc(GuiView & lv);
4426 Dialog * createGuiThesaurus(GuiView & lv);
4427 Dialog * createGuiViewSource(GuiView & lv);
4428 Dialog * createGuiWrap(GuiView & lv);
4429 Dialog * createGuiProgressView(GuiView & lv);
4433 Dialog * GuiView::build(string const & name)
4435 LASSERT(isValidName(name), return 0);
4437 Dialog * dialog = createDialog(*this, name);
4441 if (name == "aboutlyx")
4442 return createGuiAbout(*this);
4443 if (name == "bibtex")
4444 return createGuiBibtex(*this);
4445 if (name == "changes")
4446 return createGuiChanges(*this);
4447 if (name == "character")
4448 return createGuiCharacter(*this);
4449 if (name == "citation")
4450 return createGuiCitation(*this);
4451 if (name == "compare")
4452 return createGuiCompare(*this);
4453 if (name == "comparehistory")
4454 return createGuiCompareHistory(*this);
4455 if (name == "document")
4456 return createGuiDocument(*this);
4457 if (name == "errorlist")
4458 return createGuiErrorList(*this);
4459 if (name == "external")
4460 return createGuiExternal(*this);
4462 return createGuiShowFile(*this);
4463 if (name == "findreplace")
4464 return createGuiSearch(*this);
4465 if (name == "findreplaceadv")
4466 return createGuiSearchAdv(*this);
4467 if (name == "graphics")
4468 return createGuiGraphics(*this);
4469 if (name == "include")
4470 return createGuiInclude(*this);
4471 if (name == "index")
4472 return createGuiIndex(*this);
4473 if (name == "index_print")
4474 return createGuiPrintindex(*this);
4475 if (name == "listings")
4476 return createGuiListings(*this);
4478 return createGuiLog(*this);
4479 if (name == "mathdelimiter")
4480 return createGuiDelimiter(*this);
4481 if (name == "mathmatrix")
4482 return createGuiMathMatrix(*this);
4484 return createGuiNote(*this);
4485 if (name == "paragraph")
4486 return createGuiParagraph(*this);
4487 if (name == "phantom")
4488 return createGuiPhantom(*this);
4489 if (name == "prefs")
4490 return createGuiPreferences(*this);
4492 return createGuiRef(*this);
4493 if (name == "sendto")
4494 return createGuiSendTo(*this);
4495 if (name == "spellchecker")
4496 return createGuiSpellchecker(*this);
4497 if (name == "symbols")
4498 return createGuiSymbols(*this);
4499 if (name == "tabularcreate")
4500 return createGuiTabularCreate(*this);
4501 if (name == "texinfo")
4502 return createGuiTexInfo(*this);
4503 if (name == "thesaurus")
4504 return createGuiThesaurus(*this);
4506 return createGuiToc(*this);
4507 if (name == "view-source")
4508 return createGuiViewSource(*this);
4510 return createGuiWrap(*this);
4511 if (name == "progress")
4512 return createGuiProgressView(*this);
4518 } // namespace frontend
4521 #include "moc_GuiView.cpp"