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_;
330 QMenu * toolBarPopup(GuiView * parent)
332 // FIXME: translation
333 QMenu * menu = new QMenu(parent);
334 QActionGroup * iconSizeGroup = new QActionGroup(parent);
336 QAction * smallIcons = new QAction(iconSizeGroup);
337 smallIcons->setText(qt_("Small-sized icons"));
338 smallIcons->setCheckable(true);
339 QObject::connect(smallIcons, SIGNAL(triggered()),
340 parent, SLOT(smallSizedIcons()));
341 menu->addAction(smallIcons);
343 QAction * normalIcons = new QAction(iconSizeGroup);
344 normalIcons->setText(qt_("Normal-sized icons"));
345 normalIcons->setCheckable(true);
346 QObject::connect(normalIcons, SIGNAL(triggered()),
347 parent, SLOT(normalSizedIcons()));
348 menu->addAction(normalIcons);
350 QAction * bigIcons = new QAction(iconSizeGroup);
351 bigIcons->setText(qt_("Big-sized icons"));
352 bigIcons->setCheckable(true);
353 QObject::connect(bigIcons, SIGNAL(triggered()),
354 parent, SLOT(bigSizedIcons()));
355 menu->addAction(bigIcons);
357 QAction * hugeIcons = new QAction(iconSizeGroup);
358 hugeIcons->setText(qt_("Huge-sized icons"));
359 hugeIcons->setCheckable(true);
360 QObject::connect(hugeIcons, SIGNAL(triggered()),
361 parent, SLOT(hugeSizedIcons()));
362 menu->addAction(hugeIcons);
364 QAction * giantIcons = new QAction(iconSizeGroup);
365 giantIcons->setText(qt_("Giant-sized icons"));
366 giantIcons->setCheckable(true);
367 QObject::connect(giantIcons, SIGNAL(triggered()),
368 parent, SLOT(giantSizedIcons()));
369 menu->addAction(giantIcons);
371 unsigned int cur = parent->iconSize().width();
372 if ( cur == parent->d.smallIconSize)
373 smallIcons->setChecked(true);
374 else if (cur == parent->d.normalIconSize)
375 normalIcons->setChecked(true);
376 else if (cur == parent->d.bigIconSize)
377 bigIcons->setChecked(true);
378 else if (cur == parent->d.hugeIconSize)
379 hugeIcons->setChecked(true);
380 else if (cur == parent->d.giantIconSize)
381 giantIcons->setChecked(true);
388 stack_widget_->setCurrentWidget(bg_widget_);
389 bg_widget_->setUpdatesEnabled(true);
390 bg_widget_->setFocus();
393 int tabWorkAreaCount()
395 return splitter_->count();
398 TabWorkArea * tabWorkArea(int i)
400 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
403 TabWorkArea * currentTabWorkArea()
405 int areas = tabWorkAreaCount();
407 // The first TabWorkArea is always the first one, if any.
408 return tabWorkArea(0);
410 for (int i = 0; i != areas; ++i) {
411 TabWorkArea * twa = tabWorkArea(i);
412 if (current_main_work_area_ == twa->currentWorkArea())
416 // None has the focus so we just take the first one.
417 return tabWorkArea(0);
420 int countWorkAreasOf(Buffer & buf)
422 int areas = tabWorkAreaCount();
424 for (int i = 0; i != areas; ++i) {
425 TabWorkArea * twa = tabWorkArea(i);
426 if (twa->workArea(buf))
432 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
434 if (processing_thread_watcher_.isRunning()) {
435 // we prefer to cancel this preview in order to keep a snappy
439 processing_thread_watcher_.setFuture(f);
444 GuiWorkArea * current_work_area_;
445 GuiWorkArea * current_main_work_area_;
446 QSplitter * splitter_;
447 QStackedWidget * stack_widget_;
448 BackgroundWidget * bg_widget_;
450 ToolbarMap toolbars_;
451 ProgressInterface* progress_;
452 /// The main layout box.
454 * \warning Don't Delete! The layout box is actually owned by
455 * whichever toolbar contains it. All the GuiView class needs is a
456 * means of accessing it.
458 * FIXME: replace that with a proper model so that we are not limited
459 * to only one dialog.
464 map<string, DialogPtr> dialogs_;
466 unsigned int smallIconSize;
467 unsigned int normalIconSize;
468 unsigned int bigIconSize;
469 unsigned int hugeIconSize;
470 unsigned int giantIconSize;
472 QTimer statusbar_timer_;
473 /// auto-saving of buffers
474 Timeout autosave_timeout_;
475 /// flag against a race condition due to multiclicks, see bug #1119
479 TocModels toc_models_;
482 QFutureWatcher<docstring> autosave_watcher_;
483 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
485 string last_export_format;
486 string processing_format;
488 static QSet<Buffer const *> busyBuffers;
489 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
492 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
495 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
497 // TODO syncFunc/previewFunc: use bind
498 bool asyncBufferProcessing(string const & argument,
499 Buffer const * used_buffer,
500 docstring const & msg,
501 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
502 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
503 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
505 QVector<GuiWorkArea*> guiWorkAreas();
508 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
511 GuiView::GuiView(int id)
512 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
513 command_execute_(false), minibuffer_focus_(false)
515 // GuiToolbars *must* be initialised before the menu bar.
516 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
519 // set ourself as the current view. This is needed for the menu bar
520 // filling, at least for the static special menu item on Mac. Otherwise
521 // they are greyed out.
522 guiApp->setCurrentView(this);
524 // Fill up the menu bar.
525 guiApp->menus().fillMenuBar(menuBar(), this, true);
527 setCentralWidget(d.stack_widget_);
529 // Start autosave timer
530 if (lyxrc.autosave) {
531 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
532 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
533 d.autosave_timeout_.start();
535 connect(&d.statusbar_timer_, SIGNAL(timeout()),
536 this, SLOT(clearMessage()));
538 // We don't want to keep the window in memory if it is closed.
539 setAttribute(Qt::WA_DeleteOnClose, true);
541 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
542 // QIcon::fromTheme was introduced in Qt 4.6
543 #if (QT_VERSION >= 0x040600)
544 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
545 // since the icon is provided in the application bundle. We use a themed
546 // version when available and use the bundled one as fallback.
547 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
549 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
555 // use tabbed dock area for multiple docks
556 // (such as "source" and "messages")
557 setDockOptions(QMainWindow::ForceTabbedDocks);
560 setAcceptDrops(true);
562 // add busy indicator to statusbar
563 QLabel * busylabel = new QLabel(statusBar());
564 statusBar()->addPermanentWidget(busylabel);
565 search_mode mode = theGuiApp()->imageSearchMode();
566 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
567 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
568 busylabel->setMovie(busyanim);
572 connect(&d.processing_thread_watcher_, SIGNAL(started()),
573 busylabel, SLOT(show()));
574 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
575 busylabel, SLOT(hide()));
577 QFontMetrics const fm(statusBar()->fontMetrics());
578 int const roheight = max(int(d.normalIconSize), fm.height());
579 QSize const rosize(roheight, roheight);
580 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
581 read_only_ = new QLabel(statusBar());
582 read_only_->setPixmap(readonly);
583 read_only_->setScaledContents(true);
584 read_only_->setAlignment(Qt::AlignCenter);
586 statusBar()->addPermanentWidget(read_only_);
588 version_control_ = new QLabel(statusBar());
589 version_control_->setAlignment(Qt::AlignCenter);
590 version_control_->setFrameStyle(QFrame::StyledPanel);
591 version_control_->hide();
592 statusBar()->addPermanentWidget(version_control_);
594 statusBar()->setSizeGripEnabled(true);
597 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
598 SLOT(autoSaveThreadFinished()));
600 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
601 SLOT(processingThreadStarted()));
602 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
603 SLOT(processingThreadFinished()));
605 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
606 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
608 // Forbid too small unresizable window because it can happen
609 // with some window manager under X11.
610 setMinimumSize(300, 200);
612 if (lyxrc.allow_geometry_session) {
613 // Now take care of session management.
618 // no session handling, default to a sane size.
619 setGeometry(50, 50, 690, 510);
622 // clear session data if any.
624 settings.remove("views");
634 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
636 QVector<GuiWorkArea*> areas;
637 for (int i = 0; i < tabWorkAreaCount(); i++) {
638 TabWorkArea* ta = tabWorkArea(i);
639 for (int u = 0; u < ta->count(); u++) {
640 areas << ta->workArea(u);
646 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
647 string const & format)
649 docstring const fmt = formats.prettyName(format);
652 case Buffer::ExportSuccess:
653 msg = bformat(_("Successful export to format: %1$s"), fmt);
655 case Buffer::ExportCancel:
656 msg = _("Document export cancelled.");
658 case Buffer::ExportError:
659 case Buffer::ExportNoPathToFormat:
660 case Buffer::ExportTexPathHasSpaces:
661 case Buffer::ExportConverterError:
662 msg = bformat(_("Error while exporting format: %1$s"), fmt);
664 case Buffer::PreviewSuccess:
665 msg = bformat(_("Successful preview of format: %1$s"), fmt);
667 case Buffer::PreviewError:
668 msg = bformat(_("Error while previewing format: %1$s"), fmt);
675 void GuiView::processingThreadStarted()
680 void GuiView::processingThreadFinished()
682 QFutureWatcher<Buffer::ExportStatus> const * watcher =
683 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
685 Buffer::ExportStatus const status = watcher->result();
686 handleExportStatus(this, status, d.processing_format);
689 BufferView const * const bv = currentBufferView();
690 if (bv && !bv->buffer().errorList("Export").empty()) {
694 errors(d.last_export_format);
698 void GuiView::autoSaveThreadFinished()
700 QFutureWatcher<docstring> const * watcher =
701 static_cast<QFutureWatcher<docstring> const *>(sender());
702 message(watcher->result());
707 void GuiView::saveLayout() const
710 settings.beginGroup("views");
711 settings.beginGroup(QString::number(id_));
712 #if defined(Q_WS_X11) || defined(QPA_XCB)
713 settings.setValue("pos", pos());
714 settings.setValue("size", size());
716 settings.setValue("geometry", saveGeometry());
718 settings.setValue("layout", saveState(0));
719 settings.setValue("icon_size", iconSize());
723 void GuiView::saveUISettings() const
725 // Save the toolbar private states
726 ToolbarMap::iterator end = d.toolbars_.end();
727 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
728 it->second->saveSession();
729 // Now take care of all other dialogs
730 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
731 for (; it!= d.dialogs_.end(); ++it)
732 it->second->saveSession();
736 bool GuiView::restoreLayout()
739 settings.beginGroup("views");
740 settings.beginGroup(QString::number(id_));
741 QString const icon_key = "icon_size";
742 if (!settings.contains(icon_key))
745 //code below is skipped when when ~/.config/LyX is (re)created
746 QSize icon_size = settings.value(icon_key).toSize();
747 // Check whether session size changed.
748 if (icon_size.width() != int(d.smallIconSize) &&
749 icon_size.width() != int(d.normalIconSize) &&
750 icon_size.width() != int(d.bigIconSize) &&
751 icon_size.width() != int(d.hugeIconSize) &&
752 icon_size.width() != int(d.giantIconSize)) {
753 icon_size.setWidth(d.normalIconSize);
754 icon_size.setHeight(d.normalIconSize);
756 setIconSize(icon_size);
758 #if defined(Q_WS_X11) || defined(QPA_XCB)
759 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
760 QSize size = settings.value("size", QSize(690, 510)).toSize();
764 // Work-around for bug #6034: the window ends up in an undetermined
765 // state when trying to restore a maximized window when it is
766 // already maximized.
767 if (!(windowState() & Qt::WindowMaximized))
768 if (!restoreGeometry(settings.value("geometry").toByteArray()))
769 setGeometry(50, 50, 690, 510);
771 // Make sure layout is correctly oriented.
772 setLayoutDirection(qApp->layoutDirection());
774 // Allow the toc and view-source dock widget to be restored if needed.
776 if ((dialog = findOrBuild("toc", true)))
777 // see bug 5082. At least setup title and enabled state.
778 // Visibility will be adjusted by restoreState below.
779 dialog->prepareView();
780 if ((dialog = findOrBuild("view-source", true)))
781 dialog->prepareView();
782 if ((dialog = findOrBuild("progress", true)))
783 dialog->prepareView();
785 if (!restoreState(settings.value("layout").toByteArray(), 0))
788 // init the toolbars that have not been restored
789 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
790 Toolbars::Infos::iterator end = guiApp->toolbars().end();
791 for (; cit != end; ++cit) {
792 GuiToolbar * tb = toolbar(cit->name);
793 if (tb && !tb->isRestored())
794 initToolbar(cit->name);
802 GuiToolbar * GuiView::toolbar(string const & name)
804 ToolbarMap::iterator it = d.toolbars_.find(name);
805 if (it != d.toolbars_.end())
808 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
813 void GuiView::constructToolbars()
815 ToolbarMap::iterator it = d.toolbars_.begin();
816 for (; it != d.toolbars_.end(); ++it)
820 // I don't like doing this here, but the standard toolbar
821 // destroys this object when it's destroyed itself (vfr)
822 d.layout_ = new LayoutBox(*this);
823 d.stack_widget_->addWidget(d.layout_);
824 d.layout_->move(0,0);
826 // extracts the toolbars from the backend
827 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
828 Toolbars::Infos::iterator end = guiApp->toolbars().end();
829 for (; cit != end; ++cit)
830 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
834 void GuiView::initToolbars()
836 // extracts the toolbars from the backend
837 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
838 Toolbars::Infos::iterator end = guiApp->toolbars().end();
839 for (; cit != end; ++cit)
840 initToolbar(cit->name);
844 void GuiView::initToolbar(string const & name)
846 GuiToolbar * tb = toolbar(name);
849 int const visibility = guiApp->toolbars().defaultVisibility(name);
850 bool newline = !(visibility & Toolbars::SAMEROW);
851 tb->setVisible(false);
852 tb->setVisibility(visibility);
854 if (visibility & Toolbars::TOP) {
856 addToolBarBreak(Qt::TopToolBarArea);
857 addToolBar(Qt::TopToolBarArea, tb);
860 if (visibility & Toolbars::BOTTOM) {
862 addToolBarBreak(Qt::BottomToolBarArea);
863 addToolBar(Qt::BottomToolBarArea, tb);
866 if (visibility & Toolbars::LEFT) {
868 addToolBarBreak(Qt::LeftToolBarArea);
869 addToolBar(Qt::LeftToolBarArea, tb);
872 if (visibility & Toolbars::RIGHT) {
874 addToolBarBreak(Qt::RightToolBarArea);
875 addToolBar(Qt::RightToolBarArea, tb);
878 if (visibility & Toolbars::ON)
879 tb->setVisible(true);
883 TocModels & GuiView::tocModels()
885 return d.toc_models_;
889 void GuiView::setFocus()
891 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
892 QMainWindow::setFocus();
896 bool GuiView::hasFocus() const
898 if (currentWorkArea())
899 return currentWorkArea()->hasFocus();
900 if (currentMainWorkArea())
901 return currentMainWorkArea()->hasFocus();
902 return d.bg_widget_->hasFocus();
906 void GuiView::focusInEvent(QFocusEvent * e)
908 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
909 QMainWindow::focusInEvent(e);
910 // Make sure guiApp points to the correct view.
911 guiApp->setCurrentView(this);
912 if (currentWorkArea())
913 currentWorkArea()->setFocus();
914 else if (currentMainWorkArea())
915 currentMainWorkArea()->setFocus();
917 d.bg_widget_->setFocus();
921 QMenu * GuiView::createPopupMenu()
923 return d.toolBarPopup(this);
927 void GuiView::showEvent(QShowEvent * e)
929 LYXERR(Debug::GUI, "Passed Geometry "
930 << size().height() << "x" << size().width()
931 << "+" << pos().x() << "+" << pos().y());
933 if (d.splitter_->count() == 0)
934 // No work area, switch to the background widget.
938 QMainWindow::showEvent(e);
942 bool GuiView::closeScheduled()
949 bool GuiView::prepareAllBuffersForLogout()
951 Buffer * first = theBufferList().first();
955 // First, iterate over all buffers and ask the users if unsaved
956 // changes should be saved.
957 // We cannot use a for loop as the buffer list cycles.
960 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
962 b = theBufferList().next(b);
963 } while (b != first);
965 // Next, save session state
966 // When a view/window was closed before without quitting LyX, there
967 // are already entries in the lastOpened list.
968 theSession().lastOpened().clear();
975 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
976 ** is responsibility of the container (e.g., dialog)
978 void GuiView::closeEvent(QCloseEvent * close_event)
980 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
982 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
983 Alert::warning(_("Exit LyX"),
984 _("LyX could not be closed because documents are being processed by LyX."));
985 close_event->setAccepted(false);
989 // If the user pressed the x (so we didn't call closeView
990 // programmatically), we want to clear all existing entries.
992 theSession().lastOpened().clear();
997 // it can happen that this event arrives without selecting the view,
998 // e.g. when clicking the close button on a background window.
1000 if (!closeWorkAreaAll()) {
1002 close_event->ignore();
1006 // Make sure that nothing will use this to be closed View.
1007 guiApp->unregisterView(this);
1009 if (isFullScreen()) {
1010 // Switch off fullscreen before closing.
1015 // Make sure the timer time out will not trigger a statusbar update.
1016 d.statusbar_timer_.stop();
1018 // Saving fullscreen requires additional tweaks in the toolbar code.
1019 // It wouldn't also work under linux natively.
1020 if (lyxrc.allow_geometry_session) {
1025 close_event->accept();
1029 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1031 if (event->mimeData()->hasUrls())
1033 /// \todo Ask lyx-devel is this is enough:
1034 /// if (event->mimeData()->hasFormat("text/plain"))
1035 /// event->acceptProposedAction();
1039 void GuiView::dropEvent(QDropEvent * event)
1041 QList<QUrl> files = event->mimeData()->urls();
1042 if (files.isEmpty())
1045 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1046 for (int i = 0; i != files.size(); ++i) {
1047 string const file = os::internal_path(fromqstr(
1048 files.at(i).toLocalFile()));
1052 string const ext = support::getExtension(file);
1053 vector<const Format *> found_formats;
1055 // Find all formats that have the correct extension.
1056 vector<const Format *> const & import_formats
1057 = theConverters().importableFormats();
1058 vector<const Format *>::const_iterator it = import_formats.begin();
1059 for (; it != import_formats.end(); ++it)
1060 if ((*it)->hasExtension(ext))
1061 found_formats.push_back(*it);
1064 if (found_formats.size() >= 1) {
1065 if (found_formats.size() > 1) {
1066 //FIXME: show a dialog to choose the correct importable format
1067 LYXERR(Debug::FILES,
1068 "Multiple importable formats found, selecting first");
1070 string const arg = found_formats[0]->name() + " " + file;
1071 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1074 //FIXME: do we have to explicitly check whether it's a lyx file?
1075 LYXERR(Debug::FILES,
1076 "No formats found, trying to open it as a lyx file");
1077 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1079 // add the functions to the queue
1080 guiApp->addToFuncRequestQueue(cmd);
1083 // now process the collected functions. We perform the events
1084 // asynchronously. This prevents potential problems in case the
1085 // BufferView is closed within an event.
1086 guiApp->processFuncRequestQueueAsync();
1090 void GuiView::message(docstring const & str)
1092 if (ForkedProcess::iAmAChild())
1095 // call is moved to GUI-thread by GuiProgress
1096 d.progress_->appendMessage(toqstr(str));
1100 void GuiView::clearMessageText()
1102 message(docstring());
1106 void GuiView::updateStatusBarMessage(QString const & str)
1108 statusBar()->showMessage(str);
1109 d.statusbar_timer_.stop();
1110 d.statusbar_timer_.start(3000);
1114 void GuiView::smallSizedIcons()
1116 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1120 void GuiView::normalSizedIcons()
1122 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1126 void GuiView::bigSizedIcons()
1128 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1132 void GuiView::hugeSizedIcons()
1134 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1138 void GuiView::giantSizedIcons()
1140 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1144 void GuiView::clearMessage()
1146 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1147 // the hasFocus function mostly returns false, even if the focus is on
1148 // a workarea in this view.
1152 d.statusbar_timer_.stop();
1156 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1158 if (wa != d.current_work_area_
1159 || wa->bufferView().buffer().isInternal())
1161 Buffer const & buf = wa->bufferView().buffer();
1162 // Set the windows title
1163 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1165 title += from_ascii(" - LyX");
1167 setWindowTitle(toqstr(title));
1168 // Sets the path for the window: this is used by OSX to
1169 // allow a context click on the title bar showing a menu
1170 // with the path up to the file
1171 setWindowFilePath(toqstr(buf.absFileName()));
1172 // Tell Qt whether the current document is changed
1173 setWindowModified(!buf.isClean());
1175 if (buf.isReadonly())
1180 if (buf.lyxvc().inUse()) {
1181 version_control_->show();
1182 if (buf.lyxvc().locking())
1183 version_control_->setText(
1184 toqstr(bformat(_("%1$s lock"),
1185 from_ascii(buf.lyxvc().vcname()))));
1187 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1189 version_control_->hide();
1193 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1195 if (d.current_work_area_)
1196 // disconnect the current work area from all slots
1197 QObject::disconnect(d.current_work_area_, 0, this, 0);
1199 disconnectBufferView();
1200 connectBufferView(wa->bufferView());
1201 connectBuffer(wa->bufferView().buffer());
1202 d.current_work_area_ = wa;
1203 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1204 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1205 QObject::connect(wa, SIGNAL(busy(bool)),
1206 this, SLOT(setBusy(bool)));
1207 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1208 this, SIGNAL(bufferViewChanged()));
1209 Q_EMIT updateWindowTitle(wa);
1210 Q_EMIT bufferViewChanged();
1214 // The document settings needs to be reinitialised.
1215 // TODO: no longer needed now there is bufferViewChanged?
1216 updateDialog("document", "");
1218 // Buffer-dependent dialogs must be updated. This is done here because
1219 // some dialogs require buffer()->text.
1220 // TODO: no longer needed now there is bufferViewChanged?
1225 void GuiView::on_lastWorkAreaRemoved()
1228 // We already are in a close event. Nothing more to do.
1231 if (d.splitter_->count() > 1)
1232 // We have a splitter so don't close anything.
1235 // Reset and updates the dialogs.
1236 Q_EMIT bufferViewChanged();
1237 // TODO: no longer needed now there is bufferViewChanged?
1238 d.toc_models_.reset(0);
1239 updateDialog("document", "");
1245 if (lyxrc.open_buffers_in_tabs)
1246 // Nothing more to do, the window should stay open.
1249 if (guiApp->viewIds().size() > 1) {
1255 // On Mac we also close the last window because the application stay
1256 // resident in memory. On other platforms we don't close the last
1257 // window because this would quit the application.
1263 void GuiView::updateStatusBar()
1265 // let the user see the explicit message
1266 if (d.statusbar_timer_.isActive())
1273 void GuiView::showMessage()
1277 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1278 if (msg.isEmpty()) {
1279 BufferView const * bv = currentBufferView();
1281 msg = toqstr(bv->cursor().currentState());
1283 msg = qt_("Welcome to LyX!");
1285 statusBar()->showMessage(msg);
1289 bool GuiView::event(QEvent * e)
1293 // Useful debug code:
1294 //case QEvent::ActivationChange:
1295 //case QEvent::WindowDeactivate:
1296 //case QEvent::Paint:
1297 //case QEvent::Enter:
1298 //case QEvent::Leave:
1299 //case QEvent::HoverEnter:
1300 //case QEvent::HoverLeave:
1301 //case QEvent::HoverMove:
1302 //case QEvent::StatusTip:
1303 //case QEvent::DragEnter:
1304 //case QEvent::DragLeave:
1305 //case QEvent::Drop:
1308 case QEvent::WindowActivate: {
1309 GuiView * old_view = guiApp->currentView();
1310 if (this == old_view) {
1312 return QMainWindow::event(e);
1314 if (old_view && old_view->currentBufferView()) {
1315 // save current selection to the selection buffer to allow
1316 // middle-button paste in this window.
1317 cap::saveSelection(old_view->currentBufferView()->cursor());
1319 guiApp->setCurrentView(this);
1320 if (d.current_work_area_)
1321 on_currentWorkAreaChanged(d.current_work_area_);
1325 return QMainWindow::event(e);
1328 case QEvent::ShortcutOverride: {
1330 if (isFullScreen() && menuBar()->isHidden()) {
1331 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1332 // FIXME: we should also try to detect special LyX shortcut such as
1333 // Alt-P and Alt-M. Right now there is a hack in
1334 // GuiWorkArea::processKeySym() that hides again the menubar for
1336 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1338 return QMainWindow::event(e);
1341 return QMainWindow::event(e);
1345 return QMainWindow::event(e);
1349 void GuiView::resetWindowTitle()
1351 setWindowTitle(qt_("LyX"));
1354 bool GuiView::focusNextPrevChild(bool /*next*/)
1361 bool GuiView::busy() const
1367 void GuiView::setBusy(bool busy)
1369 bool const busy_before = busy_ > 0;
1370 busy ? ++busy_ : --busy_;
1371 if ((busy_ > 0) == busy_before)
1372 // busy state didn't change
1376 QApplication::setOverrideCursor(Qt::WaitCursor);
1379 QApplication::restoreOverrideCursor();
1384 void GuiView::resetCommandExecute()
1386 command_execute_ = false;
1391 double GuiView::pixelRatio() const
1393 #if QT_VERSION >= 0x050000
1394 return devicePixelRatio();
1401 GuiWorkArea * GuiView::workArea(int index)
1403 if (TabWorkArea * twa = d.currentTabWorkArea())
1404 if (index < twa->count())
1405 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1410 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1412 if (currentWorkArea()
1413 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1414 return currentWorkArea();
1415 if (TabWorkArea * twa = d.currentTabWorkArea())
1416 return twa->workArea(buffer);
1421 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1423 // Automatically create a TabWorkArea if there are none yet.
1424 TabWorkArea * tab_widget = d.splitter_->count()
1425 ? d.currentTabWorkArea() : addTabWorkArea();
1426 return tab_widget->addWorkArea(buffer, *this);
1430 TabWorkArea * GuiView::addTabWorkArea()
1432 TabWorkArea * twa = new TabWorkArea;
1433 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1434 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1435 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1436 this, SLOT(on_lastWorkAreaRemoved()));
1438 d.splitter_->addWidget(twa);
1439 d.stack_widget_->setCurrentWidget(d.splitter_);
1444 GuiWorkArea const * GuiView::currentWorkArea() const
1446 return d.current_work_area_;
1450 GuiWorkArea * GuiView::currentWorkArea()
1452 return d.current_work_area_;
1456 GuiWorkArea const * GuiView::currentMainWorkArea() const
1458 if (!d.currentTabWorkArea())
1460 return d.currentTabWorkArea()->currentWorkArea();
1464 GuiWorkArea * GuiView::currentMainWorkArea()
1466 if (!d.currentTabWorkArea())
1468 return d.currentTabWorkArea()->currentWorkArea();
1472 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1474 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1476 d.current_work_area_ = 0;
1478 Q_EMIT bufferViewChanged();
1482 // FIXME: I've no clue why this is here and why it accesses
1483 // theGuiApp()->currentView, which might be 0 (bug 6464).
1484 // See also 27525 (vfr).
1485 if (theGuiApp()->currentView() == this
1486 && theGuiApp()->currentView()->currentWorkArea() == wa)
1489 if (currentBufferView())
1490 cap::saveSelection(currentBufferView()->cursor());
1492 theGuiApp()->setCurrentView(this);
1493 d.current_work_area_ = wa;
1495 // We need to reset this now, because it will need to be
1496 // right if the tabWorkArea gets reset in the for loop. We
1497 // will change it back if we aren't in that case.
1498 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1499 d.current_main_work_area_ = wa;
1501 for (int i = 0; i != d.splitter_->count(); ++i) {
1502 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1503 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1504 << ", Current main wa: " << currentMainWorkArea());
1509 d.current_main_work_area_ = old_cmwa;
1511 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1512 on_currentWorkAreaChanged(wa);
1513 BufferView & bv = wa->bufferView();
1514 bv.cursor().fixIfBroken();
1516 wa->setUpdatesEnabled(true);
1517 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1521 void GuiView::removeWorkArea(GuiWorkArea * wa)
1523 LASSERT(wa, return);
1524 if (wa == d.current_work_area_) {
1526 disconnectBufferView();
1527 d.current_work_area_ = 0;
1528 d.current_main_work_area_ = 0;
1531 bool found_twa = false;
1532 for (int i = 0; i != d.splitter_->count(); ++i) {
1533 TabWorkArea * twa = d.tabWorkArea(i);
1534 if (twa->removeWorkArea(wa)) {
1535 // Found in this tab group, and deleted the GuiWorkArea.
1537 if (twa->count() != 0) {
1538 if (d.current_work_area_ == 0)
1539 // This means that we are closing the current GuiWorkArea, so
1540 // switch to the next GuiWorkArea in the found TabWorkArea.
1541 setCurrentWorkArea(twa->currentWorkArea());
1543 // No more WorkAreas in this tab group, so delete it.
1550 // It is not a tabbed work area (i.e., the search work area), so it
1551 // should be deleted by other means.
1552 LASSERT(found_twa, return);
1554 if (d.current_work_area_ == 0) {
1555 if (d.splitter_->count() != 0) {
1556 TabWorkArea * twa = d.currentTabWorkArea();
1557 setCurrentWorkArea(twa->currentWorkArea());
1559 // No more work areas, switch to the background widget.
1560 setCurrentWorkArea(0);
1566 LayoutBox * GuiView::getLayoutDialog() const
1572 void GuiView::updateLayoutList()
1575 d.layout_->updateContents(false);
1579 void GuiView::updateToolbars()
1581 ToolbarMap::iterator end = d.toolbars_.end();
1582 if (d.current_work_area_) {
1584 if (d.current_work_area_->bufferView().cursor().inMathed()
1585 && !d.current_work_area_->bufferView().cursor().inRegexped())
1586 context |= Toolbars::MATH;
1587 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1588 context |= Toolbars::TABLE;
1589 if (currentBufferView()->buffer().areChangesPresent()
1590 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1591 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1592 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1593 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1594 context |= Toolbars::REVIEW;
1595 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1596 context |= Toolbars::MATHMACROTEMPLATE;
1597 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1598 context |= Toolbars::IPA;
1599 if (command_execute_)
1600 context |= Toolbars::MINIBUFFER;
1601 if (minibuffer_focus_) {
1602 context |= Toolbars::MINIBUFFER_FOCUS;
1603 minibuffer_focus_ = false;
1606 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1607 it->second->update(context);
1609 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1610 it->second->update();
1614 void GuiView::setBuffer(Buffer * newBuffer)
1616 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1617 LASSERT(newBuffer, return);
1619 GuiWorkArea * wa = workArea(*newBuffer);
1622 newBuffer->masterBuffer()->updateBuffer();
1624 wa = addWorkArea(*newBuffer);
1625 // scroll to the position when the BufferView was last closed
1626 if (lyxrc.use_lastfilepos) {
1627 LastFilePosSection::FilePos filepos =
1628 theSession().lastFilePos().load(newBuffer->fileName());
1629 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1632 //Disconnect the old buffer...there's no new one.
1635 connectBuffer(*newBuffer);
1636 connectBufferView(wa->bufferView());
1637 setCurrentWorkArea(wa);
1641 void GuiView::connectBuffer(Buffer & buf)
1643 buf.setGuiDelegate(this);
1647 void GuiView::disconnectBuffer()
1649 if (d.current_work_area_)
1650 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1654 void GuiView::connectBufferView(BufferView & bv)
1656 bv.setGuiDelegate(this);
1660 void GuiView::disconnectBufferView()
1662 if (d.current_work_area_)
1663 d.current_work_area_->bufferView().setGuiDelegate(0);
1667 void GuiView::errors(string const & error_type, bool from_master)
1669 BufferView const * const bv = currentBufferView();
1673 #if EXPORT_in_THREAD
1674 // We are called with from_master == false by default, so we
1675 // have to figure out whether that is the case or not.
1676 ErrorList & el = bv->buffer().errorList(error_type);
1678 el = bv->buffer().masterBuffer()->errorList(error_type);
1682 ErrorList const & el = from_master ?
1683 bv->buffer().masterBuffer()->errorList(error_type) :
1684 bv->buffer().errorList(error_type);
1690 string data = error_type;
1692 data = "from_master|" + error_type;
1693 showDialog("errorlist", data);
1697 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1699 d.toc_models_.updateItem(toqstr(type), dit);
1703 void GuiView::structureChanged()
1705 // FIXME: This is slightly expensive, though less than the tocBackend update
1706 // (#9880). This also resets the view in the Toc Widget (#6675).
1707 d.toc_models_.reset(documentBufferView());
1708 // Navigator needs more than a simple update in this case. It needs to be
1710 updateDialog("toc", "");
1714 void GuiView::updateDialog(string const & name, string const & data)
1716 if (!isDialogVisible(name))
1719 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1720 if (it == d.dialogs_.end())
1723 Dialog * const dialog = it->second.get();
1724 if (dialog->isVisibleView())
1725 dialog->initialiseParams(data);
1729 BufferView * GuiView::documentBufferView()
1731 return currentMainWorkArea()
1732 ? ¤tMainWorkArea()->bufferView()
1737 BufferView const * GuiView::documentBufferView() const
1739 return currentMainWorkArea()
1740 ? ¤tMainWorkArea()->bufferView()
1745 BufferView * GuiView::currentBufferView()
1747 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1751 BufferView const * GuiView::currentBufferView() const
1753 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1757 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1758 Buffer const * orig, Buffer * clone)
1760 bool const success = clone->autoSave();
1762 busyBuffers.remove(orig);
1764 ? _("Automatic save done.")
1765 : _("Automatic save failed!");
1769 void GuiView::autoSave()
1771 LYXERR(Debug::INFO, "Running autoSave()");
1773 Buffer * buffer = documentBufferView()
1774 ? &documentBufferView()->buffer() : 0;
1776 resetAutosaveTimers();
1780 GuiViewPrivate::busyBuffers.insert(buffer);
1781 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1782 buffer, buffer->cloneBufferOnly());
1783 d.autosave_watcher_.setFuture(f);
1784 resetAutosaveTimers();
1788 void GuiView::resetAutosaveTimers()
1791 d.autosave_timeout_.restart();
1795 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1798 Buffer * buf = currentBufferView()
1799 ? ¤tBufferView()->buffer() : 0;
1800 Buffer * doc_buffer = documentBufferView()
1801 ? &(documentBufferView()->buffer()) : 0;
1804 /* In LyX/Mac, when a dialog is open, the menus of the
1805 application can still be accessed without giving focus to
1806 the main window. In this case, we want to disable the menu
1807 entries that are buffer-related.
1808 This code must not be used on Linux and Windows, since it
1809 would disable buffer-related entries when hovering over the
1810 menu (see bug #9574).
1812 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1818 // Check whether we need a buffer
1819 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1820 // no, exit directly
1821 flag.message(from_utf8(N_("Command not allowed with"
1822 "out any document open")));
1823 flag.setEnabled(false);
1827 if (cmd.origin() == FuncRequest::TOC) {
1828 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1829 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1830 flag.setEnabled(false);
1834 switch(cmd.action()) {
1835 case LFUN_BUFFER_IMPORT:
1838 case LFUN_MASTER_BUFFER_UPDATE:
1839 case LFUN_MASTER_BUFFER_VIEW:
1841 && (doc_buffer->parent() != 0
1842 || doc_buffer->hasChildren())
1843 && !d.processing_thread_watcher_.isRunning();
1846 case LFUN_BUFFER_UPDATE:
1847 case LFUN_BUFFER_VIEW: {
1848 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1852 string format = to_utf8(cmd.argument());
1853 if (cmd.argument().empty())
1854 format = doc_buffer->params().getDefaultOutputFormat();
1855 enable = doc_buffer->params().isExportableFormat(format);
1859 case LFUN_BUFFER_RELOAD:
1860 enable = doc_buffer && !doc_buffer->isUnnamed()
1861 && doc_buffer->fileName().exists()
1862 && (!doc_buffer->isClean()
1863 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1866 case LFUN_BUFFER_CHILD_OPEN:
1867 enable = doc_buffer != 0;
1870 case LFUN_BUFFER_WRITE:
1871 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1874 //FIXME: This LFUN should be moved to GuiApplication.
1875 case LFUN_BUFFER_WRITE_ALL: {
1876 // We enable the command only if there are some modified buffers
1877 Buffer * first = theBufferList().first();
1882 // We cannot use a for loop as the buffer list is a cycle.
1884 if (!b->isClean()) {
1888 b = theBufferList().next(b);
1889 } while (b != first);
1893 case LFUN_BUFFER_WRITE_AS:
1894 case LFUN_BUFFER_EXPORT_AS:
1895 enable = doc_buffer != 0;
1898 case LFUN_BUFFER_CLOSE:
1899 case LFUN_VIEW_CLOSE:
1900 enable = doc_buffer != 0;
1903 case LFUN_BUFFER_CLOSE_ALL:
1904 enable = theBufferList().last() != theBufferList().first();
1907 case LFUN_VIEW_SPLIT:
1908 if (cmd.getArg(0) == "vertical")
1909 enable = doc_buffer && (d.splitter_->count() == 1 ||
1910 d.splitter_->orientation() == Qt::Vertical);
1912 enable = doc_buffer && (d.splitter_->count() == 1 ||
1913 d.splitter_->orientation() == Qt::Horizontal);
1916 case LFUN_TAB_GROUP_CLOSE:
1917 enable = d.tabWorkAreaCount() > 1;
1920 case LFUN_TOOLBAR_TOGGLE: {
1921 string const name = cmd.getArg(0);
1922 if (GuiToolbar * t = toolbar(name))
1923 flag.setOnOff(t->isVisible());
1926 docstring const msg =
1927 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1933 case LFUN_DROP_LAYOUTS_CHOICE:
1937 case LFUN_UI_TOGGLE:
1938 flag.setOnOff(isFullScreen());
1941 case LFUN_DIALOG_DISCONNECT_INSET:
1944 case LFUN_DIALOG_HIDE:
1945 // FIXME: should we check if the dialog is shown?
1948 case LFUN_DIALOG_TOGGLE:
1949 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1950 // fall through to set "enable"
1951 case LFUN_DIALOG_SHOW: {
1952 string const name = cmd.getArg(0);
1954 enable = name == "aboutlyx"
1955 || name == "file" //FIXME: should be removed.
1957 || name == "texinfo"
1958 || name == "progress"
1959 || name == "compare";
1960 else if (name == "character" || name == "symbols"
1961 || name == "mathdelimiter" || name == "mathmatrix") {
1962 if (!buf || buf->isReadonly())
1965 Cursor const & cur = currentBufferView()->cursor();
1966 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1969 else if (name == "latexlog")
1970 enable = FileName(doc_buffer->logName()).isReadableFile();
1971 else if (name == "spellchecker")
1972 enable = theSpellChecker()
1973 && !doc_buffer->isReadonly()
1974 && !doc_buffer->text().empty();
1975 else if (name == "vclog")
1976 enable = doc_buffer->lyxvc().inUse();
1980 case LFUN_DIALOG_UPDATE: {
1981 string const name = cmd.getArg(0);
1983 enable = name == "prefs";
1987 case LFUN_COMMAND_EXECUTE:
1989 case LFUN_MENU_OPEN:
1990 // Nothing to check.
1993 case LFUN_COMPLETION_INLINE:
1994 if (!d.current_work_area_
1995 || !d.current_work_area_->completer().inlinePossible(
1996 currentBufferView()->cursor()))
2000 case LFUN_COMPLETION_POPUP:
2001 if (!d.current_work_area_
2002 || !d.current_work_area_->completer().popupPossible(
2003 currentBufferView()->cursor()))
2008 if (!d.current_work_area_
2009 || !d.current_work_area_->completer().inlinePossible(
2010 currentBufferView()->cursor()))
2014 case LFUN_COMPLETION_ACCEPT:
2015 if (!d.current_work_area_
2016 || (!d.current_work_area_->completer().popupVisible()
2017 && !d.current_work_area_->completer().inlineVisible()
2018 && !d.current_work_area_->completer().completionAvailable()))
2022 case LFUN_COMPLETION_CANCEL:
2023 if (!d.current_work_area_
2024 || (!d.current_work_area_->completer().popupVisible()
2025 && !d.current_work_area_->completer().inlineVisible()))
2029 case LFUN_BUFFER_ZOOM_OUT:
2030 case LFUN_BUFFER_ZOOM_IN: {
2031 // only diff between these two is that the default for ZOOM_OUT
2033 bool const neg_zoom =
2034 convert<int>(cmd.argument()) < 0 ||
2035 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2036 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2037 docstring const msg =
2038 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2042 enable = doc_buffer;
2045 case LFUN_BUFFER_MOVE_NEXT:
2046 case LFUN_BUFFER_MOVE_PREVIOUS:
2047 // we do not cycle when moving
2048 case LFUN_BUFFER_NEXT:
2049 case LFUN_BUFFER_PREVIOUS:
2050 // because we cycle, it doesn't matter whether on first or last
2051 enable = (d.currentTabWorkArea()->count() > 1);
2053 case LFUN_BUFFER_SWITCH:
2054 // toggle on the current buffer, but do not toggle off
2055 // the other ones (is that a good idea?)
2057 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2058 flag.setOnOff(true);
2061 case LFUN_VC_REGISTER:
2062 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2064 case LFUN_VC_RENAME:
2065 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2068 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2070 case LFUN_VC_CHECK_IN:
2071 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2073 case LFUN_VC_CHECK_OUT:
2074 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2076 case LFUN_VC_LOCKING_TOGGLE:
2077 enable = doc_buffer && !doc_buffer->isReadonly()
2078 && doc_buffer->lyxvc().lockingToggleEnabled();
2079 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2081 case LFUN_VC_REVERT:
2082 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2084 case LFUN_VC_UNDO_LAST:
2085 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2087 case LFUN_VC_REPO_UPDATE:
2088 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2090 case LFUN_VC_COMMAND: {
2091 if (cmd.argument().empty())
2093 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2097 case LFUN_VC_COMPARE:
2098 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2101 case LFUN_SERVER_GOTO_FILE_ROW:
2102 case LFUN_LYX_ACTIVATE:
2104 case LFUN_FORWARD_SEARCH:
2105 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2108 case LFUN_FILE_INSERT_PLAINTEXT:
2109 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2110 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2113 case LFUN_SPELLING_CONTINUOUSLY:
2114 flag.setOnOff(lyxrc.spellcheck_continuously);
2122 flag.setEnabled(false);
2128 static FileName selectTemplateFile()
2130 FileDialog dlg(qt_("Select template file"));
2131 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2132 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2134 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2135 QStringList(qt_("LyX Documents (*.lyx)")));
2137 if (result.first == FileDialog::Later)
2139 if (result.second.isEmpty())
2141 return FileName(fromqstr(result.second));
2145 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2149 Buffer * newBuffer = 0;
2151 newBuffer = checkAndLoadLyXFile(filename);
2152 } catch (ExceptionMessage const & e) {
2159 message(_("Document not loaded."));
2163 setBuffer(newBuffer);
2164 newBuffer->errors("Parse");
2167 theSession().lastFiles().add(filename);
2173 void GuiView::openDocument(string const & fname)
2175 string initpath = lyxrc.document_path;
2177 if (documentBufferView()) {
2178 string const trypath = documentBufferView()->buffer().filePath();
2179 // If directory is writeable, use this as default.
2180 if (FileName(trypath).isDirWritable())
2186 if (fname.empty()) {
2187 FileDialog dlg(qt_("Select document to open"));
2188 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2189 dlg.setButton2(qt_("Examples|#E#e"),
2190 toqstr(addPath(package().system_support().absFileName(), "examples")));
2192 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2193 FileDialog::Result result =
2194 dlg.open(toqstr(initpath), filter);
2196 if (result.first == FileDialog::Later)
2199 filename = fromqstr(result.second);
2201 // check selected filename
2202 if (filename.empty()) {
2203 message(_("Canceled."));
2209 // get absolute path of file and add ".lyx" to the filename if
2211 FileName const fullname =
2212 fileSearch(string(), filename, "lyx", support::may_not_exist);
2213 if (!fullname.empty())
2214 filename = fullname.absFileName();
2216 if (!fullname.onlyPath().isDirectory()) {
2217 Alert::warning(_("Invalid filename"),
2218 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2219 from_utf8(fullname.absFileName())));
2223 // if the file doesn't exist and isn't already open (bug 6645),
2224 // let the user create one
2225 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2226 !LyXVC::file_not_found_hook(fullname)) {
2227 // the user specifically chose this name. Believe him.
2228 Buffer * const b = newFile(filename, string(), true);
2234 docstring const disp_fn = makeDisplayPath(filename);
2235 message(bformat(_("Opening document %1$s..."), disp_fn));
2238 Buffer * buf = loadDocument(fullname);
2240 str2 = bformat(_("Document %1$s opened."), disp_fn);
2241 if (buf->lyxvc().inUse())
2242 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2243 " " + _("Version control detected.");
2245 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2250 // FIXME: clean that
2251 static bool import(GuiView * lv, FileName const & filename,
2252 string const & format, ErrorList & errorList)
2254 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2256 string loader_format;
2257 vector<string> loaders = theConverters().loaders();
2258 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2259 vector<string>::const_iterator it = loaders.begin();
2260 vector<string>::const_iterator en = loaders.end();
2261 for (; it != en; ++it) {
2262 if (!theConverters().isReachable(format, *it))
2265 string const tofile =
2266 support::changeExtension(filename.absFileName(),
2267 formats.extension(*it));
2268 if (!theConverters().convert(0, filename, FileName(tofile),
2269 filename, format, *it, errorList))
2271 loader_format = *it;
2274 if (loader_format.empty()) {
2275 frontend::Alert::error(_("Couldn't import file"),
2276 bformat(_("No information for importing the format %1$s."),
2277 formats.prettyName(format)));
2281 loader_format = format;
2283 if (loader_format == "lyx") {
2284 Buffer * buf = lv->loadDocument(lyxfile);
2288 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2292 bool as_paragraphs = loader_format == "textparagraph";
2293 string filename2 = (loader_format == format) ? filename.absFileName()
2294 : support::changeExtension(filename.absFileName(),
2295 formats.extension(loader_format));
2296 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2298 guiApp->setCurrentView(lv);
2299 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2306 void GuiView::importDocument(string const & argument)
2309 string filename = split(argument, format, ' ');
2311 LYXERR(Debug::INFO, format << " file: " << filename);
2313 // need user interaction
2314 if (filename.empty()) {
2315 string initpath = lyxrc.document_path;
2316 if (documentBufferView()) {
2317 string const trypath = documentBufferView()->buffer().filePath();
2318 // If directory is writeable, use this as default.
2319 if (FileName(trypath).isDirWritable())
2323 docstring const text = bformat(_("Select %1$s file to import"),
2324 formats.prettyName(format));
2326 FileDialog dlg(toqstr(text));
2327 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2328 dlg.setButton2(qt_("Examples|#E#e"),
2329 toqstr(addPath(package().system_support().absFileName(), "examples")));
2331 docstring filter = formats.prettyName(format);
2334 filter += from_utf8(formats.extensions(format));
2337 FileDialog::Result result =
2338 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2340 if (result.first == FileDialog::Later)
2343 filename = fromqstr(result.second);
2345 // check selected filename
2346 if (filename.empty())
2347 message(_("Canceled."));
2350 if (filename.empty())
2353 // get absolute path of file
2354 FileName const fullname(support::makeAbsPath(filename));
2356 // Can happen if the user entered a path into the dialog
2358 if (fullname.onlyFileName().empty()) {
2359 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2360 "Aborting import."),
2361 from_utf8(fullname.absFileName()));
2362 frontend::Alert::error(_("File name error"), msg);
2363 message(_("Canceled."));
2368 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2370 // Check if the document already is open
2371 Buffer * buf = theBufferList().getBuffer(lyxfile);
2374 if (!closeBuffer()) {
2375 message(_("Canceled."));
2380 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2382 // if the file exists already, and we didn't do
2383 // -i lyx thefile.lyx, warn
2384 if (lyxfile.exists() && fullname != lyxfile) {
2386 docstring text = bformat(_("The document %1$s already exists.\n\n"
2387 "Do you want to overwrite that document?"), displaypath);
2388 int const ret = Alert::prompt(_("Overwrite document?"),
2389 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2392 message(_("Canceled."));
2397 message(bformat(_("Importing %1$s..."), displaypath));
2398 ErrorList errorList;
2399 if (import(this, fullname, format, errorList))
2400 message(_("imported."));
2402 message(_("file not imported!"));
2404 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2408 void GuiView::newDocument(string const & filename, bool from_template)
2410 FileName initpath(lyxrc.document_path);
2411 if (documentBufferView()) {
2412 FileName const trypath(documentBufferView()->buffer().filePath());
2413 // If directory is writeable, use this as default.
2414 if (trypath.isDirWritable())
2418 string templatefile;
2419 if (from_template) {
2420 templatefile = selectTemplateFile().absFileName();
2421 if (templatefile.empty())
2426 if (filename.empty())
2427 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2429 b = newFile(filename, templatefile, true);
2434 // If no new document could be created, it is unsure
2435 // whether there is a valid BufferView.
2436 if (currentBufferView())
2437 // Ensure the cursor is correctly positioned on screen.
2438 currentBufferView()->showCursor();
2442 void GuiView::insertLyXFile(docstring const & fname)
2444 BufferView * bv = documentBufferView();
2449 FileName filename(to_utf8(fname));
2450 if (filename.empty()) {
2451 // Launch a file browser
2453 string initpath = lyxrc.document_path;
2454 string const trypath = bv->buffer().filePath();
2455 // If directory is writeable, use this as default.
2456 if (FileName(trypath).isDirWritable())
2460 FileDialog dlg(qt_("Select LyX document to insert"));
2461 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2462 dlg.setButton2(qt_("Examples|#E#e"),
2463 toqstr(addPath(package().system_support().absFileName(),
2466 FileDialog::Result result = dlg.open(toqstr(initpath),
2467 QStringList(qt_("LyX Documents (*.lyx)")));
2469 if (result.first == FileDialog::Later)
2473 filename.set(fromqstr(result.second));
2475 // check selected filename
2476 if (filename.empty()) {
2477 // emit message signal.
2478 message(_("Canceled."));
2483 bv->insertLyXFile(filename);
2484 bv->buffer().errors("Parse");
2488 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2490 FileName fname = b.fileName();
2491 FileName const oldname = fname;
2493 if (!newname.empty()) {
2495 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2497 // Switch to this Buffer.
2500 // No argument? Ask user through dialog.
2502 FileDialog dlg(qt_("Choose a filename to save document as"));
2503 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2504 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2506 if (!isLyXFileName(fname.absFileName()))
2507 fname.changeExtension(".lyx");
2509 FileDialog::Result result =
2510 dlg.save(toqstr(fname.onlyPath().absFileName()),
2511 QStringList(qt_("LyX Documents (*.lyx)")),
2512 toqstr(fname.onlyFileName()));
2514 if (result.first == FileDialog::Later)
2517 fname.set(fromqstr(result.second));
2522 if (!isLyXFileName(fname.absFileName()))
2523 fname.changeExtension(".lyx");
2526 // fname is now the new Buffer location.
2528 // if there is already a Buffer open with this name, we do not want
2529 // to have another one. (the second test makes sure we're not just
2530 // trying to overwrite ourselves, which is fine.)
2531 if (theBufferList().exists(fname) && fname != oldname
2532 && theBufferList().getBuffer(fname) != &b) {
2533 docstring const text =
2534 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2535 "Please close it before attempting to overwrite it.\n"
2536 "Do you want to choose a new filename?"),
2537 from_utf8(fname.absFileName()));
2538 int const ret = Alert::prompt(_("Chosen File Already Open"),
2539 text, 0, 1, _("&Rename"), _("&Cancel"));
2541 case 0: return renameBuffer(b, docstring(), kind);
2542 case 1: return false;
2547 bool const existsLocal = fname.exists();
2548 bool const existsInVC = LyXVC::fileInVC(fname);
2549 if (existsLocal || existsInVC) {
2550 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2551 if (kind != LV_WRITE_AS && existsInVC) {
2552 // renaming to a name that is already in VC
2554 docstring text = bformat(_("The document %1$s "
2555 "is already registered.\n\n"
2556 "Do you want to choose a new name?"),
2558 docstring const title = (kind == LV_VC_RENAME) ?
2559 _("Rename document?") : _("Copy document?");
2560 docstring const button = (kind == LV_VC_RENAME) ?
2561 _("&Rename") : _("&Copy");
2562 int const ret = Alert::prompt(title, text, 0, 1,
2563 button, _("&Cancel"));
2565 case 0: return renameBuffer(b, docstring(), kind);
2566 case 1: return false;
2571 docstring text = bformat(_("The document %1$s "
2572 "already exists.\n\n"
2573 "Do you want to overwrite that document?"),
2575 int const ret = Alert::prompt(_("Overwrite document?"),
2576 text, 0, 2, _("&Overwrite"),
2577 _("&Rename"), _("&Cancel"));
2580 case 1: return renameBuffer(b, docstring(), kind);
2581 case 2: return false;
2587 case LV_VC_RENAME: {
2588 string msg = b.lyxvc().rename(fname);
2591 message(from_utf8(msg));
2595 string msg = b.lyxvc().copy(fname);
2598 message(from_utf8(msg));
2604 // LyXVC created the file already in case of LV_VC_RENAME or
2605 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2606 // relative paths of included stuff right if we moved e.g. from
2607 // /a/b.lyx to /a/c/b.lyx.
2609 bool const saved = saveBuffer(b, fname);
2616 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2618 FileName fname = b.fileName();
2620 FileDialog dlg(qt_("Choose a filename to export the document as"));
2621 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2624 QString const anyformat = qt_("Guess from extension (*.*)");
2627 vector<Format const *> export_formats;
2628 for (Format const & f : formats)
2629 if (f.documentFormat())
2630 export_formats.push_back(&f);
2631 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2632 map<QString, string> fmap;
2635 for (Format const * f : export_formats) {
2636 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2637 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2639 from_ascii(f->extension())));
2640 types << loc_filter;
2641 fmap[loc_filter] = f->name();
2642 if (from_ascii(f->name()) == iformat) {
2643 filter = loc_filter;
2644 ext = f->extension();
2647 string ofname = fname.onlyFileName();
2649 ofname = support::changeExtension(ofname, ext);
2650 FileDialog::Result result =
2651 dlg.save(toqstr(fname.onlyPath().absFileName()),
2655 if (result.first != FileDialog::Chosen)
2659 fname.set(fromqstr(result.second));
2660 if (filter == anyformat)
2661 fmt_name = formats.getFormatFromExtension(fname.extension());
2663 fmt_name = fmap[filter];
2664 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2665 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2667 if (fmt_name.empty() || fname.empty())
2670 // fname is now the new Buffer location.
2671 if (FileName(fname).exists()) {
2672 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2673 docstring text = bformat(_("The document %1$s already "
2674 "exists.\n\nDo you want to "
2675 "overwrite that document?"),
2677 int const ret = Alert::prompt(_("Overwrite document?"),
2678 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2681 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2682 case 2: return false;
2686 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2689 return dr.dispatched();
2693 bool GuiView::saveBuffer(Buffer & b)
2695 return saveBuffer(b, FileName());
2699 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2701 if (workArea(b) && workArea(b)->inDialogMode())
2704 if (fn.empty() && b.isUnnamed())
2705 return renameBuffer(b, docstring());
2707 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2709 theSession().lastFiles().add(b.fileName());
2713 // Switch to this Buffer.
2716 // FIXME: we don't tell the user *WHY* the save failed !!
2717 docstring const file = makeDisplayPath(b.absFileName(), 30);
2718 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2719 "Do you want to rename the document and "
2720 "try again?"), file);
2721 int const ret = Alert::prompt(_("Rename and save?"),
2722 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2725 if (!renameBuffer(b, docstring()))
2734 return saveBuffer(b, fn);
2738 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2740 return closeWorkArea(wa, false);
2744 // We only want to close the buffer if it is not visible in other workareas
2745 // of the same view, nor in other views, and if this is not a child
2746 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2748 Buffer & buf = wa->bufferView().buffer();
2750 bool last_wa = d.countWorkAreasOf(buf) == 1
2751 && !inOtherView(buf) && !buf.parent();
2753 bool close_buffer = last_wa;
2756 if (lyxrc.close_buffer_with_last_view == "yes")
2758 else if (lyxrc.close_buffer_with_last_view == "no")
2759 close_buffer = false;
2762 if (buf.isUnnamed())
2763 file = from_utf8(buf.fileName().onlyFileName());
2765 file = buf.fileName().displayName(30);
2766 docstring const text = bformat(
2767 _("Last view on document %1$s is being closed.\n"
2768 "Would you like to close or hide the document?\n"
2770 "Hidden documents can be displayed back through\n"
2771 "the menu: View->Hidden->...\n"
2773 "To remove this question, set your preference in:\n"
2774 " Tools->Preferences->Look&Feel->UserInterface\n"
2776 int ret = Alert::prompt(_("Close or hide document?"),
2777 text, 0, 1, _("&Close"), _("&Hide"));
2778 close_buffer = (ret == 0);
2782 return closeWorkArea(wa, close_buffer);
2786 bool GuiView::closeBuffer()
2788 GuiWorkArea * wa = currentMainWorkArea();
2789 // coverity complained about this
2790 // it seems unnecessary, but perhaps is worth the check
2791 LASSERT(wa, return false);
2793 setCurrentWorkArea(wa);
2794 Buffer & buf = wa->bufferView().buffer();
2795 return closeWorkArea(wa, !buf.parent());
2799 void GuiView::writeSession() const {
2800 GuiWorkArea const * active_wa = currentMainWorkArea();
2801 for (int i = 0; i < d.splitter_->count(); ++i) {
2802 TabWorkArea * twa = d.tabWorkArea(i);
2803 for (int j = 0; j < twa->count(); ++j) {
2804 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2805 Buffer & buf = wa->bufferView().buffer();
2806 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2812 bool GuiView::closeBufferAll()
2814 // Close the workareas in all other views
2815 QList<int> const ids = guiApp->viewIds();
2816 for (int i = 0; i != ids.size(); ++i) {
2817 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2821 // Close our own workareas
2822 if (!closeWorkAreaAll())
2825 // Now close the hidden buffers. We prevent hidden buffers from being
2826 // dirty, so we can just close them.
2827 theBufferList().closeAll();
2832 bool GuiView::closeWorkAreaAll()
2834 setCurrentWorkArea(currentMainWorkArea());
2836 // We might be in a situation that there is still a tabWorkArea, but
2837 // there are no tabs anymore. This can happen when we get here after a
2838 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2839 // many TabWorkArea's have no documents anymore.
2842 // We have to call count() each time, because it can happen that
2843 // more than one splitter will disappear in one iteration (bug 5998).
2844 while (d.splitter_->count() > empty_twa) {
2845 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2847 if (twa->count() == 0)
2850 setCurrentWorkArea(twa->currentWorkArea());
2851 if (!closeTabWorkArea(twa))
2859 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2864 Buffer & buf = wa->bufferView().buffer();
2866 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2867 Alert::warning(_("Close document"),
2868 _("Document could not be closed because it is being processed by LyX."));
2873 return closeBuffer(buf);
2875 if (!inMultiTabs(wa))
2876 if (!saveBufferIfNeeded(buf, true))
2884 bool GuiView::closeBuffer(Buffer & buf)
2886 // If we are in a close_event all children will be closed in some time,
2887 // so no need to do it here. This will ensure that the children end up
2888 // in the session file in the correct order. If we close the master
2889 // buffer, we can close or release the child buffers here too.
2890 bool success = true;
2892 ListOfBuffers clist = buf.getChildren();
2893 ListOfBuffers::const_iterator it = clist.begin();
2894 ListOfBuffers::const_iterator const bend = clist.end();
2895 for (; it != bend; ++it) {
2896 Buffer * child_buf = *it;
2897 if (theBufferList().isOthersChild(&buf, child_buf)) {
2898 child_buf->setParent(0);
2902 // FIXME: should we look in other tabworkareas?
2903 // ANSWER: I don't think so. I've tested, and if the child is
2904 // open in some other window, it closes without a problem.
2905 GuiWorkArea * child_wa = workArea(*child_buf);
2907 success = closeWorkArea(child_wa, true);
2911 // In this case the child buffer is open but hidden.
2912 // It therefore should not (MUST NOT) be dirty!
2913 LATTEST(child_buf->isClean());
2914 theBufferList().release(child_buf);
2919 // goto bookmark to update bookmark pit.
2920 // FIXME: we should update only the bookmarks related to this buffer!
2921 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2922 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2923 guiApp->gotoBookmark(i+1, false, false);
2925 if (saveBufferIfNeeded(buf, false)) {
2926 buf.removeAutosaveFile();
2927 theBufferList().release(&buf);
2931 // open all children again to avoid a crash because of dangling
2932 // pointers (bug 6603)
2938 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2940 while (twa == d.currentTabWorkArea()) {
2941 twa->setCurrentIndex(twa->count() - 1);
2943 GuiWorkArea * wa = twa->currentWorkArea();
2944 Buffer & b = wa->bufferView().buffer();
2946 // We only want to close the buffer if the same buffer is not visible
2947 // in another view, and if this is not a child and if we are closing
2948 // a view (not a tabgroup).
2949 bool const close_buffer =
2950 !inOtherView(b) && !b.parent() && closing_;
2952 if (!closeWorkArea(wa, close_buffer))
2959 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2961 if (buf.isClean() || buf.paragraphs().empty())
2964 // Switch to this Buffer.
2969 if (buf.isUnnamed())
2970 file = from_utf8(buf.fileName().onlyFileName());
2972 file = buf.fileName().displayName(30);
2974 // Bring this window to top before asking questions.
2979 if (hiding && buf.isUnnamed()) {
2980 docstring const text = bformat(_("The document %1$s has not been "
2981 "saved yet.\n\nDo you want to save "
2982 "the document?"), file);
2983 ret = Alert::prompt(_("Save new document?"),
2984 text, 0, 1, _("&Save"), _("&Cancel"));
2988 docstring const text = bformat(_("The document %1$s has unsaved changes."
2989 "\n\nDo you want to save the document or discard the changes?"), file);
2990 ret = Alert::prompt(_("Save changed document?"),
2991 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2996 if (!saveBuffer(buf))
3000 // If we crash after this we could have no autosave file
3001 // but I guess this is really improbable (Jug).
3002 // Sometimes improbable things happen:
3003 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3004 // buf.removeAutosaveFile();
3006 // revert all changes
3017 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3019 Buffer & buf = wa->bufferView().buffer();
3021 for (int i = 0; i != d.splitter_->count(); ++i) {
3022 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3023 if (wa_ && wa_ != wa)
3026 return inOtherView(buf);
3030 bool GuiView::inOtherView(Buffer & buf)
3032 QList<int> const ids = guiApp->viewIds();
3034 for (int i = 0; i != ids.size(); ++i) {
3038 if (guiApp->view(ids[i]).workArea(buf))
3045 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3047 if (!documentBufferView())
3050 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3051 Buffer * const curbuf = &documentBufferView()->buffer();
3052 int nwa = twa->count();
3053 for (int i = 0; i < nwa; ++i) {
3054 if (&workArea(i)->bufferView().buffer() == curbuf) {
3056 if (np == NEXTBUFFER)
3057 next_index = (i == nwa - 1 ? 0 : i + 1);
3059 next_index = (i == 0 ? nwa - 1 : i - 1);
3061 twa->moveTab(i, next_index);
3063 setBuffer(&workArea(next_index)->bufferView().buffer());
3071 /// make sure the document is saved
3072 static bool ensureBufferClean(Buffer * buffer)
3074 LASSERT(buffer, return false);
3075 if (buffer->isClean() && !buffer->isUnnamed())
3078 docstring const file = buffer->fileName().displayName(30);
3081 if (!buffer->isUnnamed()) {
3082 text = bformat(_("The document %1$s has unsaved "
3083 "changes.\n\nDo you want to save "
3084 "the document?"), file);
3085 title = _("Save changed document?");
3088 text = bformat(_("The document %1$s has not been "
3089 "saved yet.\n\nDo you want to save "
3090 "the document?"), file);
3091 title = _("Save new document?");
3093 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3096 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3098 return buffer->isClean() && !buffer->isUnnamed();
3102 bool GuiView::reloadBuffer(Buffer & buf)
3104 Buffer::ReadStatus status = buf.reload();
3105 return status == Buffer::ReadSuccess;
3109 void GuiView::checkExternallyModifiedBuffers()
3111 BufferList::iterator bit = theBufferList().begin();
3112 BufferList::iterator const bend = theBufferList().end();
3113 for (; bit != bend; ++bit) {
3114 Buffer * buf = *bit;
3115 if (buf->fileName().exists()
3116 && buf->isExternallyModified(Buffer::checksum_method)) {
3117 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3118 " Reload now? Any local changes will be lost."),
3119 from_utf8(buf->absFileName()));
3120 int const ret = Alert::prompt(_("Reload externally changed document?"),
3121 text, 0, 1, _("&Reload"), _("&Cancel"));
3129 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3131 Buffer * buffer = documentBufferView()
3132 ? &(documentBufferView()->buffer()) : 0;
3134 switch (cmd.action()) {
3135 case LFUN_VC_REGISTER:
3136 if (!buffer || !ensureBufferClean(buffer))
3138 if (!buffer->lyxvc().inUse()) {
3139 if (buffer->lyxvc().registrer()) {
3140 reloadBuffer(*buffer);
3141 dr.clearMessageUpdate();
3146 case LFUN_VC_RENAME:
3147 case LFUN_VC_COPY: {
3148 if (!buffer || !ensureBufferClean(buffer))
3150 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3151 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3152 // Some changes are not yet committed.
3153 // We test here and not in getStatus(), since
3154 // this test is expensive.
3156 LyXVC::CommandResult ret =
3157 buffer->lyxvc().checkIn(log);
3159 if (ret == LyXVC::ErrorCommand ||
3160 ret == LyXVC::VCSuccess)
3161 reloadBuffer(*buffer);
3162 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3163 frontend::Alert::error(
3164 _("Revision control error."),
3165 _("Document could not be checked in."));
3169 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3170 LV_VC_RENAME : LV_VC_COPY;
3171 renameBuffer(*buffer, cmd.argument(), kind);
3176 case LFUN_VC_CHECK_IN:
3177 if (!buffer || !ensureBufferClean(buffer))
3179 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3181 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3183 // Only skip reloading if the checkin was cancelled or
3184 // an error occurred before the real checkin VCS command
3185 // was executed, since the VCS might have changed the
3186 // file even if it could not checkin successfully.
3187 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3188 reloadBuffer(*buffer);
3192 case LFUN_VC_CHECK_OUT:
3193 if (!buffer || !ensureBufferClean(buffer))
3195 if (buffer->lyxvc().inUse()) {
3196 dr.setMessage(buffer->lyxvc().checkOut());
3197 reloadBuffer(*buffer);
3201 case LFUN_VC_LOCKING_TOGGLE:
3202 LASSERT(buffer, return);
3203 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3205 if (buffer->lyxvc().inUse()) {
3206 string res = buffer->lyxvc().lockingToggle();
3208 frontend::Alert::error(_("Revision control error."),
3209 _("Error when setting the locking property."));
3212 reloadBuffer(*buffer);
3217 case LFUN_VC_REVERT:
3218 LASSERT(buffer, return);
3219 if (buffer->lyxvc().revert()) {
3220 reloadBuffer(*buffer);
3221 dr.clearMessageUpdate();
3225 case LFUN_VC_UNDO_LAST:
3226 LASSERT(buffer, return);
3227 buffer->lyxvc().undoLast();
3228 reloadBuffer(*buffer);
3229 dr.clearMessageUpdate();
3232 case LFUN_VC_REPO_UPDATE:
3233 LASSERT(buffer, return);
3234 if (ensureBufferClean(buffer)) {
3235 dr.setMessage(buffer->lyxvc().repoUpdate());
3236 checkExternallyModifiedBuffers();
3240 case LFUN_VC_COMMAND: {
3241 string flag = cmd.getArg(0);
3242 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3245 if (contains(flag, 'M')) {
3246 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3249 string path = cmd.getArg(1);
3250 if (contains(path, "$$p") && buffer)
3251 path = subst(path, "$$p", buffer->filePath());
3252 LYXERR(Debug::LYXVC, "Directory: " << path);
3254 if (!pp.isReadableDirectory()) {
3255 lyxerr << _("Directory is not accessible.") << endl;
3258 support::PathChanger p(pp);
3260 string command = cmd.getArg(2);
3261 if (command.empty())
3264 command = subst(command, "$$i", buffer->absFileName());
3265 command = subst(command, "$$p", buffer->filePath());
3267 command = subst(command, "$$m", to_utf8(message));
3268 LYXERR(Debug::LYXVC, "Command: " << command);
3270 one.startscript(Systemcall::Wait, command);
3274 if (contains(flag, 'I'))
3275 buffer->markDirty();
3276 if (contains(flag, 'R'))
3277 reloadBuffer(*buffer);
3282 case LFUN_VC_COMPARE: {
3283 if (cmd.argument().empty()) {
3284 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3288 string rev1 = cmd.getArg(0);
3292 // it seems safe to assume we have a buffer
3293 // coverity[FORWARD_NULL]
3294 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3297 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3298 f2 = buffer->absFileName();
3300 string rev2 = cmd.getArg(1);
3304 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3308 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3309 f1 << "\n" << f2 << "\n" );
3310 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3311 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3321 void GuiView::openChildDocument(string const & fname)
3323 LASSERT(documentBufferView(), return);
3324 Buffer & buffer = documentBufferView()->buffer();
3325 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3326 documentBufferView()->saveBookmark(false);
3328 if (theBufferList().exists(filename)) {
3329 child = theBufferList().getBuffer(filename);
3332 message(bformat(_("Opening child document %1$s..."),
3333 makeDisplayPath(filename.absFileName())));
3334 child = loadDocument(filename, false);
3336 // Set the parent name of the child document.
3337 // This makes insertion of citations and references in the child work,
3338 // when the target is in the parent or another child document.
3340 child->setParent(&buffer);
3344 bool GuiView::goToFileRow(string const & argument)
3348 size_t i = argument.find_last_of(' ');
3349 if (i != string::npos) {
3350 file_name = os::internal_path(trim(argument.substr(0, i)));
3351 istringstream is(argument.substr(i + 1));
3356 if (i == string::npos) {
3357 LYXERR0("Wrong argument: " << argument);
3361 string const abstmp = package().temp_dir().absFileName();
3362 string const realtmp = package().temp_dir().realPath();
3363 // We have to use os::path_prefix_is() here, instead of
3364 // simply prefixIs(), because the file name comes from
3365 // an external application and may need case adjustment.
3366 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3367 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3368 // Needed by inverse dvi search. If it is a file
3369 // in tmpdir, call the apropriated function.
3370 // If tmpdir is a symlink, we may have the real
3371 // path passed back, so we correct for that.
3372 if (!prefixIs(file_name, abstmp))
3373 file_name = subst(file_name, realtmp, abstmp);
3374 buf = theBufferList().getBufferFromTmp(file_name);
3376 // Must replace extension of the file to be .lyx
3377 // and get full path
3378 FileName const s = fileSearch(string(),
3379 support::changeExtension(file_name, ".lyx"), "lyx");
3380 // Either change buffer or load the file
3381 if (theBufferList().exists(s))
3382 buf = theBufferList().getBuffer(s);
3383 else if (s.exists()) {
3384 buf = loadDocument(s);
3389 _("File does not exist: %1$s"),
3390 makeDisplayPath(file_name)));
3396 _("No buffer for file: %1$s."),
3397 makeDisplayPath(file_name))
3402 bool success = documentBufferView()->setCursorFromRow(row);
3404 LYXERR(Debug::LATEX,
3405 "setCursorFromRow: invalid position for row " << row);
3406 frontend::Alert::error(_("Inverse Search Failed"),
3407 _("Invalid position requested by inverse search.\n"
3408 "You may need to update the viewed document."));
3415 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3417 Buffer::ExportStatus const status = func(format);
3419 // the cloning operation will have produced a clone of the entire set of
3420 // documents, starting from the master. so we must delete those.
3421 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3423 busyBuffers.remove(orig);
3428 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3430 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3431 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3435 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3437 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3438 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3442 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3444 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3445 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3449 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3450 string const & argument,
3451 Buffer const * used_buffer,
3452 docstring const & msg,
3453 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3454 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3455 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3460 string format = argument;
3462 format = used_buffer->params().getDefaultOutputFormat();
3463 processing_format = format;
3465 progress_->clearMessages();
3468 #if EXPORT_in_THREAD
3469 GuiViewPrivate::busyBuffers.insert(used_buffer);
3470 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3471 if (!cloned_buffer) {
3472 Alert::error(_("Export Error"),
3473 _("Error cloning the Buffer."));
3476 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3481 setPreviewFuture(f);
3482 last_export_format = used_buffer->params().bufferFormat();
3485 // We are asynchronous, so we don't know here anything about the success
3488 Buffer::ExportStatus status;
3490 status = (used_buffer->*syncFunc)(format, true);
3491 } else if (previewFunc) {
3492 status = (used_buffer->*previewFunc)(format);
3495 handleExportStatus(gv_, status, format);
3497 return (status == Buffer::ExportSuccess
3498 || status == Buffer::PreviewSuccess);
3502 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3504 BufferView * bv = currentBufferView();
3505 LASSERT(bv, return);
3507 // Let the current BufferView dispatch its own actions.
3508 bv->dispatch(cmd, dr);
3509 if (dr.dispatched())
3512 // Try with the document BufferView dispatch if any.
3513 BufferView * doc_bv = documentBufferView();
3514 if (doc_bv && doc_bv != bv) {
3515 doc_bv->dispatch(cmd, dr);
3516 if (dr.dispatched())
3520 // Then let the current Cursor dispatch its own actions.
3521 bv->cursor().dispatch(cmd);
3523 // update completion. We do it here and not in
3524 // processKeySym to avoid another redraw just for a
3525 // changed inline completion
3526 if (cmd.origin() == FuncRequest::KEYBOARD) {
3527 if (cmd.action() == LFUN_SELF_INSERT
3528 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3529 updateCompletion(bv->cursor(), true, true);
3530 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3531 updateCompletion(bv->cursor(), false, true);
3533 updateCompletion(bv->cursor(), false, false);
3536 dr = bv->cursor().result();
3540 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3542 BufferView * bv = currentBufferView();
3543 // By default we won't need any update.
3544 dr.screenUpdate(Update::None);
3545 // assume cmd will be dispatched
3546 dr.dispatched(true);
3548 Buffer * doc_buffer = documentBufferView()
3549 ? &(documentBufferView()->buffer()) : 0;
3551 if (cmd.origin() == FuncRequest::TOC) {
3552 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3553 // FIXME: do we need to pass a DispatchResult object here?
3554 toc->doDispatch(bv->cursor(), cmd);
3558 string const argument = to_utf8(cmd.argument());
3560 switch(cmd.action()) {
3561 case LFUN_BUFFER_CHILD_OPEN:
3562 openChildDocument(to_utf8(cmd.argument()));
3565 case LFUN_BUFFER_IMPORT:
3566 importDocument(to_utf8(cmd.argument()));
3569 case LFUN_BUFFER_EXPORT: {
3572 // GCC only sees strfwd.h when building merged
3573 if (::lyx::operator==(cmd.argument(), "custom")) {
3574 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3578 string const dest = cmd.getArg(1);
3579 FileName target_dir;
3580 if (!dest.empty() && FileName::isAbsolute(dest))
3581 target_dir = FileName(support::onlyPath(dest));
3583 target_dir = doc_buffer->fileName().onlyPath();
3585 if ((dest.empty() && doc_buffer->isUnnamed())
3586 || !target_dir.isDirWritable()) {
3587 exportBufferAs(*doc_buffer, cmd.argument());
3590 /* TODO/Review: Is it a problem to also export the children?
3591 See the update_unincluded flag */
3592 d.asyncBufferProcessing(argument,
3595 &GuiViewPrivate::exportAndDestroy,
3598 // TODO Inform user about success
3602 case LFUN_BUFFER_EXPORT_AS: {
3603 LASSERT(doc_buffer, break);
3604 docstring f = cmd.argument();
3606 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3607 exportBufferAs(*doc_buffer, f);
3611 case LFUN_BUFFER_UPDATE: {
3612 d.asyncBufferProcessing(argument,
3615 &GuiViewPrivate::compileAndDestroy,
3620 case LFUN_BUFFER_VIEW: {
3621 d.asyncBufferProcessing(argument,
3623 _("Previewing ..."),
3624 &GuiViewPrivate::previewAndDestroy,
3629 case LFUN_MASTER_BUFFER_UPDATE: {
3630 d.asyncBufferProcessing(argument,
3631 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3633 &GuiViewPrivate::compileAndDestroy,
3638 case LFUN_MASTER_BUFFER_VIEW: {
3639 d.asyncBufferProcessing(argument,
3640 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3642 &GuiViewPrivate::previewAndDestroy,
3643 0, &Buffer::preview);
3646 case LFUN_BUFFER_SWITCH: {
3647 string const file_name = to_utf8(cmd.argument());
3648 if (!FileName::isAbsolute(file_name)) {
3650 dr.setMessage(_("Absolute filename expected."));
3654 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3657 dr.setMessage(_("Document not loaded"));
3661 // Do we open or switch to the buffer in this view ?
3662 if (workArea(*buffer)
3663 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3668 // Look for the buffer in other views
3669 QList<int> const ids = guiApp->viewIds();
3671 for (; i != ids.size(); ++i) {
3672 GuiView & gv = guiApp->view(ids[i]);
3673 if (gv.workArea(*buffer)) {
3675 gv.activateWindow();
3677 gv.setBuffer(buffer);
3682 // If necessary, open a new window as a last resort
3683 if (i == ids.size()) {
3684 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3690 case LFUN_BUFFER_NEXT:
3691 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3694 case LFUN_BUFFER_MOVE_NEXT:
3695 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3698 case LFUN_BUFFER_PREVIOUS:
3699 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3702 case LFUN_BUFFER_MOVE_PREVIOUS:
3703 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3706 case LFUN_COMMAND_EXECUTE: {
3707 command_execute_ = true;
3708 minibuffer_focus_ = true;
3711 case LFUN_DROP_LAYOUTS_CHOICE:
3712 d.layout_->showPopup();
3715 case LFUN_MENU_OPEN:
3716 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3717 menu->exec(QCursor::pos());
3720 case LFUN_FILE_INSERT:
3721 insertLyXFile(cmd.argument());
3724 case LFUN_FILE_INSERT_PLAINTEXT:
3725 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3726 string const fname = to_utf8(cmd.argument());
3727 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3728 dr.setMessage(_("Absolute filename expected."));
3732 FileName filename(fname);
3733 if (fname.empty()) {
3734 FileDialog dlg(qt_("Select file to insert"));
3736 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3737 QStringList(qt_("All Files (*)")));
3739 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3740 dr.setMessage(_("Canceled."));
3744 filename.set(fromqstr(result.second));
3748 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3749 bv->dispatch(new_cmd, dr);
3754 case LFUN_BUFFER_RELOAD: {
3755 LASSERT(doc_buffer, break);
3758 if (!doc_buffer->isClean()) {
3759 docstring const file =
3760 makeDisplayPath(doc_buffer->absFileName(), 20);
3761 docstring text = bformat(_("Any changes will be lost. "
3762 "Are you sure you want to revert to the saved version "
3763 "of the document %1$s?"), file);
3764 ret = Alert::prompt(_("Revert to saved document?"),
3765 text, 1, 1, _("&Revert"), _("&Cancel"));
3769 doc_buffer->markClean();
3770 reloadBuffer(*doc_buffer);
3771 dr.forceBufferUpdate();
3776 case LFUN_BUFFER_WRITE:
3777 LASSERT(doc_buffer, break);
3778 saveBuffer(*doc_buffer);
3781 case LFUN_BUFFER_WRITE_AS:
3782 LASSERT(doc_buffer, break);
3783 renameBuffer(*doc_buffer, cmd.argument());
3786 case LFUN_BUFFER_WRITE_ALL: {
3787 Buffer * first = theBufferList().first();
3790 message(_("Saving all documents..."));
3791 // We cannot use a for loop as the buffer list cycles.
3794 if (!b->isClean()) {
3796 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3798 b = theBufferList().next(b);
3799 } while (b != first);
3800 dr.setMessage(_("All documents saved."));
3804 case LFUN_BUFFER_CLOSE:
3808 case LFUN_BUFFER_CLOSE_ALL:
3812 case LFUN_TOOLBAR_TOGGLE: {
3813 string const name = cmd.getArg(0);
3814 if (GuiToolbar * t = toolbar(name))
3819 case LFUN_DIALOG_UPDATE: {
3820 string const name = to_utf8(cmd.argument());
3821 if (name == "prefs" || name == "document")
3822 updateDialog(name, string());
3823 else if (name == "paragraph")
3824 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3825 else if (currentBufferView()) {
3826 Inset * inset = currentBufferView()->editedInset(name);
3827 // Can only update a dialog connected to an existing inset
3829 // FIXME: get rid of this indirection; GuiView ask the inset
3830 // if he is kind enough to update itself...
3831 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3832 //FIXME: pass DispatchResult here?
3833 inset->dispatch(currentBufferView()->cursor(), fr);
3839 case LFUN_DIALOG_TOGGLE: {
3840 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3841 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3842 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3846 case LFUN_DIALOG_DISCONNECT_INSET:
3847 disconnectDialog(to_utf8(cmd.argument()));
3850 case LFUN_DIALOG_HIDE: {
3851 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3855 case LFUN_DIALOG_SHOW: {
3856 string const name = cmd.getArg(0);
3857 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3859 if (name == "character") {
3860 data = freefont2string();
3862 showDialog("character", data);
3863 } else if (name == "latexlog") {
3864 Buffer::LogType type;
3865 string const logfile = doc_buffer->logName(&type);
3867 case Buffer::latexlog:
3870 case Buffer::buildlog:
3874 data += Lexer::quoteString(logfile);
3875 showDialog("log", data);
3876 } else if (name == "vclog") {
3877 string const data = "vc " +
3878 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3879 showDialog("log", data);
3880 } else if (name == "symbols") {
3881 data = bv->cursor().getEncoding()->name();
3883 showDialog("symbols", data);
3885 } else if (name == "prefs" && isFullScreen()) {
3886 lfunUiToggle("fullscreen");
3887 showDialog("prefs", data);
3889 showDialog(name, data);
3894 dr.setMessage(cmd.argument());
3897 case LFUN_UI_TOGGLE: {
3898 string arg = cmd.getArg(0);
3899 if (!lfunUiToggle(arg)) {
3900 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3901 dr.setMessage(bformat(msg, from_utf8(arg)));
3903 // Make sure the keyboard focus stays in the work area.
3908 case LFUN_VIEW_SPLIT: {
3909 LASSERT(doc_buffer, break);
3910 string const orientation = cmd.getArg(0);
3911 d.splitter_->setOrientation(orientation == "vertical"
3912 ? Qt::Vertical : Qt::Horizontal);
3913 TabWorkArea * twa = addTabWorkArea();
3914 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3915 setCurrentWorkArea(wa);
3918 case LFUN_TAB_GROUP_CLOSE:
3919 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3920 closeTabWorkArea(twa);
3921 d.current_work_area_ = 0;
3922 twa = d.currentTabWorkArea();
3923 // Switch to the next GuiWorkArea in the found TabWorkArea.
3925 // Make sure the work area is up to date.
3926 setCurrentWorkArea(twa->currentWorkArea());
3928 setCurrentWorkArea(0);
3933 case LFUN_VIEW_CLOSE:
3934 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3935 closeWorkArea(twa->currentWorkArea());
3936 d.current_work_area_ = 0;
3937 twa = d.currentTabWorkArea();
3938 // Switch to the next GuiWorkArea in the found TabWorkArea.
3940 // Make sure the work area is up to date.
3941 setCurrentWorkArea(twa->currentWorkArea());
3943 setCurrentWorkArea(0);
3948 case LFUN_COMPLETION_INLINE:
3949 if (d.current_work_area_)
3950 d.current_work_area_->completer().showInline();
3953 case LFUN_COMPLETION_POPUP:
3954 if (d.current_work_area_)
3955 d.current_work_area_->completer().showPopup();
3960 if (d.current_work_area_)
3961 d.current_work_area_->completer().tab();
3964 case LFUN_COMPLETION_CANCEL:
3965 if (d.current_work_area_) {
3966 if (d.current_work_area_->completer().popupVisible())
3967 d.current_work_area_->completer().hidePopup();
3969 d.current_work_area_->completer().hideInline();
3973 case LFUN_COMPLETION_ACCEPT:
3974 if (d.current_work_area_)
3975 d.current_work_area_->completer().activate();
3978 case LFUN_BUFFER_ZOOM_IN:
3979 case LFUN_BUFFER_ZOOM_OUT: {
3980 // use a signed temp to avoid overflow
3981 int zoom = lyxrc.zoom;
3982 if (cmd.argument().empty()) {
3983 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3988 zoom += convert<int>(cmd.argument());
3990 if (zoom < static_cast<int>(zoom_min_))
3994 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3996 // The global QPixmapCache is used in GuiPainter to cache text
3997 // painting so we must reset it.
3998 QPixmapCache::clear();
3999 guiApp->fontLoader().update();
4000 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4004 case LFUN_VC_REGISTER:
4005 case LFUN_VC_RENAME:
4007 case LFUN_VC_CHECK_IN:
4008 case LFUN_VC_CHECK_OUT:
4009 case LFUN_VC_REPO_UPDATE:
4010 case LFUN_VC_LOCKING_TOGGLE:
4011 case LFUN_VC_REVERT:
4012 case LFUN_VC_UNDO_LAST:
4013 case LFUN_VC_COMMAND:
4014 case LFUN_VC_COMPARE:
4015 dispatchVC(cmd, dr);
4018 case LFUN_SERVER_GOTO_FILE_ROW:
4019 if(goToFileRow(to_utf8(cmd.argument())))
4020 dr.screenUpdate(Update::Force | Update::FitCursor);
4023 case LFUN_LYX_ACTIVATE:
4027 case LFUN_FORWARD_SEARCH: {
4028 // it seems safe to assume we have a document buffer, since
4029 // getStatus wants one.
4030 // coverity[FORWARD_NULL]
4031 Buffer const * doc_master = doc_buffer->masterBuffer();
4032 FileName const path(doc_master->temppath());
4033 string const texname = doc_master->isChild(doc_buffer)
4034 ? DocFileName(changeExtension(
4035 doc_buffer->absFileName(),
4036 "tex")).mangledFileName()
4037 : doc_buffer->latexName();
4038 string const fulltexname =
4039 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4040 string const mastername =
4041 removeExtension(doc_master->latexName());
4042 FileName const dviname(addName(path.absFileName(),
4043 addExtension(mastername, "dvi")));
4044 FileName const pdfname(addName(path.absFileName(),
4045 addExtension(mastername, "pdf")));
4046 bool const have_dvi = dviname.exists();
4047 bool const have_pdf = pdfname.exists();
4048 if (!have_dvi && !have_pdf) {
4049 dr.setMessage(_("Please, preview the document first."));
4052 string outname = dviname.onlyFileName();
4053 string command = lyxrc.forward_search_dvi;
4054 if (!have_dvi || (have_pdf &&
4055 pdfname.lastModified() > dviname.lastModified())) {
4056 outname = pdfname.onlyFileName();
4057 command = lyxrc.forward_search_pdf;
4060 DocIterator cur = bv->cursor();
4061 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4062 LYXERR(Debug::ACTION, "Forward search: row:" << row
4064 if (row == -1 || command.empty()) {
4065 dr.setMessage(_("Couldn't proceed."));
4068 string texrow = convert<string>(row);
4070 command = subst(command, "$$n", texrow);
4071 command = subst(command, "$$f", fulltexname);
4072 command = subst(command, "$$t", texname);
4073 command = subst(command, "$$o", outname);
4075 PathChanger p(path);
4077 one.startscript(Systemcall::DontWait, command);
4081 case LFUN_SPELLING_CONTINUOUSLY:
4082 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4083 dr.screenUpdate(Update::Force | Update::FitCursor);
4087 // The LFUN must be for one of BufferView, Buffer or Cursor;
4089 dispatchToBufferView(cmd, dr);
4093 // Part of automatic menu appearance feature.
4094 if (isFullScreen()) {
4095 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4099 // Need to update bv because many LFUNs here might have destroyed it
4100 bv = currentBufferView();
4102 // Clear non-empty selections
4103 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4105 Cursor & cur = bv->cursor();
4106 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4107 cur.clearSelection();
4113 bool GuiView::lfunUiToggle(string const & ui_component)
4115 if (ui_component == "scrollbar") {
4116 // hide() is of no help
4117 if (d.current_work_area_->verticalScrollBarPolicy() ==
4118 Qt::ScrollBarAlwaysOff)
4120 d.current_work_area_->setVerticalScrollBarPolicy(
4121 Qt::ScrollBarAsNeeded);
4123 d.current_work_area_->setVerticalScrollBarPolicy(
4124 Qt::ScrollBarAlwaysOff);
4125 } else if (ui_component == "statusbar") {
4126 statusBar()->setVisible(!statusBar()->isVisible());
4127 } else if (ui_component == "menubar") {
4128 menuBar()->setVisible(!menuBar()->isVisible());
4130 if (ui_component == "frame") {
4132 getContentsMargins(&l, &t, &r, &b);
4133 //are the frames in default state?
4134 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4136 setContentsMargins(-2, -2, -2, -2);
4138 setContentsMargins(0, 0, 0, 0);
4141 if (ui_component == "fullscreen") {
4149 void GuiView::toggleFullScreen()
4151 if (isFullScreen()) {
4152 for (int i = 0; i != d.splitter_->count(); ++i)
4153 d.tabWorkArea(i)->setFullScreen(false);
4154 setContentsMargins(0, 0, 0, 0);
4155 setWindowState(windowState() ^ Qt::WindowFullScreen);
4158 statusBar()->show();
4161 hideDialogs("prefs", 0);
4162 for (int i = 0; i != d.splitter_->count(); ++i)
4163 d.tabWorkArea(i)->setFullScreen(true);
4164 setContentsMargins(-2, -2, -2, -2);
4166 setWindowState(windowState() ^ Qt::WindowFullScreen);
4167 if (lyxrc.full_screen_statusbar)
4168 statusBar()->hide();
4169 if (lyxrc.full_screen_menubar)
4171 if (lyxrc.full_screen_toolbars) {
4172 ToolbarMap::iterator end = d.toolbars_.end();
4173 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4178 // give dialogs like the TOC a chance to adapt
4183 Buffer const * GuiView::updateInset(Inset const * inset)
4188 Buffer const * inset_buffer = &(inset->buffer());
4190 for (int i = 0; i != d.splitter_->count(); ++i) {
4191 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4194 Buffer const * buffer = &(wa->bufferView().buffer());
4195 if (inset_buffer == buffer)
4196 wa->scheduleRedraw();
4198 return inset_buffer;
4202 void GuiView::restartCursor()
4204 /* When we move around, or type, it's nice to be able to see
4205 * the cursor immediately after the keypress.
4207 if (d.current_work_area_)
4208 d.current_work_area_->startBlinkingCursor();
4210 // Take this occasion to update the other GUI elements.
4216 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4218 if (d.current_work_area_)
4219 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4224 // This list should be kept in sync with the list of insets in
4225 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4226 // dialog should have the same name as the inset.
4227 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4228 // docs in LyXAction.cpp.
4230 char const * const dialognames[] = {
4232 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4233 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4234 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4235 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4236 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4237 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4238 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4239 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4241 char const * const * const end_dialognames =
4242 dialognames + (sizeof(dialognames) / sizeof(char *));
4246 cmpCStr(char const * name) : name_(name) {}
4247 bool operator()(char const * other) {
4248 return strcmp(other, name_) == 0;
4255 bool isValidName(string const & name)
4257 return find_if(dialognames, end_dialognames,
4258 cmpCStr(name.c_str())) != end_dialognames;
4264 void GuiView::resetDialogs()
4266 // Make sure that no LFUN uses any GuiView.
4267 guiApp->setCurrentView(0);
4271 constructToolbars();
4272 guiApp->menus().fillMenuBar(menuBar(), this, false);
4273 d.layout_->updateContents(true);
4274 // Now update controls with current buffer.
4275 guiApp->setCurrentView(this);
4281 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4283 if (!isValidName(name))
4286 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4288 if (it != d.dialogs_.end()) {
4290 it->second->hideView();
4291 return it->second.get();
4294 Dialog * dialog = build(name);
4295 d.dialogs_[name].reset(dialog);
4296 if (lyxrc.allow_geometry_session)
4297 dialog->restoreSession();
4304 void GuiView::showDialog(string const & name, string const & data,
4307 triggerShowDialog(toqstr(name), toqstr(data), inset);
4311 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4317 const string name = fromqstr(qname);
4318 const string data = fromqstr(qdata);
4322 Dialog * dialog = findOrBuild(name, false);
4324 bool const visible = dialog->isVisibleView();
4325 dialog->showData(data);
4326 if (inset && currentBufferView())
4327 currentBufferView()->editInset(name, inset);
4328 // We only set the focus to the new dialog if it was not yet
4329 // visible in order not to change the existing previous behaviour
4331 // activateWindow is needed for floating dockviews
4332 dialog->asQWidget()->raise();
4333 dialog->asQWidget()->activateWindow();
4334 dialog->asQWidget()->setFocus();
4338 catch (ExceptionMessage const & ex) {
4346 bool GuiView::isDialogVisible(string const & name) const
4348 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4349 if (it == d.dialogs_.end())
4351 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4355 void GuiView::hideDialog(string const & name, Inset * inset)
4357 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4358 if (it == d.dialogs_.end())
4362 if (!currentBufferView())
4364 if (inset != currentBufferView()->editedInset(name))
4368 Dialog * const dialog = it->second.get();
4369 if (dialog->isVisibleView())
4371 if (currentBufferView())
4372 currentBufferView()->editInset(name, 0);
4376 void GuiView::disconnectDialog(string const & name)
4378 if (!isValidName(name))
4380 if (currentBufferView())
4381 currentBufferView()->editInset(name, 0);
4385 void GuiView::hideAll() const
4387 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4388 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4390 for(; it != end; ++it)
4391 it->second->hideView();
4395 void GuiView::updateDialogs()
4397 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4398 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4400 for(; it != end; ++it) {
4401 Dialog * dialog = it->second.get();
4403 if (dialog->needBufferOpen() && !documentBufferView())
4404 hideDialog(fromqstr(dialog->name()), 0);
4405 else if (dialog->isVisibleView())
4406 dialog->checkStatus();
4413 Dialog * createDialog(GuiView & lv, string const & name);
4415 // will be replaced by a proper factory...
4416 Dialog * createGuiAbout(GuiView & lv);
4417 Dialog * createGuiBibtex(GuiView & lv);
4418 Dialog * createGuiChanges(GuiView & lv);
4419 Dialog * createGuiCharacter(GuiView & lv);
4420 Dialog * createGuiCitation(GuiView & lv);
4421 Dialog * createGuiCompare(GuiView & lv);
4422 Dialog * createGuiCompareHistory(GuiView & lv);
4423 Dialog * createGuiDelimiter(GuiView & lv);
4424 Dialog * createGuiDocument(GuiView & lv);
4425 Dialog * createGuiErrorList(GuiView & lv);
4426 Dialog * createGuiExternal(GuiView & lv);
4427 Dialog * createGuiGraphics(GuiView & lv);
4428 Dialog * createGuiInclude(GuiView & lv);
4429 Dialog * createGuiIndex(GuiView & lv);
4430 Dialog * createGuiListings(GuiView & lv);
4431 Dialog * createGuiLog(GuiView & lv);
4432 Dialog * createGuiMathMatrix(GuiView & lv);
4433 Dialog * createGuiNote(GuiView & lv);
4434 Dialog * createGuiParagraph(GuiView & lv);
4435 Dialog * createGuiPhantom(GuiView & lv);
4436 Dialog * createGuiPreferences(GuiView & lv);
4437 Dialog * createGuiPrint(GuiView & lv);
4438 Dialog * createGuiPrintindex(GuiView & lv);
4439 Dialog * createGuiRef(GuiView & lv);
4440 Dialog * createGuiSearch(GuiView & lv);
4441 Dialog * createGuiSearchAdv(GuiView & lv);
4442 Dialog * createGuiSendTo(GuiView & lv);
4443 Dialog * createGuiShowFile(GuiView & lv);
4444 Dialog * createGuiSpellchecker(GuiView & lv);
4445 Dialog * createGuiSymbols(GuiView & lv);
4446 Dialog * createGuiTabularCreate(GuiView & lv);
4447 Dialog * createGuiTexInfo(GuiView & lv);
4448 Dialog * createGuiToc(GuiView & lv);
4449 Dialog * createGuiThesaurus(GuiView & lv);
4450 Dialog * createGuiViewSource(GuiView & lv);
4451 Dialog * createGuiWrap(GuiView & lv);
4452 Dialog * createGuiProgressView(GuiView & lv);
4456 Dialog * GuiView::build(string const & name)
4458 LASSERT(isValidName(name), return 0);
4460 Dialog * dialog = createDialog(*this, name);
4464 if (name == "aboutlyx")
4465 return createGuiAbout(*this);
4466 if (name == "bibtex")
4467 return createGuiBibtex(*this);
4468 if (name == "changes")
4469 return createGuiChanges(*this);
4470 if (name == "character")
4471 return createGuiCharacter(*this);
4472 if (name == "citation")
4473 return createGuiCitation(*this);
4474 if (name == "compare")
4475 return createGuiCompare(*this);
4476 if (name == "comparehistory")
4477 return createGuiCompareHistory(*this);
4478 if (name == "document")
4479 return createGuiDocument(*this);
4480 if (name == "errorlist")
4481 return createGuiErrorList(*this);
4482 if (name == "external")
4483 return createGuiExternal(*this);
4485 return createGuiShowFile(*this);
4486 if (name == "findreplace")
4487 return createGuiSearch(*this);
4488 if (name == "findreplaceadv")
4489 return createGuiSearchAdv(*this);
4490 if (name == "graphics")
4491 return createGuiGraphics(*this);
4492 if (name == "include")
4493 return createGuiInclude(*this);
4494 if (name == "index")
4495 return createGuiIndex(*this);
4496 if (name == "index_print")
4497 return createGuiPrintindex(*this);
4498 if (name == "listings")
4499 return createGuiListings(*this);
4501 return createGuiLog(*this);
4502 if (name == "mathdelimiter")
4503 return createGuiDelimiter(*this);
4504 if (name == "mathmatrix")
4505 return createGuiMathMatrix(*this);
4507 return createGuiNote(*this);
4508 if (name == "paragraph")
4509 return createGuiParagraph(*this);
4510 if (name == "phantom")
4511 return createGuiPhantom(*this);
4512 if (name == "prefs")
4513 return createGuiPreferences(*this);
4515 return createGuiRef(*this);
4516 if (name == "sendto")
4517 return createGuiSendTo(*this);
4518 if (name == "spellchecker")
4519 return createGuiSpellchecker(*this);
4520 if (name == "symbols")
4521 return createGuiSymbols(*this);
4522 if (name == "tabularcreate")
4523 return createGuiTabularCreate(*this);
4524 if (name == "texinfo")
4525 return createGuiTexInfo(*this);
4526 if (name == "thesaurus")
4527 return createGuiThesaurus(*this);
4529 return createGuiToc(*this);
4530 if (name == "view-source")
4531 return createGuiViewSource(*this);
4533 return createGuiWrap(*this);
4534 if (name == "progress")
4535 return createGuiProgressView(*this);
4541 } // namespace frontend
4544 #include "moc_GuiView.cpp"