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() && !doc_buffer->isClean();
1818 case LFUN_BUFFER_CHILD_OPEN:
1819 enable = doc_buffer != 0;
1822 case LFUN_BUFFER_WRITE:
1823 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1826 //FIXME: This LFUN should be moved to GuiApplication.
1827 case LFUN_BUFFER_WRITE_ALL: {
1828 // We enable the command only if there are some modified buffers
1829 Buffer * first = theBufferList().first();
1834 // We cannot use a for loop as the buffer list is a cycle.
1836 if (!b->isClean()) {
1840 b = theBufferList().next(b);
1841 } while (b != first);
1845 case LFUN_BUFFER_WRITE_AS:
1846 case LFUN_BUFFER_EXPORT_AS:
1847 enable = doc_buffer != 0;
1850 case LFUN_BUFFER_CLOSE:
1851 case LFUN_VIEW_CLOSE:
1852 enable = doc_buffer != 0;
1855 case LFUN_BUFFER_CLOSE_ALL:
1856 enable = theBufferList().last() != theBufferList().first();
1859 case LFUN_VIEW_SPLIT:
1860 if (cmd.getArg(0) == "vertical")
1861 enable = doc_buffer && (d.splitter_->count() == 1 ||
1862 d.splitter_->orientation() == Qt::Vertical);
1864 enable = doc_buffer && (d.splitter_->count() == 1 ||
1865 d.splitter_->orientation() == Qt::Horizontal);
1868 case LFUN_TAB_GROUP_CLOSE:
1869 enable = d.tabWorkAreaCount() > 1;
1872 case LFUN_TOOLBAR_TOGGLE: {
1873 string const name = cmd.getArg(0);
1874 if (GuiToolbar * t = toolbar(name))
1875 flag.setOnOff(t->isVisible());
1878 docstring const msg =
1879 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1885 case LFUN_ICON_SIZE:
1886 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1889 case LFUN_DROP_LAYOUTS_CHOICE:
1893 case LFUN_UI_TOGGLE:
1894 flag.setOnOff(isFullScreen());
1897 case LFUN_DIALOG_DISCONNECT_INSET:
1900 case LFUN_DIALOG_HIDE:
1901 // FIXME: should we check if the dialog is shown?
1904 case LFUN_DIALOG_TOGGLE:
1905 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1906 // fall through to set "enable"
1907 case LFUN_DIALOG_SHOW: {
1908 string const name = cmd.getArg(0);
1910 enable = name == "aboutlyx"
1911 || name == "file" //FIXME: should be removed.
1913 || name == "texinfo"
1914 || name == "progress"
1915 || name == "compare";
1916 else if (name == "character" || name == "symbols"
1917 || name == "mathdelimiter" || name == "mathmatrix") {
1918 if (!buf || buf->isReadonly())
1921 Cursor const & cur = currentBufferView()->cursor();
1922 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1925 else if (name == "latexlog")
1926 enable = FileName(doc_buffer->logName()).isReadableFile();
1927 else if (name == "spellchecker")
1928 enable = theSpellChecker()
1929 && !doc_buffer->isReadonly()
1930 && !doc_buffer->text().empty();
1931 else if (name == "vclog")
1932 enable = doc_buffer->lyxvc().inUse();
1936 case LFUN_DIALOG_UPDATE: {
1937 string const name = cmd.getArg(0);
1939 enable = name == "prefs";
1943 case LFUN_COMMAND_EXECUTE:
1945 case LFUN_MENU_OPEN:
1946 // Nothing to check.
1949 case LFUN_COMPLETION_INLINE:
1950 if (!d.current_work_area_
1951 || !d.current_work_area_->completer().inlinePossible(
1952 currentBufferView()->cursor()))
1956 case LFUN_COMPLETION_POPUP:
1957 if (!d.current_work_area_
1958 || !d.current_work_area_->completer().popupPossible(
1959 currentBufferView()->cursor()))
1964 if (!d.current_work_area_
1965 || !d.current_work_area_->completer().inlinePossible(
1966 currentBufferView()->cursor()))
1970 case LFUN_COMPLETION_ACCEPT:
1971 if (!d.current_work_area_
1972 || (!d.current_work_area_->completer().popupVisible()
1973 && !d.current_work_area_->completer().inlineVisible()
1974 && !d.current_work_area_->completer().completionAvailable()))
1978 case LFUN_COMPLETION_CANCEL:
1979 if (!d.current_work_area_
1980 || (!d.current_work_area_->completer().popupVisible()
1981 && !d.current_work_area_->completer().inlineVisible()))
1985 case LFUN_BUFFER_ZOOM_OUT:
1986 case LFUN_BUFFER_ZOOM_IN: {
1987 // only diff between these two is that the default for ZOOM_OUT
1989 bool const neg_zoom =
1990 convert<int>(cmd.argument()) < 0 ||
1991 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
1992 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
1993 docstring const msg =
1994 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
1998 enable = doc_buffer;
2001 case LFUN_BUFFER_MOVE_NEXT:
2002 case LFUN_BUFFER_MOVE_PREVIOUS:
2003 // we do not cycle when moving
2004 case LFUN_BUFFER_NEXT:
2005 case LFUN_BUFFER_PREVIOUS:
2006 // because we cycle, it doesn't matter whether on first or last
2007 enable = (d.currentTabWorkArea()->count() > 1);
2009 case LFUN_BUFFER_SWITCH:
2010 // toggle on the current buffer, but do not toggle off
2011 // the other ones (is that a good idea?)
2013 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2014 flag.setOnOff(true);
2017 case LFUN_VC_REGISTER:
2018 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2020 case LFUN_VC_RENAME:
2021 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2024 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2026 case LFUN_VC_CHECK_IN:
2027 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2029 case LFUN_VC_CHECK_OUT:
2030 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2032 case LFUN_VC_LOCKING_TOGGLE:
2033 enable = doc_buffer && !doc_buffer->isReadonly()
2034 && doc_buffer->lyxvc().lockingToggleEnabled();
2035 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2037 case LFUN_VC_REVERT:
2038 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2040 case LFUN_VC_UNDO_LAST:
2041 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2043 case LFUN_VC_REPO_UPDATE:
2044 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2046 case LFUN_VC_COMMAND: {
2047 if (cmd.argument().empty())
2049 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2053 case LFUN_VC_COMPARE:
2054 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2057 case LFUN_SERVER_GOTO_FILE_ROW:
2058 case LFUN_LYX_ACTIVATE:
2060 case LFUN_FORWARD_SEARCH:
2061 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2064 case LFUN_FILE_INSERT_PLAINTEXT:
2065 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2066 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2069 case LFUN_SPELLING_CONTINUOUSLY:
2070 flag.setOnOff(lyxrc.spellcheck_continuously);
2078 flag.setEnabled(false);
2084 static FileName selectTemplateFile()
2086 FileDialog dlg(qt_("Select template file"));
2087 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2088 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2090 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2091 QStringList(qt_("LyX Documents (*.lyx)")));
2093 if (result.first == FileDialog::Later)
2095 if (result.second.isEmpty())
2097 return FileName(fromqstr(result.second));
2101 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2105 Buffer * newBuffer = 0;
2107 newBuffer = checkAndLoadLyXFile(filename);
2108 } catch (ExceptionMessage const & e) {
2115 message(_("Document not loaded."));
2119 setBuffer(newBuffer);
2120 newBuffer->errors("Parse");
2123 theSession().lastFiles().add(filename);
2129 void GuiView::openDocument(string const & fname)
2131 string initpath = lyxrc.document_path;
2133 if (documentBufferView()) {
2134 string const trypath = documentBufferView()->buffer().filePath();
2135 // If directory is writeable, use this as default.
2136 if (FileName(trypath).isDirWritable())
2142 if (fname.empty()) {
2143 FileDialog dlg(qt_("Select document to open"));
2144 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2145 dlg.setButton2(qt_("Examples|#E#e"),
2146 toqstr(addPath(package().system_support().absFileName(), "examples")));
2148 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2149 FileDialog::Result result =
2150 dlg.open(toqstr(initpath), filter);
2152 if (result.first == FileDialog::Later)
2155 filename = fromqstr(result.second);
2157 // check selected filename
2158 if (filename.empty()) {
2159 message(_("Canceled."));
2165 // get absolute path of file and add ".lyx" to the filename if
2167 FileName const fullname =
2168 fileSearch(string(), filename, "lyx", support::may_not_exist);
2169 if (!fullname.empty())
2170 filename = fullname.absFileName();
2172 if (!fullname.onlyPath().isDirectory()) {
2173 Alert::warning(_("Invalid filename"),
2174 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2175 from_utf8(fullname.absFileName())));
2179 // if the file doesn't exist and isn't already open (bug 6645),
2180 // let the user create one
2181 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2182 !LyXVC::file_not_found_hook(fullname)) {
2183 // the user specifically chose this name. Believe him.
2184 Buffer * const b = newFile(filename, string(), true);
2190 docstring const disp_fn = makeDisplayPath(filename);
2191 message(bformat(_("Opening document %1$s..."), disp_fn));
2194 Buffer * buf = loadDocument(fullname);
2196 str2 = bformat(_("Document %1$s opened."), disp_fn);
2197 if (buf->lyxvc().inUse())
2198 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2199 " " + _("Version control detected.");
2201 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2206 // FIXME: clean that
2207 static bool import(GuiView * lv, FileName const & filename,
2208 string const & format, ErrorList & errorList)
2210 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2212 string loader_format;
2213 vector<string> loaders = theConverters().loaders();
2214 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2215 vector<string>::const_iterator it = loaders.begin();
2216 vector<string>::const_iterator en = loaders.end();
2217 for (; it != en; ++it) {
2218 if (!theConverters().isReachable(format, *it))
2221 string const tofile =
2222 support::changeExtension(filename.absFileName(),
2223 formats.extension(*it));
2224 if (!theConverters().convert(0, filename, FileName(tofile),
2225 filename, format, *it, errorList))
2227 loader_format = *it;
2230 if (loader_format.empty()) {
2231 frontend::Alert::error(_("Couldn't import file"),
2232 bformat(_("No information for importing the format %1$s."),
2233 formats.prettyName(format)));
2237 loader_format = format;
2239 if (loader_format == "lyx") {
2240 Buffer * buf = lv->loadDocument(lyxfile);
2244 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2248 bool as_paragraphs = loader_format == "textparagraph";
2249 string filename2 = (loader_format == format) ? filename.absFileName()
2250 : support::changeExtension(filename.absFileName(),
2251 formats.extension(loader_format));
2252 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2254 guiApp->setCurrentView(lv);
2255 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2262 void GuiView::importDocument(string const & argument)
2265 string filename = split(argument, format, ' ');
2267 LYXERR(Debug::INFO, format << " file: " << filename);
2269 // need user interaction
2270 if (filename.empty()) {
2271 string initpath = lyxrc.document_path;
2272 if (documentBufferView()) {
2273 string const trypath = documentBufferView()->buffer().filePath();
2274 // If directory is writeable, use this as default.
2275 if (FileName(trypath).isDirWritable())
2279 docstring const text = bformat(_("Select %1$s file to import"),
2280 formats.prettyName(format));
2282 FileDialog dlg(toqstr(text));
2283 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2284 dlg.setButton2(qt_("Examples|#E#e"),
2285 toqstr(addPath(package().system_support().absFileName(), "examples")));
2287 docstring filter = formats.prettyName(format);
2290 filter += from_utf8(formats.extensions(format));
2293 FileDialog::Result result =
2294 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2296 if (result.first == FileDialog::Later)
2299 filename = fromqstr(result.second);
2301 // check selected filename
2302 if (filename.empty())
2303 message(_("Canceled."));
2306 if (filename.empty())
2309 // get absolute path of file
2310 FileName const fullname(support::makeAbsPath(filename));
2312 // Can happen if the user entered a path into the dialog
2314 if (fullname.onlyFileName().empty()) {
2315 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2316 "Aborting import."),
2317 from_utf8(fullname.absFileName()));
2318 frontend::Alert::error(_("File name error"), msg);
2319 message(_("Canceled."));
2324 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2326 // Check if the document already is open
2327 Buffer * buf = theBufferList().getBuffer(lyxfile);
2330 if (!closeBuffer()) {
2331 message(_("Canceled."));
2336 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2338 // if the file exists already, and we didn't do
2339 // -i lyx thefile.lyx, warn
2340 if (lyxfile.exists() && fullname != lyxfile) {
2342 docstring text = bformat(_("The document %1$s already exists.\n\n"
2343 "Do you want to overwrite that document?"), displaypath);
2344 int const ret = Alert::prompt(_("Overwrite document?"),
2345 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2348 message(_("Canceled."));
2353 message(bformat(_("Importing %1$s..."), displaypath));
2354 ErrorList errorList;
2355 if (import(this, fullname, format, errorList))
2356 message(_("imported."));
2358 message(_("file not imported!"));
2360 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2364 void GuiView::newDocument(string const & filename, bool from_template)
2366 FileName initpath(lyxrc.document_path);
2367 if (documentBufferView()) {
2368 FileName const trypath(documentBufferView()->buffer().filePath());
2369 // If directory is writeable, use this as default.
2370 if (trypath.isDirWritable())
2374 string templatefile;
2375 if (from_template) {
2376 templatefile = selectTemplateFile().absFileName();
2377 if (templatefile.empty())
2382 if (filename.empty())
2383 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2385 b = newFile(filename, templatefile, true);
2390 // If no new document could be created, it is unsure
2391 // whether there is a valid BufferView.
2392 if (currentBufferView())
2393 // Ensure the cursor is correctly positioned on screen.
2394 currentBufferView()->showCursor();
2398 void GuiView::insertLyXFile(docstring const & fname)
2400 BufferView * bv = documentBufferView();
2405 FileName filename(to_utf8(fname));
2406 if (filename.empty()) {
2407 // Launch a file browser
2409 string initpath = lyxrc.document_path;
2410 string const trypath = bv->buffer().filePath();
2411 // If directory is writeable, use this as default.
2412 if (FileName(trypath).isDirWritable())
2416 FileDialog dlg(qt_("Select LyX document to insert"));
2417 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2418 dlg.setButton2(qt_("Examples|#E#e"),
2419 toqstr(addPath(package().system_support().absFileName(),
2422 FileDialog::Result result = dlg.open(toqstr(initpath),
2423 QStringList(qt_("LyX Documents (*.lyx)")));
2425 if (result.first == FileDialog::Later)
2429 filename.set(fromqstr(result.second));
2431 // check selected filename
2432 if (filename.empty()) {
2433 // emit message signal.
2434 message(_("Canceled."));
2439 bv->insertLyXFile(filename);
2440 bv->buffer().errors("Parse");
2444 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2446 FileName fname = b.fileName();
2447 FileName const oldname = fname;
2449 if (!newname.empty()) {
2451 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2453 // Switch to this Buffer.
2456 // No argument? Ask user through dialog.
2458 FileDialog dlg(qt_("Choose a filename to save document as"));
2459 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2460 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2462 if (!isLyXFileName(fname.absFileName()))
2463 fname.changeExtension(".lyx");
2465 FileDialog::Result result =
2466 dlg.save(toqstr(fname.onlyPath().absFileName()),
2467 QStringList(qt_("LyX Documents (*.lyx)")),
2468 toqstr(fname.onlyFileName()));
2470 if (result.first == FileDialog::Later)
2473 fname.set(fromqstr(result.second));
2478 if (!isLyXFileName(fname.absFileName()))
2479 fname.changeExtension(".lyx");
2482 // fname is now the new Buffer location.
2484 // if there is already a Buffer open with this name, we do not want
2485 // to have another one. (the second test makes sure we're not just
2486 // trying to overwrite ourselves, which is fine.)
2487 if (theBufferList().exists(fname) && fname != oldname
2488 && theBufferList().getBuffer(fname) != &b) {
2489 docstring const text =
2490 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2491 "Please close it before attempting to overwrite it.\n"
2492 "Do you want to choose a new filename?"),
2493 from_utf8(fname.absFileName()));
2494 int const ret = Alert::prompt(_("Chosen File Already Open"),
2495 text, 0, 1, _("&Rename"), _("&Cancel"));
2497 case 0: return renameBuffer(b, docstring(), kind);
2498 case 1: return false;
2503 bool const existsLocal = fname.exists();
2504 bool const existsInVC = LyXVC::fileInVC(fname);
2505 if (existsLocal || existsInVC) {
2506 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2507 if (kind != LV_WRITE_AS && existsInVC) {
2508 // renaming to a name that is already in VC
2510 docstring text = bformat(_("The document %1$s "
2511 "is already registered.\n\n"
2512 "Do you want to choose a new name?"),
2514 docstring const title = (kind == LV_VC_RENAME) ?
2515 _("Rename document?") : _("Copy document?");
2516 docstring const button = (kind == LV_VC_RENAME) ?
2517 _("&Rename") : _("&Copy");
2518 int const ret = Alert::prompt(title, text, 0, 1,
2519 button, _("&Cancel"));
2521 case 0: return renameBuffer(b, docstring(), kind);
2522 case 1: return false;
2527 docstring text = bformat(_("The document %1$s "
2528 "already exists.\n\n"
2529 "Do you want to overwrite that document?"),
2531 int const ret = Alert::prompt(_("Overwrite document?"),
2532 text, 0, 2, _("&Overwrite"),
2533 _("&Rename"), _("&Cancel"));
2536 case 1: return renameBuffer(b, docstring(), kind);
2537 case 2: return false;
2543 case LV_VC_RENAME: {
2544 string msg = b.lyxvc().rename(fname);
2547 message(from_utf8(msg));
2551 string msg = b.lyxvc().copy(fname);
2554 message(from_utf8(msg));
2560 // LyXVC created the file already in case of LV_VC_RENAME or
2561 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2562 // relative paths of included stuff right if we moved e.g. from
2563 // /a/b.lyx to /a/c/b.lyx.
2565 bool const saved = saveBuffer(b, fname);
2572 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2574 FileName fname = b.fileName();
2576 FileDialog dlg(qt_("Choose a filename to export the document as"));
2577 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2580 QString const anyformat = qt_("Guess from extension (*.*)");
2583 vector<Format const *> export_formats;
2584 for (Format const & f : formats)
2585 if (f.documentFormat())
2586 export_formats.push_back(&f);
2587 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2588 map<QString, string> fmap;
2591 for (Format const * f : export_formats) {
2592 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2593 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2595 from_ascii(f->extension())));
2596 types << loc_filter;
2597 fmap[loc_filter] = f->name();
2598 if (from_ascii(f->name()) == iformat) {
2599 filter = loc_filter;
2600 ext = f->extension();
2603 string ofname = fname.onlyFileName();
2605 ofname = support::changeExtension(ofname, ext);
2606 FileDialog::Result result =
2607 dlg.save(toqstr(fname.onlyPath().absFileName()),
2611 if (result.first != FileDialog::Chosen)
2615 fname.set(fromqstr(result.second));
2616 if (filter == anyformat)
2617 fmt_name = formats.getFormatFromExtension(fname.extension());
2619 fmt_name = fmap[filter];
2620 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2621 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2623 if (fmt_name.empty() || fname.empty())
2626 // fname is now the new Buffer location.
2627 if (FileName(fname).exists()) {
2628 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2629 docstring text = bformat(_("The document %1$s already "
2630 "exists.\n\nDo you want to "
2631 "overwrite that document?"),
2633 int const ret = Alert::prompt(_("Overwrite document?"),
2634 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2637 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2638 case 2: return false;
2642 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2645 return dr.dispatched();
2649 bool GuiView::saveBuffer(Buffer & b)
2651 return saveBuffer(b, FileName());
2655 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2657 if (workArea(b) && workArea(b)->inDialogMode())
2660 if (fn.empty() && b.isUnnamed())
2661 return renameBuffer(b, docstring());
2663 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2665 theSession().lastFiles().add(b.fileName());
2669 // Switch to this Buffer.
2672 // FIXME: we don't tell the user *WHY* the save failed !!
2673 docstring const file = makeDisplayPath(b.absFileName(), 30);
2674 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2675 "Do you want to rename the document and "
2676 "try again?"), file);
2677 int const ret = Alert::prompt(_("Rename and save?"),
2678 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2681 if (!renameBuffer(b, docstring()))
2690 return saveBuffer(b, fn);
2694 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2696 return closeWorkArea(wa, false);
2700 // We only want to close the buffer if it is not visible in other workareas
2701 // of the same view, nor in other views, and if this is not a child
2702 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2704 Buffer & buf = wa->bufferView().buffer();
2706 bool last_wa = d.countWorkAreasOf(buf) == 1
2707 && !inOtherView(buf) && !buf.parent();
2709 bool close_buffer = last_wa;
2712 if (lyxrc.close_buffer_with_last_view == "yes")
2714 else if (lyxrc.close_buffer_with_last_view == "no")
2715 close_buffer = false;
2718 if (buf.isUnnamed())
2719 file = from_utf8(buf.fileName().onlyFileName());
2721 file = buf.fileName().displayName(30);
2722 docstring const text = bformat(
2723 _("Last view on document %1$s is being closed.\n"
2724 "Would you like to close or hide the document?\n"
2726 "Hidden documents can be displayed back through\n"
2727 "the menu: View->Hidden->...\n"
2729 "To remove this question, set your preference in:\n"
2730 " Tools->Preferences->Look&Feel->UserInterface\n"
2732 int ret = Alert::prompt(_("Close or hide document?"),
2733 text, 0, 1, _("&Close"), _("&Hide"));
2734 close_buffer = (ret == 0);
2738 return closeWorkArea(wa, close_buffer);
2742 bool GuiView::closeBuffer()
2744 GuiWorkArea * wa = currentMainWorkArea();
2745 // coverity complained about this
2746 // it seems unnecessary, but perhaps is worth the check
2747 LASSERT(wa, return false);
2749 setCurrentWorkArea(wa);
2750 Buffer & buf = wa->bufferView().buffer();
2751 return closeWorkArea(wa, !buf.parent());
2755 void GuiView::writeSession() const {
2756 GuiWorkArea const * active_wa = currentMainWorkArea();
2757 for (int i = 0; i < d.splitter_->count(); ++i) {
2758 TabWorkArea * twa = d.tabWorkArea(i);
2759 for (int j = 0; j < twa->count(); ++j) {
2760 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2761 Buffer & buf = wa->bufferView().buffer();
2762 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2768 bool GuiView::closeBufferAll()
2770 // Close the workareas in all other views
2771 QList<int> const ids = guiApp->viewIds();
2772 for (int i = 0; i != ids.size(); ++i) {
2773 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2777 // Close our own workareas
2778 if (!closeWorkAreaAll())
2781 // Now close the hidden buffers. We prevent hidden buffers from being
2782 // dirty, so we can just close them.
2783 theBufferList().closeAll();
2788 bool GuiView::closeWorkAreaAll()
2790 setCurrentWorkArea(currentMainWorkArea());
2792 // We might be in a situation that there is still a tabWorkArea, but
2793 // there are no tabs anymore. This can happen when we get here after a
2794 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2795 // many TabWorkArea's have no documents anymore.
2798 // We have to call count() each time, because it can happen that
2799 // more than one splitter will disappear in one iteration (bug 5998).
2800 while (d.splitter_->count() > empty_twa) {
2801 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2803 if (twa->count() == 0)
2806 setCurrentWorkArea(twa->currentWorkArea());
2807 if (!closeTabWorkArea(twa))
2815 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2820 Buffer & buf = wa->bufferView().buffer();
2822 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2823 Alert::warning(_("Close document"),
2824 _("Document could not be closed because it is being processed by LyX."));
2829 return closeBuffer(buf);
2831 if (!inMultiTabs(wa))
2832 if (!saveBufferIfNeeded(buf, true))
2840 bool GuiView::closeBuffer(Buffer & buf)
2842 // If we are in a close_event all children will be closed in some time,
2843 // so no need to do it here. This will ensure that the children end up
2844 // in the session file in the correct order. If we close the master
2845 // buffer, we can close or release the child buffers here too.
2846 bool success = true;
2848 ListOfBuffers clist = buf.getChildren();
2849 ListOfBuffers::const_iterator it = clist.begin();
2850 ListOfBuffers::const_iterator const bend = clist.end();
2851 for (; it != bend; ++it) {
2852 Buffer * child_buf = *it;
2853 if (theBufferList().isOthersChild(&buf, child_buf)) {
2854 child_buf->setParent(0);
2858 // FIXME: should we look in other tabworkareas?
2859 // ANSWER: I don't think so. I've tested, and if the child is
2860 // open in some other window, it closes without a problem.
2861 GuiWorkArea * child_wa = workArea(*child_buf);
2863 success = closeWorkArea(child_wa, true);
2867 // In this case the child buffer is open but hidden.
2868 // It therefore should not (MUST NOT) be dirty!
2869 LATTEST(child_buf->isClean());
2870 theBufferList().release(child_buf);
2875 // goto bookmark to update bookmark pit.
2876 // FIXME: we should update only the bookmarks related to this buffer!
2877 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2878 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2879 guiApp->gotoBookmark(i+1, false, false);
2881 if (saveBufferIfNeeded(buf, false)) {
2882 buf.removeAutosaveFile();
2883 theBufferList().release(&buf);
2887 // open all children again to avoid a crash because of dangling
2888 // pointers (bug 6603)
2894 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2896 while (twa == d.currentTabWorkArea()) {
2897 twa->setCurrentIndex(twa->count() - 1);
2899 GuiWorkArea * wa = twa->currentWorkArea();
2900 Buffer & b = wa->bufferView().buffer();
2902 // We only want to close the buffer if the same buffer is not visible
2903 // in another view, and if this is not a child and if we are closing
2904 // a view (not a tabgroup).
2905 bool const close_buffer =
2906 !inOtherView(b) && !b.parent() && closing_;
2908 if (!closeWorkArea(wa, close_buffer))
2915 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2917 if (buf.isClean() || buf.paragraphs().empty())
2920 // Switch to this Buffer.
2925 if (buf.isUnnamed())
2926 file = from_utf8(buf.fileName().onlyFileName());
2928 file = buf.fileName().displayName(30);
2930 // Bring this window to top before asking questions.
2935 if (hiding && buf.isUnnamed()) {
2936 docstring const text = bformat(_("The document %1$s has not been "
2937 "saved yet.\n\nDo you want to save "
2938 "the document?"), file);
2939 ret = Alert::prompt(_("Save new document?"),
2940 text, 0, 1, _("&Save"), _("&Cancel"));
2944 docstring const text = bformat(_("The document %1$s has unsaved changes."
2945 "\n\nDo you want to save the document or discard the changes?"), file);
2946 ret = Alert::prompt(_("Save changed document?"),
2947 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2952 if (!saveBuffer(buf))
2956 // If we crash after this we could have no autosave file
2957 // but I guess this is really improbable (Jug).
2958 // Sometimes improbable things happen:
2959 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2960 // buf.removeAutosaveFile();
2962 // revert all changes
2973 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2975 Buffer & buf = wa->bufferView().buffer();
2977 for (int i = 0; i != d.splitter_->count(); ++i) {
2978 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2979 if (wa_ && wa_ != wa)
2982 return inOtherView(buf);
2986 bool GuiView::inOtherView(Buffer & buf)
2988 QList<int> const ids = guiApp->viewIds();
2990 for (int i = 0; i != ids.size(); ++i) {
2994 if (guiApp->view(ids[i]).workArea(buf))
3001 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3003 if (!documentBufferView())
3006 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3007 Buffer * const curbuf = &documentBufferView()->buffer();
3008 int nwa = twa->count();
3009 for (int i = 0; i < nwa; ++i) {
3010 if (&workArea(i)->bufferView().buffer() == curbuf) {
3012 if (np == NEXTBUFFER)
3013 next_index = (i == nwa - 1 ? 0 : i + 1);
3015 next_index = (i == 0 ? nwa - 1 : i - 1);
3017 twa->moveTab(i, next_index);
3019 setBuffer(&workArea(next_index)->bufferView().buffer());
3027 /// make sure the document is saved
3028 static bool ensureBufferClean(Buffer * buffer)
3030 LASSERT(buffer, return false);
3031 if (buffer->isClean() && !buffer->isUnnamed())
3034 docstring const file = buffer->fileName().displayName(30);
3037 if (!buffer->isUnnamed()) {
3038 text = bformat(_("The document %1$s has unsaved "
3039 "changes.\n\nDo you want to save "
3040 "the document?"), file);
3041 title = _("Save changed document?");
3044 text = bformat(_("The document %1$s has not been "
3045 "saved yet.\n\nDo you want to save "
3046 "the document?"), file);
3047 title = _("Save new document?");
3049 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3052 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3054 return buffer->isClean() && !buffer->isUnnamed();
3058 bool GuiView::reloadBuffer(Buffer & buf)
3060 Buffer::ReadStatus status = buf.reload();
3061 return status == Buffer::ReadSuccess;
3065 void GuiView::checkExternallyModifiedBuffers()
3067 BufferList::iterator bit = theBufferList().begin();
3068 BufferList::iterator const bend = theBufferList().end();
3069 for (; bit != bend; ++bit) {
3070 Buffer * buf = *bit;
3071 if (buf->fileName().exists() && buf->isChecksumModified()) {
3072 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3073 " Reload now? Any local changes will be lost."),
3074 from_utf8(buf->absFileName()));
3075 int const ret = Alert::prompt(_("Reload externally changed document?"),
3076 text, 0, 1, _("&Reload"), _("&Cancel"));
3084 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3086 Buffer * buffer = documentBufferView()
3087 ? &(documentBufferView()->buffer()) : 0;
3089 switch (cmd.action()) {
3090 case LFUN_VC_REGISTER:
3091 if (!buffer || !ensureBufferClean(buffer))
3093 if (!buffer->lyxvc().inUse()) {
3094 if (buffer->lyxvc().registrer()) {
3095 reloadBuffer(*buffer);
3096 dr.clearMessageUpdate();
3101 case LFUN_VC_RENAME:
3102 case LFUN_VC_COPY: {
3103 if (!buffer || !ensureBufferClean(buffer))
3105 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3106 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3107 // Some changes are not yet committed.
3108 // We test here and not in getStatus(), since
3109 // this test is expensive.
3111 LyXVC::CommandResult ret =
3112 buffer->lyxvc().checkIn(log);
3114 if (ret == LyXVC::ErrorCommand ||
3115 ret == LyXVC::VCSuccess)
3116 reloadBuffer(*buffer);
3117 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3118 frontend::Alert::error(
3119 _("Revision control error."),
3120 _("Document could not be checked in."));
3124 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3125 LV_VC_RENAME : LV_VC_COPY;
3126 renameBuffer(*buffer, cmd.argument(), kind);
3131 case LFUN_VC_CHECK_IN:
3132 if (!buffer || !ensureBufferClean(buffer))
3134 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3136 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3138 // Only skip reloading if the checkin was cancelled or
3139 // an error occurred before the real checkin VCS command
3140 // was executed, since the VCS might have changed the
3141 // file even if it could not checkin successfully.
3142 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3143 reloadBuffer(*buffer);
3147 case LFUN_VC_CHECK_OUT:
3148 if (!buffer || !ensureBufferClean(buffer))
3150 if (buffer->lyxvc().inUse()) {
3151 dr.setMessage(buffer->lyxvc().checkOut());
3152 reloadBuffer(*buffer);
3156 case LFUN_VC_LOCKING_TOGGLE:
3157 LASSERT(buffer, return);
3158 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3160 if (buffer->lyxvc().inUse()) {
3161 string res = buffer->lyxvc().lockingToggle();
3163 frontend::Alert::error(_("Revision control error."),
3164 _("Error when setting the locking property."));
3167 reloadBuffer(*buffer);
3172 case LFUN_VC_REVERT:
3173 LASSERT(buffer, return);
3174 if (buffer->lyxvc().revert()) {
3175 reloadBuffer(*buffer);
3176 dr.clearMessageUpdate();
3180 case LFUN_VC_UNDO_LAST:
3181 LASSERT(buffer, return);
3182 buffer->lyxvc().undoLast();
3183 reloadBuffer(*buffer);
3184 dr.clearMessageUpdate();
3187 case LFUN_VC_REPO_UPDATE:
3188 LASSERT(buffer, return);
3189 if (ensureBufferClean(buffer)) {
3190 dr.setMessage(buffer->lyxvc().repoUpdate());
3191 checkExternallyModifiedBuffers();
3195 case LFUN_VC_COMMAND: {
3196 string flag = cmd.getArg(0);
3197 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3200 if (contains(flag, 'M')) {
3201 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3204 string path = cmd.getArg(1);
3205 if (contains(path, "$$p") && buffer)
3206 path = subst(path, "$$p", buffer->filePath());
3207 LYXERR(Debug::LYXVC, "Directory: " << path);
3209 if (!pp.isReadableDirectory()) {
3210 lyxerr << _("Directory is not accessible.") << endl;
3213 support::PathChanger p(pp);
3215 string command = cmd.getArg(2);
3216 if (command.empty())
3219 command = subst(command, "$$i", buffer->absFileName());
3220 command = subst(command, "$$p", buffer->filePath());
3222 command = subst(command, "$$m", to_utf8(message));
3223 LYXERR(Debug::LYXVC, "Command: " << command);
3225 one.startscript(Systemcall::Wait, command);
3229 if (contains(flag, 'I'))
3230 buffer->markDirty();
3231 if (contains(flag, 'R'))
3232 reloadBuffer(*buffer);
3237 case LFUN_VC_COMPARE: {
3238 if (cmd.argument().empty()) {
3239 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3243 string rev1 = cmd.getArg(0);
3247 // it seems safe to assume we have a buffer
3248 // coverity[FORWARD_NULL]
3249 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3252 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3253 f2 = buffer->absFileName();
3255 string rev2 = cmd.getArg(1);
3259 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3263 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3264 f1 << "\n" << f2 << "\n" );
3265 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3266 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3276 void GuiView::openChildDocument(string const & fname)
3278 LASSERT(documentBufferView(), return);
3279 Buffer & buffer = documentBufferView()->buffer();
3280 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3281 documentBufferView()->saveBookmark(false);
3283 if (theBufferList().exists(filename)) {
3284 child = theBufferList().getBuffer(filename);
3287 message(bformat(_("Opening child document %1$s..."),
3288 makeDisplayPath(filename.absFileName())));
3289 child = loadDocument(filename, false);
3291 // Set the parent name of the child document.
3292 // This makes insertion of citations and references in the child work,
3293 // when the target is in the parent or another child document.
3295 child->setParent(&buffer);
3299 bool GuiView::goToFileRow(string const & argument)
3303 size_t i = argument.find_last_of(' ');
3304 if (i != string::npos) {
3305 file_name = os::internal_path(trim(argument.substr(0, i)));
3306 istringstream is(argument.substr(i + 1));
3311 if (i == string::npos) {
3312 LYXERR0("Wrong argument: " << argument);
3316 string const abstmp = package().temp_dir().absFileName();
3317 string const realtmp = package().temp_dir().realPath();
3318 // We have to use os::path_prefix_is() here, instead of
3319 // simply prefixIs(), because the file name comes from
3320 // an external application and may need case adjustment.
3321 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3322 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3323 // Needed by inverse dvi search. If it is a file
3324 // in tmpdir, call the apropriated function.
3325 // If tmpdir is a symlink, we may have the real
3326 // path passed back, so we correct for that.
3327 if (!prefixIs(file_name, abstmp))
3328 file_name = subst(file_name, realtmp, abstmp);
3329 buf = theBufferList().getBufferFromTmp(file_name);
3331 // Must replace extension of the file to be .lyx
3332 // and get full path
3333 FileName const s = fileSearch(string(),
3334 support::changeExtension(file_name, ".lyx"), "lyx");
3335 // Either change buffer or load the file
3336 if (theBufferList().exists(s))
3337 buf = theBufferList().getBuffer(s);
3338 else if (s.exists()) {
3339 buf = loadDocument(s);
3344 _("File does not exist: %1$s"),
3345 makeDisplayPath(file_name)));
3351 _("No buffer for file: %1$s."),
3352 makeDisplayPath(file_name))
3357 bool success = documentBufferView()->setCursorFromRow(row);
3359 LYXERR(Debug::LATEX,
3360 "setCursorFromRow: invalid position for row " << row);
3361 frontend::Alert::error(_("Inverse Search Failed"),
3362 _("Invalid position requested by inverse search.\n"
3363 "You may need to update the viewed document."));
3369 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3371 QMenu * menu = new QMenu;
3372 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3373 menu->exec(QCursor::pos());
3378 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3380 Buffer::ExportStatus const status = func(format);
3382 // the cloning operation will have produced a clone of the entire set of
3383 // documents, starting from the master. so we must delete those.
3384 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3386 busyBuffers.remove(orig);
3391 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3393 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3394 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3398 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3400 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3401 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3405 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3407 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3408 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3412 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3413 string const & argument,
3414 Buffer const * used_buffer,
3415 docstring const & msg,
3416 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3417 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3418 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3423 string format = argument;
3425 format = used_buffer->params().getDefaultOutputFormat();
3426 processing_format = format;
3428 progress_->clearMessages();
3431 #if EXPORT_in_THREAD
3432 GuiViewPrivate::busyBuffers.insert(used_buffer);
3433 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3434 if (!cloned_buffer) {
3435 Alert::error(_("Export Error"),
3436 _("Error cloning the Buffer."));
3439 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3444 setPreviewFuture(f);
3445 last_export_format = used_buffer->params().bufferFormat();
3448 // We are asynchronous, so we don't know here anything about the success
3451 Buffer::ExportStatus status;
3453 status = (used_buffer->*syncFunc)(format, true);
3454 } else if (previewFunc) {
3455 status = (used_buffer->*previewFunc)(format);
3458 handleExportStatus(gv_, status, format);
3460 return (status == Buffer::ExportSuccess
3461 || status == Buffer::PreviewSuccess);
3465 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3467 BufferView * bv = currentBufferView();
3468 LASSERT(bv, return);
3470 // Let the current BufferView dispatch its own actions.
3471 bv->dispatch(cmd, dr);
3472 if (dr.dispatched())
3475 // Try with the document BufferView dispatch if any.
3476 BufferView * doc_bv = documentBufferView();
3477 if (doc_bv && doc_bv != bv) {
3478 doc_bv->dispatch(cmd, dr);
3479 if (dr.dispatched())
3483 // Then let the current Cursor dispatch its own actions.
3484 bv->cursor().dispatch(cmd);
3486 // update completion. We do it here and not in
3487 // processKeySym to avoid another redraw just for a
3488 // changed inline completion
3489 if (cmd.origin() == FuncRequest::KEYBOARD) {
3490 if (cmd.action() == LFUN_SELF_INSERT
3491 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3492 updateCompletion(bv->cursor(), true, true);
3493 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3494 updateCompletion(bv->cursor(), false, true);
3496 updateCompletion(bv->cursor(), false, false);
3499 dr = bv->cursor().result();
3503 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3505 BufferView * bv = currentBufferView();
3506 // By default we won't need any update.
3507 dr.screenUpdate(Update::None);
3508 // assume cmd will be dispatched
3509 dr.dispatched(true);
3511 Buffer * doc_buffer = documentBufferView()
3512 ? &(documentBufferView()->buffer()) : 0;
3514 if (cmd.origin() == FuncRequest::TOC) {
3515 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3516 // FIXME: do we need to pass a DispatchResult object here?
3517 toc->doDispatch(bv->cursor(), cmd);
3521 string const argument = to_utf8(cmd.argument());
3523 switch(cmd.action()) {
3524 case LFUN_BUFFER_CHILD_OPEN:
3525 openChildDocument(to_utf8(cmd.argument()));
3528 case LFUN_BUFFER_IMPORT:
3529 importDocument(to_utf8(cmd.argument()));
3532 case LFUN_BUFFER_EXPORT: {
3535 // GCC only sees strfwd.h when building merged
3536 if (::lyx::operator==(cmd.argument(), "custom")) {
3537 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3541 string const dest = cmd.getArg(1);
3542 FileName target_dir;
3543 if (!dest.empty() && FileName::isAbsolute(dest))
3544 target_dir = FileName(support::onlyPath(dest));
3546 target_dir = doc_buffer->fileName().onlyPath();
3548 string const format = (argument.empty() || argument == "default") ?
3549 doc_buffer->params().getDefaultOutputFormat() : argument;
3551 if ((dest.empty() && doc_buffer->isUnnamed())
3552 || !target_dir.isDirWritable()) {
3553 exportBufferAs(*doc_buffer, from_utf8(format));
3556 /* TODO/Review: Is it a problem to also export the children?
3557 See the update_unincluded flag */
3558 d.asyncBufferProcessing(format,
3561 &GuiViewPrivate::exportAndDestroy,
3564 // TODO Inform user about success
3568 case LFUN_BUFFER_EXPORT_AS: {
3569 LASSERT(doc_buffer, break);
3570 docstring f = cmd.argument();
3572 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3573 exportBufferAs(*doc_buffer, f);
3577 case LFUN_BUFFER_UPDATE: {
3578 d.asyncBufferProcessing(argument,
3581 &GuiViewPrivate::compileAndDestroy,
3586 case LFUN_BUFFER_VIEW: {
3587 d.asyncBufferProcessing(argument,
3589 _("Previewing ..."),
3590 &GuiViewPrivate::previewAndDestroy,
3595 case LFUN_MASTER_BUFFER_UPDATE: {
3596 d.asyncBufferProcessing(argument,
3597 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3599 &GuiViewPrivate::compileAndDestroy,
3604 case LFUN_MASTER_BUFFER_VIEW: {
3605 d.asyncBufferProcessing(argument,
3606 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3608 &GuiViewPrivate::previewAndDestroy,
3609 0, &Buffer::preview);
3612 case LFUN_BUFFER_SWITCH: {
3613 string const file_name = to_utf8(cmd.argument());
3614 if (!FileName::isAbsolute(file_name)) {
3616 dr.setMessage(_("Absolute filename expected."));
3620 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3623 dr.setMessage(_("Document not loaded"));
3627 // Do we open or switch to the buffer in this view ?
3628 if (workArea(*buffer)
3629 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3634 // Look for the buffer in other views
3635 QList<int> const ids = guiApp->viewIds();
3637 for (; i != ids.size(); ++i) {
3638 GuiView & gv = guiApp->view(ids[i]);
3639 if (gv.workArea(*buffer)) {
3641 gv.activateWindow();
3643 gv.setBuffer(buffer);
3648 // If necessary, open a new window as a last resort
3649 if (i == ids.size()) {
3650 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3656 case LFUN_BUFFER_NEXT:
3657 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3660 case LFUN_BUFFER_MOVE_NEXT:
3661 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3664 case LFUN_BUFFER_PREVIOUS:
3665 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3668 case LFUN_BUFFER_MOVE_PREVIOUS:
3669 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3672 case LFUN_COMMAND_EXECUTE: {
3673 command_execute_ = true;
3674 minibuffer_focus_ = true;
3677 case LFUN_DROP_LAYOUTS_CHOICE:
3678 d.layout_->showPopup();
3681 case LFUN_MENU_OPEN:
3682 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3683 menu->exec(QCursor::pos());
3686 case LFUN_FILE_INSERT:
3687 insertLyXFile(cmd.argument());
3690 case LFUN_FILE_INSERT_PLAINTEXT:
3691 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3692 string const fname = to_utf8(cmd.argument());
3693 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3694 dr.setMessage(_("Absolute filename expected."));
3698 FileName filename(fname);
3699 if (fname.empty()) {
3700 FileDialog dlg(qt_("Select file to insert"));
3702 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3703 QStringList(qt_("All Files (*)")));
3705 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3706 dr.setMessage(_("Canceled."));
3710 filename.set(fromqstr(result.second));
3714 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3715 bv->dispatch(new_cmd, dr);
3720 case LFUN_BUFFER_RELOAD: {
3721 LASSERT(doc_buffer, break);
3724 if (!doc_buffer->isClean()) {
3725 docstring const file =
3726 makeDisplayPath(doc_buffer->absFileName(), 20);
3727 docstring text = bformat(_("Any changes will be lost. "
3728 "Are you sure you want to revert to the saved version "
3729 "of the document %1$s?"), file);
3730 ret = Alert::prompt(_("Revert to saved document?"),
3731 text, 1, 1, _("&Revert"), _("&Cancel"));
3735 doc_buffer->markClean();
3736 reloadBuffer(*doc_buffer);
3737 dr.forceBufferUpdate();
3742 case LFUN_BUFFER_WRITE:
3743 LASSERT(doc_buffer, break);
3744 saveBuffer(*doc_buffer);
3747 case LFUN_BUFFER_WRITE_AS:
3748 LASSERT(doc_buffer, break);
3749 renameBuffer(*doc_buffer, cmd.argument());
3752 case LFUN_BUFFER_WRITE_ALL: {
3753 Buffer * first = theBufferList().first();
3756 message(_("Saving all documents..."));
3757 // We cannot use a for loop as the buffer list cycles.
3760 if (!b->isClean()) {
3762 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3764 b = theBufferList().next(b);
3765 } while (b != first);
3766 dr.setMessage(_("All documents saved."));
3770 case LFUN_BUFFER_CLOSE:
3774 case LFUN_BUFFER_CLOSE_ALL:
3778 case LFUN_TOOLBAR_TOGGLE: {
3779 string const name = cmd.getArg(0);
3780 if (GuiToolbar * t = toolbar(name))
3785 case LFUN_ICON_SIZE: {
3786 QSize size = d.iconSize(cmd.argument());
3788 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3789 size.width(), size.height()));
3793 case LFUN_DIALOG_UPDATE: {
3794 string const name = to_utf8(cmd.argument());
3795 if (name == "prefs" || name == "document")
3796 updateDialog(name, string());
3797 else if (name == "paragraph")
3798 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3799 else if (currentBufferView()) {
3800 Inset * inset = currentBufferView()->editedInset(name);
3801 // Can only update a dialog connected to an existing inset
3803 // FIXME: get rid of this indirection; GuiView ask the inset
3804 // if he is kind enough to update itself...
3805 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3806 //FIXME: pass DispatchResult here?
3807 inset->dispatch(currentBufferView()->cursor(), fr);
3813 case LFUN_DIALOG_TOGGLE: {
3814 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3815 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3816 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3820 case LFUN_DIALOG_DISCONNECT_INSET:
3821 disconnectDialog(to_utf8(cmd.argument()));
3824 case LFUN_DIALOG_HIDE: {
3825 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3829 case LFUN_DIALOG_SHOW: {
3830 string const name = cmd.getArg(0);
3831 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3833 if (name == "character") {
3834 data = freefont2string();
3836 showDialog("character", data);
3837 } else if (name == "latexlog") {
3838 Buffer::LogType type;
3839 string const logfile = doc_buffer->logName(&type);
3841 case Buffer::latexlog:
3844 case Buffer::buildlog:
3848 data += Lexer::quoteString(logfile);
3849 showDialog("log", data);
3850 } else if (name == "vclog") {
3851 string const data = "vc " +
3852 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3853 showDialog("log", data);
3854 } else if (name == "symbols") {
3855 data = bv->cursor().getEncoding()->name();
3857 showDialog("symbols", data);
3859 } else if (name == "prefs" && isFullScreen()) {
3860 lfunUiToggle("fullscreen");
3861 showDialog("prefs", data);
3863 showDialog(name, data);
3868 dr.setMessage(cmd.argument());
3871 case LFUN_UI_TOGGLE: {
3872 string arg = cmd.getArg(0);
3873 if (!lfunUiToggle(arg)) {
3874 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3875 dr.setMessage(bformat(msg, from_utf8(arg)));
3877 // Make sure the keyboard focus stays in the work area.
3882 case LFUN_VIEW_SPLIT: {
3883 LASSERT(doc_buffer, break);
3884 string const orientation = cmd.getArg(0);
3885 d.splitter_->setOrientation(orientation == "vertical"
3886 ? Qt::Vertical : Qt::Horizontal);
3887 TabWorkArea * twa = addTabWorkArea();
3888 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3889 setCurrentWorkArea(wa);
3892 case LFUN_TAB_GROUP_CLOSE:
3893 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3894 closeTabWorkArea(twa);
3895 d.current_work_area_ = 0;
3896 twa = d.currentTabWorkArea();
3897 // Switch to the next GuiWorkArea in the found TabWorkArea.
3899 // Make sure the work area is up to date.
3900 setCurrentWorkArea(twa->currentWorkArea());
3902 setCurrentWorkArea(0);
3907 case LFUN_VIEW_CLOSE:
3908 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3909 closeWorkArea(twa->currentWorkArea());
3910 d.current_work_area_ = 0;
3911 twa = d.currentTabWorkArea();
3912 // Switch to the next GuiWorkArea in the found TabWorkArea.
3914 // Make sure the work area is up to date.
3915 setCurrentWorkArea(twa->currentWorkArea());
3917 setCurrentWorkArea(0);
3922 case LFUN_COMPLETION_INLINE:
3923 if (d.current_work_area_)
3924 d.current_work_area_->completer().showInline();
3927 case LFUN_COMPLETION_POPUP:
3928 if (d.current_work_area_)
3929 d.current_work_area_->completer().showPopup();
3934 if (d.current_work_area_)
3935 d.current_work_area_->completer().tab();
3938 case LFUN_COMPLETION_CANCEL:
3939 if (d.current_work_area_) {
3940 if (d.current_work_area_->completer().popupVisible())
3941 d.current_work_area_->completer().hidePopup();
3943 d.current_work_area_->completer().hideInline();
3947 case LFUN_COMPLETION_ACCEPT:
3948 if (d.current_work_area_)
3949 d.current_work_area_->completer().activate();
3952 case LFUN_BUFFER_ZOOM_IN:
3953 case LFUN_BUFFER_ZOOM_OUT: {
3954 // use a signed temp to avoid overflow
3955 int zoom = lyxrc.zoom;
3956 if (cmd.argument().empty()) {
3957 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3962 zoom += convert<int>(cmd.argument());
3964 if (zoom < static_cast<int>(zoom_min_))
3968 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3970 // The global QPixmapCache is used in GuiPainter to cache text
3971 // painting so we must reset it.
3972 QPixmapCache::clear();
3973 guiApp->fontLoader().update();
3974 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3978 case LFUN_VC_REGISTER:
3979 case LFUN_VC_RENAME:
3981 case LFUN_VC_CHECK_IN:
3982 case LFUN_VC_CHECK_OUT:
3983 case LFUN_VC_REPO_UPDATE:
3984 case LFUN_VC_LOCKING_TOGGLE:
3985 case LFUN_VC_REVERT:
3986 case LFUN_VC_UNDO_LAST:
3987 case LFUN_VC_COMMAND:
3988 case LFUN_VC_COMPARE:
3989 dispatchVC(cmd, dr);
3992 case LFUN_SERVER_GOTO_FILE_ROW:
3993 if(goToFileRow(to_utf8(cmd.argument())))
3994 dr.screenUpdate(Update::Force | Update::FitCursor);
3997 case LFUN_LYX_ACTIVATE:
4001 case LFUN_FORWARD_SEARCH: {
4002 // it seems safe to assume we have a document buffer, since
4003 // getStatus wants one.
4004 // coverity[FORWARD_NULL]
4005 Buffer const * doc_master = doc_buffer->masterBuffer();
4006 FileName const path(doc_master->temppath());
4007 string const texname = doc_master->isChild(doc_buffer)
4008 ? DocFileName(changeExtension(
4009 doc_buffer->absFileName(),
4010 "tex")).mangledFileName()
4011 : doc_buffer->latexName();
4012 string const fulltexname =
4013 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4014 string const mastername =
4015 removeExtension(doc_master->latexName());
4016 FileName const dviname(addName(path.absFileName(),
4017 addExtension(mastername, "dvi")));
4018 FileName const pdfname(addName(path.absFileName(),
4019 addExtension(mastername, "pdf")));
4020 bool const have_dvi = dviname.exists();
4021 bool const have_pdf = pdfname.exists();
4022 if (!have_dvi && !have_pdf) {
4023 dr.setMessage(_("Please, preview the document first."));
4026 string outname = dviname.onlyFileName();
4027 string command = lyxrc.forward_search_dvi;
4028 if (!have_dvi || (have_pdf &&
4029 pdfname.lastModified() > dviname.lastModified())) {
4030 outname = pdfname.onlyFileName();
4031 command = lyxrc.forward_search_pdf;
4034 DocIterator cur = bv->cursor();
4035 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4036 LYXERR(Debug::ACTION, "Forward search: row:" << row
4038 if (row == -1 || command.empty()) {
4039 dr.setMessage(_("Couldn't proceed."));
4042 string texrow = convert<string>(row);
4044 command = subst(command, "$$n", texrow);
4045 command = subst(command, "$$f", fulltexname);
4046 command = subst(command, "$$t", texname);
4047 command = subst(command, "$$o", outname);
4049 PathChanger p(path);
4051 one.startscript(Systemcall::DontWait, command);
4055 case LFUN_SPELLING_CONTINUOUSLY:
4056 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4057 dr.screenUpdate(Update::Force);
4061 // The LFUN must be for one of BufferView, Buffer or Cursor;
4063 dispatchToBufferView(cmd, dr);
4067 // Part of automatic menu appearance feature.
4068 if (isFullScreen()) {
4069 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4073 // Need to update bv because many LFUNs here might have destroyed it
4074 bv = currentBufferView();
4076 // Clear non-empty selections
4077 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4079 Cursor & cur = bv->cursor();
4080 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4081 cur.clearSelection();
4087 bool GuiView::lfunUiToggle(string const & ui_component)
4089 if (ui_component == "scrollbar") {
4090 // hide() is of no help
4091 if (d.current_work_area_->verticalScrollBarPolicy() ==
4092 Qt::ScrollBarAlwaysOff)
4094 d.current_work_area_->setVerticalScrollBarPolicy(
4095 Qt::ScrollBarAsNeeded);
4097 d.current_work_area_->setVerticalScrollBarPolicy(
4098 Qt::ScrollBarAlwaysOff);
4099 } else if (ui_component == "statusbar") {
4100 statusBar()->setVisible(!statusBar()->isVisible());
4101 } else if (ui_component == "menubar") {
4102 menuBar()->setVisible(!menuBar()->isVisible());
4104 if (ui_component == "frame") {
4106 getContentsMargins(&l, &t, &r, &b);
4107 //are the frames in default state?
4108 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4110 setContentsMargins(-2, -2, -2, -2);
4112 setContentsMargins(0, 0, 0, 0);
4115 if (ui_component == "fullscreen") {
4123 void GuiView::toggleFullScreen()
4125 if (isFullScreen()) {
4126 for (int i = 0; i != d.splitter_->count(); ++i)
4127 d.tabWorkArea(i)->setFullScreen(false);
4128 setContentsMargins(0, 0, 0, 0);
4129 setWindowState(windowState() ^ Qt::WindowFullScreen);
4132 statusBar()->show();
4135 hideDialogs("prefs", 0);
4136 for (int i = 0; i != d.splitter_->count(); ++i)
4137 d.tabWorkArea(i)->setFullScreen(true);
4138 setContentsMargins(-2, -2, -2, -2);
4140 setWindowState(windowState() ^ Qt::WindowFullScreen);
4141 if (lyxrc.full_screen_statusbar)
4142 statusBar()->hide();
4143 if (lyxrc.full_screen_menubar)
4145 if (lyxrc.full_screen_toolbars) {
4146 ToolbarMap::iterator end = d.toolbars_.end();
4147 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4152 // give dialogs like the TOC a chance to adapt
4157 Buffer const * GuiView::updateInset(Inset const * inset)
4162 Buffer const * inset_buffer = &(inset->buffer());
4164 for (int i = 0; i != d.splitter_->count(); ++i) {
4165 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4168 Buffer const * buffer = &(wa->bufferView().buffer());
4169 if (inset_buffer == buffer)
4170 wa->scheduleRedraw();
4172 return inset_buffer;
4176 void GuiView::restartCursor()
4178 /* When we move around, or type, it's nice to be able to see
4179 * the cursor immediately after the keypress.
4181 if (d.current_work_area_)
4182 d.current_work_area_->startBlinkingCursor();
4184 // Take this occasion to update the other GUI elements.
4190 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4192 if (d.current_work_area_)
4193 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4198 // This list should be kept in sync with the list of insets in
4199 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4200 // dialog should have the same name as the inset.
4201 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4202 // docs in LyXAction.cpp.
4204 char const * const dialognames[] = {
4206 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4207 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4208 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4209 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4210 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4211 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4212 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4213 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4215 char const * const * const end_dialognames =
4216 dialognames + (sizeof(dialognames) / sizeof(char *));
4220 cmpCStr(char const * name) : name_(name) {}
4221 bool operator()(char const * other) {
4222 return strcmp(other, name_) == 0;
4229 bool isValidName(string const & name)
4231 return find_if(dialognames, end_dialognames,
4232 cmpCStr(name.c_str())) != end_dialognames;
4238 void GuiView::resetDialogs()
4240 // Make sure that no LFUN uses any GuiView.
4241 guiApp->setCurrentView(0);
4245 constructToolbars();
4246 guiApp->menus().fillMenuBar(menuBar(), this, false);
4247 d.layout_->updateContents(true);
4248 // Now update controls with current buffer.
4249 guiApp->setCurrentView(this);
4255 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4257 if (!isValidName(name))
4260 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4262 if (it != d.dialogs_.end()) {
4264 it->second->hideView();
4265 return it->second.get();
4268 Dialog * dialog = build(name);
4269 d.dialogs_[name].reset(dialog);
4270 if (lyxrc.allow_geometry_session)
4271 dialog->restoreSession();
4278 void GuiView::showDialog(string const & name, string const & data,
4281 triggerShowDialog(toqstr(name), toqstr(data), inset);
4285 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4291 const string name = fromqstr(qname);
4292 const string data = fromqstr(qdata);
4296 Dialog * dialog = findOrBuild(name, false);
4298 bool const visible = dialog->isVisibleView();
4299 dialog->showData(data);
4300 if (inset && currentBufferView())
4301 currentBufferView()->editInset(name, inset);
4302 // We only set the focus to the new dialog if it was not yet
4303 // visible in order not to change the existing previous behaviour
4305 // activateWindow is needed for floating dockviews
4306 dialog->asQWidget()->raise();
4307 dialog->asQWidget()->activateWindow();
4308 dialog->asQWidget()->setFocus();
4312 catch (ExceptionMessage const & ex) {
4320 bool GuiView::isDialogVisible(string const & name) const
4322 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4323 if (it == d.dialogs_.end())
4325 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4329 void GuiView::hideDialog(string const & name, Inset * inset)
4331 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4332 if (it == d.dialogs_.end())
4336 if (!currentBufferView())
4338 if (inset != currentBufferView()->editedInset(name))
4342 Dialog * const dialog = it->second.get();
4343 if (dialog->isVisibleView())
4345 if (currentBufferView())
4346 currentBufferView()->editInset(name, 0);
4350 void GuiView::disconnectDialog(string const & name)
4352 if (!isValidName(name))
4354 if (currentBufferView())
4355 currentBufferView()->editInset(name, 0);
4359 void GuiView::hideAll() const
4361 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4362 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4364 for(; it != end; ++it)
4365 it->second->hideView();
4369 void GuiView::updateDialogs()
4371 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4372 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4374 for(; it != end; ++it) {
4375 Dialog * dialog = it->second.get();
4377 if (dialog->needBufferOpen() && !documentBufferView())
4378 hideDialog(fromqstr(dialog->name()), 0);
4379 else if (dialog->isVisibleView())
4380 dialog->checkStatus();
4387 Dialog * createDialog(GuiView & lv, string const & name);
4389 // will be replaced by a proper factory...
4390 Dialog * createGuiAbout(GuiView & lv);
4391 Dialog * createGuiBibtex(GuiView & lv);
4392 Dialog * createGuiChanges(GuiView & lv);
4393 Dialog * createGuiCharacter(GuiView & lv);
4394 Dialog * createGuiCitation(GuiView & lv);
4395 Dialog * createGuiCompare(GuiView & lv);
4396 Dialog * createGuiCompareHistory(GuiView & lv);
4397 Dialog * createGuiDelimiter(GuiView & lv);
4398 Dialog * createGuiDocument(GuiView & lv);
4399 Dialog * createGuiErrorList(GuiView & lv);
4400 Dialog * createGuiExternal(GuiView & lv);
4401 Dialog * createGuiGraphics(GuiView & lv);
4402 Dialog * createGuiInclude(GuiView & lv);
4403 Dialog * createGuiIndex(GuiView & lv);
4404 Dialog * createGuiListings(GuiView & lv);
4405 Dialog * createGuiLog(GuiView & lv);
4406 Dialog * createGuiMathMatrix(GuiView & lv);
4407 Dialog * createGuiNote(GuiView & lv);
4408 Dialog * createGuiParagraph(GuiView & lv);
4409 Dialog * createGuiPhantom(GuiView & lv);
4410 Dialog * createGuiPreferences(GuiView & lv);
4411 Dialog * createGuiPrint(GuiView & lv);
4412 Dialog * createGuiPrintindex(GuiView & lv);
4413 Dialog * createGuiRef(GuiView & lv);
4414 Dialog * createGuiSearch(GuiView & lv);
4415 Dialog * createGuiSearchAdv(GuiView & lv);
4416 Dialog * createGuiSendTo(GuiView & lv);
4417 Dialog * createGuiShowFile(GuiView & lv);
4418 Dialog * createGuiSpellchecker(GuiView & lv);
4419 Dialog * createGuiSymbols(GuiView & lv);
4420 Dialog * createGuiTabularCreate(GuiView & lv);
4421 Dialog * createGuiTexInfo(GuiView & lv);
4422 Dialog * createGuiToc(GuiView & lv);
4423 Dialog * createGuiThesaurus(GuiView & lv);
4424 Dialog * createGuiViewSource(GuiView & lv);
4425 Dialog * createGuiWrap(GuiView & lv);
4426 Dialog * createGuiProgressView(GuiView & lv);
4430 Dialog * GuiView::build(string const & name)
4432 LASSERT(isValidName(name), return 0);
4434 Dialog * dialog = createDialog(*this, name);
4438 if (name == "aboutlyx")
4439 return createGuiAbout(*this);
4440 if (name == "bibtex")
4441 return createGuiBibtex(*this);
4442 if (name == "changes")
4443 return createGuiChanges(*this);
4444 if (name == "character")
4445 return createGuiCharacter(*this);
4446 if (name == "citation")
4447 return createGuiCitation(*this);
4448 if (name == "compare")
4449 return createGuiCompare(*this);
4450 if (name == "comparehistory")
4451 return createGuiCompareHistory(*this);
4452 if (name == "document")
4453 return createGuiDocument(*this);
4454 if (name == "errorlist")
4455 return createGuiErrorList(*this);
4456 if (name == "external")
4457 return createGuiExternal(*this);
4459 return createGuiShowFile(*this);
4460 if (name == "findreplace")
4461 return createGuiSearch(*this);
4462 if (name == "findreplaceadv")
4463 return createGuiSearchAdv(*this);
4464 if (name == "graphics")
4465 return createGuiGraphics(*this);
4466 if (name == "include")
4467 return createGuiInclude(*this);
4468 if (name == "index")
4469 return createGuiIndex(*this);
4470 if (name == "index_print")
4471 return createGuiPrintindex(*this);
4472 if (name == "listings")
4473 return createGuiListings(*this);
4475 return createGuiLog(*this);
4476 if (name == "mathdelimiter")
4477 return createGuiDelimiter(*this);
4478 if (name == "mathmatrix")
4479 return createGuiMathMatrix(*this);
4481 return createGuiNote(*this);
4482 if (name == "paragraph")
4483 return createGuiParagraph(*this);
4484 if (name == "phantom")
4485 return createGuiPhantom(*this);
4486 if (name == "prefs")
4487 return createGuiPreferences(*this);
4489 return createGuiRef(*this);
4490 if (name == "sendto")
4491 return createGuiSendTo(*this);
4492 if (name == "spellchecker")
4493 return createGuiSpellchecker(*this);
4494 if (name == "symbols")
4495 return createGuiSymbols(*this);
4496 if (name == "tabularcreate")
4497 return createGuiTabularCreate(*this);
4498 if (name == "texinfo")
4499 return createGuiTexInfo(*this);
4500 if (name == "thesaurus")
4501 return createGuiThesaurus(*this);
4503 return createGuiToc(*this);
4504 if (name == "view-source")
4505 return createGuiViewSource(*this);
4507 return createGuiWrap(*this);
4508 if (name == "progress")
4509 return createGuiProgressView(*this);
4515 } // namespace frontend
4518 #include "moc_GuiView.cpp"