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 connect(this, SIGNAL(bufferViewChanged()),
516 this, SLOT(on_bufferViewChanged()));
518 // GuiToolbars *must* be initialised before the menu bar.
519 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
522 // set ourself as the current view. This is needed for the menu bar
523 // filling, at least for the static special menu item on Mac. Otherwise
524 // they are greyed out.
525 guiApp->setCurrentView(this);
527 // Fill up the menu bar.
528 guiApp->menus().fillMenuBar(menuBar(), this, true);
530 setCentralWidget(d.stack_widget_);
532 // Start autosave timer
533 if (lyxrc.autosave) {
534 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
535 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
536 d.autosave_timeout_.start();
538 connect(&d.statusbar_timer_, SIGNAL(timeout()),
539 this, SLOT(clearMessage()));
541 // We don't want to keep the window in memory if it is closed.
542 setAttribute(Qt::WA_DeleteOnClose, true);
544 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
545 // QIcon::fromTheme was introduced in Qt 4.6
546 #if (QT_VERSION >= 0x040600)
547 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
548 // since the icon is provided in the application bundle. We use a themed
549 // version when available and use the bundled one as fallback.
550 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
552 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
558 // use tabbed dock area for multiple docks
559 // (such as "source" and "messages")
560 setDockOptions(QMainWindow::ForceTabbedDocks);
563 setAcceptDrops(true);
565 // add busy indicator to statusbar
566 QLabel * busylabel = new QLabel(statusBar());
567 statusBar()->addPermanentWidget(busylabel);
568 search_mode mode = theGuiApp()->imageSearchMode();
569 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
570 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
571 busylabel->setMovie(busyanim);
575 connect(&d.processing_thread_watcher_, SIGNAL(started()),
576 busylabel, SLOT(show()));
577 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
578 busylabel, SLOT(hide()));
580 QFontMetrics const fm(statusBar()->fontMetrics());
581 int const roheight = max(int(d.normalIconSize), fm.height());
582 QSize const rosize(roheight, roheight);
583 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
584 read_only_ = new QLabel(statusBar());
585 read_only_->setPixmap(readonly);
586 read_only_->setScaledContents(true);
587 read_only_->setAlignment(Qt::AlignCenter);
589 statusBar()->addPermanentWidget(read_only_);
591 version_control_ = new QLabel(statusBar());
592 version_control_->setAlignment(Qt::AlignCenter);
593 version_control_->setFrameStyle(QFrame::StyledPanel);
594 version_control_->hide();
595 statusBar()->addPermanentWidget(version_control_);
597 statusBar()->setSizeGripEnabled(true);
600 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
601 SLOT(autoSaveThreadFinished()));
603 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
604 SLOT(processingThreadStarted()));
605 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
606 SLOT(processingThreadFinished()));
608 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
609 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
611 // Forbid too small unresizable window because it can happen
612 // with some window manager under X11.
613 setMinimumSize(300, 200);
615 if (lyxrc.allow_geometry_session) {
616 // Now take care of session management.
621 // no session handling, default to a sane size.
622 setGeometry(50, 50, 690, 510);
625 // clear session data if any.
627 settings.remove("views");
637 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
639 QVector<GuiWorkArea*> areas;
640 for (int i = 0; i < tabWorkAreaCount(); i++) {
641 TabWorkArea* ta = tabWorkArea(i);
642 for (int u = 0; u < ta->count(); u++) {
643 areas << ta->workArea(u);
649 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
650 string const & format)
652 docstring const fmt = formats.prettyName(format);
655 case Buffer::ExportSuccess:
656 msg = bformat(_("Successful export to format: %1$s"), fmt);
658 case Buffer::ExportCancel:
659 msg = _("Document export cancelled.");
661 case Buffer::ExportError:
662 case Buffer::ExportNoPathToFormat:
663 case Buffer::ExportTexPathHasSpaces:
664 case Buffer::ExportConverterError:
665 msg = bformat(_("Error while exporting format: %1$s"), fmt);
667 case Buffer::PreviewSuccess:
668 msg = bformat(_("Successful preview of format: %1$s"), fmt);
670 case Buffer::PreviewError:
671 msg = bformat(_("Error while previewing format: %1$s"), fmt);
678 void GuiView::processingThreadStarted()
683 void GuiView::processingThreadFinished()
685 QFutureWatcher<Buffer::ExportStatus> const * watcher =
686 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
688 Buffer::ExportStatus const status = watcher->result();
689 handleExportStatus(this, status, d.processing_format);
692 BufferView const * const bv = currentBufferView();
693 if (bv && !bv->buffer().errorList("Export").empty()) {
697 errors(d.last_export_format);
701 void GuiView::autoSaveThreadFinished()
703 QFutureWatcher<docstring> const * watcher =
704 static_cast<QFutureWatcher<docstring> const *>(sender());
705 message(watcher->result());
710 void GuiView::saveLayout() const
713 settings.beginGroup("views");
714 settings.beginGroup(QString::number(id_));
715 #if defined(Q_WS_X11) || defined(QPA_XCB)
716 settings.setValue("pos", pos());
717 settings.setValue("size", size());
719 settings.setValue("geometry", saveGeometry());
721 settings.setValue("layout", saveState(0));
722 settings.setValue("icon_size", iconSize());
726 void GuiView::saveUISettings() const
728 // Save the toolbar private states
729 ToolbarMap::iterator end = d.toolbars_.end();
730 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
731 it->second->saveSession();
732 // Now take care of all other dialogs
733 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
734 for (; it!= d.dialogs_.end(); ++it)
735 it->second->saveSession();
739 bool GuiView::restoreLayout()
742 settings.beginGroup("views");
743 settings.beginGroup(QString::number(id_));
744 QString const icon_key = "icon_size";
745 if (!settings.contains(icon_key))
748 //code below is skipped when when ~/.config/LyX is (re)created
749 QSize icon_size = settings.value(icon_key).toSize();
750 // Check whether session size changed.
751 if (icon_size.width() != int(d.smallIconSize) &&
752 icon_size.width() != int(d.normalIconSize) &&
753 icon_size.width() != int(d.bigIconSize) &&
754 icon_size.width() != int(d.hugeIconSize) &&
755 icon_size.width() != int(d.giantIconSize)) {
756 icon_size.setWidth(d.normalIconSize);
757 icon_size.setHeight(d.normalIconSize);
759 setIconSize(icon_size);
761 #if defined(Q_WS_X11) || defined(QPA_XCB)
762 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
763 QSize size = settings.value("size", QSize(690, 510)).toSize();
767 // Work-around for bug #6034: the window ends up in an undetermined
768 // state when trying to restore a maximized window when it is
769 // already maximized.
770 if (!(windowState() & Qt::WindowMaximized))
771 if (!restoreGeometry(settings.value("geometry").toByteArray()))
772 setGeometry(50, 50, 690, 510);
774 // Make sure layout is correctly oriented.
775 setLayoutDirection(qApp->layoutDirection());
777 // Allow the toc and view-source dock widget to be restored if needed.
779 if ((dialog = findOrBuild("toc", true)))
780 // see bug 5082. At least setup title and enabled state.
781 // Visibility will be adjusted by restoreState below.
782 dialog->prepareView();
783 if ((dialog = findOrBuild("view-source", true)))
784 dialog->prepareView();
785 if ((dialog = findOrBuild("progress", true)))
786 dialog->prepareView();
788 if (!restoreState(settings.value("layout").toByteArray(), 0))
791 // init the toolbars that have not been restored
792 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
793 Toolbars::Infos::iterator end = guiApp->toolbars().end();
794 for (; cit != end; ++cit) {
795 GuiToolbar * tb = toolbar(cit->name);
796 if (tb && !tb->isRestored())
797 initToolbar(cit->name);
805 GuiToolbar * GuiView::toolbar(string const & name)
807 ToolbarMap::iterator it = d.toolbars_.find(name);
808 if (it != d.toolbars_.end())
811 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
816 void GuiView::constructToolbars()
818 ToolbarMap::iterator it = d.toolbars_.begin();
819 for (; it != d.toolbars_.end(); ++it)
823 // I don't like doing this here, but the standard toolbar
824 // destroys this object when it's destroyed itself (vfr)
825 d.layout_ = new LayoutBox(*this);
826 d.stack_widget_->addWidget(d.layout_);
827 d.layout_->move(0,0);
829 // extracts the toolbars from the backend
830 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
831 Toolbars::Infos::iterator end = guiApp->toolbars().end();
832 for (; cit != end; ++cit)
833 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
837 void GuiView::initToolbars()
839 // extracts the toolbars from the backend
840 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
841 Toolbars::Infos::iterator end = guiApp->toolbars().end();
842 for (; cit != end; ++cit)
843 initToolbar(cit->name);
847 void GuiView::initToolbar(string const & name)
849 GuiToolbar * tb = toolbar(name);
852 int const visibility = guiApp->toolbars().defaultVisibility(name);
853 bool newline = !(visibility & Toolbars::SAMEROW);
854 tb->setVisible(false);
855 tb->setVisibility(visibility);
857 if (visibility & Toolbars::TOP) {
859 addToolBarBreak(Qt::TopToolBarArea);
860 addToolBar(Qt::TopToolBarArea, tb);
863 if (visibility & Toolbars::BOTTOM) {
865 addToolBarBreak(Qt::BottomToolBarArea);
866 addToolBar(Qt::BottomToolBarArea, tb);
869 if (visibility & Toolbars::LEFT) {
871 addToolBarBreak(Qt::LeftToolBarArea);
872 addToolBar(Qt::LeftToolBarArea, tb);
875 if (visibility & Toolbars::RIGHT) {
877 addToolBarBreak(Qt::RightToolBarArea);
878 addToolBar(Qt::RightToolBarArea, tb);
881 if (visibility & Toolbars::ON)
882 tb->setVisible(true);
886 TocModels & GuiView::tocModels()
888 return d.toc_models_;
892 void GuiView::setFocus()
894 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
895 QMainWindow::setFocus();
899 bool GuiView::hasFocus() const
901 if (currentWorkArea())
902 return currentWorkArea()->hasFocus();
903 if (currentMainWorkArea())
904 return currentMainWorkArea()->hasFocus();
905 return d.bg_widget_->hasFocus();
909 void GuiView::focusInEvent(QFocusEvent * e)
911 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
912 QMainWindow::focusInEvent(e);
913 // Make sure guiApp points to the correct view.
914 guiApp->setCurrentView(this);
915 if (currentWorkArea())
916 currentWorkArea()->setFocus();
917 else if (currentMainWorkArea())
918 currentMainWorkArea()->setFocus();
920 d.bg_widget_->setFocus();
924 QMenu * GuiView::createPopupMenu()
926 return d.toolBarPopup(this);
930 void GuiView::showEvent(QShowEvent * e)
932 LYXERR(Debug::GUI, "Passed Geometry "
933 << size().height() << "x" << size().width()
934 << "+" << pos().x() << "+" << pos().y());
936 if (d.splitter_->count() == 0)
937 // No work area, switch to the background widget.
941 QMainWindow::showEvent(e);
945 bool GuiView::closeScheduled()
952 bool GuiView::prepareAllBuffersForLogout()
954 Buffer * first = theBufferList().first();
958 // First, iterate over all buffers and ask the users if unsaved
959 // changes should be saved.
960 // We cannot use a for loop as the buffer list cycles.
963 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
965 b = theBufferList().next(b);
966 } while (b != first);
968 // Next, save session state
969 // When a view/window was closed before without quitting LyX, there
970 // are already entries in the lastOpened list.
971 theSession().lastOpened().clear();
978 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
979 ** is responsibility of the container (e.g., dialog)
981 void GuiView::closeEvent(QCloseEvent * close_event)
983 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
985 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
986 Alert::warning(_("Exit LyX"),
987 _("LyX could not be closed because documents are being processed by LyX."));
988 close_event->setAccepted(false);
992 // If the user pressed the x (so we didn't call closeView
993 // programmatically), we want to clear all existing entries.
995 theSession().lastOpened().clear();
1000 // it can happen that this event arrives without selecting the view,
1001 // e.g. when clicking the close button on a background window.
1003 if (!closeWorkAreaAll()) {
1005 close_event->ignore();
1009 // Make sure that nothing will use this to be closed View.
1010 guiApp->unregisterView(this);
1012 if (isFullScreen()) {
1013 // Switch off fullscreen before closing.
1018 // Make sure the timer time out will not trigger a statusbar update.
1019 d.statusbar_timer_.stop();
1021 // Saving fullscreen requires additional tweaks in the toolbar code.
1022 // It wouldn't also work under linux natively.
1023 if (lyxrc.allow_geometry_session) {
1028 close_event->accept();
1032 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1034 if (event->mimeData()->hasUrls())
1036 /// \todo Ask lyx-devel is this is enough:
1037 /// if (event->mimeData()->hasFormat("text/plain"))
1038 /// event->acceptProposedAction();
1042 void GuiView::dropEvent(QDropEvent * event)
1044 QList<QUrl> files = event->mimeData()->urls();
1045 if (files.isEmpty())
1048 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1049 for (int i = 0; i != files.size(); ++i) {
1050 string const file = os::internal_path(fromqstr(
1051 files.at(i).toLocalFile()));
1055 string const ext = support::getExtension(file);
1056 vector<const Format *> found_formats;
1058 // Find all formats that have the correct extension.
1059 vector<const Format *> const & import_formats
1060 = theConverters().importableFormats();
1061 vector<const Format *>::const_iterator it = import_formats.begin();
1062 for (; it != import_formats.end(); ++it)
1063 if ((*it)->hasExtension(ext))
1064 found_formats.push_back(*it);
1067 if (found_formats.size() >= 1) {
1068 if (found_formats.size() > 1) {
1069 //FIXME: show a dialog to choose the correct importable format
1070 LYXERR(Debug::FILES,
1071 "Multiple importable formats found, selecting first");
1073 string const arg = found_formats[0]->name() + " " + file;
1074 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1077 //FIXME: do we have to explicitly check whether it's a lyx file?
1078 LYXERR(Debug::FILES,
1079 "No formats found, trying to open it as a lyx file");
1080 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1082 // add the functions to the queue
1083 guiApp->addToFuncRequestQueue(cmd);
1086 // now process the collected functions. We perform the events
1087 // asynchronously. This prevents potential problems in case the
1088 // BufferView is closed within an event.
1089 guiApp->processFuncRequestQueueAsync();
1093 void GuiView::message(docstring const & str)
1095 if (ForkedProcess::iAmAChild())
1098 // call is moved to GUI-thread by GuiProgress
1099 d.progress_->appendMessage(toqstr(str));
1103 void GuiView::clearMessageText()
1105 message(docstring());
1109 void GuiView::updateStatusBarMessage(QString const & str)
1111 statusBar()->showMessage(str);
1112 d.statusbar_timer_.stop();
1113 d.statusbar_timer_.start(3000);
1117 void GuiView::smallSizedIcons()
1119 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1123 void GuiView::normalSizedIcons()
1125 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1129 void GuiView::bigSizedIcons()
1131 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1135 void GuiView::hugeSizedIcons()
1137 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1141 void GuiView::giantSizedIcons()
1143 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1147 void GuiView::clearMessage()
1149 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1150 // the hasFocus function mostly returns false, even if the focus is on
1151 // a workarea in this view.
1155 d.statusbar_timer_.stop();
1159 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1161 if (wa != d.current_work_area_
1162 || wa->bufferView().buffer().isInternal())
1164 Buffer const & buf = wa->bufferView().buffer();
1165 // Set the windows title
1166 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1168 title += from_ascii(" - LyX");
1170 setWindowTitle(toqstr(title));
1171 // Sets the path for the window: this is used by OSX to
1172 // allow a context click on the title bar showing a menu
1173 // with the path up to the file
1174 setWindowFilePath(toqstr(buf.absFileName()));
1175 // Tell Qt whether the current document is changed
1176 setWindowModified(!buf.isClean());
1178 if (buf.isReadonly())
1183 if (buf.lyxvc().inUse()) {
1184 version_control_->show();
1185 if (buf.lyxvc().locking())
1186 version_control_->setText(
1187 toqstr(bformat(_("%1$s lock"),
1188 from_ascii(buf.lyxvc().vcname()))));
1190 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1192 version_control_->hide();
1196 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1198 if (d.current_work_area_)
1199 // disconnect the current work area from all slots
1200 QObject::disconnect(d.current_work_area_, 0, this, 0);
1202 disconnectBufferView();
1203 connectBufferView(wa->bufferView());
1204 connectBuffer(wa->bufferView().buffer());
1205 d.current_work_area_ = wa;
1206 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1207 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1208 QObject::connect(wa, SIGNAL(busy(bool)),
1209 this, SLOT(setBusy(bool)));
1210 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1211 this, SIGNAL(bufferViewChanged()));
1212 Q_EMIT updateWindowTitle(wa);
1213 Q_EMIT bufferViewChanged();
1217 void GuiView::on_bufferViewChanged()
1221 // The document settings needs to be reinitialised.
1222 // TODO: no longer needed now there is bufferViewChanged?
1223 updateDialog("document", "");
1225 // Buffer-dependent dialogs must be updated. This is done here because
1226 // some dialogs require buffer()->text.
1231 void GuiView::on_lastWorkAreaRemoved()
1234 // We already are in a close event. Nothing more to do.
1237 if (d.splitter_->count() > 1)
1238 // We have a splitter so don't close anything.
1241 // Reset and updates the dialogs.
1242 Q_EMIT bufferViewChanged();
1247 if (lyxrc.open_buffers_in_tabs)
1248 // Nothing more to do, the window should stay open.
1251 if (guiApp->viewIds().size() > 1) {
1257 // On Mac we also close the last window because the application stay
1258 // resident in memory. On other platforms we don't close the last
1259 // window because this would quit the application.
1265 void GuiView::updateStatusBar()
1267 // let the user see the explicit message
1268 if (d.statusbar_timer_.isActive())
1275 void GuiView::showMessage()
1279 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1280 if (msg.isEmpty()) {
1281 BufferView const * bv = currentBufferView();
1283 msg = toqstr(bv->cursor().currentState());
1285 msg = qt_("Welcome to LyX!");
1287 statusBar()->showMessage(msg);
1291 bool GuiView::event(QEvent * e)
1295 // Useful debug code:
1296 //case QEvent::ActivationChange:
1297 //case QEvent::WindowDeactivate:
1298 //case QEvent::Paint:
1299 //case QEvent::Enter:
1300 //case QEvent::Leave:
1301 //case QEvent::HoverEnter:
1302 //case QEvent::HoverLeave:
1303 //case QEvent::HoverMove:
1304 //case QEvent::StatusTip:
1305 //case QEvent::DragEnter:
1306 //case QEvent::DragLeave:
1307 //case QEvent::Drop:
1310 case QEvent::WindowActivate: {
1311 GuiView * old_view = guiApp->currentView();
1312 if (this == old_view) {
1314 return QMainWindow::event(e);
1316 if (old_view && old_view->currentBufferView()) {
1317 // save current selection to the selection buffer to allow
1318 // middle-button paste in this window.
1319 cap::saveSelection(old_view->currentBufferView()->cursor());
1321 guiApp->setCurrentView(this);
1322 if (d.current_work_area_)
1323 on_currentWorkAreaChanged(d.current_work_area_);
1327 return QMainWindow::event(e);
1330 case QEvent::ShortcutOverride: {
1332 if (isFullScreen() && menuBar()->isHidden()) {
1333 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1334 // FIXME: we should also try to detect special LyX shortcut such as
1335 // Alt-P and Alt-M. Right now there is a hack in
1336 // GuiWorkArea::processKeySym() that hides again the menubar for
1338 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1340 return QMainWindow::event(e);
1343 return QMainWindow::event(e);
1347 return QMainWindow::event(e);
1351 void GuiView::resetWindowTitle()
1353 setWindowTitle(qt_("LyX"));
1356 bool GuiView::focusNextPrevChild(bool /*next*/)
1363 bool GuiView::busy() const
1369 void GuiView::setBusy(bool busy)
1371 bool const busy_before = busy_ > 0;
1372 busy ? ++busy_ : --busy_;
1373 if ((busy_ > 0) == busy_before)
1374 // busy state didn't change
1378 QApplication::setOverrideCursor(Qt::WaitCursor);
1381 QApplication::restoreOverrideCursor();
1386 void GuiView::resetCommandExecute()
1388 command_execute_ = false;
1393 double GuiView::pixelRatio() const
1395 #if QT_VERSION >= 0x050000
1396 return devicePixelRatio();
1403 GuiWorkArea * GuiView::workArea(int index)
1405 if (TabWorkArea * twa = d.currentTabWorkArea())
1406 if (index < twa->count())
1407 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1412 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1414 if (currentWorkArea()
1415 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1416 return currentWorkArea();
1417 if (TabWorkArea * twa = d.currentTabWorkArea())
1418 return twa->workArea(buffer);
1423 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1425 // Automatically create a TabWorkArea if there are none yet.
1426 TabWorkArea * tab_widget = d.splitter_->count()
1427 ? d.currentTabWorkArea() : addTabWorkArea();
1428 return tab_widget->addWorkArea(buffer, *this);
1432 TabWorkArea * GuiView::addTabWorkArea()
1434 TabWorkArea * twa = new TabWorkArea;
1435 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1436 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1437 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1438 this, SLOT(on_lastWorkAreaRemoved()));
1440 d.splitter_->addWidget(twa);
1441 d.stack_widget_->setCurrentWidget(d.splitter_);
1446 GuiWorkArea const * GuiView::currentWorkArea() const
1448 return d.current_work_area_;
1452 GuiWorkArea * GuiView::currentWorkArea()
1454 return d.current_work_area_;
1458 GuiWorkArea const * GuiView::currentMainWorkArea() const
1460 if (!d.currentTabWorkArea())
1462 return d.currentTabWorkArea()->currentWorkArea();
1466 GuiWorkArea * GuiView::currentMainWorkArea()
1468 if (!d.currentTabWorkArea())
1470 return d.currentTabWorkArea()->currentWorkArea();
1474 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1476 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1478 d.current_work_area_ = 0;
1480 Q_EMIT bufferViewChanged();
1484 // FIXME: I've no clue why this is here and why it accesses
1485 // theGuiApp()->currentView, which might be 0 (bug 6464).
1486 // See also 27525 (vfr).
1487 if (theGuiApp()->currentView() == this
1488 && theGuiApp()->currentView()->currentWorkArea() == wa)
1491 if (currentBufferView())
1492 cap::saveSelection(currentBufferView()->cursor());
1494 theGuiApp()->setCurrentView(this);
1495 d.current_work_area_ = wa;
1497 // We need to reset this now, because it will need to be
1498 // right if the tabWorkArea gets reset in the for loop. We
1499 // will change it back if we aren't in that case.
1500 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1501 d.current_main_work_area_ = wa;
1503 for (int i = 0; i != d.splitter_->count(); ++i) {
1504 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1505 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1506 << ", Current main wa: " << currentMainWorkArea());
1511 d.current_main_work_area_ = old_cmwa;
1513 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1514 on_currentWorkAreaChanged(wa);
1515 BufferView & bv = wa->bufferView();
1516 bv.cursor().fixIfBroken();
1518 wa->setUpdatesEnabled(true);
1519 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1523 void GuiView::removeWorkArea(GuiWorkArea * wa)
1525 LASSERT(wa, return);
1526 if (wa == d.current_work_area_) {
1528 disconnectBufferView();
1529 d.current_work_area_ = 0;
1530 d.current_main_work_area_ = 0;
1533 bool found_twa = false;
1534 for (int i = 0; i != d.splitter_->count(); ++i) {
1535 TabWorkArea * twa = d.tabWorkArea(i);
1536 if (twa->removeWorkArea(wa)) {
1537 // Found in this tab group, and deleted the GuiWorkArea.
1539 if (twa->count() != 0) {
1540 if (d.current_work_area_ == 0)
1541 // This means that we are closing the current GuiWorkArea, so
1542 // switch to the next GuiWorkArea in the found TabWorkArea.
1543 setCurrentWorkArea(twa->currentWorkArea());
1545 // No more WorkAreas in this tab group, so delete it.
1552 // It is not a tabbed work area (i.e., the search work area), so it
1553 // should be deleted by other means.
1554 LASSERT(found_twa, return);
1556 if (d.current_work_area_ == 0) {
1557 if (d.splitter_->count() != 0) {
1558 TabWorkArea * twa = d.currentTabWorkArea();
1559 setCurrentWorkArea(twa->currentWorkArea());
1561 // No more work areas, switch to the background widget.
1562 setCurrentWorkArea(0);
1568 LayoutBox * GuiView::getLayoutDialog() const
1574 void GuiView::updateLayoutList()
1577 d.layout_->updateContents(false);
1581 void GuiView::updateToolbars()
1583 ToolbarMap::iterator end = d.toolbars_.end();
1584 if (d.current_work_area_) {
1586 if (d.current_work_area_->bufferView().cursor().inMathed()
1587 && !d.current_work_area_->bufferView().cursor().inRegexped())
1588 context |= Toolbars::MATH;
1589 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1590 context |= Toolbars::TABLE;
1591 if (currentBufferView()->buffer().areChangesPresent()
1592 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1593 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1594 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1595 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1596 context |= Toolbars::REVIEW;
1597 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1598 context |= Toolbars::MATHMACROTEMPLATE;
1599 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1600 context |= Toolbars::IPA;
1601 if (command_execute_)
1602 context |= Toolbars::MINIBUFFER;
1603 if (minibuffer_focus_) {
1604 context |= Toolbars::MINIBUFFER_FOCUS;
1605 minibuffer_focus_ = false;
1608 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1609 it->second->update(context);
1611 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1612 it->second->update();
1616 void GuiView::setBuffer(Buffer * newBuffer)
1618 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1619 LASSERT(newBuffer, return);
1621 GuiWorkArea * wa = workArea(*newBuffer);
1624 newBuffer->masterBuffer()->updateBuffer();
1626 wa = addWorkArea(*newBuffer);
1627 // scroll to the position when the BufferView was last closed
1628 if (lyxrc.use_lastfilepos) {
1629 LastFilePosSection::FilePos filepos =
1630 theSession().lastFilePos().load(newBuffer->fileName());
1631 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1634 //Disconnect the old buffer...there's no new one.
1637 connectBuffer(*newBuffer);
1638 connectBufferView(wa->bufferView());
1639 setCurrentWorkArea(wa);
1643 void GuiView::connectBuffer(Buffer & buf)
1645 buf.setGuiDelegate(this);
1649 void GuiView::disconnectBuffer()
1651 if (d.current_work_area_)
1652 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1656 void GuiView::connectBufferView(BufferView & bv)
1658 bv.setGuiDelegate(this);
1662 void GuiView::disconnectBufferView()
1664 if (d.current_work_area_)
1665 d.current_work_area_->bufferView().setGuiDelegate(0);
1669 void GuiView::errors(string const & error_type, bool from_master)
1671 BufferView const * const bv = currentBufferView();
1675 #if EXPORT_in_THREAD
1676 // We are called with from_master == false by default, so we
1677 // have to figure out whether that is the case or not.
1678 ErrorList & el = bv->buffer().errorList(error_type);
1680 el = bv->buffer().masterBuffer()->errorList(error_type);
1684 ErrorList const & el = from_master ?
1685 bv->buffer().masterBuffer()->errorList(error_type) :
1686 bv->buffer().errorList(error_type);
1692 string data = error_type;
1694 data = "from_master|" + error_type;
1695 showDialog("errorlist", data);
1699 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1701 d.toc_models_.updateItem(toqstr(type), dit);
1705 void GuiView::structureChanged()
1707 // FIXME: This is slightly expensive, though less than the tocBackend update
1708 // (#9880). This also resets the view in the Toc Widget (#6675).
1709 d.toc_models_.reset(documentBufferView());
1710 // Navigator needs more than a simple update in this case. It needs to be
1712 updateDialog("toc", "");
1716 void GuiView::updateDialog(string const & name, string const & data)
1718 if (!isDialogVisible(name))
1721 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1722 if (it == d.dialogs_.end())
1725 Dialog * const dialog = it->second.get();
1726 if (dialog->isVisibleView())
1727 dialog->initialiseParams(data);
1731 BufferView * GuiView::documentBufferView()
1733 return currentMainWorkArea()
1734 ? ¤tMainWorkArea()->bufferView()
1739 BufferView const * GuiView::documentBufferView() const
1741 return currentMainWorkArea()
1742 ? ¤tMainWorkArea()->bufferView()
1747 BufferView * GuiView::currentBufferView()
1749 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1753 BufferView const * GuiView::currentBufferView() const
1755 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1759 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1760 Buffer const * orig, Buffer * clone)
1762 bool const success = clone->autoSave();
1764 busyBuffers.remove(orig);
1766 ? _("Automatic save done.")
1767 : _("Automatic save failed!");
1771 void GuiView::autoSave()
1773 LYXERR(Debug::INFO, "Running autoSave()");
1775 Buffer * buffer = documentBufferView()
1776 ? &documentBufferView()->buffer() : 0;
1778 resetAutosaveTimers();
1782 GuiViewPrivate::busyBuffers.insert(buffer);
1783 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1784 buffer, buffer->cloneBufferOnly());
1785 d.autosave_watcher_.setFuture(f);
1786 resetAutosaveTimers();
1790 void GuiView::resetAutosaveTimers()
1793 d.autosave_timeout_.restart();
1797 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1800 Buffer * buf = currentBufferView()
1801 ? ¤tBufferView()->buffer() : 0;
1802 Buffer * doc_buffer = documentBufferView()
1803 ? &(documentBufferView()->buffer()) : 0;
1806 /* In LyX/Mac, when a dialog is open, the menus of the
1807 application can still be accessed without giving focus to
1808 the main window. In this case, we want to disable the menu
1809 entries that are buffer-related.
1810 This code must not be used on Linux and Windows, since it
1811 would disable buffer-related entries when hovering over the
1812 menu (see bug #9574).
1814 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1820 // Check whether we need a buffer
1821 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1822 // no, exit directly
1823 flag.message(from_utf8(N_("Command not allowed with"
1824 "out any document open")));
1825 flag.setEnabled(false);
1829 if (cmd.origin() == FuncRequest::TOC) {
1830 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1831 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1832 flag.setEnabled(false);
1836 switch(cmd.action()) {
1837 case LFUN_BUFFER_IMPORT:
1840 case LFUN_MASTER_BUFFER_UPDATE:
1841 case LFUN_MASTER_BUFFER_VIEW:
1843 && (doc_buffer->parent() != 0
1844 || doc_buffer->hasChildren())
1845 && !d.processing_thread_watcher_.isRunning();
1848 case LFUN_BUFFER_UPDATE:
1849 case LFUN_BUFFER_VIEW: {
1850 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1854 string format = to_utf8(cmd.argument());
1855 if (cmd.argument().empty())
1856 format = doc_buffer->params().getDefaultOutputFormat();
1857 enable = doc_buffer->params().isExportableFormat(format);
1861 case LFUN_BUFFER_RELOAD:
1862 enable = doc_buffer && !doc_buffer->isUnnamed()
1863 && doc_buffer->fileName().exists()
1864 && (!doc_buffer->isClean()
1865 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1868 case LFUN_BUFFER_CHILD_OPEN:
1869 enable = doc_buffer != 0;
1872 case LFUN_BUFFER_WRITE:
1873 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1876 //FIXME: This LFUN should be moved to GuiApplication.
1877 case LFUN_BUFFER_WRITE_ALL: {
1878 // We enable the command only if there are some modified buffers
1879 Buffer * first = theBufferList().first();
1884 // We cannot use a for loop as the buffer list is a cycle.
1886 if (!b->isClean()) {
1890 b = theBufferList().next(b);
1891 } while (b != first);
1895 case LFUN_BUFFER_WRITE_AS:
1896 case LFUN_BUFFER_EXPORT_AS:
1897 enable = doc_buffer != 0;
1900 case LFUN_BUFFER_CLOSE:
1901 case LFUN_VIEW_CLOSE:
1902 enable = doc_buffer != 0;
1905 case LFUN_BUFFER_CLOSE_ALL:
1906 enable = theBufferList().last() != theBufferList().first();
1909 case LFUN_VIEW_SPLIT:
1910 if (cmd.getArg(0) == "vertical")
1911 enable = doc_buffer && (d.splitter_->count() == 1 ||
1912 d.splitter_->orientation() == Qt::Vertical);
1914 enable = doc_buffer && (d.splitter_->count() == 1 ||
1915 d.splitter_->orientation() == Qt::Horizontal);
1918 case LFUN_TAB_GROUP_CLOSE:
1919 enable = d.tabWorkAreaCount() > 1;
1922 case LFUN_TOOLBAR_TOGGLE: {
1923 string const name = cmd.getArg(0);
1924 if (GuiToolbar * t = toolbar(name))
1925 flag.setOnOff(t->isVisible());
1928 docstring const msg =
1929 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1935 case LFUN_DROP_LAYOUTS_CHOICE:
1939 case LFUN_UI_TOGGLE:
1940 flag.setOnOff(isFullScreen());
1943 case LFUN_DIALOG_DISCONNECT_INSET:
1946 case LFUN_DIALOG_HIDE:
1947 // FIXME: should we check if the dialog is shown?
1950 case LFUN_DIALOG_TOGGLE:
1951 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1952 // fall through to set "enable"
1953 case LFUN_DIALOG_SHOW: {
1954 string const name = cmd.getArg(0);
1956 enable = name == "aboutlyx"
1957 || name == "file" //FIXME: should be removed.
1959 || name == "texinfo"
1960 || name == "progress"
1961 || name == "compare";
1962 else if (name == "character" || name == "symbols"
1963 || name == "mathdelimiter" || name == "mathmatrix") {
1964 if (!buf || buf->isReadonly())
1967 Cursor const & cur = currentBufferView()->cursor();
1968 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1971 else if (name == "latexlog")
1972 enable = FileName(doc_buffer->logName()).isReadableFile();
1973 else if (name == "spellchecker")
1974 enable = theSpellChecker()
1975 && !doc_buffer->isReadonly()
1976 && !doc_buffer->text().empty();
1977 else if (name == "vclog")
1978 enable = doc_buffer->lyxvc().inUse();
1982 case LFUN_DIALOG_UPDATE: {
1983 string const name = cmd.getArg(0);
1985 enable = name == "prefs";
1989 case LFUN_COMMAND_EXECUTE:
1991 case LFUN_MENU_OPEN:
1992 // Nothing to check.
1995 case LFUN_COMPLETION_INLINE:
1996 if (!d.current_work_area_
1997 || !d.current_work_area_->completer().inlinePossible(
1998 currentBufferView()->cursor()))
2002 case LFUN_COMPLETION_POPUP:
2003 if (!d.current_work_area_
2004 || !d.current_work_area_->completer().popupPossible(
2005 currentBufferView()->cursor()))
2010 if (!d.current_work_area_
2011 || !d.current_work_area_->completer().inlinePossible(
2012 currentBufferView()->cursor()))
2016 case LFUN_COMPLETION_ACCEPT:
2017 if (!d.current_work_area_
2018 || (!d.current_work_area_->completer().popupVisible()
2019 && !d.current_work_area_->completer().inlineVisible()
2020 && !d.current_work_area_->completer().completionAvailable()))
2024 case LFUN_COMPLETION_CANCEL:
2025 if (!d.current_work_area_
2026 || (!d.current_work_area_->completer().popupVisible()
2027 && !d.current_work_area_->completer().inlineVisible()))
2031 case LFUN_BUFFER_ZOOM_OUT:
2032 case LFUN_BUFFER_ZOOM_IN: {
2033 // only diff between these two is that the default for ZOOM_OUT
2035 bool const neg_zoom =
2036 convert<int>(cmd.argument()) < 0 ||
2037 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2038 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2039 docstring const msg =
2040 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2044 enable = doc_buffer;
2047 case LFUN_BUFFER_MOVE_NEXT:
2048 case LFUN_BUFFER_MOVE_PREVIOUS:
2049 // we do not cycle when moving
2050 case LFUN_BUFFER_NEXT:
2051 case LFUN_BUFFER_PREVIOUS:
2052 // because we cycle, it doesn't matter whether on first or last
2053 enable = (d.currentTabWorkArea()->count() > 1);
2055 case LFUN_BUFFER_SWITCH:
2056 // toggle on the current buffer, but do not toggle off
2057 // the other ones (is that a good idea?)
2059 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2060 flag.setOnOff(true);
2063 case LFUN_VC_REGISTER:
2064 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2066 case LFUN_VC_RENAME:
2067 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2070 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2072 case LFUN_VC_CHECK_IN:
2073 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2075 case LFUN_VC_CHECK_OUT:
2076 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2078 case LFUN_VC_LOCKING_TOGGLE:
2079 enable = doc_buffer && !doc_buffer->isReadonly()
2080 && doc_buffer->lyxvc().lockingToggleEnabled();
2081 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2083 case LFUN_VC_REVERT:
2084 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2086 case LFUN_VC_UNDO_LAST:
2087 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2089 case LFUN_VC_REPO_UPDATE:
2090 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2092 case LFUN_VC_COMMAND: {
2093 if (cmd.argument().empty())
2095 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2099 case LFUN_VC_COMPARE:
2100 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2103 case LFUN_SERVER_GOTO_FILE_ROW:
2104 case LFUN_LYX_ACTIVATE:
2106 case LFUN_FORWARD_SEARCH:
2107 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2110 case LFUN_FILE_INSERT_PLAINTEXT:
2111 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2112 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2115 case LFUN_SPELLING_CONTINUOUSLY:
2116 flag.setOnOff(lyxrc.spellcheck_continuously);
2124 flag.setEnabled(false);
2130 static FileName selectTemplateFile()
2132 FileDialog dlg(qt_("Select template file"));
2133 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2134 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2136 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2137 QStringList(qt_("LyX Documents (*.lyx)")));
2139 if (result.first == FileDialog::Later)
2141 if (result.second.isEmpty())
2143 return FileName(fromqstr(result.second));
2147 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2151 Buffer * newBuffer = 0;
2153 newBuffer = checkAndLoadLyXFile(filename);
2154 } catch (ExceptionMessage const & e) {
2161 message(_("Document not loaded."));
2165 setBuffer(newBuffer);
2166 newBuffer->errors("Parse");
2169 theSession().lastFiles().add(filename);
2175 void GuiView::openDocument(string const & fname)
2177 string initpath = lyxrc.document_path;
2179 if (documentBufferView()) {
2180 string const trypath = documentBufferView()->buffer().filePath();
2181 // If directory is writeable, use this as default.
2182 if (FileName(trypath).isDirWritable())
2188 if (fname.empty()) {
2189 FileDialog dlg(qt_("Select document to open"));
2190 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2191 dlg.setButton2(qt_("Examples|#E#e"),
2192 toqstr(addPath(package().system_support().absFileName(), "examples")));
2194 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2195 FileDialog::Result result =
2196 dlg.open(toqstr(initpath), filter);
2198 if (result.first == FileDialog::Later)
2201 filename = fromqstr(result.second);
2203 // check selected filename
2204 if (filename.empty()) {
2205 message(_("Canceled."));
2211 // get absolute path of file and add ".lyx" to the filename if
2213 FileName const fullname =
2214 fileSearch(string(), filename, "lyx", support::may_not_exist);
2215 if (!fullname.empty())
2216 filename = fullname.absFileName();
2218 if (!fullname.onlyPath().isDirectory()) {
2219 Alert::warning(_("Invalid filename"),
2220 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2221 from_utf8(fullname.absFileName())));
2225 // if the file doesn't exist and isn't already open (bug 6645),
2226 // let the user create one
2227 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2228 !LyXVC::file_not_found_hook(fullname)) {
2229 // the user specifically chose this name. Believe him.
2230 Buffer * const b = newFile(filename, string(), true);
2236 docstring const disp_fn = makeDisplayPath(filename);
2237 message(bformat(_("Opening document %1$s..."), disp_fn));
2240 Buffer * buf = loadDocument(fullname);
2242 str2 = bformat(_("Document %1$s opened."), disp_fn);
2243 if (buf->lyxvc().inUse())
2244 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2245 " " + _("Version control detected.");
2247 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2252 // FIXME: clean that
2253 static bool import(GuiView * lv, FileName const & filename,
2254 string const & format, ErrorList & errorList)
2256 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2258 string loader_format;
2259 vector<string> loaders = theConverters().loaders();
2260 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2261 vector<string>::const_iterator it = loaders.begin();
2262 vector<string>::const_iterator en = loaders.end();
2263 for (; it != en; ++it) {
2264 if (!theConverters().isReachable(format, *it))
2267 string const tofile =
2268 support::changeExtension(filename.absFileName(),
2269 formats.extension(*it));
2270 if (!theConverters().convert(0, filename, FileName(tofile),
2271 filename, format, *it, errorList))
2273 loader_format = *it;
2276 if (loader_format.empty()) {
2277 frontend::Alert::error(_("Couldn't import file"),
2278 bformat(_("No information for importing the format %1$s."),
2279 formats.prettyName(format)));
2283 loader_format = format;
2285 if (loader_format == "lyx") {
2286 Buffer * buf = lv->loadDocument(lyxfile);
2290 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2294 bool as_paragraphs = loader_format == "textparagraph";
2295 string filename2 = (loader_format == format) ? filename.absFileName()
2296 : support::changeExtension(filename.absFileName(),
2297 formats.extension(loader_format));
2298 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2300 guiApp->setCurrentView(lv);
2301 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2308 void GuiView::importDocument(string const & argument)
2311 string filename = split(argument, format, ' ');
2313 LYXERR(Debug::INFO, format << " file: " << filename);
2315 // need user interaction
2316 if (filename.empty()) {
2317 string initpath = lyxrc.document_path;
2318 if (documentBufferView()) {
2319 string const trypath = documentBufferView()->buffer().filePath();
2320 // If directory is writeable, use this as default.
2321 if (FileName(trypath).isDirWritable())
2325 docstring const text = bformat(_("Select %1$s file to import"),
2326 formats.prettyName(format));
2328 FileDialog dlg(toqstr(text));
2329 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2330 dlg.setButton2(qt_("Examples|#E#e"),
2331 toqstr(addPath(package().system_support().absFileName(), "examples")));
2333 docstring filter = formats.prettyName(format);
2336 filter += from_utf8(formats.extensions(format));
2339 FileDialog::Result result =
2340 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2342 if (result.first == FileDialog::Later)
2345 filename = fromqstr(result.second);
2347 // check selected filename
2348 if (filename.empty())
2349 message(_("Canceled."));
2352 if (filename.empty())
2355 // get absolute path of file
2356 FileName const fullname(support::makeAbsPath(filename));
2358 // Can happen if the user entered a path into the dialog
2360 if (fullname.onlyFileName().empty()) {
2361 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2362 "Aborting import."),
2363 from_utf8(fullname.absFileName()));
2364 frontend::Alert::error(_("File name error"), msg);
2365 message(_("Canceled."));
2370 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2372 // Check if the document already is open
2373 Buffer * buf = theBufferList().getBuffer(lyxfile);
2376 if (!closeBuffer()) {
2377 message(_("Canceled."));
2382 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2384 // if the file exists already, and we didn't do
2385 // -i lyx thefile.lyx, warn
2386 if (lyxfile.exists() && fullname != lyxfile) {
2388 docstring text = bformat(_("The document %1$s already exists.\n\n"
2389 "Do you want to overwrite that document?"), displaypath);
2390 int const ret = Alert::prompt(_("Overwrite document?"),
2391 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2394 message(_("Canceled."));
2399 message(bformat(_("Importing %1$s..."), displaypath));
2400 ErrorList errorList;
2401 if (import(this, fullname, format, errorList))
2402 message(_("imported."));
2404 message(_("file not imported!"));
2406 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2410 void GuiView::newDocument(string const & filename, bool from_template)
2412 FileName initpath(lyxrc.document_path);
2413 if (documentBufferView()) {
2414 FileName const trypath(documentBufferView()->buffer().filePath());
2415 // If directory is writeable, use this as default.
2416 if (trypath.isDirWritable())
2420 string templatefile;
2421 if (from_template) {
2422 templatefile = selectTemplateFile().absFileName();
2423 if (templatefile.empty())
2428 if (filename.empty())
2429 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2431 b = newFile(filename, templatefile, true);
2436 // If no new document could be created, it is unsure
2437 // whether there is a valid BufferView.
2438 if (currentBufferView())
2439 // Ensure the cursor is correctly positioned on screen.
2440 currentBufferView()->showCursor();
2444 void GuiView::insertLyXFile(docstring const & fname)
2446 BufferView * bv = documentBufferView();
2451 FileName filename(to_utf8(fname));
2452 if (filename.empty()) {
2453 // Launch a file browser
2455 string initpath = lyxrc.document_path;
2456 string const trypath = bv->buffer().filePath();
2457 // If directory is writeable, use this as default.
2458 if (FileName(trypath).isDirWritable())
2462 FileDialog dlg(qt_("Select LyX document to insert"));
2463 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2464 dlg.setButton2(qt_("Examples|#E#e"),
2465 toqstr(addPath(package().system_support().absFileName(),
2468 FileDialog::Result result = dlg.open(toqstr(initpath),
2469 QStringList(qt_("LyX Documents (*.lyx)")));
2471 if (result.first == FileDialog::Later)
2475 filename.set(fromqstr(result.second));
2477 // check selected filename
2478 if (filename.empty()) {
2479 // emit message signal.
2480 message(_("Canceled."));
2485 bv->insertLyXFile(filename);
2486 bv->buffer().errors("Parse");
2490 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2492 FileName fname = b.fileName();
2493 FileName const oldname = fname;
2495 if (!newname.empty()) {
2497 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2499 // Switch to this Buffer.
2502 // No argument? Ask user through dialog.
2504 FileDialog dlg(qt_("Choose a filename to save document as"));
2505 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2506 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2508 if (!isLyXFileName(fname.absFileName()))
2509 fname.changeExtension(".lyx");
2511 FileDialog::Result result =
2512 dlg.save(toqstr(fname.onlyPath().absFileName()),
2513 QStringList(qt_("LyX Documents (*.lyx)")),
2514 toqstr(fname.onlyFileName()));
2516 if (result.first == FileDialog::Later)
2519 fname.set(fromqstr(result.second));
2524 if (!isLyXFileName(fname.absFileName()))
2525 fname.changeExtension(".lyx");
2528 // fname is now the new Buffer location.
2530 // if there is already a Buffer open with this name, we do not want
2531 // to have another one. (the second test makes sure we're not just
2532 // trying to overwrite ourselves, which is fine.)
2533 if (theBufferList().exists(fname) && fname != oldname
2534 && theBufferList().getBuffer(fname) != &b) {
2535 docstring const text =
2536 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2537 "Please close it before attempting to overwrite it.\n"
2538 "Do you want to choose a new filename?"),
2539 from_utf8(fname.absFileName()));
2540 int const ret = Alert::prompt(_("Chosen File Already Open"),
2541 text, 0, 1, _("&Rename"), _("&Cancel"));
2543 case 0: return renameBuffer(b, docstring(), kind);
2544 case 1: return false;
2549 bool const existsLocal = fname.exists();
2550 bool const existsInVC = LyXVC::fileInVC(fname);
2551 if (existsLocal || existsInVC) {
2552 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2553 if (kind != LV_WRITE_AS && existsInVC) {
2554 // renaming to a name that is already in VC
2556 docstring text = bformat(_("The document %1$s "
2557 "is already registered.\n\n"
2558 "Do you want to choose a new name?"),
2560 docstring const title = (kind == LV_VC_RENAME) ?
2561 _("Rename document?") : _("Copy document?");
2562 docstring const button = (kind == LV_VC_RENAME) ?
2563 _("&Rename") : _("&Copy");
2564 int const ret = Alert::prompt(title, text, 0, 1,
2565 button, _("&Cancel"));
2567 case 0: return renameBuffer(b, docstring(), kind);
2568 case 1: return false;
2573 docstring text = bformat(_("The document %1$s "
2574 "already exists.\n\n"
2575 "Do you want to overwrite that document?"),
2577 int const ret = Alert::prompt(_("Overwrite document?"),
2578 text, 0, 2, _("&Overwrite"),
2579 _("&Rename"), _("&Cancel"));
2582 case 1: return renameBuffer(b, docstring(), kind);
2583 case 2: return false;
2589 case LV_VC_RENAME: {
2590 string msg = b.lyxvc().rename(fname);
2593 message(from_utf8(msg));
2597 string msg = b.lyxvc().copy(fname);
2600 message(from_utf8(msg));
2606 // LyXVC created the file already in case of LV_VC_RENAME or
2607 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2608 // relative paths of included stuff right if we moved e.g. from
2609 // /a/b.lyx to /a/c/b.lyx.
2611 bool const saved = saveBuffer(b, fname);
2618 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2620 FileName fname = b.fileName();
2622 FileDialog dlg(qt_("Choose a filename to export the document as"));
2623 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2626 QString const anyformat = qt_("Guess from extension (*.*)");
2629 vector<Format const *> export_formats;
2630 for (Format const & f : formats)
2631 if (f.documentFormat())
2632 export_formats.push_back(&f);
2633 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2634 map<QString, string> fmap;
2637 for (Format const * f : export_formats) {
2638 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2639 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2641 from_ascii(f->extension())));
2642 types << loc_filter;
2643 fmap[loc_filter] = f->name();
2644 if (from_ascii(f->name()) == iformat) {
2645 filter = loc_filter;
2646 ext = f->extension();
2649 string ofname = fname.onlyFileName();
2651 ofname = support::changeExtension(ofname, ext);
2652 FileDialog::Result result =
2653 dlg.save(toqstr(fname.onlyPath().absFileName()),
2657 if (result.first != FileDialog::Chosen)
2661 fname.set(fromqstr(result.second));
2662 if (filter == anyformat)
2663 fmt_name = formats.getFormatFromExtension(fname.extension());
2665 fmt_name = fmap[filter];
2666 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2667 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2669 if (fmt_name.empty() || fname.empty())
2672 // fname is now the new Buffer location.
2673 if (FileName(fname).exists()) {
2674 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2675 docstring text = bformat(_("The document %1$s already "
2676 "exists.\n\nDo you want to "
2677 "overwrite that document?"),
2679 int const ret = Alert::prompt(_("Overwrite document?"),
2680 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2683 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2684 case 2: return false;
2688 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2691 return dr.dispatched();
2695 bool GuiView::saveBuffer(Buffer & b)
2697 return saveBuffer(b, FileName());
2701 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2703 if (workArea(b) && workArea(b)->inDialogMode())
2706 if (fn.empty() && b.isUnnamed())
2707 return renameBuffer(b, docstring());
2709 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2711 theSession().lastFiles().add(b.fileName());
2715 // Switch to this Buffer.
2718 // FIXME: we don't tell the user *WHY* the save failed !!
2719 docstring const file = makeDisplayPath(b.absFileName(), 30);
2720 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2721 "Do you want to rename the document and "
2722 "try again?"), file);
2723 int const ret = Alert::prompt(_("Rename and save?"),
2724 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2727 if (!renameBuffer(b, docstring()))
2736 return saveBuffer(b, fn);
2740 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2742 return closeWorkArea(wa, false);
2746 // We only want to close the buffer if it is not visible in other workareas
2747 // of the same view, nor in other views, and if this is not a child
2748 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2750 Buffer & buf = wa->bufferView().buffer();
2752 bool last_wa = d.countWorkAreasOf(buf) == 1
2753 && !inOtherView(buf) && !buf.parent();
2755 bool close_buffer = last_wa;
2758 if (lyxrc.close_buffer_with_last_view == "yes")
2760 else if (lyxrc.close_buffer_with_last_view == "no")
2761 close_buffer = false;
2764 if (buf.isUnnamed())
2765 file = from_utf8(buf.fileName().onlyFileName());
2767 file = buf.fileName().displayName(30);
2768 docstring const text = bformat(
2769 _("Last view on document %1$s is being closed.\n"
2770 "Would you like to close or hide the document?\n"
2772 "Hidden documents can be displayed back through\n"
2773 "the menu: View->Hidden->...\n"
2775 "To remove this question, set your preference in:\n"
2776 " Tools->Preferences->Look&Feel->UserInterface\n"
2778 int ret = Alert::prompt(_("Close or hide document?"),
2779 text, 0, 1, _("&Close"), _("&Hide"));
2780 close_buffer = (ret == 0);
2784 return closeWorkArea(wa, close_buffer);
2788 bool GuiView::closeBuffer()
2790 GuiWorkArea * wa = currentMainWorkArea();
2791 // coverity complained about this
2792 // it seems unnecessary, but perhaps is worth the check
2793 LASSERT(wa, return false);
2795 setCurrentWorkArea(wa);
2796 Buffer & buf = wa->bufferView().buffer();
2797 return closeWorkArea(wa, !buf.parent());
2801 void GuiView::writeSession() const {
2802 GuiWorkArea const * active_wa = currentMainWorkArea();
2803 for (int i = 0; i < d.splitter_->count(); ++i) {
2804 TabWorkArea * twa = d.tabWorkArea(i);
2805 for (int j = 0; j < twa->count(); ++j) {
2806 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2807 Buffer & buf = wa->bufferView().buffer();
2808 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2814 bool GuiView::closeBufferAll()
2816 // Close the workareas in all other views
2817 QList<int> const ids = guiApp->viewIds();
2818 for (int i = 0; i != ids.size(); ++i) {
2819 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2823 // Close our own workareas
2824 if (!closeWorkAreaAll())
2827 // Now close the hidden buffers. We prevent hidden buffers from being
2828 // dirty, so we can just close them.
2829 theBufferList().closeAll();
2834 bool GuiView::closeWorkAreaAll()
2836 setCurrentWorkArea(currentMainWorkArea());
2838 // We might be in a situation that there is still a tabWorkArea, but
2839 // there are no tabs anymore. This can happen when we get here after a
2840 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2841 // many TabWorkArea's have no documents anymore.
2844 // We have to call count() each time, because it can happen that
2845 // more than one splitter will disappear in one iteration (bug 5998).
2846 while (d.splitter_->count() > empty_twa) {
2847 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2849 if (twa->count() == 0)
2852 setCurrentWorkArea(twa->currentWorkArea());
2853 if (!closeTabWorkArea(twa))
2861 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2866 Buffer & buf = wa->bufferView().buffer();
2868 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2869 Alert::warning(_("Close document"),
2870 _("Document could not be closed because it is being processed by LyX."));
2875 return closeBuffer(buf);
2877 if (!inMultiTabs(wa))
2878 if (!saveBufferIfNeeded(buf, true))
2886 bool GuiView::closeBuffer(Buffer & buf)
2888 // If we are in a close_event all children will be closed in some time,
2889 // so no need to do it here. This will ensure that the children end up
2890 // in the session file in the correct order. If we close the master
2891 // buffer, we can close or release the child buffers here too.
2892 bool success = true;
2894 ListOfBuffers clist = buf.getChildren();
2895 ListOfBuffers::const_iterator it = clist.begin();
2896 ListOfBuffers::const_iterator const bend = clist.end();
2897 for (; it != bend; ++it) {
2898 Buffer * child_buf = *it;
2899 if (theBufferList().isOthersChild(&buf, child_buf)) {
2900 child_buf->setParent(0);
2904 // FIXME: should we look in other tabworkareas?
2905 // ANSWER: I don't think so. I've tested, and if the child is
2906 // open in some other window, it closes without a problem.
2907 GuiWorkArea * child_wa = workArea(*child_buf);
2909 success = closeWorkArea(child_wa, true);
2913 // In this case the child buffer is open but hidden.
2914 // It therefore should not (MUST NOT) be dirty!
2915 LATTEST(child_buf->isClean());
2916 theBufferList().release(child_buf);
2921 // goto bookmark to update bookmark pit.
2922 // FIXME: we should update only the bookmarks related to this buffer!
2923 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2924 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2925 guiApp->gotoBookmark(i+1, false, false);
2927 if (saveBufferIfNeeded(buf, false)) {
2928 buf.removeAutosaveFile();
2929 theBufferList().release(&buf);
2933 // open all children again to avoid a crash because of dangling
2934 // pointers (bug 6603)
2940 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2942 while (twa == d.currentTabWorkArea()) {
2943 twa->setCurrentIndex(twa->count() - 1);
2945 GuiWorkArea * wa = twa->currentWorkArea();
2946 Buffer & b = wa->bufferView().buffer();
2948 // We only want to close the buffer if the same buffer is not visible
2949 // in another view, and if this is not a child and if we are closing
2950 // a view (not a tabgroup).
2951 bool const close_buffer =
2952 !inOtherView(b) && !b.parent() && closing_;
2954 if (!closeWorkArea(wa, close_buffer))
2961 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2963 if (buf.isClean() || buf.paragraphs().empty())
2966 // Switch to this Buffer.
2971 if (buf.isUnnamed())
2972 file = from_utf8(buf.fileName().onlyFileName());
2974 file = buf.fileName().displayName(30);
2976 // Bring this window to top before asking questions.
2981 if (hiding && buf.isUnnamed()) {
2982 docstring const text = bformat(_("The document %1$s has not been "
2983 "saved yet.\n\nDo you want to save "
2984 "the document?"), file);
2985 ret = Alert::prompt(_("Save new document?"),
2986 text, 0, 1, _("&Save"), _("&Cancel"));
2990 docstring const text = bformat(_("The document %1$s has unsaved changes."
2991 "\n\nDo you want to save the document or discard the changes?"), file);
2992 ret = Alert::prompt(_("Save changed document?"),
2993 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2998 if (!saveBuffer(buf))
3002 // If we crash after this we could have no autosave file
3003 // but I guess this is really improbable (Jug).
3004 // Sometimes improbable things happen:
3005 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3006 // buf.removeAutosaveFile();
3008 // revert all changes
3019 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3021 Buffer & buf = wa->bufferView().buffer();
3023 for (int i = 0; i != d.splitter_->count(); ++i) {
3024 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3025 if (wa_ && wa_ != wa)
3028 return inOtherView(buf);
3032 bool GuiView::inOtherView(Buffer & buf)
3034 QList<int> const ids = guiApp->viewIds();
3036 for (int i = 0; i != ids.size(); ++i) {
3040 if (guiApp->view(ids[i]).workArea(buf))
3047 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3049 if (!documentBufferView())
3052 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3053 Buffer * const curbuf = &documentBufferView()->buffer();
3054 int nwa = twa->count();
3055 for (int i = 0; i < nwa; ++i) {
3056 if (&workArea(i)->bufferView().buffer() == curbuf) {
3058 if (np == NEXTBUFFER)
3059 next_index = (i == nwa - 1 ? 0 : i + 1);
3061 next_index = (i == 0 ? nwa - 1 : i - 1);
3063 twa->moveTab(i, next_index);
3065 setBuffer(&workArea(next_index)->bufferView().buffer());
3073 /// make sure the document is saved
3074 static bool ensureBufferClean(Buffer * buffer)
3076 LASSERT(buffer, return false);
3077 if (buffer->isClean() && !buffer->isUnnamed())
3080 docstring const file = buffer->fileName().displayName(30);
3083 if (!buffer->isUnnamed()) {
3084 text = bformat(_("The document %1$s has unsaved "
3085 "changes.\n\nDo you want to save "
3086 "the document?"), file);
3087 title = _("Save changed document?");
3090 text = bformat(_("The document %1$s has not been "
3091 "saved yet.\n\nDo you want to save "
3092 "the document?"), file);
3093 title = _("Save new document?");
3095 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3098 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3100 return buffer->isClean() && !buffer->isUnnamed();
3104 bool GuiView::reloadBuffer(Buffer & buf)
3106 Buffer::ReadStatus status = buf.reload();
3107 return status == Buffer::ReadSuccess;
3111 void GuiView::checkExternallyModifiedBuffers()
3113 BufferList::iterator bit = theBufferList().begin();
3114 BufferList::iterator const bend = theBufferList().end();
3115 for (; bit != bend; ++bit) {
3116 Buffer * buf = *bit;
3117 if (buf->fileName().exists()
3118 && buf->isExternallyModified(Buffer::checksum_method)) {
3119 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3120 " Reload now? Any local changes will be lost."),
3121 from_utf8(buf->absFileName()));
3122 int const ret = Alert::prompt(_("Reload externally changed document?"),
3123 text, 0, 1, _("&Reload"), _("&Cancel"));
3131 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3133 Buffer * buffer = documentBufferView()
3134 ? &(documentBufferView()->buffer()) : 0;
3136 switch (cmd.action()) {
3137 case LFUN_VC_REGISTER:
3138 if (!buffer || !ensureBufferClean(buffer))
3140 if (!buffer->lyxvc().inUse()) {
3141 if (buffer->lyxvc().registrer()) {
3142 reloadBuffer(*buffer);
3143 dr.clearMessageUpdate();
3148 case LFUN_VC_RENAME:
3149 case LFUN_VC_COPY: {
3150 if (!buffer || !ensureBufferClean(buffer))
3152 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3153 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3154 // Some changes are not yet committed.
3155 // We test here and not in getStatus(), since
3156 // this test is expensive.
3158 LyXVC::CommandResult ret =
3159 buffer->lyxvc().checkIn(log);
3161 if (ret == LyXVC::ErrorCommand ||
3162 ret == LyXVC::VCSuccess)
3163 reloadBuffer(*buffer);
3164 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3165 frontend::Alert::error(
3166 _("Revision control error."),
3167 _("Document could not be checked in."));
3171 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3172 LV_VC_RENAME : LV_VC_COPY;
3173 renameBuffer(*buffer, cmd.argument(), kind);
3178 case LFUN_VC_CHECK_IN:
3179 if (!buffer || !ensureBufferClean(buffer))
3181 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3183 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3185 // Only skip reloading if the checkin was cancelled or
3186 // an error occurred before the real checkin VCS command
3187 // was executed, since the VCS might have changed the
3188 // file even if it could not checkin successfully.
3189 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3190 reloadBuffer(*buffer);
3194 case LFUN_VC_CHECK_OUT:
3195 if (!buffer || !ensureBufferClean(buffer))
3197 if (buffer->lyxvc().inUse()) {
3198 dr.setMessage(buffer->lyxvc().checkOut());
3199 reloadBuffer(*buffer);
3203 case LFUN_VC_LOCKING_TOGGLE:
3204 LASSERT(buffer, return);
3205 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3207 if (buffer->lyxvc().inUse()) {
3208 string res = buffer->lyxvc().lockingToggle();
3210 frontend::Alert::error(_("Revision control error."),
3211 _("Error when setting the locking property."));
3214 reloadBuffer(*buffer);
3219 case LFUN_VC_REVERT:
3220 LASSERT(buffer, return);
3221 if (buffer->lyxvc().revert()) {
3222 reloadBuffer(*buffer);
3223 dr.clearMessageUpdate();
3227 case LFUN_VC_UNDO_LAST:
3228 LASSERT(buffer, return);
3229 buffer->lyxvc().undoLast();
3230 reloadBuffer(*buffer);
3231 dr.clearMessageUpdate();
3234 case LFUN_VC_REPO_UPDATE:
3235 LASSERT(buffer, return);
3236 if (ensureBufferClean(buffer)) {
3237 dr.setMessage(buffer->lyxvc().repoUpdate());
3238 checkExternallyModifiedBuffers();
3242 case LFUN_VC_COMMAND: {
3243 string flag = cmd.getArg(0);
3244 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3247 if (contains(flag, 'M')) {
3248 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3251 string path = cmd.getArg(1);
3252 if (contains(path, "$$p") && buffer)
3253 path = subst(path, "$$p", buffer->filePath());
3254 LYXERR(Debug::LYXVC, "Directory: " << path);
3256 if (!pp.isReadableDirectory()) {
3257 lyxerr << _("Directory is not accessible.") << endl;
3260 support::PathChanger p(pp);
3262 string command = cmd.getArg(2);
3263 if (command.empty())
3266 command = subst(command, "$$i", buffer->absFileName());
3267 command = subst(command, "$$p", buffer->filePath());
3269 command = subst(command, "$$m", to_utf8(message));
3270 LYXERR(Debug::LYXVC, "Command: " << command);
3272 one.startscript(Systemcall::Wait, command);
3276 if (contains(flag, 'I'))
3277 buffer->markDirty();
3278 if (contains(flag, 'R'))
3279 reloadBuffer(*buffer);
3284 case LFUN_VC_COMPARE: {
3285 if (cmd.argument().empty()) {
3286 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3290 string rev1 = cmd.getArg(0);
3294 // it seems safe to assume we have a buffer
3295 // coverity[FORWARD_NULL]
3296 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3299 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3300 f2 = buffer->absFileName();
3302 string rev2 = cmd.getArg(1);
3306 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3310 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3311 f1 << "\n" << f2 << "\n" );
3312 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3313 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3323 void GuiView::openChildDocument(string const & fname)
3325 LASSERT(documentBufferView(), return);
3326 Buffer & buffer = documentBufferView()->buffer();
3327 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3328 documentBufferView()->saveBookmark(false);
3330 if (theBufferList().exists(filename)) {
3331 child = theBufferList().getBuffer(filename);
3334 message(bformat(_("Opening child document %1$s..."),
3335 makeDisplayPath(filename.absFileName())));
3336 child = loadDocument(filename, false);
3338 // Set the parent name of the child document.
3339 // This makes insertion of citations and references in the child work,
3340 // when the target is in the parent or another child document.
3342 child->setParent(&buffer);
3346 bool GuiView::goToFileRow(string const & argument)
3350 size_t i = argument.find_last_of(' ');
3351 if (i != string::npos) {
3352 file_name = os::internal_path(trim(argument.substr(0, i)));
3353 istringstream is(argument.substr(i + 1));
3358 if (i == string::npos) {
3359 LYXERR0("Wrong argument: " << argument);
3363 string const abstmp = package().temp_dir().absFileName();
3364 string const realtmp = package().temp_dir().realPath();
3365 // We have to use os::path_prefix_is() here, instead of
3366 // simply prefixIs(), because the file name comes from
3367 // an external application and may need case adjustment.
3368 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3369 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3370 // Needed by inverse dvi search. If it is a file
3371 // in tmpdir, call the apropriated function.
3372 // If tmpdir is a symlink, we may have the real
3373 // path passed back, so we correct for that.
3374 if (!prefixIs(file_name, abstmp))
3375 file_name = subst(file_name, realtmp, abstmp);
3376 buf = theBufferList().getBufferFromTmp(file_name);
3378 // Must replace extension of the file to be .lyx
3379 // and get full path
3380 FileName const s = fileSearch(string(),
3381 support::changeExtension(file_name, ".lyx"), "lyx");
3382 // Either change buffer or load the file
3383 if (theBufferList().exists(s))
3384 buf = theBufferList().getBuffer(s);
3385 else if (s.exists()) {
3386 buf = loadDocument(s);
3391 _("File does not exist: %1$s"),
3392 makeDisplayPath(file_name)));
3398 _("No buffer for file: %1$s."),
3399 makeDisplayPath(file_name))
3404 bool success = documentBufferView()->setCursorFromRow(row);
3406 LYXERR(Debug::LATEX,
3407 "setCursorFromRow: invalid position for row " << row);
3408 frontend::Alert::error(_("Inverse Search Failed"),
3409 _("Invalid position requested by inverse search.\n"
3410 "You may need to update the viewed document."));
3417 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3419 Buffer::ExportStatus const status = func(format);
3421 // the cloning operation will have produced a clone of the entire set of
3422 // documents, starting from the master. so we must delete those.
3423 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3425 busyBuffers.remove(orig);
3430 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3432 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3433 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3437 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3439 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3440 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3444 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3446 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3447 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3451 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3452 string const & argument,
3453 Buffer const * used_buffer,
3454 docstring const & msg,
3455 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3456 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3457 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3462 string format = argument;
3464 format = used_buffer->params().getDefaultOutputFormat();
3465 processing_format = format;
3467 progress_->clearMessages();
3470 #if EXPORT_in_THREAD
3471 GuiViewPrivate::busyBuffers.insert(used_buffer);
3472 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3473 if (!cloned_buffer) {
3474 Alert::error(_("Export Error"),
3475 _("Error cloning the Buffer."));
3478 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3483 setPreviewFuture(f);
3484 last_export_format = used_buffer->params().bufferFormat();
3487 // We are asynchronous, so we don't know here anything about the success
3490 Buffer::ExportStatus status;
3492 status = (used_buffer->*syncFunc)(format, true);
3493 } else if (previewFunc) {
3494 status = (used_buffer->*previewFunc)(format);
3497 handleExportStatus(gv_, status, format);
3499 return (status == Buffer::ExportSuccess
3500 || status == Buffer::PreviewSuccess);
3504 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3506 BufferView * bv = currentBufferView();
3507 LASSERT(bv, return);
3509 // Let the current BufferView dispatch its own actions.
3510 bv->dispatch(cmd, dr);
3511 if (dr.dispatched())
3514 // Try with the document BufferView dispatch if any.
3515 BufferView * doc_bv = documentBufferView();
3516 if (doc_bv && doc_bv != bv) {
3517 doc_bv->dispatch(cmd, dr);
3518 if (dr.dispatched())
3522 // Then let the current Cursor dispatch its own actions.
3523 bv->cursor().dispatch(cmd);
3525 // update completion. We do it here and not in
3526 // processKeySym to avoid another redraw just for a
3527 // changed inline completion
3528 if (cmd.origin() == FuncRequest::KEYBOARD) {
3529 if (cmd.action() == LFUN_SELF_INSERT
3530 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3531 updateCompletion(bv->cursor(), true, true);
3532 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3533 updateCompletion(bv->cursor(), false, true);
3535 updateCompletion(bv->cursor(), false, false);
3538 dr = bv->cursor().result();
3542 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3544 BufferView * bv = currentBufferView();
3545 // By default we won't need any update.
3546 dr.screenUpdate(Update::None);
3547 // assume cmd will be dispatched
3548 dr.dispatched(true);
3550 Buffer * doc_buffer = documentBufferView()
3551 ? &(documentBufferView()->buffer()) : 0;
3553 if (cmd.origin() == FuncRequest::TOC) {
3554 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3555 // FIXME: do we need to pass a DispatchResult object here?
3556 toc->doDispatch(bv->cursor(), cmd);
3560 string const argument = to_utf8(cmd.argument());
3562 switch(cmd.action()) {
3563 case LFUN_BUFFER_CHILD_OPEN:
3564 openChildDocument(to_utf8(cmd.argument()));
3567 case LFUN_BUFFER_IMPORT:
3568 importDocument(to_utf8(cmd.argument()));
3571 case LFUN_BUFFER_EXPORT: {
3574 // GCC only sees strfwd.h when building merged
3575 if (::lyx::operator==(cmd.argument(), "custom")) {
3576 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3580 string const dest = cmd.getArg(1);
3581 FileName target_dir;
3582 if (!dest.empty() && FileName::isAbsolute(dest))
3583 target_dir = FileName(support::onlyPath(dest));
3585 target_dir = doc_buffer->fileName().onlyPath();
3587 if ((dest.empty() && doc_buffer->isUnnamed())
3588 || !target_dir.isDirWritable()) {
3589 exportBufferAs(*doc_buffer, cmd.argument());
3592 /* TODO/Review: Is it a problem to also export the children?
3593 See the update_unincluded flag */
3594 d.asyncBufferProcessing(argument,
3597 &GuiViewPrivate::exportAndDestroy,
3600 // TODO Inform user about success
3604 case LFUN_BUFFER_EXPORT_AS: {
3605 LASSERT(doc_buffer, break);
3606 docstring f = cmd.argument();
3608 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3609 exportBufferAs(*doc_buffer, f);
3613 case LFUN_BUFFER_UPDATE: {
3614 d.asyncBufferProcessing(argument,
3617 &GuiViewPrivate::compileAndDestroy,
3622 case LFUN_BUFFER_VIEW: {
3623 d.asyncBufferProcessing(argument,
3625 _("Previewing ..."),
3626 &GuiViewPrivate::previewAndDestroy,
3631 case LFUN_MASTER_BUFFER_UPDATE: {
3632 d.asyncBufferProcessing(argument,
3633 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3635 &GuiViewPrivate::compileAndDestroy,
3640 case LFUN_MASTER_BUFFER_VIEW: {
3641 d.asyncBufferProcessing(argument,
3642 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3644 &GuiViewPrivate::previewAndDestroy,
3645 0, &Buffer::preview);
3648 case LFUN_BUFFER_SWITCH: {
3649 string const file_name = to_utf8(cmd.argument());
3650 if (!FileName::isAbsolute(file_name)) {
3652 dr.setMessage(_("Absolute filename expected."));
3656 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3659 dr.setMessage(_("Document not loaded"));
3663 // Do we open or switch to the buffer in this view ?
3664 if (workArea(*buffer)
3665 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3670 // Look for the buffer in other views
3671 QList<int> const ids = guiApp->viewIds();
3673 for (; i != ids.size(); ++i) {
3674 GuiView & gv = guiApp->view(ids[i]);
3675 if (gv.workArea(*buffer)) {
3677 gv.activateWindow();
3679 gv.setBuffer(buffer);
3684 // If necessary, open a new window as a last resort
3685 if (i == ids.size()) {
3686 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3692 case LFUN_BUFFER_NEXT:
3693 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3696 case LFUN_BUFFER_MOVE_NEXT:
3697 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3700 case LFUN_BUFFER_PREVIOUS:
3701 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3704 case LFUN_BUFFER_MOVE_PREVIOUS:
3705 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3708 case LFUN_COMMAND_EXECUTE: {
3709 command_execute_ = true;
3710 minibuffer_focus_ = true;
3713 case LFUN_DROP_LAYOUTS_CHOICE:
3714 d.layout_->showPopup();
3717 case LFUN_MENU_OPEN:
3718 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3719 menu->exec(QCursor::pos());
3722 case LFUN_FILE_INSERT:
3723 insertLyXFile(cmd.argument());
3726 case LFUN_FILE_INSERT_PLAINTEXT:
3727 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3728 string const fname = to_utf8(cmd.argument());
3729 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3730 dr.setMessage(_("Absolute filename expected."));
3734 FileName filename(fname);
3735 if (fname.empty()) {
3736 FileDialog dlg(qt_("Select file to insert"));
3738 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3739 QStringList(qt_("All Files (*)")));
3741 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3742 dr.setMessage(_("Canceled."));
3746 filename.set(fromqstr(result.second));
3750 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3751 bv->dispatch(new_cmd, dr);
3756 case LFUN_BUFFER_RELOAD: {
3757 LASSERT(doc_buffer, break);
3760 if (!doc_buffer->isClean()) {
3761 docstring const file =
3762 makeDisplayPath(doc_buffer->absFileName(), 20);
3763 docstring text = bformat(_("Any changes will be lost. "
3764 "Are you sure you want to revert to the saved version "
3765 "of the document %1$s?"), file);
3766 ret = Alert::prompt(_("Revert to saved document?"),
3767 text, 1, 1, _("&Revert"), _("&Cancel"));
3771 doc_buffer->markClean();
3772 reloadBuffer(*doc_buffer);
3773 dr.forceBufferUpdate();
3778 case LFUN_BUFFER_WRITE:
3779 LASSERT(doc_buffer, break);
3780 saveBuffer(*doc_buffer);
3783 case LFUN_BUFFER_WRITE_AS:
3784 LASSERT(doc_buffer, break);
3785 renameBuffer(*doc_buffer, cmd.argument());
3788 case LFUN_BUFFER_WRITE_ALL: {
3789 Buffer * first = theBufferList().first();
3792 message(_("Saving all documents..."));
3793 // We cannot use a for loop as the buffer list cycles.
3796 if (!b->isClean()) {
3798 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3800 b = theBufferList().next(b);
3801 } while (b != first);
3802 dr.setMessage(_("All documents saved."));
3806 case LFUN_BUFFER_CLOSE:
3810 case LFUN_BUFFER_CLOSE_ALL:
3814 case LFUN_TOOLBAR_TOGGLE: {
3815 string const name = cmd.getArg(0);
3816 if (GuiToolbar * t = toolbar(name))
3821 case LFUN_DIALOG_UPDATE: {
3822 string const name = to_utf8(cmd.argument());
3823 if (name == "prefs" || name == "document")
3824 updateDialog(name, string());
3825 else if (name == "paragraph")
3826 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3827 else if (currentBufferView()) {
3828 Inset * inset = currentBufferView()->editedInset(name);
3829 // Can only update a dialog connected to an existing inset
3831 // FIXME: get rid of this indirection; GuiView ask the inset
3832 // if he is kind enough to update itself...
3833 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3834 //FIXME: pass DispatchResult here?
3835 inset->dispatch(currentBufferView()->cursor(), fr);
3841 case LFUN_DIALOG_TOGGLE: {
3842 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3843 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3844 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3848 case LFUN_DIALOG_DISCONNECT_INSET:
3849 disconnectDialog(to_utf8(cmd.argument()));
3852 case LFUN_DIALOG_HIDE: {
3853 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3857 case LFUN_DIALOG_SHOW: {
3858 string const name = cmd.getArg(0);
3859 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3861 if (name == "character") {
3862 data = freefont2string();
3864 showDialog("character", data);
3865 } else if (name == "latexlog") {
3866 Buffer::LogType type;
3867 string const logfile = doc_buffer->logName(&type);
3869 case Buffer::latexlog:
3872 case Buffer::buildlog:
3876 data += Lexer::quoteString(logfile);
3877 showDialog("log", data);
3878 } else if (name == "vclog") {
3879 string const data = "vc " +
3880 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3881 showDialog("log", data);
3882 } else if (name == "symbols") {
3883 data = bv->cursor().getEncoding()->name();
3885 showDialog("symbols", data);
3887 } else if (name == "prefs" && isFullScreen()) {
3888 lfunUiToggle("fullscreen");
3889 showDialog("prefs", data);
3891 showDialog(name, data);
3896 dr.setMessage(cmd.argument());
3899 case LFUN_UI_TOGGLE: {
3900 string arg = cmd.getArg(0);
3901 if (!lfunUiToggle(arg)) {
3902 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3903 dr.setMessage(bformat(msg, from_utf8(arg)));
3905 // Make sure the keyboard focus stays in the work area.
3910 case LFUN_VIEW_SPLIT: {
3911 LASSERT(doc_buffer, break);
3912 string const orientation = cmd.getArg(0);
3913 d.splitter_->setOrientation(orientation == "vertical"
3914 ? Qt::Vertical : Qt::Horizontal);
3915 TabWorkArea * twa = addTabWorkArea();
3916 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3917 setCurrentWorkArea(wa);
3920 case LFUN_TAB_GROUP_CLOSE:
3921 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3922 closeTabWorkArea(twa);
3923 d.current_work_area_ = 0;
3924 twa = d.currentTabWorkArea();
3925 // Switch to the next GuiWorkArea in the found TabWorkArea.
3927 // Make sure the work area is up to date.
3928 setCurrentWorkArea(twa->currentWorkArea());
3930 setCurrentWorkArea(0);
3935 case LFUN_VIEW_CLOSE:
3936 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3937 closeWorkArea(twa->currentWorkArea());
3938 d.current_work_area_ = 0;
3939 twa = d.currentTabWorkArea();
3940 // Switch to the next GuiWorkArea in the found TabWorkArea.
3942 // Make sure the work area is up to date.
3943 setCurrentWorkArea(twa->currentWorkArea());
3945 setCurrentWorkArea(0);
3950 case LFUN_COMPLETION_INLINE:
3951 if (d.current_work_area_)
3952 d.current_work_area_->completer().showInline();
3955 case LFUN_COMPLETION_POPUP:
3956 if (d.current_work_area_)
3957 d.current_work_area_->completer().showPopup();
3962 if (d.current_work_area_)
3963 d.current_work_area_->completer().tab();
3966 case LFUN_COMPLETION_CANCEL:
3967 if (d.current_work_area_) {
3968 if (d.current_work_area_->completer().popupVisible())
3969 d.current_work_area_->completer().hidePopup();
3971 d.current_work_area_->completer().hideInline();
3975 case LFUN_COMPLETION_ACCEPT:
3976 if (d.current_work_area_)
3977 d.current_work_area_->completer().activate();
3980 case LFUN_BUFFER_ZOOM_IN:
3981 case LFUN_BUFFER_ZOOM_OUT: {
3982 // use a signed temp to avoid overflow
3983 int zoom = lyxrc.zoom;
3984 if (cmd.argument().empty()) {
3985 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3990 zoom += convert<int>(cmd.argument());
3992 if (zoom < static_cast<int>(zoom_min_))
3996 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3998 // The global QPixmapCache is used in GuiPainter to cache text
3999 // painting so we must reset it.
4000 QPixmapCache::clear();
4001 guiApp->fontLoader().update();
4002 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4006 case LFUN_VC_REGISTER:
4007 case LFUN_VC_RENAME:
4009 case LFUN_VC_CHECK_IN:
4010 case LFUN_VC_CHECK_OUT:
4011 case LFUN_VC_REPO_UPDATE:
4012 case LFUN_VC_LOCKING_TOGGLE:
4013 case LFUN_VC_REVERT:
4014 case LFUN_VC_UNDO_LAST:
4015 case LFUN_VC_COMMAND:
4016 case LFUN_VC_COMPARE:
4017 dispatchVC(cmd, dr);
4020 case LFUN_SERVER_GOTO_FILE_ROW:
4021 if(goToFileRow(to_utf8(cmd.argument())))
4022 dr.screenUpdate(Update::Force | Update::FitCursor);
4025 case LFUN_LYX_ACTIVATE:
4029 case LFUN_FORWARD_SEARCH: {
4030 // it seems safe to assume we have a document buffer, since
4031 // getStatus wants one.
4032 // coverity[FORWARD_NULL]
4033 Buffer const * doc_master = doc_buffer->masterBuffer();
4034 FileName const path(doc_master->temppath());
4035 string const texname = doc_master->isChild(doc_buffer)
4036 ? DocFileName(changeExtension(
4037 doc_buffer->absFileName(),
4038 "tex")).mangledFileName()
4039 : doc_buffer->latexName();
4040 string const fulltexname =
4041 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4042 string const mastername =
4043 removeExtension(doc_master->latexName());
4044 FileName const dviname(addName(path.absFileName(),
4045 addExtension(mastername, "dvi")));
4046 FileName const pdfname(addName(path.absFileName(),
4047 addExtension(mastername, "pdf")));
4048 bool const have_dvi = dviname.exists();
4049 bool const have_pdf = pdfname.exists();
4050 if (!have_dvi && !have_pdf) {
4051 dr.setMessage(_("Please, preview the document first."));
4054 string outname = dviname.onlyFileName();
4055 string command = lyxrc.forward_search_dvi;
4056 if (!have_dvi || (have_pdf &&
4057 pdfname.lastModified() > dviname.lastModified())) {
4058 outname = pdfname.onlyFileName();
4059 command = lyxrc.forward_search_pdf;
4062 DocIterator cur = bv->cursor();
4063 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4064 LYXERR(Debug::ACTION, "Forward search: row:" << row
4066 if (row == -1 || command.empty()) {
4067 dr.setMessage(_("Couldn't proceed."));
4070 string texrow = convert<string>(row);
4072 command = subst(command, "$$n", texrow);
4073 command = subst(command, "$$f", fulltexname);
4074 command = subst(command, "$$t", texname);
4075 command = subst(command, "$$o", outname);
4077 PathChanger p(path);
4079 one.startscript(Systemcall::DontWait, command);
4083 case LFUN_SPELLING_CONTINUOUSLY:
4084 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4085 dr.screenUpdate(Update::Force | Update::FitCursor);
4089 // The LFUN must be for one of BufferView, Buffer or Cursor;
4091 dispatchToBufferView(cmd, dr);
4095 // Part of automatic menu appearance feature.
4096 if (isFullScreen()) {
4097 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4101 // Need to update bv because many LFUNs here might have destroyed it
4102 bv = currentBufferView();
4104 // Clear non-empty selections
4105 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4107 Cursor & cur = bv->cursor();
4108 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4109 cur.clearSelection();
4115 bool GuiView::lfunUiToggle(string const & ui_component)
4117 if (ui_component == "scrollbar") {
4118 // hide() is of no help
4119 if (d.current_work_area_->verticalScrollBarPolicy() ==
4120 Qt::ScrollBarAlwaysOff)
4122 d.current_work_area_->setVerticalScrollBarPolicy(
4123 Qt::ScrollBarAsNeeded);
4125 d.current_work_area_->setVerticalScrollBarPolicy(
4126 Qt::ScrollBarAlwaysOff);
4127 } else if (ui_component == "statusbar") {
4128 statusBar()->setVisible(!statusBar()->isVisible());
4129 } else if (ui_component == "menubar") {
4130 menuBar()->setVisible(!menuBar()->isVisible());
4132 if (ui_component == "frame") {
4134 getContentsMargins(&l, &t, &r, &b);
4135 //are the frames in default state?
4136 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4138 setContentsMargins(-2, -2, -2, -2);
4140 setContentsMargins(0, 0, 0, 0);
4143 if (ui_component == "fullscreen") {
4151 void GuiView::toggleFullScreen()
4153 if (isFullScreen()) {
4154 for (int i = 0; i != d.splitter_->count(); ++i)
4155 d.tabWorkArea(i)->setFullScreen(false);
4156 setContentsMargins(0, 0, 0, 0);
4157 setWindowState(windowState() ^ Qt::WindowFullScreen);
4160 statusBar()->show();
4163 hideDialogs("prefs", 0);
4164 for (int i = 0; i != d.splitter_->count(); ++i)
4165 d.tabWorkArea(i)->setFullScreen(true);
4166 setContentsMargins(-2, -2, -2, -2);
4168 setWindowState(windowState() ^ Qt::WindowFullScreen);
4169 if (lyxrc.full_screen_statusbar)
4170 statusBar()->hide();
4171 if (lyxrc.full_screen_menubar)
4173 if (lyxrc.full_screen_toolbars) {
4174 ToolbarMap::iterator end = d.toolbars_.end();
4175 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4180 // give dialogs like the TOC a chance to adapt
4185 Buffer const * GuiView::updateInset(Inset const * inset)
4190 Buffer const * inset_buffer = &(inset->buffer());
4192 for (int i = 0; i != d.splitter_->count(); ++i) {
4193 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4196 Buffer const * buffer = &(wa->bufferView().buffer());
4197 if (inset_buffer == buffer)
4198 wa->scheduleRedraw();
4200 return inset_buffer;
4204 void GuiView::restartCursor()
4206 /* When we move around, or type, it's nice to be able to see
4207 * the cursor immediately after the keypress.
4209 if (d.current_work_area_)
4210 d.current_work_area_->startBlinkingCursor();
4212 // Take this occasion to update the other GUI elements.
4218 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4220 if (d.current_work_area_)
4221 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4226 // This list should be kept in sync with the list of insets in
4227 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4228 // dialog should have the same name as the inset.
4229 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4230 // docs in LyXAction.cpp.
4232 char const * const dialognames[] = {
4234 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4235 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4236 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4237 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4238 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4239 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4240 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4241 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4243 char const * const * const end_dialognames =
4244 dialognames + (sizeof(dialognames) / sizeof(char *));
4248 cmpCStr(char const * name) : name_(name) {}
4249 bool operator()(char const * other) {
4250 return strcmp(other, name_) == 0;
4257 bool isValidName(string const & name)
4259 return find_if(dialognames, end_dialognames,
4260 cmpCStr(name.c_str())) != end_dialognames;
4266 void GuiView::resetDialogs()
4268 // Make sure that no LFUN uses any GuiView.
4269 guiApp->setCurrentView(0);
4273 constructToolbars();
4274 guiApp->menus().fillMenuBar(menuBar(), this, false);
4275 d.layout_->updateContents(true);
4276 // Now update controls with current buffer.
4277 guiApp->setCurrentView(this);
4283 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4285 if (!isValidName(name))
4288 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4290 if (it != d.dialogs_.end()) {
4292 it->second->hideView();
4293 return it->second.get();
4296 Dialog * dialog = build(name);
4297 d.dialogs_[name].reset(dialog);
4298 if (lyxrc.allow_geometry_session)
4299 dialog->restoreSession();
4306 void GuiView::showDialog(string const & name, string const & data,
4309 triggerShowDialog(toqstr(name), toqstr(data), inset);
4313 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4319 const string name = fromqstr(qname);
4320 const string data = fromqstr(qdata);
4324 Dialog * dialog = findOrBuild(name, false);
4326 bool const visible = dialog->isVisibleView();
4327 dialog->showData(data);
4328 if (inset && currentBufferView())
4329 currentBufferView()->editInset(name, inset);
4330 // We only set the focus to the new dialog if it was not yet
4331 // visible in order not to change the existing previous behaviour
4333 // activateWindow is needed for floating dockviews
4334 dialog->asQWidget()->raise();
4335 dialog->asQWidget()->activateWindow();
4336 dialog->asQWidget()->setFocus();
4340 catch (ExceptionMessage const & ex) {
4348 bool GuiView::isDialogVisible(string const & name) const
4350 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4351 if (it == d.dialogs_.end())
4353 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4357 void GuiView::hideDialog(string const & name, Inset * inset)
4359 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4360 if (it == d.dialogs_.end())
4364 if (!currentBufferView())
4366 if (inset != currentBufferView()->editedInset(name))
4370 Dialog * const dialog = it->second.get();
4371 if (dialog->isVisibleView())
4373 if (currentBufferView())
4374 currentBufferView()->editInset(name, 0);
4378 void GuiView::disconnectDialog(string const & name)
4380 if (!isValidName(name))
4382 if (currentBufferView())
4383 currentBufferView()->editInset(name, 0);
4387 void GuiView::hideAll() const
4389 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4390 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4392 for(; it != end; ++it)
4393 it->second->hideView();
4397 void GuiView::updateDialogs()
4399 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4400 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4402 for(; it != end; ++it) {
4403 Dialog * dialog = it->second.get();
4405 if (dialog->needBufferOpen() && !documentBufferView())
4406 hideDialog(fromqstr(dialog->name()), 0);
4407 else if (dialog->isVisibleView())
4408 dialog->checkStatus();
4415 Dialog * createDialog(GuiView & lv, string const & name);
4417 // will be replaced by a proper factory...
4418 Dialog * createGuiAbout(GuiView & lv);
4419 Dialog * createGuiBibtex(GuiView & lv);
4420 Dialog * createGuiChanges(GuiView & lv);
4421 Dialog * createGuiCharacter(GuiView & lv);
4422 Dialog * createGuiCitation(GuiView & lv);
4423 Dialog * createGuiCompare(GuiView & lv);
4424 Dialog * createGuiCompareHistory(GuiView & lv);
4425 Dialog * createGuiDelimiter(GuiView & lv);
4426 Dialog * createGuiDocument(GuiView & lv);
4427 Dialog * createGuiErrorList(GuiView & lv);
4428 Dialog * createGuiExternal(GuiView & lv);
4429 Dialog * createGuiGraphics(GuiView & lv);
4430 Dialog * createGuiInclude(GuiView & lv);
4431 Dialog * createGuiIndex(GuiView & lv);
4432 Dialog * createGuiListings(GuiView & lv);
4433 Dialog * createGuiLog(GuiView & lv);
4434 Dialog * createGuiMathMatrix(GuiView & lv);
4435 Dialog * createGuiNote(GuiView & lv);
4436 Dialog * createGuiParagraph(GuiView & lv);
4437 Dialog * createGuiPhantom(GuiView & lv);
4438 Dialog * createGuiPreferences(GuiView & lv);
4439 Dialog * createGuiPrint(GuiView & lv);
4440 Dialog * createGuiPrintindex(GuiView & lv);
4441 Dialog * createGuiRef(GuiView & lv);
4442 Dialog * createGuiSearch(GuiView & lv);
4443 Dialog * createGuiSearchAdv(GuiView & lv);
4444 Dialog * createGuiSendTo(GuiView & lv);
4445 Dialog * createGuiShowFile(GuiView & lv);
4446 Dialog * createGuiSpellchecker(GuiView & lv);
4447 Dialog * createGuiSymbols(GuiView & lv);
4448 Dialog * createGuiTabularCreate(GuiView & lv);
4449 Dialog * createGuiTexInfo(GuiView & lv);
4450 Dialog * createGuiToc(GuiView & lv);
4451 Dialog * createGuiThesaurus(GuiView & lv);
4452 Dialog * createGuiViewSource(GuiView & lv);
4453 Dialog * createGuiWrap(GuiView & lv);
4454 Dialog * createGuiProgressView(GuiView & lv);
4458 Dialog * GuiView::build(string const & name)
4460 LASSERT(isValidName(name), return 0);
4462 Dialog * dialog = createDialog(*this, name);
4466 if (name == "aboutlyx")
4467 return createGuiAbout(*this);
4468 if (name == "bibtex")
4469 return createGuiBibtex(*this);
4470 if (name == "changes")
4471 return createGuiChanges(*this);
4472 if (name == "character")
4473 return createGuiCharacter(*this);
4474 if (name == "citation")
4475 return createGuiCitation(*this);
4476 if (name == "compare")
4477 return createGuiCompare(*this);
4478 if (name == "comparehistory")
4479 return createGuiCompareHistory(*this);
4480 if (name == "document")
4481 return createGuiDocument(*this);
4482 if (name == "errorlist")
4483 return createGuiErrorList(*this);
4484 if (name == "external")
4485 return createGuiExternal(*this);
4487 return createGuiShowFile(*this);
4488 if (name == "findreplace")
4489 return createGuiSearch(*this);
4490 if (name == "findreplaceadv")
4491 return createGuiSearchAdv(*this);
4492 if (name == "graphics")
4493 return createGuiGraphics(*this);
4494 if (name == "include")
4495 return createGuiInclude(*this);
4496 if (name == "index")
4497 return createGuiIndex(*this);
4498 if (name == "index_print")
4499 return createGuiPrintindex(*this);
4500 if (name == "listings")
4501 return createGuiListings(*this);
4503 return createGuiLog(*this);
4504 if (name == "mathdelimiter")
4505 return createGuiDelimiter(*this);
4506 if (name == "mathmatrix")
4507 return createGuiMathMatrix(*this);
4509 return createGuiNote(*this);
4510 if (name == "paragraph")
4511 return createGuiParagraph(*this);
4512 if (name == "phantom")
4513 return createGuiPhantom(*this);
4514 if (name == "prefs")
4515 return createGuiPreferences(*this);
4517 return createGuiRef(*this);
4518 if (name == "sendto")
4519 return createGuiSendTo(*this);
4520 if (name == "spellchecker")
4521 return createGuiSpellchecker(*this);
4522 if (name == "symbols")
4523 return createGuiSymbols(*this);
4524 if (name == "tabularcreate")
4525 return createGuiTabularCreate(*this);
4526 if (name == "texinfo")
4527 return createGuiTexInfo(*this);
4528 if (name == "thesaurus")
4529 return createGuiThesaurus(*this);
4531 return createGuiToc(*this);
4532 if (name == "view-source")
4533 return createGuiViewSource(*this);
4535 return createGuiWrap(*this);
4536 if (name == "progress")
4537 return createGuiProgressView(*this);
4543 } // namespace frontend
4546 #include "moc_GuiView.cpp"