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 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 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 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 // FIXME: This is slightly expensive, though less than the tocBackend update
1656 // (#9880). This also resets the view in the Toc Widget (#6675).
1657 d.toc_models_.reset(documentBufferView());
1658 // Navigator needs more than a simple update in this case. It needs to be
1660 updateDialog("toc", "");
1664 void GuiView::updateDialog(string const & name, string const & data)
1666 if (!isDialogVisible(name))
1669 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1670 if (it == d.dialogs_.end())
1673 Dialog * const dialog = it->second.get();
1674 if (dialog->isVisibleView())
1675 dialog->initialiseParams(data);
1679 BufferView * GuiView::documentBufferView()
1681 return currentMainWorkArea()
1682 ? ¤tMainWorkArea()->bufferView()
1687 BufferView const * GuiView::documentBufferView() const
1689 return currentMainWorkArea()
1690 ? ¤tMainWorkArea()->bufferView()
1695 BufferView * GuiView::currentBufferView()
1697 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1701 BufferView const * GuiView::currentBufferView() const
1703 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1707 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1708 Buffer const * orig, Buffer * clone)
1710 bool const success = clone->autoSave();
1712 busyBuffers.remove(orig);
1714 ? _("Automatic save done.")
1715 : _("Automatic save failed!");
1719 void GuiView::autoSave()
1721 LYXERR(Debug::INFO, "Running autoSave()");
1723 Buffer * buffer = documentBufferView()
1724 ? &documentBufferView()->buffer() : 0;
1726 resetAutosaveTimers();
1730 GuiViewPrivate::busyBuffers.insert(buffer);
1731 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1732 buffer, buffer->cloneBufferOnly());
1733 d.autosave_watcher_.setFuture(f);
1734 resetAutosaveTimers();
1738 void GuiView::resetAutosaveTimers()
1741 d.autosave_timeout_.restart();
1745 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1748 Buffer * buf = currentBufferView()
1749 ? ¤tBufferView()->buffer() : 0;
1750 Buffer * doc_buffer = documentBufferView()
1751 ? &(documentBufferView()->buffer()) : 0;
1754 /* In LyX/Mac, when a dialog is open, the menus of the
1755 application can still be accessed without giving focus to
1756 the main window. In this case, we want to disable the menu
1757 entries that are buffer-related.
1758 This code must not be used on Linux and Windows, since it
1759 would disable buffer-related entries when hovering over the
1760 menu (see bug #9574).
1762 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1768 // Check whether we need a buffer
1769 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1770 // no, exit directly
1771 flag.message(from_utf8(N_("Command not allowed with"
1772 "out any document open")));
1773 flag.setEnabled(false);
1777 if (cmd.origin() == FuncRequest::TOC) {
1778 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1779 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1780 flag.setEnabled(false);
1784 switch(cmd.action()) {
1785 case LFUN_BUFFER_IMPORT:
1788 case LFUN_MASTER_BUFFER_UPDATE:
1789 case LFUN_MASTER_BUFFER_VIEW:
1791 && (doc_buffer->parent() != 0
1792 || doc_buffer->hasChildren())
1793 && !d.processing_thread_watcher_.isRunning();
1796 case LFUN_BUFFER_UPDATE:
1797 case LFUN_BUFFER_VIEW: {
1798 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1802 string format = to_utf8(cmd.argument());
1803 if (cmd.argument().empty())
1804 format = doc_buffer->params().getDefaultOutputFormat();
1805 enable = doc_buffer->params().isExportable(format, true);
1809 case LFUN_BUFFER_RELOAD:
1810 enable = doc_buffer && !doc_buffer->isUnnamed()
1811 && doc_buffer->fileName().exists()
1812 && (!doc_buffer->isClean()
1813 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1816 case LFUN_BUFFER_CHILD_OPEN:
1817 enable = doc_buffer != 0;
1820 case LFUN_BUFFER_WRITE:
1821 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1824 //FIXME: This LFUN should be moved to GuiApplication.
1825 case LFUN_BUFFER_WRITE_ALL: {
1826 // We enable the command only if there are some modified buffers
1827 Buffer * first = theBufferList().first();
1832 // We cannot use a for loop as the buffer list is a cycle.
1834 if (!b->isClean()) {
1838 b = theBufferList().next(b);
1839 } while (b != first);
1843 case LFUN_BUFFER_WRITE_AS:
1844 case LFUN_BUFFER_EXPORT_AS:
1845 enable = doc_buffer != 0;
1848 case LFUN_BUFFER_CLOSE:
1849 case LFUN_VIEW_CLOSE:
1850 enable = doc_buffer != 0;
1853 case LFUN_BUFFER_CLOSE_ALL:
1854 enable = theBufferList().last() != theBufferList().first();
1857 case LFUN_VIEW_SPLIT:
1858 if (cmd.getArg(0) == "vertical")
1859 enable = doc_buffer && (d.splitter_->count() == 1 ||
1860 d.splitter_->orientation() == Qt::Vertical);
1862 enable = doc_buffer && (d.splitter_->count() == 1 ||
1863 d.splitter_->orientation() == Qt::Horizontal);
1866 case LFUN_TAB_GROUP_CLOSE:
1867 enable = d.tabWorkAreaCount() > 1;
1870 case LFUN_TOOLBAR_TOGGLE: {
1871 string const name = cmd.getArg(0);
1872 if (GuiToolbar * t = toolbar(name))
1873 flag.setOnOff(t->isVisible());
1876 docstring const msg =
1877 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1883 case LFUN_ICON_SIZE:
1884 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1887 case LFUN_DROP_LAYOUTS_CHOICE:
1891 case LFUN_UI_TOGGLE:
1892 flag.setOnOff(isFullScreen());
1895 case LFUN_DIALOG_DISCONNECT_INSET:
1898 case LFUN_DIALOG_HIDE:
1899 // FIXME: should we check if the dialog is shown?
1902 case LFUN_DIALOG_TOGGLE:
1903 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1904 // fall through to set "enable"
1905 case LFUN_DIALOG_SHOW: {
1906 string const name = cmd.getArg(0);
1908 enable = name == "aboutlyx"
1909 || name == "file" //FIXME: should be removed.
1911 || name == "texinfo"
1912 || name == "progress"
1913 || name == "compare";
1914 else if (name == "character" || name == "symbols"
1915 || name == "mathdelimiter" || name == "mathmatrix") {
1916 if (!buf || buf->isReadonly())
1919 Cursor const & cur = currentBufferView()->cursor();
1920 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1923 else if (name == "latexlog")
1924 enable = FileName(doc_buffer->logName()).isReadableFile();
1925 else if (name == "spellchecker")
1926 enable = theSpellChecker()
1927 && !doc_buffer->isReadonly()
1928 && !doc_buffer->text().empty();
1929 else if (name == "vclog")
1930 enable = doc_buffer->lyxvc().inUse();
1934 case LFUN_DIALOG_UPDATE: {
1935 string const name = cmd.getArg(0);
1937 enable = name == "prefs";
1941 case LFUN_COMMAND_EXECUTE:
1943 case LFUN_MENU_OPEN:
1944 // Nothing to check.
1947 case LFUN_COMPLETION_INLINE:
1948 if (!d.current_work_area_
1949 || !d.current_work_area_->completer().inlinePossible(
1950 currentBufferView()->cursor()))
1954 case LFUN_COMPLETION_POPUP:
1955 if (!d.current_work_area_
1956 || !d.current_work_area_->completer().popupPossible(
1957 currentBufferView()->cursor()))
1962 if (!d.current_work_area_
1963 || !d.current_work_area_->completer().inlinePossible(
1964 currentBufferView()->cursor()))
1968 case LFUN_COMPLETION_ACCEPT:
1969 if (!d.current_work_area_
1970 || (!d.current_work_area_->completer().popupVisible()
1971 && !d.current_work_area_->completer().inlineVisible()
1972 && !d.current_work_area_->completer().completionAvailable()))
1976 case LFUN_COMPLETION_CANCEL:
1977 if (!d.current_work_area_
1978 || (!d.current_work_area_->completer().popupVisible()
1979 && !d.current_work_area_->completer().inlineVisible()))
1983 case LFUN_BUFFER_ZOOM_OUT:
1984 case LFUN_BUFFER_ZOOM_IN: {
1985 // only diff between these two is that the default for ZOOM_OUT
1987 bool const neg_zoom =
1988 convert<int>(cmd.argument()) < 0 ||
1989 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
1990 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
1991 docstring const msg =
1992 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
1996 enable = doc_buffer;
1999 case LFUN_BUFFER_MOVE_NEXT:
2000 case LFUN_BUFFER_MOVE_PREVIOUS:
2001 // we do not cycle when moving
2002 case LFUN_BUFFER_NEXT:
2003 case LFUN_BUFFER_PREVIOUS:
2004 // because we cycle, it doesn't matter whether on first or last
2005 enable = (d.currentTabWorkArea()->count() > 1);
2007 case LFUN_BUFFER_SWITCH:
2008 // toggle on the current buffer, but do not toggle off
2009 // the other ones (is that a good idea?)
2011 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2012 flag.setOnOff(true);
2015 case LFUN_VC_REGISTER:
2016 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2018 case LFUN_VC_RENAME:
2019 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2022 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2024 case LFUN_VC_CHECK_IN:
2025 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2027 case LFUN_VC_CHECK_OUT:
2028 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2030 case LFUN_VC_LOCKING_TOGGLE:
2031 enable = doc_buffer && !doc_buffer->isReadonly()
2032 && doc_buffer->lyxvc().lockingToggleEnabled();
2033 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2035 case LFUN_VC_REVERT:
2036 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2038 case LFUN_VC_UNDO_LAST:
2039 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2041 case LFUN_VC_REPO_UPDATE:
2042 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2044 case LFUN_VC_COMMAND: {
2045 if (cmd.argument().empty())
2047 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2051 case LFUN_VC_COMPARE:
2052 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2055 case LFUN_SERVER_GOTO_FILE_ROW:
2056 case LFUN_LYX_ACTIVATE:
2058 case LFUN_FORWARD_SEARCH:
2059 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2062 case LFUN_FILE_INSERT_PLAINTEXT:
2063 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2064 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2067 case LFUN_SPELLING_CONTINUOUSLY:
2068 flag.setOnOff(lyxrc.spellcheck_continuously);
2076 flag.setEnabled(false);
2082 static FileName selectTemplateFile()
2084 FileDialog dlg(qt_("Select template file"));
2085 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2086 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2088 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2089 QStringList(qt_("LyX Documents (*.lyx)")));
2091 if (result.first == FileDialog::Later)
2093 if (result.second.isEmpty())
2095 return FileName(fromqstr(result.second));
2099 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2103 Buffer * newBuffer = 0;
2105 newBuffer = checkAndLoadLyXFile(filename);
2106 } catch (ExceptionMessage const & e) {
2113 message(_("Document not loaded."));
2117 setBuffer(newBuffer);
2118 newBuffer->errors("Parse");
2121 theSession().lastFiles().add(filename);
2127 void GuiView::openDocument(string const & fname)
2129 string initpath = lyxrc.document_path;
2131 if (documentBufferView()) {
2132 string const trypath = documentBufferView()->buffer().filePath();
2133 // If directory is writeable, use this as default.
2134 if (FileName(trypath).isDirWritable())
2140 if (fname.empty()) {
2141 FileDialog dlg(qt_("Select document to open"));
2142 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2143 dlg.setButton2(qt_("Examples|#E#e"),
2144 toqstr(addPath(package().system_support().absFileName(), "examples")));
2146 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2147 FileDialog::Result result =
2148 dlg.open(toqstr(initpath), filter);
2150 if (result.first == FileDialog::Later)
2153 filename = fromqstr(result.second);
2155 // check selected filename
2156 if (filename.empty()) {
2157 message(_("Canceled."));
2163 // get absolute path of file and add ".lyx" to the filename if
2165 FileName const fullname =
2166 fileSearch(string(), filename, "lyx", support::may_not_exist);
2167 if (!fullname.empty())
2168 filename = fullname.absFileName();
2170 if (!fullname.onlyPath().isDirectory()) {
2171 Alert::warning(_("Invalid filename"),
2172 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2173 from_utf8(fullname.absFileName())));
2177 // if the file doesn't exist and isn't already open (bug 6645),
2178 // let the user create one
2179 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2180 !LyXVC::file_not_found_hook(fullname)) {
2181 // the user specifically chose this name. Believe him.
2182 Buffer * const b = newFile(filename, string(), true);
2188 docstring const disp_fn = makeDisplayPath(filename);
2189 message(bformat(_("Opening document %1$s..."), disp_fn));
2192 Buffer * buf = loadDocument(fullname);
2194 str2 = bformat(_("Document %1$s opened."), disp_fn);
2195 if (buf->lyxvc().inUse())
2196 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2197 " " + _("Version control detected.");
2199 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2204 // FIXME: clean that
2205 static bool import(GuiView * lv, FileName const & filename,
2206 string const & format, ErrorList & errorList)
2208 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2210 string loader_format;
2211 vector<string> loaders = theConverters().loaders();
2212 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2213 vector<string>::const_iterator it = loaders.begin();
2214 vector<string>::const_iterator en = loaders.end();
2215 for (; it != en; ++it) {
2216 if (!theConverters().isReachable(format, *it))
2219 string const tofile =
2220 support::changeExtension(filename.absFileName(),
2221 formats.extension(*it));
2222 if (!theConverters().convert(0, filename, FileName(tofile),
2223 filename, format, *it, errorList))
2225 loader_format = *it;
2228 if (loader_format.empty()) {
2229 frontend::Alert::error(_("Couldn't import file"),
2230 bformat(_("No information for importing the format %1$s."),
2231 formats.prettyName(format)));
2235 loader_format = format;
2237 if (loader_format == "lyx") {
2238 Buffer * buf = lv->loadDocument(lyxfile);
2242 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2246 bool as_paragraphs = loader_format == "textparagraph";
2247 string filename2 = (loader_format == format) ? filename.absFileName()
2248 : support::changeExtension(filename.absFileName(),
2249 formats.extension(loader_format));
2250 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2252 guiApp->setCurrentView(lv);
2253 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2260 void GuiView::importDocument(string const & argument)
2263 string filename = split(argument, format, ' ');
2265 LYXERR(Debug::INFO, format << " file: " << filename);
2267 // need user interaction
2268 if (filename.empty()) {
2269 string initpath = lyxrc.document_path;
2270 if (documentBufferView()) {
2271 string const trypath = documentBufferView()->buffer().filePath();
2272 // If directory is writeable, use this as default.
2273 if (FileName(trypath).isDirWritable())
2277 docstring const text = bformat(_("Select %1$s file to import"),
2278 formats.prettyName(format));
2280 FileDialog dlg(toqstr(text));
2281 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2282 dlg.setButton2(qt_("Examples|#E#e"),
2283 toqstr(addPath(package().system_support().absFileName(), "examples")));
2285 docstring filter = formats.prettyName(format);
2288 filter += from_utf8(formats.extensions(format));
2291 FileDialog::Result result =
2292 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2294 if (result.first == FileDialog::Later)
2297 filename = fromqstr(result.second);
2299 // check selected filename
2300 if (filename.empty())
2301 message(_("Canceled."));
2304 if (filename.empty())
2307 // get absolute path of file
2308 FileName const fullname(support::makeAbsPath(filename));
2310 // Can happen if the user entered a path into the dialog
2312 if (fullname.onlyFileName().empty()) {
2313 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2314 "Aborting import."),
2315 from_utf8(fullname.absFileName()));
2316 frontend::Alert::error(_("File name error"), msg);
2317 message(_("Canceled."));
2322 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2324 // Check if the document already is open
2325 Buffer * buf = theBufferList().getBuffer(lyxfile);
2328 if (!closeBuffer()) {
2329 message(_("Canceled."));
2334 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2336 // if the file exists already, and we didn't do
2337 // -i lyx thefile.lyx, warn
2338 if (lyxfile.exists() && fullname != lyxfile) {
2340 docstring text = bformat(_("The document %1$s already exists.\n\n"
2341 "Do you want to overwrite that document?"), displaypath);
2342 int const ret = Alert::prompt(_("Overwrite document?"),
2343 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2346 message(_("Canceled."));
2351 message(bformat(_("Importing %1$s..."), displaypath));
2352 ErrorList errorList;
2353 if (import(this, fullname, format, errorList))
2354 message(_("imported."));
2356 message(_("file not imported!"));
2358 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2362 void GuiView::newDocument(string const & filename, bool from_template)
2364 FileName initpath(lyxrc.document_path);
2365 if (documentBufferView()) {
2366 FileName const trypath(documentBufferView()->buffer().filePath());
2367 // If directory is writeable, use this as default.
2368 if (trypath.isDirWritable())
2372 string templatefile;
2373 if (from_template) {
2374 templatefile = selectTemplateFile().absFileName();
2375 if (templatefile.empty())
2380 if (filename.empty())
2381 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2383 b = newFile(filename, templatefile, true);
2388 // If no new document could be created, it is unsure
2389 // whether there is a valid BufferView.
2390 if (currentBufferView())
2391 // Ensure the cursor is correctly positioned on screen.
2392 currentBufferView()->showCursor();
2396 void GuiView::insertLyXFile(docstring const & fname)
2398 BufferView * bv = documentBufferView();
2403 FileName filename(to_utf8(fname));
2404 if (filename.empty()) {
2405 // Launch a file browser
2407 string initpath = lyxrc.document_path;
2408 string const trypath = bv->buffer().filePath();
2409 // If directory is writeable, use this as default.
2410 if (FileName(trypath).isDirWritable())
2414 FileDialog dlg(qt_("Select LyX document to insert"));
2415 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2416 dlg.setButton2(qt_("Examples|#E#e"),
2417 toqstr(addPath(package().system_support().absFileName(),
2420 FileDialog::Result result = dlg.open(toqstr(initpath),
2421 QStringList(qt_("LyX Documents (*.lyx)")));
2423 if (result.first == FileDialog::Later)
2427 filename.set(fromqstr(result.second));
2429 // check selected filename
2430 if (filename.empty()) {
2431 // emit message signal.
2432 message(_("Canceled."));
2437 bv->insertLyXFile(filename);
2438 bv->buffer().errors("Parse");
2442 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2444 FileName fname = b.fileName();
2445 FileName const oldname = fname;
2447 if (!newname.empty()) {
2449 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2451 // Switch to this Buffer.
2454 // No argument? Ask user through dialog.
2456 FileDialog dlg(qt_("Choose a filename to save document as"));
2457 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2458 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2460 if (!isLyXFileName(fname.absFileName()))
2461 fname.changeExtension(".lyx");
2463 FileDialog::Result result =
2464 dlg.save(toqstr(fname.onlyPath().absFileName()),
2465 QStringList(qt_("LyX Documents (*.lyx)")),
2466 toqstr(fname.onlyFileName()));
2468 if (result.first == FileDialog::Later)
2471 fname.set(fromqstr(result.second));
2476 if (!isLyXFileName(fname.absFileName()))
2477 fname.changeExtension(".lyx");
2480 // fname is now the new Buffer location.
2482 // if there is already a Buffer open with this name, we do not want
2483 // to have another one. (the second test makes sure we're not just
2484 // trying to overwrite ourselves, which is fine.)
2485 if (theBufferList().exists(fname) && fname != oldname
2486 && theBufferList().getBuffer(fname) != &b) {
2487 docstring const text =
2488 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2489 "Please close it before attempting to overwrite it.\n"
2490 "Do you want to choose a new filename?"),
2491 from_utf8(fname.absFileName()));
2492 int const ret = Alert::prompt(_("Chosen File Already Open"),
2493 text, 0, 1, _("&Rename"), _("&Cancel"));
2495 case 0: return renameBuffer(b, docstring(), kind);
2496 case 1: return false;
2501 bool const existsLocal = fname.exists();
2502 bool const existsInVC = LyXVC::fileInVC(fname);
2503 if (existsLocal || existsInVC) {
2504 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2505 if (kind != LV_WRITE_AS && existsInVC) {
2506 // renaming to a name that is already in VC
2508 docstring text = bformat(_("The document %1$s "
2509 "is already registered.\n\n"
2510 "Do you want to choose a new name?"),
2512 docstring const title = (kind == LV_VC_RENAME) ?
2513 _("Rename document?") : _("Copy document?");
2514 docstring const button = (kind == LV_VC_RENAME) ?
2515 _("&Rename") : _("&Copy");
2516 int const ret = Alert::prompt(title, text, 0, 1,
2517 button, _("&Cancel"));
2519 case 0: return renameBuffer(b, docstring(), kind);
2520 case 1: return false;
2525 docstring text = bformat(_("The document %1$s "
2526 "already exists.\n\n"
2527 "Do you want to overwrite that document?"),
2529 int const ret = Alert::prompt(_("Overwrite document?"),
2530 text, 0, 2, _("&Overwrite"),
2531 _("&Rename"), _("&Cancel"));
2534 case 1: return renameBuffer(b, docstring(), kind);
2535 case 2: return false;
2541 case LV_VC_RENAME: {
2542 string msg = b.lyxvc().rename(fname);
2545 message(from_utf8(msg));
2549 string msg = b.lyxvc().copy(fname);
2552 message(from_utf8(msg));
2558 // LyXVC created the file already in case of LV_VC_RENAME or
2559 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2560 // relative paths of included stuff right if we moved e.g. from
2561 // /a/b.lyx to /a/c/b.lyx.
2563 bool const saved = saveBuffer(b, fname);
2570 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2572 FileName fname = b.fileName();
2574 FileDialog dlg(qt_("Choose a filename to export the document as"));
2575 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2578 QString const anyformat = qt_("Guess from extension (*.*)");
2581 vector<Format const *> export_formats;
2582 for (Format const & f : formats)
2583 if (f.documentFormat())
2584 export_formats.push_back(&f);
2585 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2586 map<QString, string> fmap;
2589 for (Format const * f : export_formats) {
2590 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2591 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2593 from_ascii(f->extension())));
2594 types << loc_filter;
2595 fmap[loc_filter] = f->name();
2596 if (from_ascii(f->name()) == iformat) {
2597 filter = loc_filter;
2598 ext = f->extension();
2601 string ofname = fname.onlyFileName();
2603 ofname = support::changeExtension(ofname, ext);
2604 FileDialog::Result result =
2605 dlg.save(toqstr(fname.onlyPath().absFileName()),
2609 if (result.first != FileDialog::Chosen)
2613 fname.set(fromqstr(result.second));
2614 if (filter == anyformat)
2615 fmt_name = formats.getFormatFromExtension(fname.extension());
2617 fmt_name = fmap[filter];
2618 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2619 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2621 if (fmt_name.empty() || fname.empty())
2624 // fname is now the new Buffer location.
2625 if (FileName(fname).exists()) {
2626 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2627 docstring text = bformat(_("The document %1$s already "
2628 "exists.\n\nDo you want to "
2629 "overwrite that document?"),
2631 int const ret = Alert::prompt(_("Overwrite document?"),
2632 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2635 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2636 case 2: return false;
2640 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2643 return dr.dispatched();
2647 bool GuiView::saveBuffer(Buffer & b)
2649 return saveBuffer(b, FileName());
2653 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2655 if (workArea(b) && workArea(b)->inDialogMode())
2658 if (fn.empty() && b.isUnnamed())
2659 return renameBuffer(b, docstring());
2661 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2663 theSession().lastFiles().add(b.fileName());
2667 // Switch to this Buffer.
2670 // FIXME: we don't tell the user *WHY* the save failed !!
2671 docstring const file = makeDisplayPath(b.absFileName(), 30);
2672 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2673 "Do you want to rename the document and "
2674 "try again?"), file);
2675 int const ret = Alert::prompt(_("Rename and save?"),
2676 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2679 if (!renameBuffer(b, docstring()))
2688 return saveBuffer(b, fn);
2692 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2694 return closeWorkArea(wa, false);
2698 // We only want to close the buffer if it is not visible in other workareas
2699 // of the same view, nor in other views, and if this is not a child
2700 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2702 Buffer & buf = wa->bufferView().buffer();
2704 bool last_wa = d.countWorkAreasOf(buf) == 1
2705 && !inOtherView(buf) && !buf.parent();
2707 bool close_buffer = last_wa;
2710 if (lyxrc.close_buffer_with_last_view == "yes")
2712 else if (lyxrc.close_buffer_with_last_view == "no")
2713 close_buffer = false;
2716 if (buf.isUnnamed())
2717 file = from_utf8(buf.fileName().onlyFileName());
2719 file = buf.fileName().displayName(30);
2720 docstring const text = bformat(
2721 _("Last view on document %1$s is being closed.\n"
2722 "Would you like to close or hide the document?\n"
2724 "Hidden documents can be displayed back through\n"
2725 "the menu: View->Hidden->...\n"
2727 "To remove this question, set your preference in:\n"
2728 " Tools->Preferences->Look&Feel->UserInterface\n"
2730 int ret = Alert::prompt(_("Close or hide document?"),
2731 text, 0, 1, _("&Close"), _("&Hide"));
2732 close_buffer = (ret == 0);
2736 return closeWorkArea(wa, close_buffer);
2740 bool GuiView::closeBuffer()
2742 GuiWorkArea * wa = currentMainWorkArea();
2743 // coverity complained about this
2744 // it seems unnecessary, but perhaps is worth the check
2745 LASSERT(wa, return false);
2747 setCurrentWorkArea(wa);
2748 Buffer & buf = wa->bufferView().buffer();
2749 return closeWorkArea(wa, !buf.parent());
2753 void GuiView::writeSession() const {
2754 GuiWorkArea const * active_wa = currentMainWorkArea();
2755 for (int i = 0; i < d.splitter_->count(); ++i) {
2756 TabWorkArea * twa = d.tabWorkArea(i);
2757 for (int j = 0; j < twa->count(); ++j) {
2758 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2759 Buffer & buf = wa->bufferView().buffer();
2760 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2766 bool GuiView::closeBufferAll()
2768 // Close the workareas in all other views
2769 QList<int> const ids = guiApp->viewIds();
2770 for (int i = 0; i != ids.size(); ++i) {
2771 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2775 // Close our own workareas
2776 if (!closeWorkAreaAll())
2779 // Now close the hidden buffers. We prevent hidden buffers from being
2780 // dirty, so we can just close them.
2781 theBufferList().closeAll();
2786 bool GuiView::closeWorkAreaAll()
2788 setCurrentWorkArea(currentMainWorkArea());
2790 // We might be in a situation that there is still a tabWorkArea, but
2791 // there are no tabs anymore. This can happen when we get here after a
2792 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2793 // many TabWorkArea's have no documents anymore.
2796 // We have to call count() each time, because it can happen that
2797 // more than one splitter will disappear in one iteration (bug 5998).
2798 while (d.splitter_->count() > empty_twa) {
2799 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2801 if (twa->count() == 0)
2804 setCurrentWorkArea(twa->currentWorkArea());
2805 if (!closeTabWorkArea(twa))
2813 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2818 Buffer & buf = wa->bufferView().buffer();
2820 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2821 Alert::warning(_("Close document"),
2822 _("Document could not be closed because it is being processed by LyX."));
2827 return closeBuffer(buf);
2829 if (!inMultiTabs(wa))
2830 if (!saveBufferIfNeeded(buf, true))
2838 bool GuiView::closeBuffer(Buffer & buf)
2840 // If we are in a close_event all children will be closed in some time,
2841 // so no need to do it here. This will ensure that the children end up
2842 // in the session file in the correct order. If we close the master
2843 // buffer, we can close or release the child buffers here too.
2844 bool success = true;
2846 ListOfBuffers clist = buf.getChildren();
2847 ListOfBuffers::const_iterator it = clist.begin();
2848 ListOfBuffers::const_iterator const bend = clist.end();
2849 for (; it != bend; ++it) {
2850 Buffer * child_buf = *it;
2851 if (theBufferList().isOthersChild(&buf, child_buf)) {
2852 child_buf->setParent(0);
2856 // FIXME: should we look in other tabworkareas?
2857 // ANSWER: I don't think so. I've tested, and if the child is
2858 // open in some other window, it closes without a problem.
2859 GuiWorkArea * child_wa = workArea(*child_buf);
2861 success = closeWorkArea(child_wa, true);
2865 // In this case the child buffer is open but hidden.
2866 // It therefore should not (MUST NOT) be dirty!
2867 LATTEST(child_buf->isClean());
2868 theBufferList().release(child_buf);
2873 // goto bookmark to update bookmark pit.
2874 // FIXME: we should update only the bookmarks related to this buffer!
2875 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2876 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2877 guiApp->gotoBookmark(i+1, false, false);
2879 if (saveBufferIfNeeded(buf, false)) {
2880 buf.removeAutosaveFile();
2881 theBufferList().release(&buf);
2885 // open all children again to avoid a crash because of dangling
2886 // pointers (bug 6603)
2892 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2894 while (twa == d.currentTabWorkArea()) {
2895 twa->setCurrentIndex(twa->count() - 1);
2897 GuiWorkArea * wa = twa->currentWorkArea();
2898 Buffer & b = wa->bufferView().buffer();
2900 // We only want to close the buffer if the same buffer is not visible
2901 // in another view, and if this is not a child and if we are closing
2902 // a view (not a tabgroup).
2903 bool const close_buffer =
2904 !inOtherView(b) && !b.parent() && closing_;
2906 if (!closeWorkArea(wa, close_buffer))
2913 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2915 if (buf.isClean() || buf.paragraphs().empty())
2918 // Switch to this Buffer.
2923 if (buf.isUnnamed())
2924 file = from_utf8(buf.fileName().onlyFileName());
2926 file = buf.fileName().displayName(30);
2928 // Bring this window to top before asking questions.
2933 if (hiding && buf.isUnnamed()) {
2934 docstring const text = bformat(_("The document %1$s has not been "
2935 "saved yet.\n\nDo you want to save "
2936 "the document?"), file);
2937 ret = Alert::prompt(_("Save new document?"),
2938 text, 0, 1, _("&Save"), _("&Cancel"));
2942 docstring const text = bformat(_("The document %1$s has unsaved changes."
2943 "\n\nDo you want to save the document or discard the changes?"), file);
2944 ret = Alert::prompt(_("Save changed document?"),
2945 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2950 if (!saveBuffer(buf))
2954 // If we crash after this we could have no autosave file
2955 // but I guess this is really improbable (Jug).
2956 // Sometimes improbable things happen:
2957 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2958 // buf.removeAutosaveFile();
2960 // revert all changes
2971 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2973 Buffer & buf = wa->bufferView().buffer();
2975 for (int i = 0; i != d.splitter_->count(); ++i) {
2976 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2977 if (wa_ && wa_ != wa)
2980 return inOtherView(buf);
2984 bool GuiView::inOtherView(Buffer & buf)
2986 QList<int> const ids = guiApp->viewIds();
2988 for (int i = 0; i != ids.size(); ++i) {
2992 if (guiApp->view(ids[i]).workArea(buf))
2999 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3001 if (!documentBufferView())
3004 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3005 Buffer * const curbuf = &documentBufferView()->buffer();
3006 int nwa = twa->count();
3007 for (int i = 0; i < nwa; ++i) {
3008 if (&workArea(i)->bufferView().buffer() == curbuf) {
3010 if (np == NEXTBUFFER)
3011 next_index = (i == nwa - 1 ? 0 : i + 1);
3013 next_index = (i == 0 ? nwa - 1 : i - 1);
3015 twa->moveTab(i, next_index);
3017 setBuffer(&workArea(next_index)->bufferView().buffer());
3025 /// make sure the document is saved
3026 static bool ensureBufferClean(Buffer * buffer)
3028 LASSERT(buffer, return false);
3029 if (buffer->isClean() && !buffer->isUnnamed())
3032 docstring const file = buffer->fileName().displayName(30);
3035 if (!buffer->isUnnamed()) {
3036 text = bformat(_("The document %1$s has unsaved "
3037 "changes.\n\nDo you want to save "
3038 "the document?"), file);
3039 title = _("Save changed document?");
3042 text = bformat(_("The document %1$s has not been "
3043 "saved yet.\n\nDo you want to save "
3044 "the document?"), file);
3045 title = _("Save new document?");
3047 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3050 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3052 return buffer->isClean() && !buffer->isUnnamed();
3056 bool GuiView::reloadBuffer(Buffer & buf)
3058 Buffer::ReadStatus status = buf.reload();
3059 return status == Buffer::ReadSuccess;
3063 void GuiView::checkExternallyModifiedBuffers()
3065 BufferList::iterator bit = theBufferList().begin();
3066 BufferList::iterator const bend = theBufferList().end();
3067 for (; bit != bend; ++bit) {
3068 Buffer * buf = *bit;
3069 if (buf->fileName().exists()
3070 && buf->isExternallyModified(Buffer::checksum_method)) {
3071 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3072 " Reload now? Any local changes will be lost."),
3073 from_utf8(buf->absFileName()));
3074 int const ret = Alert::prompt(_("Reload externally changed document?"),
3075 text, 0, 1, _("&Reload"), _("&Cancel"));
3083 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3085 Buffer * buffer = documentBufferView()
3086 ? &(documentBufferView()->buffer()) : 0;
3088 switch (cmd.action()) {
3089 case LFUN_VC_REGISTER:
3090 if (!buffer || !ensureBufferClean(buffer))
3092 if (!buffer->lyxvc().inUse()) {
3093 if (buffer->lyxvc().registrer()) {
3094 reloadBuffer(*buffer);
3095 dr.clearMessageUpdate();
3100 case LFUN_VC_RENAME:
3101 case LFUN_VC_COPY: {
3102 if (!buffer || !ensureBufferClean(buffer))
3104 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3105 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3106 // Some changes are not yet committed.
3107 // We test here and not in getStatus(), since
3108 // this test is expensive.
3110 LyXVC::CommandResult ret =
3111 buffer->lyxvc().checkIn(log);
3113 if (ret == LyXVC::ErrorCommand ||
3114 ret == LyXVC::VCSuccess)
3115 reloadBuffer(*buffer);
3116 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3117 frontend::Alert::error(
3118 _("Revision control error."),
3119 _("Document could not be checked in."));
3123 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3124 LV_VC_RENAME : LV_VC_COPY;
3125 renameBuffer(*buffer, cmd.argument(), kind);
3130 case LFUN_VC_CHECK_IN:
3131 if (!buffer || !ensureBufferClean(buffer))
3133 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3135 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3137 // Only skip reloading if the checkin was cancelled or
3138 // an error occurred before the real checkin VCS command
3139 // was executed, since the VCS might have changed the
3140 // file even if it could not checkin successfully.
3141 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3142 reloadBuffer(*buffer);
3146 case LFUN_VC_CHECK_OUT:
3147 if (!buffer || !ensureBufferClean(buffer))
3149 if (buffer->lyxvc().inUse()) {
3150 dr.setMessage(buffer->lyxvc().checkOut());
3151 reloadBuffer(*buffer);
3155 case LFUN_VC_LOCKING_TOGGLE:
3156 LASSERT(buffer, return);
3157 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3159 if (buffer->lyxvc().inUse()) {
3160 string res = buffer->lyxvc().lockingToggle();
3162 frontend::Alert::error(_("Revision control error."),
3163 _("Error when setting the locking property."));
3166 reloadBuffer(*buffer);
3171 case LFUN_VC_REVERT:
3172 LASSERT(buffer, return);
3173 if (buffer->lyxvc().revert()) {
3174 reloadBuffer(*buffer);
3175 dr.clearMessageUpdate();
3179 case LFUN_VC_UNDO_LAST:
3180 LASSERT(buffer, return);
3181 buffer->lyxvc().undoLast();
3182 reloadBuffer(*buffer);
3183 dr.clearMessageUpdate();
3186 case LFUN_VC_REPO_UPDATE:
3187 LASSERT(buffer, return);
3188 if (ensureBufferClean(buffer)) {
3189 dr.setMessage(buffer->lyxvc().repoUpdate());
3190 checkExternallyModifiedBuffers();
3194 case LFUN_VC_COMMAND: {
3195 string flag = cmd.getArg(0);
3196 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3199 if (contains(flag, 'M')) {
3200 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3203 string path = cmd.getArg(1);
3204 if (contains(path, "$$p") && buffer)
3205 path = subst(path, "$$p", buffer->filePath());
3206 LYXERR(Debug::LYXVC, "Directory: " << path);
3208 if (!pp.isReadableDirectory()) {
3209 lyxerr << _("Directory is not accessible.") << endl;
3212 support::PathChanger p(pp);
3214 string command = cmd.getArg(2);
3215 if (command.empty())
3218 command = subst(command, "$$i", buffer->absFileName());
3219 command = subst(command, "$$p", buffer->filePath());
3221 command = subst(command, "$$m", to_utf8(message));
3222 LYXERR(Debug::LYXVC, "Command: " << command);
3224 one.startscript(Systemcall::Wait, command);
3228 if (contains(flag, 'I'))
3229 buffer->markDirty();
3230 if (contains(flag, 'R'))
3231 reloadBuffer(*buffer);
3236 case LFUN_VC_COMPARE: {
3237 if (cmd.argument().empty()) {
3238 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3242 string rev1 = cmd.getArg(0);
3246 // it seems safe to assume we have a buffer
3247 // coverity[FORWARD_NULL]
3248 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3251 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3252 f2 = buffer->absFileName();
3254 string rev2 = cmd.getArg(1);
3258 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3262 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3263 f1 << "\n" << f2 << "\n" );
3264 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3265 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3275 void GuiView::openChildDocument(string const & fname)
3277 LASSERT(documentBufferView(), return);
3278 Buffer & buffer = documentBufferView()->buffer();
3279 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3280 documentBufferView()->saveBookmark(false);
3282 if (theBufferList().exists(filename)) {
3283 child = theBufferList().getBuffer(filename);
3286 message(bformat(_("Opening child document %1$s..."),
3287 makeDisplayPath(filename.absFileName())));
3288 child = loadDocument(filename, false);
3290 // Set the parent name of the child document.
3291 // This makes insertion of citations and references in the child work,
3292 // when the target is in the parent or another child document.
3294 child->setParent(&buffer);
3298 bool GuiView::goToFileRow(string const & argument)
3302 size_t i = argument.find_last_of(' ');
3303 if (i != string::npos) {
3304 file_name = os::internal_path(trim(argument.substr(0, i)));
3305 istringstream is(argument.substr(i + 1));
3310 if (i == string::npos) {
3311 LYXERR0("Wrong argument: " << argument);
3315 string const abstmp = package().temp_dir().absFileName();
3316 string const realtmp = package().temp_dir().realPath();
3317 // We have to use os::path_prefix_is() here, instead of
3318 // simply prefixIs(), because the file name comes from
3319 // an external application and may need case adjustment.
3320 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3321 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3322 // Needed by inverse dvi search. If it is a file
3323 // in tmpdir, call the apropriated function.
3324 // If tmpdir is a symlink, we may have the real
3325 // path passed back, so we correct for that.
3326 if (!prefixIs(file_name, abstmp))
3327 file_name = subst(file_name, realtmp, abstmp);
3328 buf = theBufferList().getBufferFromTmp(file_name);
3330 // Must replace extension of the file to be .lyx
3331 // and get full path
3332 FileName const s = fileSearch(string(),
3333 support::changeExtension(file_name, ".lyx"), "lyx");
3334 // Either change buffer or load the file
3335 if (theBufferList().exists(s))
3336 buf = theBufferList().getBuffer(s);
3337 else if (s.exists()) {
3338 buf = loadDocument(s);
3343 _("File does not exist: %1$s"),
3344 makeDisplayPath(file_name)));
3350 _("No buffer for file: %1$s."),
3351 makeDisplayPath(file_name))
3356 bool success = documentBufferView()->setCursorFromRow(row);
3358 LYXERR(Debug::LATEX,
3359 "setCursorFromRow: invalid position for row " << row);
3360 frontend::Alert::error(_("Inverse Search Failed"),
3361 _("Invalid position requested by inverse search.\n"
3362 "You may need to update the viewed document."));
3368 void GuiView::toolBarPopup(const QPoint & pos)
3370 QMenu * menu = new QMenu;
3371 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3372 menu->exec(QCursor::pos());
3377 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3379 Buffer::ExportStatus const status = func(format);
3381 // the cloning operation will have produced a clone of the entire set of
3382 // documents, starting from the master. so we must delete those.
3383 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3385 busyBuffers.remove(orig);
3390 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3392 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3393 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3397 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3399 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3400 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3404 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3406 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3407 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3411 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3412 string const & argument,
3413 Buffer const * used_buffer,
3414 docstring const & msg,
3415 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3416 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3417 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3422 string format = argument;
3424 format = used_buffer->params().getDefaultOutputFormat();
3425 processing_format = format;
3427 progress_->clearMessages();
3430 #if EXPORT_in_THREAD
3431 GuiViewPrivate::busyBuffers.insert(used_buffer);
3432 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3433 if (!cloned_buffer) {
3434 Alert::error(_("Export Error"),
3435 _("Error cloning the Buffer."));
3438 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3443 setPreviewFuture(f);
3444 last_export_format = used_buffer->params().bufferFormat();
3447 // We are asynchronous, so we don't know here anything about the success
3450 Buffer::ExportStatus status;
3452 status = (used_buffer->*syncFunc)(format, true);
3453 } else if (previewFunc) {
3454 status = (used_buffer->*previewFunc)(format);
3457 handleExportStatus(gv_, status, format);
3459 return (status == Buffer::ExportSuccess
3460 || status == Buffer::PreviewSuccess);
3464 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3466 BufferView * bv = currentBufferView();
3467 LASSERT(bv, return);
3469 // Let the current BufferView dispatch its own actions.
3470 bv->dispatch(cmd, dr);
3471 if (dr.dispatched())
3474 // Try with the document BufferView dispatch if any.
3475 BufferView * doc_bv = documentBufferView();
3476 if (doc_bv && doc_bv != bv) {
3477 doc_bv->dispatch(cmd, dr);
3478 if (dr.dispatched())
3482 // Then let the current Cursor dispatch its own actions.
3483 bv->cursor().dispatch(cmd);
3485 // update completion. We do it here and not in
3486 // processKeySym to avoid another redraw just for a
3487 // changed inline completion
3488 if (cmd.origin() == FuncRequest::KEYBOARD) {
3489 if (cmd.action() == LFUN_SELF_INSERT
3490 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3491 updateCompletion(bv->cursor(), true, true);
3492 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3493 updateCompletion(bv->cursor(), false, true);
3495 updateCompletion(bv->cursor(), false, false);
3498 dr = bv->cursor().result();
3502 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3504 BufferView * bv = currentBufferView();
3505 // By default we won't need any update.
3506 dr.screenUpdate(Update::None);
3507 // assume cmd will be dispatched
3508 dr.dispatched(true);
3510 Buffer * doc_buffer = documentBufferView()
3511 ? &(documentBufferView()->buffer()) : 0;
3513 if (cmd.origin() == FuncRequest::TOC) {
3514 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3515 // FIXME: do we need to pass a DispatchResult object here?
3516 toc->doDispatch(bv->cursor(), cmd);
3520 string const argument = to_utf8(cmd.argument());
3522 switch(cmd.action()) {
3523 case LFUN_BUFFER_CHILD_OPEN:
3524 openChildDocument(to_utf8(cmd.argument()));
3527 case LFUN_BUFFER_IMPORT:
3528 importDocument(to_utf8(cmd.argument()));
3531 case LFUN_BUFFER_EXPORT: {
3534 // GCC only sees strfwd.h when building merged
3535 if (::lyx::operator==(cmd.argument(), "custom")) {
3536 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3540 string const dest = cmd.getArg(1);
3541 FileName target_dir;
3542 if (!dest.empty() && FileName::isAbsolute(dest))
3543 target_dir = FileName(support::onlyPath(dest));
3545 target_dir = doc_buffer->fileName().onlyPath();
3547 if ((dest.empty() && doc_buffer->isUnnamed())
3548 || !target_dir.isDirWritable()) {
3549 exportBufferAs(*doc_buffer, cmd.argument());
3552 /* TODO/Review: Is it a problem to also export the children?
3553 See the update_unincluded flag */
3554 d.asyncBufferProcessing(argument,
3557 &GuiViewPrivate::exportAndDestroy,
3560 // TODO Inform user about success
3564 case LFUN_BUFFER_EXPORT_AS: {
3565 LASSERT(doc_buffer, break);
3566 docstring f = cmd.argument();
3568 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3569 exportBufferAs(*doc_buffer, f);
3573 case LFUN_BUFFER_UPDATE: {
3574 d.asyncBufferProcessing(argument,
3577 &GuiViewPrivate::compileAndDestroy,
3582 case LFUN_BUFFER_VIEW: {
3583 d.asyncBufferProcessing(argument,
3585 _("Previewing ..."),
3586 &GuiViewPrivate::previewAndDestroy,
3591 case LFUN_MASTER_BUFFER_UPDATE: {
3592 d.asyncBufferProcessing(argument,
3593 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3595 &GuiViewPrivate::compileAndDestroy,
3600 case LFUN_MASTER_BUFFER_VIEW: {
3601 d.asyncBufferProcessing(argument,
3602 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3604 &GuiViewPrivate::previewAndDestroy,
3605 0, &Buffer::preview);
3608 case LFUN_BUFFER_SWITCH: {
3609 string const file_name = to_utf8(cmd.argument());
3610 if (!FileName::isAbsolute(file_name)) {
3612 dr.setMessage(_("Absolute filename expected."));
3616 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3619 dr.setMessage(_("Document not loaded"));
3623 // Do we open or switch to the buffer in this view ?
3624 if (workArea(*buffer)
3625 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3630 // Look for the buffer in other views
3631 QList<int> const ids = guiApp->viewIds();
3633 for (; i != ids.size(); ++i) {
3634 GuiView & gv = guiApp->view(ids[i]);
3635 if (gv.workArea(*buffer)) {
3637 gv.activateWindow();
3639 gv.setBuffer(buffer);
3644 // If necessary, open a new window as a last resort
3645 if (i == ids.size()) {
3646 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3652 case LFUN_BUFFER_NEXT:
3653 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3656 case LFUN_BUFFER_MOVE_NEXT:
3657 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3660 case LFUN_BUFFER_PREVIOUS:
3661 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3664 case LFUN_BUFFER_MOVE_PREVIOUS:
3665 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3668 case LFUN_COMMAND_EXECUTE: {
3669 command_execute_ = true;
3670 minibuffer_focus_ = true;
3673 case LFUN_DROP_LAYOUTS_CHOICE:
3674 d.layout_->showPopup();
3677 case LFUN_MENU_OPEN:
3678 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3679 menu->exec(QCursor::pos());
3682 case LFUN_FILE_INSERT:
3683 insertLyXFile(cmd.argument());
3686 case LFUN_FILE_INSERT_PLAINTEXT:
3687 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3688 string const fname = to_utf8(cmd.argument());
3689 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3690 dr.setMessage(_("Absolute filename expected."));
3694 FileName filename(fname);
3695 if (fname.empty()) {
3696 FileDialog dlg(qt_("Select file to insert"));
3698 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3699 QStringList(qt_("All Files (*)")));
3701 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3702 dr.setMessage(_("Canceled."));
3706 filename.set(fromqstr(result.second));
3710 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3711 bv->dispatch(new_cmd, dr);
3716 case LFUN_BUFFER_RELOAD: {
3717 LASSERT(doc_buffer, break);
3720 if (!doc_buffer->isClean()) {
3721 docstring const file =
3722 makeDisplayPath(doc_buffer->absFileName(), 20);
3723 docstring text = bformat(_("Any changes will be lost. "
3724 "Are you sure you want to revert to the saved version "
3725 "of the document %1$s?"), file);
3726 ret = Alert::prompt(_("Revert to saved document?"),
3727 text, 1, 1, _("&Revert"), _("&Cancel"));
3731 doc_buffer->markClean();
3732 reloadBuffer(*doc_buffer);
3733 dr.forceBufferUpdate();
3738 case LFUN_BUFFER_WRITE:
3739 LASSERT(doc_buffer, break);
3740 saveBuffer(*doc_buffer);
3743 case LFUN_BUFFER_WRITE_AS:
3744 LASSERT(doc_buffer, break);
3745 renameBuffer(*doc_buffer, cmd.argument());
3748 case LFUN_BUFFER_WRITE_ALL: {
3749 Buffer * first = theBufferList().first();
3752 message(_("Saving all documents..."));
3753 // We cannot use a for loop as the buffer list cycles.
3756 if (!b->isClean()) {
3758 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3760 b = theBufferList().next(b);
3761 } while (b != first);
3762 dr.setMessage(_("All documents saved."));
3766 case LFUN_BUFFER_CLOSE:
3770 case LFUN_BUFFER_CLOSE_ALL:
3774 case LFUN_TOOLBAR_TOGGLE: {
3775 string const name = cmd.getArg(0);
3776 if (GuiToolbar * t = toolbar(name))
3781 case LFUN_ICON_SIZE: {
3782 QSize size = d.iconSize(cmd.argument());
3784 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3785 size.width(), size.height()));
3789 case LFUN_DIALOG_UPDATE: {
3790 string const name = to_utf8(cmd.argument());
3791 if (name == "prefs" || name == "document")
3792 updateDialog(name, string());
3793 else if (name == "paragraph")
3794 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3795 else if (currentBufferView()) {
3796 Inset * inset = currentBufferView()->editedInset(name);
3797 // Can only update a dialog connected to an existing inset
3799 // FIXME: get rid of this indirection; GuiView ask the inset
3800 // if he is kind enough to update itself...
3801 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3802 //FIXME: pass DispatchResult here?
3803 inset->dispatch(currentBufferView()->cursor(), fr);
3809 case LFUN_DIALOG_TOGGLE: {
3810 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3811 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3812 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3816 case LFUN_DIALOG_DISCONNECT_INSET:
3817 disconnectDialog(to_utf8(cmd.argument()));
3820 case LFUN_DIALOG_HIDE: {
3821 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3825 case LFUN_DIALOG_SHOW: {
3826 string const name = cmd.getArg(0);
3827 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3829 if (name == "character") {
3830 data = freefont2string();
3832 showDialog("character", data);
3833 } else if (name == "latexlog") {
3834 Buffer::LogType type;
3835 string const logfile = doc_buffer->logName(&type);
3837 case Buffer::latexlog:
3840 case Buffer::buildlog:
3844 data += Lexer::quoteString(logfile);
3845 showDialog("log", data);
3846 } else if (name == "vclog") {
3847 string const data = "vc " +
3848 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3849 showDialog("log", data);
3850 } else if (name == "symbols") {
3851 data = bv->cursor().getEncoding()->name();
3853 showDialog("symbols", data);
3855 } else if (name == "prefs" && isFullScreen()) {
3856 lfunUiToggle("fullscreen");
3857 showDialog("prefs", data);
3859 showDialog(name, data);
3864 dr.setMessage(cmd.argument());
3867 case LFUN_UI_TOGGLE: {
3868 string arg = cmd.getArg(0);
3869 if (!lfunUiToggle(arg)) {
3870 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3871 dr.setMessage(bformat(msg, from_utf8(arg)));
3873 // Make sure the keyboard focus stays in the work area.
3878 case LFUN_VIEW_SPLIT: {
3879 LASSERT(doc_buffer, break);
3880 string const orientation = cmd.getArg(0);
3881 d.splitter_->setOrientation(orientation == "vertical"
3882 ? Qt::Vertical : Qt::Horizontal);
3883 TabWorkArea * twa = addTabWorkArea();
3884 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3885 setCurrentWorkArea(wa);
3888 case LFUN_TAB_GROUP_CLOSE:
3889 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3890 closeTabWorkArea(twa);
3891 d.current_work_area_ = 0;
3892 twa = d.currentTabWorkArea();
3893 // Switch to the next GuiWorkArea in the found TabWorkArea.
3895 // Make sure the work area is up to date.
3896 setCurrentWorkArea(twa->currentWorkArea());
3898 setCurrentWorkArea(0);
3903 case LFUN_VIEW_CLOSE:
3904 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3905 closeWorkArea(twa->currentWorkArea());
3906 d.current_work_area_ = 0;
3907 twa = d.currentTabWorkArea();
3908 // Switch to the next GuiWorkArea in the found TabWorkArea.
3910 // Make sure the work area is up to date.
3911 setCurrentWorkArea(twa->currentWorkArea());
3913 setCurrentWorkArea(0);
3918 case LFUN_COMPLETION_INLINE:
3919 if (d.current_work_area_)
3920 d.current_work_area_->completer().showInline();
3923 case LFUN_COMPLETION_POPUP:
3924 if (d.current_work_area_)
3925 d.current_work_area_->completer().showPopup();
3930 if (d.current_work_area_)
3931 d.current_work_area_->completer().tab();
3934 case LFUN_COMPLETION_CANCEL:
3935 if (d.current_work_area_) {
3936 if (d.current_work_area_->completer().popupVisible())
3937 d.current_work_area_->completer().hidePopup();
3939 d.current_work_area_->completer().hideInline();
3943 case LFUN_COMPLETION_ACCEPT:
3944 if (d.current_work_area_)
3945 d.current_work_area_->completer().activate();
3948 case LFUN_BUFFER_ZOOM_IN:
3949 case LFUN_BUFFER_ZOOM_OUT: {
3950 // use a signed temp to avoid overflow
3951 int zoom = lyxrc.zoom;
3952 if (cmd.argument().empty()) {
3953 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3958 zoom += convert<int>(cmd.argument());
3960 if (zoom < static_cast<int>(zoom_min_))
3964 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3966 // The global QPixmapCache is used in GuiPainter to cache text
3967 // painting so we must reset it.
3968 QPixmapCache::clear();
3969 guiApp->fontLoader().update();
3970 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3974 case LFUN_VC_REGISTER:
3975 case LFUN_VC_RENAME:
3977 case LFUN_VC_CHECK_IN:
3978 case LFUN_VC_CHECK_OUT:
3979 case LFUN_VC_REPO_UPDATE:
3980 case LFUN_VC_LOCKING_TOGGLE:
3981 case LFUN_VC_REVERT:
3982 case LFUN_VC_UNDO_LAST:
3983 case LFUN_VC_COMMAND:
3984 case LFUN_VC_COMPARE:
3985 dispatchVC(cmd, dr);
3988 case LFUN_SERVER_GOTO_FILE_ROW:
3989 if(goToFileRow(to_utf8(cmd.argument())))
3990 dr.screenUpdate(Update::Force | Update::FitCursor);
3993 case LFUN_LYX_ACTIVATE:
3997 case LFUN_FORWARD_SEARCH: {
3998 // it seems safe to assume we have a document buffer, since
3999 // getStatus wants one.
4000 // coverity[FORWARD_NULL]
4001 Buffer const * doc_master = doc_buffer->masterBuffer();
4002 FileName const path(doc_master->temppath());
4003 string const texname = doc_master->isChild(doc_buffer)
4004 ? DocFileName(changeExtension(
4005 doc_buffer->absFileName(),
4006 "tex")).mangledFileName()
4007 : doc_buffer->latexName();
4008 string const fulltexname =
4009 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4010 string const mastername =
4011 removeExtension(doc_master->latexName());
4012 FileName const dviname(addName(path.absFileName(),
4013 addExtension(mastername, "dvi")));
4014 FileName const pdfname(addName(path.absFileName(),
4015 addExtension(mastername, "pdf")));
4016 bool const have_dvi = dviname.exists();
4017 bool const have_pdf = pdfname.exists();
4018 if (!have_dvi && !have_pdf) {
4019 dr.setMessage(_("Please, preview the document first."));
4022 string outname = dviname.onlyFileName();
4023 string command = lyxrc.forward_search_dvi;
4024 if (!have_dvi || (have_pdf &&
4025 pdfname.lastModified() > dviname.lastModified())) {
4026 outname = pdfname.onlyFileName();
4027 command = lyxrc.forward_search_pdf;
4030 DocIterator cur = bv->cursor();
4031 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4032 LYXERR(Debug::ACTION, "Forward search: row:" << row
4034 if (row == -1 || command.empty()) {
4035 dr.setMessage(_("Couldn't proceed."));
4038 string texrow = convert<string>(row);
4040 command = subst(command, "$$n", texrow);
4041 command = subst(command, "$$f", fulltexname);
4042 command = subst(command, "$$t", texname);
4043 command = subst(command, "$$o", outname);
4045 PathChanger p(path);
4047 one.startscript(Systemcall::DontWait, command);
4051 case LFUN_SPELLING_CONTINUOUSLY:
4052 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4053 dr.screenUpdate(Update::Force);
4057 // The LFUN must be for one of BufferView, Buffer or Cursor;
4059 dispatchToBufferView(cmd, dr);
4063 // Part of automatic menu appearance feature.
4064 if (isFullScreen()) {
4065 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4069 // Need to update bv because many LFUNs here might have destroyed it
4070 bv = currentBufferView();
4072 // Clear non-empty selections
4073 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4075 Cursor & cur = bv->cursor();
4076 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4077 cur.clearSelection();
4083 bool GuiView::lfunUiToggle(string const & ui_component)
4085 if (ui_component == "scrollbar") {
4086 // hide() is of no help
4087 if (d.current_work_area_->verticalScrollBarPolicy() ==
4088 Qt::ScrollBarAlwaysOff)
4090 d.current_work_area_->setVerticalScrollBarPolicy(
4091 Qt::ScrollBarAsNeeded);
4093 d.current_work_area_->setVerticalScrollBarPolicy(
4094 Qt::ScrollBarAlwaysOff);
4095 } else if (ui_component == "statusbar") {
4096 statusBar()->setVisible(!statusBar()->isVisible());
4097 } else if (ui_component == "menubar") {
4098 menuBar()->setVisible(!menuBar()->isVisible());
4100 if (ui_component == "frame") {
4102 getContentsMargins(&l, &t, &r, &b);
4103 //are the frames in default state?
4104 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4106 setContentsMargins(-2, -2, -2, -2);
4108 setContentsMargins(0, 0, 0, 0);
4111 if (ui_component == "fullscreen") {
4119 void GuiView::toggleFullScreen()
4121 if (isFullScreen()) {
4122 for (int i = 0; i != d.splitter_->count(); ++i)
4123 d.tabWorkArea(i)->setFullScreen(false);
4124 setContentsMargins(0, 0, 0, 0);
4125 setWindowState(windowState() ^ Qt::WindowFullScreen);
4128 statusBar()->show();
4131 hideDialogs("prefs", 0);
4132 for (int i = 0; i != d.splitter_->count(); ++i)
4133 d.tabWorkArea(i)->setFullScreen(true);
4134 setContentsMargins(-2, -2, -2, -2);
4136 setWindowState(windowState() ^ Qt::WindowFullScreen);
4137 if (lyxrc.full_screen_statusbar)
4138 statusBar()->hide();
4139 if (lyxrc.full_screen_menubar)
4141 if (lyxrc.full_screen_toolbars) {
4142 ToolbarMap::iterator end = d.toolbars_.end();
4143 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4148 // give dialogs like the TOC a chance to adapt
4153 Buffer const * GuiView::updateInset(Inset const * inset)
4158 Buffer const * inset_buffer = &(inset->buffer());
4160 for (int i = 0; i != d.splitter_->count(); ++i) {
4161 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4164 Buffer const * buffer = &(wa->bufferView().buffer());
4165 if (inset_buffer == buffer)
4166 wa->scheduleRedraw();
4168 return inset_buffer;
4172 void GuiView::restartCursor()
4174 /* When we move around, or type, it's nice to be able to see
4175 * the cursor immediately after the keypress.
4177 if (d.current_work_area_)
4178 d.current_work_area_->startBlinkingCursor();
4180 // Take this occasion to update the other GUI elements.
4186 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4188 if (d.current_work_area_)
4189 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4194 // This list should be kept in sync with the list of insets in
4195 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4196 // dialog should have the same name as the inset.
4197 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4198 // docs in LyXAction.cpp.
4200 char const * const dialognames[] = {
4202 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4203 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4204 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4205 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4206 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4207 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4208 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4209 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4211 char const * const * const end_dialognames =
4212 dialognames + (sizeof(dialognames) / sizeof(char *));
4216 cmpCStr(char const * name) : name_(name) {}
4217 bool operator()(char const * other) {
4218 return strcmp(other, name_) == 0;
4225 bool isValidName(string const & name)
4227 return find_if(dialognames, end_dialognames,
4228 cmpCStr(name.c_str())) != end_dialognames;
4234 void GuiView::resetDialogs()
4236 // Make sure that no LFUN uses any GuiView.
4237 guiApp->setCurrentView(0);
4241 constructToolbars();
4242 guiApp->menus().fillMenuBar(menuBar(), this, false);
4243 d.layout_->updateContents(true);
4244 // Now update controls with current buffer.
4245 guiApp->setCurrentView(this);
4251 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4253 if (!isValidName(name))
4256 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4258 if (it != d.dialogs_.end()) {
4260 it->second->hideView();
4261 return it->second.get();
4264 Dialog * dialog = build(name);
4265 d.dialogs_[name].reset(dialog);
4266 if (lyxrc.allow_geometry_session)
4267 dialog->restoreSession();
4274 void GuiView::showDialog(string const & name, string const & data,
4277 triggerShowDialog(toqstr(name), toqstr(data), inset);
4281 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4287 const string name = fromqstr(qname);
4288 const string data = fromqstr(qdata);
4292 Dialog * dialog = findOrBuild(name, false);
4294 bool const visible = dialog->isVisibleView();
4295 dialog->showData(data);
4296 if (inset && currentBufferView())
4297 currentBufferView()->editInset(name, inset);
4298 // We only set the focus to the new dialog if it was not yet
4299 // visible in order not to change the existing previous behaviour
4301 // activateWindow is needed for floating dockviews
4302 dialog->asQWidget()->raise();
4303 dialog->asQWidget()->activateWindow();
4304 dialog->asQWidget()->setFocus();
4308 catch (ExceptionMessage const & ex) {
4316 bool GuiView::isDialogVisible(string const & name) const
4318 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4319 if (it == d.dialogs_.end())
4321 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4325 void GuiView::hideDialog(string const & name, Inset * inset)
4327 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4328 if (it == d.dialogs_.end())
4332 if (!currentBufferView())
4334 if (inset != currentBufferView()->editedInset(name))
4338 Dialog * const dialog = it->second.get();
4339 if (dialog->isVisibleView())
4341 if (currentBufferView())
4342 currentBufferView()->editInset(name, 0);
4346 void GuiView::disconnectDialog(string const & name)
4348 if (!isValidName(name))
4350 if (currentBufferView())
4351 currentBufferView()->editInset(name, 0);
4355 void GuiView::hideAll() const
4357 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4358 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4360 for(; it != end; ++it)
4361 it->second->hideView();
4365 void GuiView::updateDialogs()
4367 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4368 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4370 for(; it != end; ++it) {
4371 Dialog * dialog = it->second.get();
4373 if (dialog->needBufferOpen() && !documentBufferView())
4374 hideDialog(fromqstr(dialog->name()), 0);
4375 else if (dialog->isVisibleView())
4376 dialog->checkStatus();
4383 Dialog * createDialog(GuiView & lv, string const & name);
4385 // will be replaced by a proper factory...
4386 Dialog * createGuiAbout(GuiView & lv);
4387 Dialog * createGuiBibtex(GuiView & lv);
4388 Dialog * createGuiChanges(GuiView & lv);
4389 Dialog * createGuiCharacter(GuiView & lv);
4390 Dialog * createGuiCitation(GuiView & lv);
4391 Dialog * createGuiCompare(GuiView & lv);
4392 Dialog * createGuiCompareHistory(GuiView & lv);
4393 Dialog * createGuiDelimiter(GuiView & lv);
4394 Dialog * createGuiDocument(GuiView & lv);
4395 Dialog * createGuiErrorList(GuiView & lv);
4396 Dialog * createGuiExternal(GuiView & lv);
4397 Dialog * createGuiGraphics(GuiView & lv);
4398 Dialog * createGuiInclude(GuiView & lv);
4399 Dialog * createGuiIndex(GuiView & lv);
4400 Dialog * createGuiListings(GuiView & lv);
4401 Dialog * createGuiLog(GuiView & lv);
4402 Dialog * createGuiMathMatrix(GuiView & lv);
4403 Dialog * createGuiNote(GuiView & lv);
4404 Dialog * createGuiParagraph(GuiView & lv);
4405 Dialog * createGuiPhantom(GuiView & lv);
4406 Dialog * createGuiPreferences(GuiView & lv);
4407 Dialog * createGuiPrint(GuiView & lv);
4408 Dialog * createGuiPrintindex(GuiView & lv);
4409 Dialog * createGuiRef(GuiView & lv);
4410 Dialog * createGuiSearch(GuiView & lv);
4411 Dialog * createGuiSearchAdv(GuiView & lv);
4412 Dialog * createGuiSendTo(GuiView & lv);
4413 Dialog * createGuiShowFile(GuiView & lv);
4414 Dialog * createGuiSpellchecker(GuiView & lv);
4415 Dialog * createGuiSymbols(GuiView & lv);
4416 Dialog * createGuiTabularCreate(GuiView & lv);
4417 Dialog * createGuiTexInfo(GuiView & lv);
4418 Dialog * createGuiToc(GuiView & lv);
4419 Dialog * createGuiThesaurus(GuiView & lv);
4420 Dialog * createGuiViewSource(GuiView & lv);
4421 Dialog * createGuiWrap(GuiView & lv);
4422 Dialog * createGuiProgressView(GuiView & lv);
4426 Dialog * GuiView::build(string const & name)
4428 LASSERT(isValidName(name), return 0);
4430 Dialog * dialog = createDialog(*this, name);
4434 if (name == "aboutlyx")
4435 return createGuiAbout(*this);
4436 if (name == "bibtex")
4437 return createGuiBibtex(*this);
4438 if (name == "changes")
4439 return createGuiChanges(*this);
4440 if (name == "character")
4441 return createGuiCharacter(*this);
4442 if (name == "citation")
4443 return createGuiCitation(*this);
4444 if (name == "compare")
4445 return createGuiCompare(*this);
4446 if (name == "comparehistory")
4447 return createGuiCompareHistory(*this);
4448 if (name == "document")
4449 return createGuiDocument(*this);
4450 if (name == "errorlist")
4451 return createGuiErrorList(*this);
4452 if (name == "external")
4453 return createGuiExternal(*this);
4455 return createGuiShowFile(*this);
4456 if (name == "findreplace")
4457 return createGuiSearch(*this);
4458 if (name == "findreplaceadv")
4459 return createGuiSearchAdv(*this);
4460 if (name == "graphics")
4461 return createGuiGraphics(*this);
4462 if (name == "include")
4463 return createGuiInclude(*this);
4464 if (name == "index")
4465 return createGuiIndex(*this);
4466 if (name == "index_print")
4467 return createGuiPrintindex(*this);
4468 if (name == "listings")
4469 return createGuiListings(*this);
4471 return createGuiLog(*this);
4472 if (name == "mathdelimiter")
4473 return createGuiDelimiter(*this);
4474 if (name == "mathmatrix")
4475 return createGuiMathMatrix(*this);
4477 return createGuiNote(*this);
4478 if (name == "paragraph")
4479 return createGuiParagraph(*this);
4480 if (name == "phantom")
4481 return createGuiPhantom(*this);
4482 if (name == "prefs")
4483 return createGuiPreferences(*this);
4485 return createGuiRef(*this);
4486 if (name == "sendto")
4487 return createGuiSendTo(*this);
4488 if (name == "spellchecker")
4489 return createGuiSpellchecker(*this);
4490 if (name == "symbols")
4491 return createGuiSymbols(*this);
4492 if (name == "tabularcreate")
4493 return createGuiTabularCreate(*this);
4494 if (name == "texinfo")
4495 return createGuiTexInfo(*this);
4496 if (name == "thesaurus")
4497 return createGuiThesaurus(*this);
4499 return createGuiToc(*this);
4500 if (name == "view-source")
4501 return createGuiViewSource(*this);
4503 return createGuiWrap(*this);
4504 if (name == "progress")
4505 return createGuiProgressView(*this);
4511 } // namespace frontend
4514 #include "moc_GuiView.cpp"