3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
386 QSize iconSize(docstring const & icon_size)
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
395 else if (icon_size == "huge")
397 else if (icon_size == "giant")
398 size = giantIconSize;
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
402 if (size < smallIconSize)
403 size = smallIconSize;
405 return QSize(size, size);
408 QSize iconSize(QString const & icon_size)
410 return iconSize(qstring_to_ucs4(icon_size));
413 string & iconSize(QSize const & qsize)
415 LATTEST(qsize.width() == qsize.height());
417 static string icon_size;
419 unsigned int size = qsize.width();
421 if (size < smallIconSize)
422 size = smallIconSize;
424 if (size == smallIconSize)
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
430 else if (size == hugeIconSize)
432 else if (size == giantIconSize)
435 icon_size = convert<string>(size);
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
452 * \warning Don't Delete! The layout box is actually owned by
453 * whichever toolbar contains it. All the GuiView class needs is a
454 * means of accessing it.
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
462 map<string, DialogPtr> dialogs_;
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
470 QTimer statusbar_timer_;
471 /// auto-saving of buffers
472 Timeout autosave_timeout_;
473 /// flag against a race condition due to multiclicks, see bug #1119
477 TocModels toc_models_;
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
483 string last_export_format;
484 string processing_format;
486 static QSet<Buffer const *> busyBuffers;
487 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
495 // TODO syncFunc/previewFunc: use bind
496 bool asyncBufferProcessing(string const & argument,
497 Buffer const * used_buffer,
498 docstring const & msg,
499 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
503 QVector<GuiWorkArea*> guiWorkAreas();
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
509 GuiView::GuiView(int id)
510 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511 command_execute_(false), minibuffer_focus_(false), devel_mode_(false)
513 connect(this, SIGNAL(bufferViewChanged()),
514 this, SLOT(onBufferViewChanged()));
516 // GuiToolbars *must* be initialised before the menu bar.
517 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 // The connection is closed when this is destroyed.
533 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535 d.autosave_timeout_.start();
537 connect(&d.statusbar_timer_, SIGNAL(timeout()),
538 this, SLOT(clearMessage()));
540 // We don't want to keep the window in memory if it is closed.
541 setAttribute(Qt::WA_DeleteOnClose, true);
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544 // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547 // since the icon is provided in the application bundle. We use a themed
548 // version when available and use the bundled one as fallback.
549 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
551 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
557 // use tabbed dock area for multiple docks
558 // (such as "source" and "messages")
559 setDockOptions(QMainWindow::ForceTabbedDocks);
562 setAcceptDrops(true);
564 // add busy indicator to statusbar
565 QLabel * busylabel = new QLabel(statusBar());
566 statusBar()->addPermanentWidget(busylabel);
567 search_mode mode = theGuiApp()->imageSearchMode();
568 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570 busylabel->setMovie(busyanim);
574 connect(&d.processing_thread_watcher_, SIGNAL(started()),
575 busylabel, SLOT(show()));
576 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577 busylabel, SLOT(hide()));
579 QFontMetrics const fm(statusBar()->fontMetrics());
580 int const roheight = max(int(d.normalIconSize), fm.height());
581 QSize const rosize(roheight, roheight);
582 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
583 read_only_ = new QLabel(statusBar());
584 read_only_->setPixmap(readonly);
585 read_only_->setScaledContents(true);
586 read_only_->setAlignment(Qt::AlignCenter);
588 statusBar()->addPermanentWidget(read_only_);
590 version_control_ = new QLabel(statusBar());
591 version_control_->setAlignment(Qt::AlignCenter);
592 version_control_->setFrameStyle(QFrame::StyledPanel);
593 version_control_->hide();
594 statusBar()->addPermanentWidget(version_control_);
596 statusBar()->setSizeGripEnabled(true);
599 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
600 SLOT(autoSaveThreadFinished()));
602 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
603 SLOT(processingThreadStarted()));
604 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
605 SLOT(processingThreadFinished()));
607 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
608 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
610 // set custom application bars context menu, e.g. tool bar and menu bar
611 setContextMenuPolicy(Qt::CustomContextMenu);
612 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
613 SLOT(toolBarPopup(const QPoint &)));
615 // Forbid too small unresizable window because it can happen
616 // with some window manager under X11.
617 setMinimumSize(300, 200);
619 if (lyxrc.allow_geometry_session) {
620 // Now take care of session management.
625 // no session handling, default to a sane size.
626 setGeometry(50, 50, 690, 510);
629 // clear session data if any.
631 settings.remove("views");
641 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
643 QVector<GuiWorkArea*> areas;
644 for (int i = 0; i < tabWorkAreaCount(); i++) {
645 TabWorkArea* ta = tabWorkArea(i);
646 for (int u = 0; u < ta->count(); u++) {
647 areas << ta->workArea(u);
653 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
654 string const & format)
656 docstring const fmt = theFormats().prettyName(format);
659 case Buffer::ExportSuccess:
660 msg = bformat(_("Successful export to format: %1$s"), fmt);
662 case Buffer::ExportCancel:
663 msg = _("Document export cancelled.");
665 case Buffer::ExportError:
666 case Buffer::ExportNoPathToFormat:
667 case Buffer::ExportTexPathHasSpaces:
668 case Buffer::ExportConverterError:
669 msg = bformat(_("Error while exporting format: %1$s"), fmt);
671 case Buffer::PreviewSuccess:
672 msg = bformat(_("Successful preview of format: %1$s"), fmt);
674 case Buffer::PreviewError:
675 msg = bformat(_("Error while previewing format: %1$s"), fmt);
682 void GuiView::processingThreadStarted()
687 void GuiView::processingThreadFinished()
689 QFutureWatcher<Buffer::ExportStatus> const * watcher =
690 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
692 Buffer::ExportStatus const status = watcher->result();
693 handleExportStatus(this, status, d.processing_format);
696 BufferView const * const bv = currentBufferView();
697 if (bv && !bv->buffer().errorList("Export").empty()) {
701 errors(d.last_export_format);
705 void GuiView::autoSaveThreadFinished()
707 QFutureWatcher<docstring> const * watcher =
708 static_cast<QFutureWatcher<docstring> const *>(sender());
709 message(watcher->result());
714 void GuiView::saveLayout() const
717 settings.setValue("zoom", lyxrc.currentZoom);
718 settings.setValue("devel_mode", devel_mode_);
719 settings.beginGroup("views");
720 settings.beginGroup(QString::number(id_));
721 #if defined(Q_WS_X11) || defined(QPA_XCB)
722 settings.setValue("pos", pos());
723 settings.setValue("size", size());
725 settings.setValue("geometry", saveGeometry());
727 settings.setValue("layout", saveState(0));
728 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
732 void GuiView::saveUISettings() const
734 // Save the toolbar private states
735 ToolbarMap::iterator end = d.toolbars_.end();
736 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
737 it->second->saveSession();
738 // Now take care of all other dialogs
739 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
740 for (; it!= d.dialogs_.end(); ++it)
741 it->second->saveSession();
745 bool GuiView::restoreLayout()
748 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
749 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
750 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
751 settings.beginGroup("views");
752 settings.beginGroup(QString::number(id_));
753 QString const icon_key = "icon_size";
754 if (!settings.contains(icon_key))
757 //code below is skipped when when ~/.config/LyX is (re)created
758 setIconSize(d.iconSize(settings.value(icon_key).toString()));
760 #if defined(Q_WS_X11) || defined(QPA_XCB)
761 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
762 QSize size = settings.value("size", QSize(690, 510)).toSize();
766 // Work-around for bug #6034: the window ends up in an undetermined
767 // state when trying to restore a maximized window when it is
768 // already maximized.
769 if (!(windowState() & Qt::WindowMaximized))
770 if (!restoreGeometry(settings.value("geometry").toByteArray()))
771 setGeometry(50, 50, 690, 510);
773 // Make sure layout is correctly oriented.
774 setLayoutDirection(qApp->layoutDirection());
776 // Allow the toc and view-source dock widget to be restored if needed.
778 if ((dialog = findOrBuild("toc", true)))
779 // see bug 5082. At least setup title and enabled state.
780 // Visibility will be adjusted by restoreState below.
781 dialog->prepareView();
782 if ((dialog = findOrBuild("view-source", true)))
783 dialog->prepareView();
784 if ((dialog = findOrBuild("progress", true)))
785 dialog->prepareView();
787 if (!restoreState(settings.value("layout").toByteArray(), 0))
790 // init the toolbars that have not been restored
791 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
792 Toolbars::Infos::iterator end = guiApp->toolbars().end();
793 for (; cit != end; ++cit) {
794 GuiToolbar * tb = toolbar(cit->name);
795 if (tb && !tb->isRestored())
796 initToolbar(cit->name);
799 // update lock (all) toolbars positions
800 updateLockToolbars();
807 GuiToolbar * GuiView::toolbar(string const & name)
809 ToolbarMap::iterator it = d.toolbars_.find(name);
810 if (it != d.toolbars_.end())
813 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
818 void GuiView::updateLockToolbars()
820 toolbarsMovable_ = false;
821 for (ToolbarInfo const & info : guiApp->toolbars()) {
822 GuiToolbar * tb = toolbar(info.name);
823 if (tb && tb->isMovable())
824 toolbarsMovable_ = true;
829 void GuiView::constructToolbars()
831 ToolbarMap::iterator it = d.toolbars_.begin();
832 for (; it != d.toolbars_.end(); ++it)
836 // I don't like doing this here, but the standard toolbar
837 // destroys this object when it's destroyed itself (vfr)
838 d.layout_ = new LayoutBox(*this);
839 d.stack_widget_->addWidget(d.layout_);
840 d.layout_->move(0,0);
842 // extracts the toolbars from the backend
843 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
844 Toolbars::Infos::iterator end = guiApp->toolbars().end();
845 for (; cit != end; ++cit)
846 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
850 void GuiView::initToolbars()
852 // extracts the toolbars from the backend
853 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
854 Toolbars::Infos::iterator end = guiApp->toolbars().end();
855 for (; cit != end; ++cit)
856 initToolbar(cit->name);
860 void GuiView::initToolbar(string const & name)
862 GuiToolbar * tb = toolbar(name);
865 int const visibility = guiApp->toolbars().defaultVisibility(name);
866 bool newline = !(visibility & Toolbars::SAMEROW);
867 tb->setVisible(false);
868 tb->setVisibility(visibility);
870 if (visibility & Toolbars::TOP) {
872 addToolBarBreak(Qt::TopToolBarArea);
873 addToolBar(Qt::TopToolBarArea, tb);
876 if (visibility & Toolbars::BOTTOM) {
878 addToolBarBreak(Qt::BottomToolBarArea);
879 addToolBar(Qt::BottomToolBarArea, tb);
882 if (visibility & Toolbars::LEFT) {
884 addToolBarBreak(Qt::LeftToolBarArea);
885 addToolBar(Qt::LeftToolBarArea, tb);
888 if (visibility & Toolbars::RIGHT) {
890 addToolBarBreak(Qt::RightToolBarArea);
891 addToolBar(Qt::RightToolBarArea, tb);
894 if (visibility & Toolbars::ON)
895 tb->setVisible(true);
897 tb->setMovable(true);
901 TocModels & GuiView::tocModels()
903 return d.toc_models_;
907 void GuiView::setFocus()
909 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
910 QMainWindow::setFocus();
914 bool GuiView::hasFocus() const
916 if (currentWorkArea())
917 return currentWorkArea()->hasFocus();
918 if (currentMainWorkArea())
919 return currentMainWorkArea()->hasFocus();
920 return d.bg_widget_->hasFocus();
924 void GuiView::focusInEvent(QFocusEvent * e)
926 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
927 QMainWindow::focusInEvent(e);
928 // Make sure guiApp points to the correct view.
929 guiApp->setCurrentView(this);
930 if (currentWorkArea())
931 currentWorkArea()->setFocus();
932 else if (currentMainWorkArea())
933 currentMainWorkArea()->setFocus();
935 d.bg_widget_->setFocus();
939 void GuiView::showEvent(QShowEvent * e)
941 LYXERR(Debug::GUI, "Passed Geometry "
942 << size().height() << "x" << size().width()
943 << "+" << pos().x() << "+" << pos().y());
945 if (d.splitter_->count() == 0)
946 // No work area, switch to the background widget.
950 QMainWindow::showEvent(e);
954 bool GuiView::closeScheduled()
961 bool GuiView::prepareAllBuffersForLogout()
963 Buffer * first = theBufferList().first();
967 // First, iterate over all buffers and ask the users if unsaved
968 // changes should be saved.
969 // We cannot use a for loop as the buffer list cycles.
972 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
974 b = theBufferList().next(b);
975 } while (b != first);
977 // Next, save session state
978 // When a view/window was closed before without quitting LyX, there
979 // are already entries in the lastOpened list.
980 theSession().lastOpened().clear();
987 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
988 ** is responsibility of the container (e.g., dialog)
990 void GuiView::closeEvent(QCloseEvent * close_event)
992 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
994 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
995 Alert::warning(_("Exit LyX"),
996 _("LyX could not be closed because documents are being processed by LyX."));
997 close_event->setAccepted(false);
1001 // If the user pressed the x (so we didn't call closeView
1002 // programmatically), we want to clear all existing entries.
1004 theSession().lastOpened().clear();
1009 // it can happen that this event arrives without selecting the view,
1010 // e.g. when clicking the close button on a background window.
1012 if (!closeWorkAreaAll()) {
1014 close_event->ignore();
1018 // Make sure that nothing will use this to be closed View.
1019 guiApp->unregisterView(this);
1021 if (isFullScreen()) {
1022 // Switch off fullscreen before closing.
1027 // Make sure the timer time out will not trigger a statusbar update.
1028 d.statusbar_timer_.stop();
1030 // Saving fullscreen requires additional tweaks in the toolbar code.
1031 // It wouldn't also work under linux natively.
1032 if (lyxrc.allow_geometry_session) {
1037 close_event->accept();
1041 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1043 if (event->mimeData()->hasUrls())
1045 /// \todo Ask lyx-devel is this is enough:
1046 /// if (event->mimeData()->hasFormat("text/plain"))
1047 /// event->acceptProposedAction();
1051 void GuiView::dropEvent(QDropEvent * event)
1053 QList<QUrl> files = event->mimeData()->urls();
1054 if (files.isEmpty())
1057 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1058 for (int i = 0; i != files.size(); ++i) {
1059 string const file = os::internal_path(fromqstr(
1060 files.at(i).toLocalFile()));
1064 string const ext = support::getExtension(file);
1065 vector<const Format *> found_formats;
1067 // Find all formats that have the correct extension.
1068 vector<const Format *> const & import_formats
1069 = theConverters().importableFormats();
1070 vector<const Format *>::const_iterator it = import_formats.begin();
1071 for (; it != import_formats.end(); ++it)
1072 if ((*it)->hasExtension(ext))
1073 found_formats.push_back(*it);
1076 if (found_formats.size() >= 1) {
1077 if (found_formats.size() > 1) {
1078 //FIXME: show a dialog to choose the correct importable format
1079 LYXERR(Debug::FILES,
1080 "Multiple importable formats found, selecting first");
1082 string const arg = found_formats[0]->name() + " " + file;
1083 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1086 //FIXME: do we have to explicitly check whether it's a lyx file?
1087 LYXERR(Debug::FILES,
1088 "No formats found, trying to open it as a lyx file");
1089 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1091 // add the functions to the queue
1092 guiApp->addToFuncRequestQueue(cmd);
1095 // now process the collected functions. We perform the events
1096 // asynchronously. This prevents potential problems in case the
1097 // BufferView is closed within an event.
1098 guiApp->processFuncRequestQueueAsync();
1102 void GuiView::message(docstring const & str)
1104 if (ForkedProcess::iAmAChild())
1107 // call is moved to GUI-thread by GuiProgress
1108 d.progress_->appendMessage(toqstr(str));
1112 void GuiView::clearMessageText()
1114 message(docstring());
1118 void GuiView::updateStatusBarMessage(QString const & str)
1120 statusBar()->showMessage(str);
1121 d.statusbar_timer_.stop();
1122 d.statusbar_timer_.start(3000);
1126 void GuiView::clearMessage()
1128 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1129 // the hasFocus function mostly returns false, even if the focus is on
1130 // a workarea in this view.
1134 d.statusbar_timer_.stop();
1138 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1140 if (wa != d.current_work_area_
1141 || wa->bufferView().buffer().isInternal())
1143 Buffer const & buf = wa->bufferView().buffer();
1144 // Set the windows title
1145 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1146 if (buf.notifiesExternalModification()) {
1147 title = bformat(_("%1$s (modified externally)"), title);
1148 // If the external modification status has changed, then maybe the status of
1149 // buffer-save has changed too.
1153 title += from_ascii(" - LyX");
1155 setWindowTitle(toqstr(title));
1156 // Sets the path for the window: this is used by OSX to
1157 // allow a context click on the title bar showing a menu
1158 // with the path up to the file
1159 setWindowFilePath(toqstr(buf.absFileName()));
1160 // Tell Qt whether the current document is changed
1161 setWindowModified(!buf.isClean());
1163 if (buf.hasReadonlyFlag())
1168 if (buf.lyxvc().inUse()) {
1169 version_control_->show();
1170 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1172 version_control_->hide();
1176 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1178 if (d.current_work_area_)
1179 // disconnect the current work area from all slots
1180 QObject::disconnect(d.current_work_area_, 0, this, 0);
1182 disconnectBufferView();
1183 connectBufferView(wa->bufferView());
1184 connectBuffer(wa->bufferView().buffer());
1185 d.current_work_area_ = wa;
1186 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1187 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1188 QObject::connect(wa, SIGNAL(busy(bool)),
1189 this, SLOT(setBusy(bool)));
1190 // connection of a signal to a signal
1191 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1192 this, SIGNAL(bufferViewChanged()));
1193 Q_EMIT updateWindowTitle(wa);
1194 Q_EMIT bufferViewChanged();
1198 void GuiView::onBufferViewChanged()
1201 // Buffer-dependent dialogs must be updated. This is done here because
1202 // some dialogs require buffer()->text.
1207 void GuiView::on_lastWorkAreaRemoved()
1210 // We already are in a close event. Nothing more to do.
1213 if (d.splitter_->count() > 1)
1214 // We have a splitter so don't close anything.
1217 // Reset and updates the dialogs.
1218 Q_EMIT bufferViewChanged();
1223 if (lyxrc.open_buffers_in_tabs)
1224 // Nothing more to do, the window should stay open.
1227 if (guiApp->viewIds().size() > 1) {
1233 // On Mac we also close the last window because the application stay
1234 // resident in memory. On other platforms we don't close the last
1235 // window because this would quit the application.
1241 void GuiView::updateStatusBar()
1243 // let the user see the explicit message
1244 if (d.statusbar_timer_.isActive())
1251 void GuiView::showMessage()
1255 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1256 if (msg.isEmpty()) {
1257 BufferView const * bv = currentBufferView();
1259 msg = toqstr(bv->cursor().currentState(devel_mode_));
1261 msg = qt_("Welcome to LyX!");
1263 statusBar()->showMessage(msg);
1267 bool GuiView::event(QEvent * e)
1271 // Useful debug code:
1272 //case QEvent::ActivationChange:
1273 //case QEvent::WindowDeactivate:
1274 //case QEvent::Paint:
1275 //case QEvent::Enter:
1276 //case QEvent::Leave:
1277 //case QEvent::HoverEnter:
1278 //case QEvent::HoverLeave:
1279 //case QEvent::HoverMove:
1280 //case QEvent::StatusTip:
1281 //case QEvent::DragEnter:
1282 //case QEvent::DragLeave:
1283 //case QEvent::Drop:
1286 case QEvent::WindowActivate: {
1287 GuiView * old_view = guiApp->currentView();
1288 if (this == old_view) {
1290 return QMainWindow::event(e);
1292 if (old_view && old_view->currentBufferView()) {
1293 // save current selection to the selection buffer to allow
1294 // middle-button paste in this window.
1295 cap::saveSelection(old_view->currentBufferView()->cursor());
1297 guiApp->setCurrentView(this);
1298 if (d.current_work_area_)
1299 on_currentWorkAreaChanged(d.current_work_area_);
1303 return QMainWindow::event(e);
1306 case QEvent::ShortcutOverride: {
1308 if (isFullScreen() && menuBar()->isHidden()) {
1309 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1310 // FIXME: we should also try to detect special LyX shortcut such as
1311 // Alt-P and Alt-M. Right now there is a hack in
1312 // GuiWorkArea::processKeySym() that hides again the menubar for
1314 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1316 return QMainWindow::event(e);
1319 return QMainWindow::event(e);
1323 return QMainWindow::event(e);
1327 void GuiView::resetWindowTitle()
1329 setWindowTitle(qt_("LyX"));
1332 bool GuiView::focusNextPrevChild(bool /*next*/)
1339 bool GuiView::busy() const
1345 void GuiView::setBusy(bool busy)
1347 bool const busy_before = busy_ > 0;
1348 busy ? ++busy_ : --busy_;
1349 if ((busy_ > 0) == busy_before)
1350 // busy state didn't change
1354 QApplication::setOverrideCursor(Qt::WaitCursor);
1357 QApplication::restoreOverrideCursor();
1362 void GuiView::resetCommandExecute()
1364 command_execute_ = false;
1369 double GuiView::pixelRatio() const
1371 #if QT_VERSION >= 0x050000
1372 return qt_scale_factor * devicePixelRatio();
1379 GuiWorkArea * GuiView::workArea(int index)
1381 if (TabWorkArea * twa = d.currentTabWorkArea())
1382 if (index < twa->count())
1383 return twa->workArea(index);
1388 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1390 if (currentWorkArea()
1391 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1392 return currentWorkArea();
1393 if (TabWorkArea * twa = d.currentTabWorkArea())
1394 return twa->workArea(buffer);
1399 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1401 // Automatically create a TabWorkArea if there are none yet.
1402 TabWorkArea * tab_widget = d.splitter_->count()
1403 ? d.currentTabWorkArea() : addTabWorkArea();
1404 return tab_widget->addWorkArea(buffer, *this);
1408 TabWorkArea * GuiView::addTabWorkArea()
1410 TabWorkArea * twa = new TabWorkArea;
1411 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1412 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1413 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1414 this, SLOT(on_lastWorkAreaRemoved()));
1416 d.splitter_->addWidget(twa);
1417 d.stack_widget_->setCurrentWidget(d.splitter_);
1422 GuiWorkArea const * GuiView::currentWorkArea() const
1424 return d.current_work_area_;
1428 GuiWorkArea * GuiView::currentWorkArea()
1430 return d.current_work_area_;
1434 GuiWorkArea const * GuiView::currentMainWorkArea() const
1436 if (!d.currentTabWorkArea())
1438 return d.currentTabWorkArea()->currentWorkArea();
1442 GuiWorkArea * GuiView::currentMainWorkArea()
1444 if (!d.currentTabWorkArea())
1446 return d.currentTabWorkArea()->currentWorkArea();
1450 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1452 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1454 d.current_work_area_ = 0;
1456 Q_EMIT bufferViewChanged();
1460 // FIXME: I've no clue why this is here and why it accesses
1461 // theGuiApp()->currentView, which might be 0 (bug 6464).
1462 // See also 27525 (vfr).
1463 if (theGuiApp()->currentView() == this
1464 && theGuiApp()->currentView()->currentWorkArea() == wa)
1467 if (currentBufferView())
1468 cap::saveSelection(currentBufferView()->cursor());
1470 theGuiApp()->setCurrentView(this);
1471 d.current_work_area_ = wa;
1473 // We need to reset this now, because it will need to be
1474 // right if the tabWorkArea gets reset in the for loop. We
1475 // will change it back if we aren't in that case.
1476 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1477 d.current_main_work_area_ = wa;
1479 for (int i = 0; i != d.splitter_->count(); ++i) {
1480 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1481 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1482 << ", Current main wa: " << currentMainWorkArea());
1487 d.current_main_work_area_ = old_cmwa;
1489 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1490 on_currentWorkAreaChanged(wa);
1491 BufferView & bv = wa->bufferView();
1492 bv.cursor().fixIfBroken();
1494 wa->setUpdatesEnabled(true);
1495 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1499 void GuiView::removeWorkArea(GuiWorkArea * wa)
1501 LASSERT(wa, return);
1502 if (wa == d.current_work_area_) {
1504 disconnectBufferView();
1505 d.current_work_area_ = 0;
1506 d.current_main_work_area_ = 0;
1509 bool found_twa = false;
1510 for (int i = 0; i != d.splitter_->count(); ++i) {
1511 TabWorkArea * twa = d.tabWorkArea(i);
1512 if (twa->removeWorkArea(wa)) {
1513 // Found in this tab group, and deleted the GuiWorkArea.
1515 if (twa->count() != 0) {
1516 if (d.current_work_area_ == 0)
1517 // This means that we are closing the current GuiWorkArea, so
1518 // switch to the next GuiWorkArea in the found TabWorkArea.
1519 setCurrentWorkArea(twa->currentWorkArea());
1521 // No more WorkAreas in this tab group, so delete it.
1528 // It is not a tabbed work area (i.e., the search work area), so it
1529 // should be deleted by other means.
1530 LASSERT(found_twa, return);
1532 if (d.current_work_area_ == 0) {
1533 if (d.splitter_->count() != 0) {
1534 TabWorkArea * twa = d.currentTabWorkArea();
1535 setCurrentWorkArea(twa->currentWorkArea());
1537 // No more work areas, switch to the background widget.
1538 setCurrentWorkArea(0);
1544 LayoutBox * GuiView::getLayoutDialog() const
1550 void GuiView::updateLayoutList()
1553 d.layout_->updateContents(false);
1557 void GuiView::updateToolbars()
1559 ToolbarMap::iterator end = d.toolbars_.end();
1560 if (d.current_work_area_) {
1562 if (d.current_work_area_->bufferView().cursor().inMathed()
1563 && !d.current_work_area_->bufferView().cursor().inRegexped())
1564 context |= Toolbars::MATH;
1565 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1566 context |= Toolbars::TABLE;
1567 if (currentBufferView()->buffer().areChangesPresent()
1568 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1569 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1570 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1571 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1572 context |= Toolbars::REVIEW;
1573 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1574 context |= Toolbars::MATHMACROTEMPLATE;
1575 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1576 context |= Toolbars::IPA;
1577 if (command_execute_)
1578 context |= Toolbars::MINIBUFFER;
1579 if (minibuffer_focus_) {
1580 context |= Toolbars::MINIBUFFER_FOCUS;
1581 minibuffer_focus_ = false;
1584 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1585 it->second->update(context);
1587 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1588 it->second->update();
1592 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1594 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1595 LASSERT(newBuffer, return);
1597 GuiWorkArea * wa = workArea(*newBuffer);
1600 newBuffer->masterBuffer()->updateBuffer();
1602 wa = addWorkArea(*newBuffer);
1603 // scroll to the position when the BufferView was last closed
1604 if (lyxrc.use_lastfilepos) {
1605 LastFilePosSection::FilePos filepos =
1606 theSession().lastFilePos().load(newBuffer->fileName());
1607 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1610 //Disconnect the old buffer...there's no new one.
1613 connectBuffer(*newBuffer);
1614 connectBufferView(wa->bufferView());
1616 setCurrentWorkArea(wa);
1620 void GuiView::connectBuffer(Buffer & buf)
1622 buf.setGuiDelegate(this);
1626 void GuiView::disconnectBuffer()
1628 if (d.current_work_area_)
1629 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1633 void GuiView::connectBufferView(BufferView & bv)
1635 bv.setGuiDelegate(this);
1639 void GuiView::disconnectBufferView()
1641 if (d.current_work_area_)
1642 d.current_work_area_->bufferView().setGuiDelegate(0);
1646 void GuiView::errors(string const & error_type, bool from_master)
1648 BufferView const * const bv = currentBufferView();
1652 #if EXPORT_in_THREAD
1653 // We are called with from_master == false by default, so we
1654 // have to figure out whether that is the case or not.
1655 ErrorList & el = bv->buffer().errorList(error_type);
1657 el = bv->buffer().masterBuffer()->errorList(error_type);
1661 ErrorList const & el = from_master ?
1662 bv->buffer().masterBuffer()->errorList(error_type) :
1663 bv->buffer().errorList(error_type);
1669 string data = error_type;
1671 data = "from_master|" + error_type;
1672 showDialog("errorlist", data);
1676 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1678 d.toc_models_.updateItem(toqstr(type), dit);
1682 void GuiView::structureChanged()
1684 // This is called from the Buffer, which has no way to ensure that cursors
1685 // in BufferView remain valid.
1686 if (documentBufferView())
1687 documentBufferView()->cursor().sanitize();
1688 // FIXME: This is slightly expensive, though less than the tocBackend update
1689 // (#9880). This also resets the view in the Toc Widget (#6675).
1690 d.toc_models_.reset(documentBufferView());
1691 // Navigator needs more than a simple update in this case. It needs to be
1693 updateDialog("toc", "");
1697 void GuiView::updateDialog(string const & name, string const & data)
1699 if (!isDialogVisible(name))
1702 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1703 if (it == d.dialogs_.end())
1706 Dialog * const dialog = it->second.get();
1707 if (dialog->isVisibleView())
1708 dialog->initialiseParams(data);
1712 BufferView * GuiView::documentBufferView()
1714 return currentMainWorkArea()
1715 ? ¤tMainWorkArea()->bufferView()
1720 BufferView const * GuiView::documentBufferView() const
1722 return currentMainWorkArea()
1723 ? ¤tMainWorkArea()->bufferView()
1728 BufferView * GuiView::currentBufferView()
1730 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1734 BufferView const * GuiView::currentBufferView() const
1736 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1740 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1741 Buffer const * orig, Buffer * clone)
1743 bool const success = clone->autoSave();
1745 busyBuffers.remove(orig);
1747 ? _("Automatic save done.")
1748 : _("Automatic save failed!");
1752 void GuiView::autoSave()
1754 LYXERR(Debug::INFO, "Running autoSave()");
1756 Buffer * buffer = documentBufferView()
1757 ? &documentBufferView()->buffer() : 0;
1759 resetAutosaveTimers();
1763 GuiViewPrivate::busyBuffers.insert(buffer);
1764 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1765 buffer, buffer->cloneBufferOnly());
1766 d.autosave_watcher_.setFuture(f);
1767 resetAutosaveTimers();
1771 void GuiView::resetAutosaveTimers()
1774 d.autosave_timeout_.restart();
1778 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1781 Buffer * buf = currentBufferView()
1782 ? ¤tBufferView()->buffer() : 0;
1783 Buffer * doc_buffer = documentBufferView()
1784 ? &(documentBufferView()->buffer()) : 0;
1787 /* In LyX/Mac, when a dialog is open, the menus of the
1788 application can still be accessed without giving focus to
1789 the main window. In this case, we want to disable the menu
1790 entries that are buffer-related.
1791 This code must not be used on Linux and Windows, since it
1792 would disable buffer-related entries when hovering over the
1793 menu (see bug #9574).
1795 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1801 // Check whether we need a buffer
1802 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1803 // no, exit directly
1804 flag.message(from_utf8(N_("Command not allowed with"
1805 "out any document open")));
1806 flag.setEnabled(false);
1810 if (cmd.origin() == FuncRequest::TOC) {
1811 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1812 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1813 flag.setEnabled(false);
1817 switch(cmd.action()) {
1818 case LFUN_BUFFER_IMPORT:
1821 case LFUN_MASTER_BUFFER_UPDATE:
1822 case LFUN_MASTER_BUFFER_VIEW:
1824 && (doc_buffer->parent() != 0
1825 || doc_buffer->hasChildren())
1826 && !d.processing_thread_watcher_.isRunning();
1829 case LFUN_BUFFER_UPDATE:
1830 case LFUN_BUFFER_VIEW: {
1831 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1835 string format = to_utf8(cmd.argument());
1836 if (cmd.argument().empty())
1837 format = doc_buffer->params().getDefaultOutputFormat();
1838 enable = doc_buffer->params().isExportable(format, true);
1842 case LFUN_BUFFER_RELOAD:
1843 enable = doc_buffer && !doc_buffer->isUnnamed()
1844 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1847 case LFUN_BUFFER_CHILD_OPEN:
1848 enable = doc_buffer != 0;
1851 case LFUN_BUFFER_WRITE:
1852 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1855 //FIXME: This LFUN should be moved to GuiApplication.
1856 case LFUN_BUFFER_WRITE_ALL: {
1857 // We enable the command only if there are some modified buffers
1858 Buffer * first = theBufferList().first();
1863 // We cannot use a for loop as the buffer list is a cycle.
1865 if (!b->isClean()) {
1869 b = theBufferList().next(b);
1870 } while (b != first);
1874 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1875 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1878 case LFUN_BUFFER_WRITE_AS:
1879 case LFUN_BUFFER_EXPORT_AS:
1880 enable = doc_buffer != 0;
1883 case LFUN_BUFFER_CLOSE:
1884 case LFUN_VIEW_CLOSE:
1885 enable = doc_buffer != 0;
1888 case LFUN_BUFFER_CLOSE_ALL:
1889 enable = theBufferList().last() != theBufferList().first();
1892 case LFUN_VIEW_SPLIT:
1893 if (cmd.getArg(0) == "vertical")
1894 enable = doc_buffer && (d.splitter_->count() == 1 ||
1895 d.splitter_->orientation() == Qt::Vertical);
1897 enable = doc_buffer && (d.splitter_->count() == 1 ||
1898 d.splitter_->orientation() == Qt::Horizontal);
1901 case LFUN_TAB_GROUP_CLOSE:
1902 enable = d.tabWorkAreaCount() > 1;
1905 case LFUN_DEVEL_MODE_TOGGLE:
1906 flag.setOnOff(devel_mode_);
1909 case LFUN_TOOLBAR_TOGGLE: {
1910 string const name = cmd.getArg(0);
1911 if (GuiToolbar * t = toolbar(name))
1912 flag.setOnOff(t->isVisible());
1915 docstring const msg =
1916 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1922 case LFUN_TOOLBAR_MOVABLE: {
1923 string const name = cmd.getArg(0);
1924 // use negation since locked == !movable
1926 // toolbar name * locks all toolbars
1927 flag.setOnOff(!toolbarsMovable_);
1928 else if (GuiToolbar * t = toolbar(name))
1929 flag.setOnOff(!(t->isMovable()));
1932 docstring const msg =
1933 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1939 case LFUN_ICON_SIZE:
1940 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1943 case LFUN_DROP_LAYOUTS_CHOICE:
1947 case LFUN_UI_TOGGLE:
1948 flag.setOnOff(isFullScreen());
1951 case LFUN_DIALOG_DISCONNECT_INSET:
1954 case LFUN_DIALOG_HIDE:
1955 // FIXME: should we check if the dialog is shown?
1958 case LFUN_DIALOG_TOGGLE:
1959 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1960 // fall through to set "enable"
1961 case LFUN_DIALOG_SHOW: {
1962 string const name = cmd.getArg(0);
1964 enable = name == "aboutlyx"
1965 || name == "file" //FIXME: should be removed.
1967 || name == "texinfo"
1968 || name == "progress"
1969 || name == "compare";
1970 else if (name == "character" || name == "symbols"
1971 || name == "mathdelimiter" || name == "mathmatrix") {
1972 if (!buf || buf->isReadonly())
1975 Cursor const & cur = currentBufferView()->cursor();
1976 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1979 else if (name == "latexlog")
1980 enable = FileName(doc_buffer->logName()).isReadableFile();
1981 else if (name == "spellchecker")
1982 enable = theSpellChecker()
1983 && !doc_buffer->isReadonly()
1984 && !doc_buffer->text().empty();
1985 else if (name == "vclog")
1986 enable = doc_buffer->lyxvc().inUse();
1990 case LFUN_DIALOG_UPDATE: {
1991 string const name = cmd.getArg(0);
1993 enable = name == "prefs";
1997 case LFUN_COMMAND_EXECUTE:
1999 case LFUN_MENU_OPEN:
2000 // Nothing to check.
2003 case LFUN_COMPLETION_INLINE:
2004 if (!d.current_work_area_
2005 || !d.current_work_area_->completer().inlinePossible(
2006 currentBufferView()->cursor()))
2010 case LFUN_COMPLETION_POPUP:
2011 if (!d.current_work_area_
2012 || !d.current_work_area_->completer().popupPossible(
2013 currentBufferView()->cursor()))
2018 if (!d.current_work_area_
2019 || !d.current_work_area_->completer().inlinePossible(
2020 currentBufferView()->cursor()))
2024 case LFUN_COMPLETION_ACCEPT:
2025 if (!d.current_work_area_
2026 || (!d.current_work_area_->completer().popupVisible()
2027 && !d.current_work_area_->completer().inlineVisible()
2028 && !d.current_work_area_->completer().completionAvailable()))
2032 case LFUN_COMPLETION_CANCEL:
2033 if (!d.current_work_area_
2034 || (!d.current_work_area_->completer().popupVisible()
2035 && !d.current_work_area_->completer().inlineVisible()))
2039 case LFUN_BUFFER_ZOOM_OUT:
2040 case LFUN_BUFFER_ZOOM_IN: {
2041 // only diff between these two is that the default for ZOOM_OUT
2043 bool const neg_zoom =
2044 convert<int>(cmd.argument()) < 0 ||
2045 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2046 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2047 docstring const msg =
2048 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2052 enable = doc_buffer;
2056 case LFUN_BUFFER_ZOOM: {
2057 bool const less_than_min_zoom =
2058 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2059 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2060 docstring const msg =
2061 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2066 enable = doc_buffer;
2070 case LFUN_BUFFER_MOVE_NEXT:
2071 case LFUN_BUFFER_MOVE_PREVIOUS:
2072 // we do not cycle when moving
2073 case LFUN_BUFFER_NEXT:
2074 case LFUN_BUFFER_PREVIOUS:
2075 // because we cycle, it doesn't matter whether on first or last
2076 enable = (d.currentTabWorkArea()->count() > 1);
2078 case LFUN_BUFFER_SWITCH:
2079 // toggle on the current buffer, but do not toggle off
2080 // the other ones (is that a good idea?)
2082 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2083 flag.setOnOff(true);
2086 case LFUN_VC_REGISTER:
2087 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2089 case LFUN_VC_RENAME:
2090 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2093 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2095 case LFUN_VC_CHECK_IN:
2096 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2098 case LFUN_VC_CHECK_OUT:
2099 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2101 case LFUN_VC_LOCKING_TOGGLE:
2102 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2103 && doc_buffer->lyxvc().lockingToggleEnabled();
2104 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2106 case LFUN_VC_REVERT:
2107 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2108 && !doc_buffer->hasReadonlyFlag();
2110 case LFUN_VC_UNDO_LAST:
2111 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2113 case LFUN_VC_REPO_UPDATE:
2114 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2116 case LFUN_VC_COMMAND: {
2117 if (cmd.argument().empty())
2119 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2123 case LFUN_VC_COMPARE:
2124 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2127 case LFUN_SERVER_GOTO_FILE_ROW:
2128 case LFUN_LYX_ACTIVATE:
2130 case LFUN_FORWARD_SEARCH:
2131 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2134 case LFUN_FILE_INSERT_PLAINTEXT:
2135 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2136 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2139 case LFUN_SPELLING_CONTINUOUSLY:
2140 flag.setOnOff(lyxrc.spellcheck_continuously);
2148 flag.setEnabled(false);
2154 static FileName selectTemplateFile()
2156 FileDialog dlg(qt_("Select template file"));
2157 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2158 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2160 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2161 QStringList(qt_("LyX Documents (*.lyx)")));
2163 if (result.first == FileDialog::Later)
2165 if (result.second.isEmpty())
2167 return FileName(fromqstr(result.second));
2171 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2175 Buffer * newBuffer = 0;
2177 newBuffer = checkAndLoadLyXFile(filename);
2178 } catch (ExceptionMessage const & e) {
2185 message(_("Document not loaded."));
2189 setBuffer(newBuffer);
2190 newBuffer->errors("Parse");
2193 theSession().lastFiles().add(filename);
2199 void GuiView::openDocument(string const & fname)
2201 string initpath = lyxrc.document_path;
2203 if (documentBufferView()) {
2204 string const trypath = documentBufferView()->buffer().filePath();
2205 // If directory is writeable, use this as default.
2206 if (FileName(trypath).isDirWritable())
2212 if (fname.empty()) {
2213 FileDialog dlg(qt_("Select document to open"));
2214 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2215 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2217 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2218 FileDialog::Result result =
2219 dlg.open(toqstr(initpath), filter);
2221 if (result.first == FileDialog::Later)
2224 filename = fromqstr(result.second);
2226 // check selected filename
2227 if (filename.empty()) {
2228 message(_("Canceled."));
2234 // get absolute path of file and add ".lyx" to the filename if
2236 FileName const fullname =
2237 fileSearch(string(), filename, "lyx", support::may_not_exist);
2238 if (!fullname.empty())
2239 filename = fullname.absFileName();
2241 if (!fullname.onlyPath().isDirectory()) {
2242 Alert::warning(_("Invalid filename"),
2243 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2244 from_utf8(fullname.absFileName())));
2248 // if the file doesn't exist and isn't already open (bug 6645),
2249 // let the user create one
2250 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2251 !LyXVC::file_not_found_hook(fullname)) {
2252 // the user specifically chose this name. Believe him.
2253 Buffer * const b = newFile(filename, string(), true);
2259 docstring const disp_fn = makeDisplayPath(filename);
2260 message(bformat(_("Opening document %1$s..."), disp_fn));
2263 Buffer * buf = loadDocument(fullname);
2265 str2 = bformat(_("Document %1$s opened."), disp_fn);
2266 if (buf->lyxvc().inUse())
2267 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2268 " " + _("Version control detected.");
2270 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2275 // FIXME: clean that
2276 static bool import(GuiView * lv, FileName const & filename,
2277 string const & format, ErrorList & errorList)
2279 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2281 string loader_format;
2282 vector<string> loaders = theConverters().loaders();
2283 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2284 vector<string>::const_iterator it = loaders.begin();
2285 vector<string>::const_iterator en = loaders.end();
2286 for (; it != en; ++it) {
2287 if (!theConverters().isReachable(format, *it))
2290 string const tofile =
2291 support::changeExtension(filename.absFileName(),
2292 theFormats().extension(*it));
2293 if (!theConverters().convert(0, filename, FileName(tofile),
2294 filename, format, *it, errorList))
2296 loader_format = *it;
2299 if (loader_format.empty()) {
2300 frontend::Alert::error(_("Couldn't import file"),
2301 bformat(_("No information for importing the format %1$s."),
2302 theFormats().prettyName(format)));
2306 loader_format = format;
2308 if (loader_format == "lyx") {
2309 Buffer * buf = lv->loadDocument(lyxfile);
2313 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2317 bool as_paragraphs = loader_format == "textparagraph";
2318 string filename2 = (loader_format == format) ? filename.absFileName()
2319 : support::changeExtension(filename.absFileName(),
2320 theFormats().extension(loader_format));
2321 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2323 guiApp->setCurrentView(lv);
2324 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2331 void GuiView::importDocument(string const & argument)
2334 string filename = split(argument, format, ' ');
2336 LYXERR(Debug::INFO, format << " file: " << filename);
2338 // need user interaction
2339 if (filename.empty()) {
2340 string initpath = lyxrc.document_path;
2341 if (documentBufferView()) {
2342 string const trypath = documentBufferView()->buffer().filePath();
2343 // If directory is writeable, use this as default.
2344 if (FileName(trypath).isDirWritable())
2348 docstring const text = bformat(_("Select %1$s file to import"),
2349 theFormats().prettyName(format));
2351 FileDialog dlg(toqstr(text));
2352 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2353 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2355 docstring filter = theFormats().prettyName(format);
2358 filter += from_utf8(theFormats().extensions(format));
2361 FileDialog::Result result =
2362 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2364 if (result.first == FileDialog::Later)
2367 filename = fromqstr(result.second);
2369 // check selected filename
2370 if (filename.empty())
2371 message(_("Canceled."));
2374 if (filename.empty())
2377 // get absolute path of file
2378 FileName const fullname(support::makeAbsPath(filename));
2380 // Can happen if the user entered a path into the dialog
2382 if (fullname.onlyFileName().empty()) {
2383 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2384 "Aborting import."),
2385 from_utf8(fullname.absFileName()));
2386 frontend::Alert::error(_("File name error"), msg);
2387 message(_("Canceled."));
2392 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2394 // Check if the document already is open
2395 Buffer * buf = theBufferList().getBuffer(lyxfile);
2398 if (!closeBuffer()) {
2399 message(_("Canceled."));
2404 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2406 // if the file exists already, and we didn't do
2407 // -i lyx thefile.lyx, warn
2408 if (lyxfile.exists() && fullname != lyxfile) {
2410 docstring text = bformat(_("The document %1$s already exists.\n\n"
2411 "Do you want to overwrite that document?"), displaypath);
2412 int const ret = Alert::prompt(_("Overwrite document?"),
2413 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2416 message(_("Canceled."));
2421 message(bformat(_("Importing %1$s..."), displaypath));
2422 ErrorList errorList;
2423 if (import(this, fullname, format, errorList))
2424 message(_("imported."));
2426 message(_("file not imported!"));
2428 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2432 void GuiView::newDocument(string const & filename, bool from_template)
2434 FileName initpath(lyxrc.document_path);
2435 if (documentBufferView()) {
2436 FileName const trypath(documentBufferView()->buffer().filePath());
2437 // If directory is writeable, use this as default.
2438 if (trypath.isDirWritable())
2442 string templatefile;
2443 if (from_template) {
2444 templatefile = selectTemplateFile().absFileName();
2445 if (templatefile.empty())
2450 if (filename.empty())
2451 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2453 b = newFile(filename, templatefile, true);
2458 // If no new document could be created, it is unsure
2459 // whether there is a valid BufferView.
2460 if (currentBufferView())
2461 // Ensure the cursor is correctly positioned on screen.
2462 currentBufferView()->showCursor();
2466 void GuiView::insertLyXFile(docstring const & fname)
2468 BufferView * bv = documentBufferView();
2473 FileName filename(to_utf8(fname));
2474 if (filename.empty()) {
2475 // Launch a file browser
2477 string initpath = lyxrc.document_path;
2478 string const trypath = bv->buffer().filePath();
2479 // If directory is writeable, use this as default.
2480 if (FileName(trypath).isDirWritable())
2484 FileDialog dlg(qt_("Select LyX document to insert"));
2485 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2486 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2488 FileDialog::Result result = dlg.open(toqstr(initpath),
2489 QStringList(qt_("LyX Documents (*.lyx)")));
2491 if (result.first == FileDialog::Later)
2495 filename.set(fromqstr(result.second));
2497 // check selected filename
2498 if (filename.empty()) {
2499 // emit message signal.
2500 message(_("Canceled."));
2505 bv->insertLyXFile(filename);
2506 bv->buffer().errors("Parse");
2510 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2512 FileName fname = b.fileName();
2513 FileName const oldname = fname;
2515 if (!newname.empty()) {
2517 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2519 // Switch to this Buffer.
2522 // No argument? Ask user through dialog.
2524 FileDialog dlg(qt_("Choose a filename to save document as"));
2525 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2526 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2528 if (!isLyXFileName(fname.absFileName()))
2529 fname.changeExtension(".lyx");
2531 FileDialog::Result result =
2532 dlg.save(toqstr(fname.onlyPath().absFileName()),
2533 QStringList(qt_("LyX Documents (*.lyx)")),
2534 toqstr(fname.onlyFileName()));
2536 if (result.first == FileDialog::Later)
2539 fname.set(fromqstr(result.second));
2544 if (!isLyXFileName(fname.absFileName()))
2545 fname.changeExtension(".lyx");
2548 // fname is now the new Buffer location.
2550 // if there is already a Buffer open with this name, we do not want
2551 // to have another one. (the second test makes sure we're not just
2552 // trying to overwrite ourselves, which is fine.)
2553 if (theBufferList().exists(fname) && fname != oldname
2554 && theBufferList().getBuffer(fname) != &b) {
2555 docstring const text =
2556 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2557 "Please close it before attempting to overwrite it.\n"
2558 "Do you want to choose a new filename?"),
2559 from_utf8(fname.absFileName()));
2560 int const ret = Alert::prompt(_("Chosen File Already Open"),
2561 text, 0, 1, _("&Rename"), _("&Cancel"));
2563 case 0: return renameBuffer(b, docstring(), kind);
2564 case 1: return false;
2569 bool const existsLocal = fname.exists();
2570 bool const existsInVC = LyXVC::fileInVC(fname);
2571 if (existsLocal || existsInVC) {
2572 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2573 if (kind != LV_WRITE_AS && existsInVC) {
2574 // renaming to a name that is already in VC
2576 docstring text = bformat(_("The document %1$s "
2577 "is already registered.\n\n"
2578 "Do you want to choose a new name?"),
2580 docstring const title = (kind == LV_VC_RENAME) ?
2581 _("Rename document?") : _("Copy document?");
2582 docstring const button = (kind == LV_VC_RENAME) ?
2583 _("&Rename") : _("&Copy");
2584 int const ret = Alert::prompt(title, text, 0, 1,
2585 button, _("&Cancel"));
2587 case 0: return renameBuffer(b, docstring(), kind);
2588 case 1: return false;
2593 docstring text = bformat(_("The document %1$s "
2594 "already exists.\n\n"
2595 "Do you want to overwrite that document?"),
2597 int const ret = Alert::prompt(_("Overwrite document?"),
2598 text, 0, 2, _("&Overwrite"),
2599 _("&Rename"), _("&Cancel"));
2602 case 1: return renameBuffer(b, docstring(), kind);
2603 case 2: return false;
2609 case LV_VC_RENAME: {
2610 string msg = b.lyxvc().rename(fname);
2613 message(from_utf8(msg));
2617 string msg = b.lyxvc().copy(fname);
2620 message(from_utf8(msg));
2626 // LyXVC created the file already in case of LV_VC_RENAME or
2627 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2628 // relative paths of included stuff right if we moved e.g. from
2629 // /a/b.lyx to /a/c/b.lyx.
2631 bool const saved = saveBuffer(b, fname);
2638 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2640 FileName fname = b.fileName();
2642 FileDialog dlg(qt_("Choose a filename to export the document as"));
2643 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2646 QString const anyformat = qt_("Guess from extension (*.*)");
2649 vector<Format const *> export_formats;
2650 for (Format const & f : theFormats())
2651 if (f.documentFormat())
2652 export_formats.push_back(&f);
2653 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2654 map<QString, string> fmap;
2657 for (Format const * f : export_formats) {
2658 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2659 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2661 from_ascii(f->extension())));
2662 types << loc_filter;
2663 fmap[loc_filter] = f->name();
2664 if (from_ascii(f->name()) == iformat) {
2665 filter = loc_filter;
2666 ext = f->extension();
2669 string ofname = fname.onlyFileName();
2671 ofname = support::changeExtension(ofname, ext);
2672 FileDialog::Result result =
2673 dlg.save(toqstr(fname.onlyPath().absFileName()),
2677 if (result.first != FileDialog::Chosen)
2681 fname.set(fromqstr(result.second));
2682 if (filter == anyformat)
2683 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2685 fmt_name = fmap[filter];
2686 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2687 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2689 if (fmt_name.empty() || fname.empty())
2692 // fname is now the new Buffer location.
2693 if (FileName(fname).exists()) {
2694 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2695 docstring text = bformat(_("The document %1$s already "
2696 "exists.\n\nDo you want to "
2697 "overwrite that document?"),
2699 int const ret = Alert::prompt(_("Overwrite document?"),
2700 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2703 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2704 case 2: return false;
2708 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2711 return dr.dispatched();
2715 bool GuiView::saveBuffer(Buffer & b)
2717 return saveBuffer(b, FileName());
2721 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2723 if (workArea(b) && workArea(b)->inDialogMode())
2726 if (fn.empty() && b.isUnnamed())
2727 return renameBuffer(b, docstring());
2729 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2731 theSession().lastFiles().add(b.fileName());
2735 // Switch to this Buffer.
2738 // FIXME: we don't tell the user *WHY* the save failed !!
2739 docstring const file = makeDisplayPath(b.absFileName(), 30);
2740 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2741 "Do you want to rename the document and "
2742 "try again?"), file);
2743 int const ret = Alert::prompt(_("Rename and save?"),
2744 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2747 if (!renameBuffer(b, docstring()))
2756 return saveBuffer(b, fn);
2760 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2762 return closeWorkArea(wa, false);
2766 // We only want to close the buffer if it is not visible in other workareas
2767 // of the same view, nor in other views, and if this is not a child
2768 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2770 Buffer & buf = wa->bufferView().buffer();
2772 bool last_wa = d.countWorkAreasOf(buf) == 1
2773 && !inOtherView(buf) && !buf.parent();
2775 bool close_buffer = last_wa;
2778 if (lyxrc.close_buffer_with_last_view == "yes")
2780 else if (lyxrc.close_buffer_with_last_view == "no")
2781 close_buffer = false;
2784 if (buf.isUnnamed())
2785 file = from_utf8(buf.fileName().onlyFileName());
2787 file = buf.fileName().displayName(30);
2788 docstring const text = bformat(
2789 _("Last view on document %1$s is being closed.\n"
2790 "Would you like to close or hide the document?\n"
2792 "Hidden documents can be displayed back through\n"
2793 "the menu: View->Hidden->...\n"
2795 "To remove this question, set your preference in:\n"
2796 " Tools->Preferences->Look&Feel->UserInterface\n"
2798 int ret = Alert::prompt(_("Close or hide document?"),
2799 text, 0, 1, _("&Close"), _("&Hide"));
2800 close_buffer = (ret == 0);
2804 return closeWorkArea(wa, close_buffer);
2808 bool GuiView::closeBuffer()
2810 GuiWorkArea * wa = currentMainWorkArea();
2811 // coverity complained about this
2812 // it seems unnecessary, but perhaps is worth the check
2813 LASSERT(wa, return false);
2815 setCurrentWorkArea(wa);
2816 Buffer & buf = wa->bufferView().buffer();
2817 return closeWorkArea(wa, !buf.parent());
2821 void GuiView::writeSession() const {
2822 GuiWorkArea const * active_wa = currentMainWorkArea();
2823 for (int i = 0; i < d.splitter_->count(); ++i) {
2824 TabWorkArea * twa = d.tabWorkArea(i);
2825 for (int j = 0; j < twa->count(); ++j) {
2826 GuiWorkArea * wa = twa->workArea(j);
2827 Buffer & buf = wa->bufferView().buffer();
2828 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2834 bool GuiView::closeBufferAll()
2836 // Close the workareas in all other views
2837 QList<int> const ids = guiApp->viewIds();
2838 for (int i = 0; i != ids.size(); ++i) {
2839 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2843 // Close our own workareas
2844 if (!closeWorkAreaAll())
2847 // Now close the hidden buffers. We prevent hidden buffers from being
2848 // dirty, so we can just close them.
2849 theBufferList().closeAll();
2854 bool GuiView::closeWorkAreaAll()
2856 setCurrentWorkArea(currentMainWorkArea());
2858 // We might be in a situation that there is still a tabWorkArea, but
2859 // there are no tabs anymore. This can happen when we get here after a
2860 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2861 // many TabWorkArea's have no documents anymore.
2864 // We have to call count() each time, because it can happen that
2865 // more than one splitter will disappear in one iteration (bug 5998).
2866 while (d.splitter_->count() > empty_twa) {
2867 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2869 if (twa->count() == 0)
2872 setCurrentWorkArea(twa->currentWorkArea());
2873 if (!closeTabWorkArea(twa))
2881 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2886 Buffer & buf = wa->bufferView().buffer();
2888 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2889 Alert::warning(_("Close document"),
2890 _("Document could not be closed because it is being processed by LyX."));
2895 return closeBuffer(buf);
2897 if (!inMultiTabs(wa))
2898 if (!saveBufferIfNeeded(buf, true))
2906 bool GuiView::closeBuffer(Buffer & buf)
2908 // If we are in a close_event all children will be closed in some time,
2909 // so no need to do it here. This will ensure that the children end up
2910 // in the session file in the correct order. If we close the master
2911 // buffer, we can close or release the child buffers here too.
2912 bool success = true;
2914 ListOfBuffers clist = buf.getChildren();
2915 ListOfBuffers::const_iterator it = clist.begin();
2916 ListOfBuffers::const_iterator const bend = clist.end();
2917 for (; it != bend; ++it) {
2918 Buffer * child_buf = *it;
2919 if (theBufferList().isOthersChild(&buf, child_buf)) {
2920 child_buf->setParent(0);
2924 // FIXME: should we look in other tabworkareas?
2925 // ANSWER: I don't think so. I've tested, and if the child is
2926 // open in some other window, it closes without a problem.
2927 GuiWorkArea * child_wa = workArea(*child_buf);
2929 success = closeWorkArea(child_wa, true);
2933 // In this case the child buffer is open but hidden.
2934 // It therefore should not (MUST NOT) be dirty!
2935 LATTEST(child_buf->isClean());
2936 theBufferList().release(child_buf);
2941 // goto bookmark to update bookmark pit.
2942 // FIXME: we should update only the bookmarks related to this buffer!
2943 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2944 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2945 guiApp->gotoBookmark(i+1, false, false);
2947 if (saveBufferIfNeeded(buf, false)) {
2948 buf.removeAutosaveFile();
2949 theBufferList().release(&buf);
2953 // open all children again to avoid a crash because of dangling
2954 // pointers (bug 6603)
2960 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2962 while (twa == d.currentTabWorkArea()) {
2963 twa->setCurrentIndex(twa->count() - 1);
2965 GuiWorkArea * wa = twa->currentWorkArea();
2966 Buffer & b = wa->bufferView().buffer();
2968 // We only want to close the buffer if the same buffer is not visible
2969 // in another view, and if this is not a child and if we are closing
2970 // a view (not a tabgroup).
2971 bool const close_buffer =
2972 !inOtherView(b) && !b.parent() && closing_;
2974 if (!closeWorkArea(wa, close_buffer))
2981 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2983 if (buf.isClean() || buf.paragraphs().empty())
2986 // Switch to this Buffer.
2992 if (buf.isUnnamed()) {
2993 file = from_utf8(buf.fileName().onlyFileName());
2996 FileName filename = buf.fileName();
2998 file = filename.displayName(30);
2999 exists = filename.exists();
3002 // Bring this window to top before asking questions.
3007 if (hiding && buf.isUnnamed()) {
3008 docstring const text = bformat(_("The document %1$s has not been "
3009 "saved yet.\n\nDo you want to save "
3010 "the document?"), file);
3011 ret = Alert::prompt(_("Save new document?"),
3012 text, 0, 1, _("&Save"), _("&Cancel"));
3016 docstring const text = exists ?
3017 bformat(_("The document %1$s has unsaved changes."
3018 "\n\nDo you want to save the document or "
3019 "discard the changes?"), file) :
3020 bformat(_("The document %1$s has not been saved yet."
3021 "\n\nDo you want to save the document or "
3022 "discard it entirely?"), file);
3023 docstring const title = exists ?
3024 _("Save changed document?") : _("Save document?");
3025 ret = Alert::prompt(title, text, 0, 2,
3026 _("&Save"), _("&Discard"), _("&Cancel"));
3031 if (!saveBuffer(buf))
3035 // If we crash after this we could have no autosave file
3036 // but I guess this is really improbable (Jug).
3037 // Sometimes improbable things happen:
3038 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3039 // buf.removeAutosaveFile();
3041 // revert all changes
3052 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3054 Buffer & buf = wa->bufferView().buffer();
3056 for (int i = 0; i != d.splitter_->count(); ++i) {
3057 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3058 if (wa_ && wa_ != wa)
3061 return inOtherView(buf);
3065 bool GuiView::inOtherView(Buffer & buf)
3067 QList<int> const ids = guiApp->viewIds();
3069 for (int i = 0; i != ids.size(); ++i) {
3073 if (guiApp->view(ids[i]).workArea(buf))
3080 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3082 if (!documentBufferView())
3085 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3086 Buffer * const curbuf = &documentBufferView()->buffer();
3087 int nwa = twa->count();
3088 for (int i = 0; i < nwa; ++i) {
3089 if (&workArea(i)->bufferView().buffer() == curbuf) {
3091 if (np == NEXTBUFFER)
3092 next_index = (i == nwa - 1 ? 0 : i + 1);
3094 next_index = (i == 0 ? nwa - 1 : i - 1);
3096 twa->moveTab(i, next_index);
3098 setBuffer(&workArea(next_index)->bufferView().buffer());
3106 /// make sure the document is saved
3107 static bool ensureBufferClean(Buffer * buffer)
3109 LASSERT(buffer, return false);
3110 if (buffer->isClean() && !buffer->isUnnamed())
3113 docstring const file = buffer->fileName().displayName(30);
3116 if (!buffer->isUnnamed()) {
3117 text = bformat(_("The document %1$s has unsaved "
3118 "changes.\n\nDo you want to save "
3119 "the document?"), file);
3120 title = _("Save changed document?");
3123 text = bformat(_("The document %1$s has not been "
3124 "saved yet.\n\nDo you want to save "
3125 "the document?"), file);
3126 title = _("Save new document?");
3128 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3131 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3133 return buffer->isClean() && !buffer->isUnnamed();
3137 bool GuiView::reloadBuffer(Buffer & buf)
3139 Buffer::ReadStatus status = buf.reload();
3140 return status == Buffer::ReadSuccess;
3144 void GuiView::checkExternallyModifiedBuffers()
3146 BufferList::iterator bit = theBufferList().begin();
3147 BufferList::iterator const bend = theBufferList().end();
3148 for (; bit != bend; ++bit) {
3149 Buffer * buf = *bit;
3150 if (buf->fileName().exists() && buf->isChecksumModified()) {
3151 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3152 " Reload now? Any local changes will be lost."),
3153 from_utf8(buf->absFileName()));
3154 int const ret = Alert::prompt(_("Reload externally changed document?"),
3155 text, 0, 1, _("&Reload"), _("&Cancel"));
3163 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3165 Buffer * buffer = documentBufferView()
3166 ? &(documentBufferView()->buffer()) : 0;
3168 switch (cmd.action()) {
3169 case LFUN_VC_REGISTER:
3170 if (!buffer || !ensureBufferClean(buffer))
3172 if (!buffer->lyxvc().inUse()) {
3173 if (buffer->lyxvc().registrer()) {
3174 reloadBuffer(*buffer);
3175 dr.clearMessageUpdate();
3180 case LFUN_VC_RENAME:
3181 case LFUN_VC_COPY: {
3182 if (!buffer || !ensureBufferClean(buffer))
3184 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3185 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3186 // Some changes are not yet committed.
3187 // We test here and not in getStatus(), since
3188 // this test is expensive.
3190 LyXVC::CommandResult ret =
3191 buffer->lyxvc().checkIn(log);
3193 if (ret == LyXVC::ErrorCommand ||
3194 ret == LyXVC::VCSuccess)
3195 reloadBuffer(*buffer);
3196 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3197 frontend::Alert::error(
3198 _("Revision control error."),
3199 _("Document could not be checked in."));
3203 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3204 LV_VC_RENAME : LV_VC_COPY;
3205 renameBuffer(*buffer, cmd.argument(), kind);
3210 case LFUN_VC_CHECK_IN:
3211 if (!buffer || !ensureBufferClean(buffer))
3213 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3215 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3217 // Only skip reloading if the checkin was cancelled or
3218 // an error occurred before the real checkin VCS command
3219 // was executed, since the VCS might have changed the
3220 // file even if it could not checkin successfully.
3221 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3222 reloadBuffer(*buffer);
3226 case LFUN_VC_CHECK_OUT:
3227 if (!buffer || !ensureBufferClean(buffer))
3229 if (buffer->lyxvc().inUse()) {
3230 dr.setMessage(buffer->lyxvc().checkOut());
3231 reloadBuffer(*buffer);
3235 case LFUN_VC_LOCKING_TOGGLE:
3236 LASSERT(buffer, return);
3237 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3239 if (buffer->lyxvc().inUse()) {
3240 string res = buffer->lyxvc().lockingToggle();
3242 frontend::Alert::error(_("Revision control error."),
3243 _("Error when setting the locking property."));
3246 reloadBuffer(*buffer);
3251 case LFUN_VC_REVERT:
3252 LASSERT(buffer, return);
3253 if (buffer->lyxvc().revert()) {
3254 reloadBuffer(*buffer);
3255 dr.clearMessageUpdate();
3259 case LFUN_VC_UNDO_LAST:
3260 LASSERT(buffer, return);
3261 buffer->lyxvc().undoLast();
3262 reloadBuffer(*buffer);
3263 dr.clearMessageUpdate();
3266 case LFUN_VC_REPO_UPDATE:
3267 LASSERT(buffer, return);
3268 if (ensureBufferClean(buffer)) {
3269 dr.setMessage(buffer->lyxvc().repoUpdate());
3270 checkExternallyModifiedBuffers();
3274 case LFUN_VC_COMMAND: {
3275 string flag = cmd.getArg(0);
3276 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3279 if (contains(flag, 'M')) {
3280 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3283 string path = cmd.getArg(1);
3284 if (contains(path, "$$p") && buffer)
3285 path = subst(path, "$$p", buffer->filePath());
3286 LYXERR(Debug::LYXVC, "Directory: " << path);
3288 if (!pp.isReadableDirectory()) {
3289 lyxerr << _("Directory is not accessible.") << endl;
3292 support::PathChanger p(pp);
3294 string command = cmd.getArg(2);
3295 if (command.empty())
3298 command = subst(command, "$$i", buffer->absFileName());
3299 command = subst(command, "$$p", buffer->filePath());
3301 command = subst(command, "$$m", to_utf8(message));
3302 LYXERR(Debug::LYXVC, "Command: " << command);
3304 one.startscript(Systemcall::Wait, command);
3308 if (contains(flag, 'I'))
3309 buffer->markDirty();
3310 if (contains(flag, 'R'))
3311 reloadBuffer(*buffer);
3316 case LFUN_VC_COMPARE: {
3317 if (cmd.argument().empty()) {
3318 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3322 string rev1 = cmd.getArg(0);
3327 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3330 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3331 f2 = buffer->absFileName();
3333 string rev2 = cmd.getArg(1);
3337 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3341 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3342 f1 << "\n" << f2 << "\n" );
3343 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3344 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3354 void GuiView::openChildDocument(string const & fname)
3356 LASSERT(documentBufferView(), return);
3357 Buffer & buffer = documentBufferView()->buffer();
3358 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3359 documentBufferView()->saveBookmark(false);
3361 if (theBufferList().exists(filename)) {
3362 child = theBufferList().getBuffer(filename);
3365 message(bformat(_("Opening child document %1$s..."),
3366 makeDisplayPath(filename.absFileName())));
3367 child = loadDocument(filename, false);
3369 // Set the parent name of the child document.
3370 // This makes insertion of citations and references in the child work,
3371 // when the target is in the parent or another child document.
3373 child->setParent(&buffer);
3377 bool GuiView::goToFileRow(string const & argument)
3381 size_t i = argument.find_last_of(' ');
3382 if (i != string::npos) {
3383 file_name = os::internal_path(trim(argument.substr(0, i)));
3384 istringstream is(argument.substr(i + 1));
3389 if (i == string::npos) {
3390 LYXERR0("Wrong argument: " << argument);
3394 string const abstmp = package().temp_dir().absFileName();
3395 string const realtmp = package().temp_dir().realPath();
3396 // We have to use os::path_prefix_is() here, instead of
3397 // simply prefixIs(), because the file name comes from
3398 // an external application and may need case adjustment.
3399 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3400 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3401 // Needed by inverse dvi search. If it is a file
3402 // in tmpdir, call the apropriated function.
3403 // If tmpdir is a symlink, we may have the real
3404 // path passed back, so we correct for that.
3405 if (!prefixIs(file_name, abstmp))
3406 file_name = subst(file_name, realtmp, abstmp);
3407 buf = theBufferList().getBufferFromTmp(file_name);
3409 // Must replace extension of the file to be .lyx
3410 // and get full path
3411 FileName const s = fileSearch(string(),
3412 support::changeExtension(file_name, ".lyx"), "lyx");
3413 // Either change buffer or load the file
3414 if (theBufferList().exists(s))
3415 buf = theBufferList().getBuffer(s);
3416 else if (s.exists()) {
3417 buf = loadDocument(s);
3422 _("File does not exist: %1$s"),
3423 makeDisplayPath(file_name)));
3429 _("No buffer for file: %1$s."),
3430 makeDisplayPath(file_name))
3435 bool success = documentBufferView()->setCursorFromRow(row);
3437 LYXERR(Debug::LATEX,
3438 "setCursorFromRow: invalid position for row " << row);
3439 frontend::Alert::error(_("Inverse Search Failed"),
3440 _("Invalid position requested by inverse search.\n"
3441 "You may need to update the viewed document."));
3447 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3449 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3450 menu->exec(QCursor::pos());
3455 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3457 Buffer::ExportStatus const status = func(format);
3459 // the cloning operation will have produced a clone of the entire set of
3460 // documents, starting from the master. so we must delete those.
3461 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3463 busyBuffers.remove(orig);
3468 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3470 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3471 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3475 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3477 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3478 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3482 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3484 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3485 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3489 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3490 string const & argument,
3491 Buffer const * used_buffer,
3492 docstring const & msg,
3493 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3494 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3495 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3500 string format = argument;
3502 format = used_buffer->params().getDefaultOutputFormat();
3503 processing_format = format;
3505 progress_->clearMessages();
3508 #if EXPORT_in_THREAD
3509 GuiViewPrivate::busyBuffers.insert(used_buffer);
3510 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3511 if (!cloned_buffer) {
3512 Alert::error(_("Export Error"),
3513 _("Error cloning the Buffer."));
3516 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3521 setPreviewFuture(f);
3522 last_export_format = used_buffer->params().bufferFormat();
3525 // We are asynchronous, so we don't know here anything about the success
3528 Buffer::ExportStatus status;
3530 status = (used_buffer->*syncFunc)(format, true);
3531 } else if (previewFunc) {
3532 status = (used_buffer->*previewFunc)(format);
3535 handleExportStatus(gv_, status, format);
3537 return (status == Buffer::ExportSuccess
3538 || status == Buffer::PreviewSuccess);
3542 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3544 BufferView * bv = currentBufferView();
3545 LASSERT(bv, return);
3547 // Let the current BufferView dispatch its own actions.
3548 bv->dispatch(cmd, dr);
3549 if (dr.dispatched())
3552 // Try with the document BufferView dispatch if any.
3553 BufferView * doc_bv = documentBufferView();
3554 if (doc_bv && doc_bv != bv) {
3555 doc_bv->dispatch(cmd, dr);
3556 if (dr.dispatched())
3560 // Then let the current Cursor dispatch its own actions.
3561 bv->cursor().dispatch(cmd);
3563 // update completion. We do it here and not in
3564 // processKeySym to avoid another redraw just for a
3565 // changed inline completion
3566 if (cmd.origin() == FuncRequest::KEYBOARD) {
3567 if (cmd.action() == LFUN_SELF_INSERT
3568 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3569 updateCompletion(bv->cursor(), true, true);
3570 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3571 updateCompletion(bv->cursor(), false, true);
3573 updateCompletion(bv->cursor(), false, false);
3576 dr = bv->cursor().result();
3580 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3582 BufferView * bv = currentBufferView();
3583 // By default we won't need any update.
3584 dr.screenUpdate(Update::None);
3585 // assume cmd will be dispatched
3586 dr.dispatched(true);
3588 Buffer * doc_buffer = documentBufferView()
3589 ? &(documentBufferView()->buffer()) : 0;
3591 if (cmd.origin() == FuncRequest::TOC) {
3592 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3593 // FIXME: do we need to pass a DispatchResult object here?
3594 toc->doDispatch(bv->cursor(), cmd);
3598 string const argument = to_utf8(cmd.argument());
3600 switch(cmd.action()) {
3601 case LFUN_BUFFER_CHILD_OPEN:
3602 openChildDocument(to_utf8(cmd.argument()));
3605 case LFUN_BUFFER_IMPORT:
3606 importDocument(to_utf8(cmd.argument()));
3609 case LFUN_BUFFER_EXPORT: {
3612 // GCC only sees strfwd.h when building merged
3613 if (::lyx::operator==(cmd.argument(), "custom")) {
3614 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3618 string const dest = cmd.getArg(1);
3619 FileName target_dir;
3620 if (!dest.empty() && FileName::isAbsolute(dest))
3621 target_dir = FileName(support::onlyPath(dest));
3623 target_dir = doc_buffer->fileName().onlyPath();
3625 string const format = (argument.empty() || argument == "default") ?
3626 doc_buffer->params().getDefaultOutputFormat() : argument;
3628 if ((dest.empty() && doc_buffer->isUnnamed())
3629 || !target_dir.isDirWritable()) {
3630 exportBufferAs(*doc_buffer, from_utf8(format));
3633 /* TODO/Review: Is it a problem to also export the children?
3634 See the update_unincluded flag */
3635 d.asyncBufferProcessing(format,
3638 &GuiViewPrivate::exportAndDestroy,
3641 // TODO Inform user about success
3645 case LFUN_BUFFER_EXPORT_AS: {
3646 LASSERT(doc_buffer, break);
3647 docstring f = cmd.argument();
3649 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3650 exportBufferAs(*doc_buffer, f);
3654 case LFUN_BUFFER_UPDATE: {
3655 d.asyncBufferProcessing(argument,
3658 &GuiViewPrivate::compileAndDestroy,
3663 case LFUN_BUFFER_VIEW: {
3664 d.asyncBufferProcessing(argument,
3666 _("Previewing ..."),
3667 &GuiViewPrivate::previewAndDestroy,
3672 case LFUN_MASTER_BUFFER_UPDATE: {
3673 d.asyncBufferProcessing(argument,
3674 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3676 &GuiViewPrivate::compileAndDestroy,
3681 case LFUN_MASTER_BUFFER_VIEW: {
3682 d.asyncBufferProcessing(argument,
3683 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3685 &GuiViewPrivate::previewAndDestroy,
3686 0, &Buffer::preview);
3689 case LFUN_BUFFER_SWITCH: {
3690 string const file_name = to_utf8(cmd.argument());
3691 if (!FileName::isAbsolute(file_name)) {
3693 dr.setMessage(_("Absolute filename expected."));
3697 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3700 dr.setMessage(_("Document not loaded"));
3704 // Do we open or switch to the buffer in this view ?
3705 if (workArea(*buffer)
3706 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3711 // Look for the buffer in other views
3712 QList<int> const ids = guiApp->viewIds();
3714 for (; i != ids.size(); ++i) {
3715 GuiView & gv = guiApp->view(ids[i]);
3716 if (gv.workArea(*buffer)) {
3718 gv.activateWindow();
3720 gv.setBuffer(buffer);
3725 // If necessary, open a new window as a last resort
3726 if (i == ids.size()) {
3727 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3733 case LFUN_BUFFER_NEXT:
3734 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3737 case LFUN_BUFFER_MOVE_NEXT:
3738 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3741 case LFUN_BUFFER_PREVIOUS:
3742 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3745 case LFUN_BUFFER_MOVE_PREVIOUS:
3746 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3749 case LFUN_COMMAND_EXECUTE: {
3750 command_execute_ = true;
3751 minibuffer_focus_ = true;
3754 case LFUN_DROP_LAYOUTS_CHOICE:
3755 d.layout_->showPopup();
3758 case LFUN_MENU_OPEN:
3759 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3760 menu->exec(QCursor::pos());
3763 case LFUN_FILE_INSERT:
3764 insertLyXFile(cmd.argument());
3767 case LFUN_FILE_INSERT_PLAINTEXT:
3768 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3769 string const fname = to_utf8(cmd.argument());
3770 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3771 dr.setMessage(_("Absolute filename expected."));
3775 FileName filename(fname);
3776 if (fname.empty()) {
3777 FileDialog dlg(qt_("Select file to insert"));
3779 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3780 QStringList(qt_("All Files (*)")));
3782 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3783 dr.setMessage(_("Canceled."));
3787 filename.set(fromqstr(result.second));
3791 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3792 bv->dispatch(new_cmd, dr);
3797 case LFUN_BUFFER_RELOAD: {
3798 LASSERT(doc_buffer, break);
3801 if (!doc_buffer->isClean()) {
3802 docstring const file =
3803 makeDisplayPath(doc_buffer->absFileName(), 20);
3804 if (doc_buffer->notifiesExternalModification()) {
3805 docstring text = _("The current version will be lost. "
3806 "Are you sure you want to load the version on disk "
3807 "of the document %1$s?");
3808 ret = Alert::prompt(_("Reload saved document?"),
3809 bformat(text, file), 1, 1,
3810 _("&Reload"), _("&Cancel"));
3812 docstring text = _("Any changes will be lost. "
3813 "Are you sure you want to revert to the saved version "
3814 "of the document %1$s?");
3815 ret = Alert::prompt(_("Revert to saved document?"),
3816 bformat(text, file), 1, 1,
3817 _("&Revert"), _("&Cancel"));
3822 doc_buffer->markClean();
3823 reloadBuffer(*doc_buffer);
3824 dr.forceBufferUpdate();
3829 case LFUN_BUFFER_WRITE:
3830 LASSERT(doc_buffer, break);
3831 saveBuffer(*doc_buffer);
3834 case LFUN_BUFFER_WRITE_AS:
3835 LASSERT(doc_buffer, break);
3836 renameBuffer(*doc_buffer, cmd.argument());
3839 case LFUN_BUFFER_WRITE_ALL: {
3840 Buffer * first = theBufferList().first();
3843 message(_("Saving all documents..."));
3844 // We cannot use a for loop as the buffer list cycles.
3847 if (!b->isClean()) {
3849 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3851 b = theBufferList().next(b);
3852 } while (b != first);
3853 dr.setMessage(_("All documents saved."));
3857 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3858 LASSERT(doc_buffer, break);
3859 doc_buffer->clearExternalModification();
3862 case LFUN_BUFFER_CLOSE:
3866 case LFUN_BUFFER_CLOSE_ALL:
3870 case LFUN_DEVEL_MODE_TOGGLE:
3871 devel_mode_ = !devel_mode_;
3873 dr.setMessage(_("Developer mode is now enabled."));
3875 dr.setMessage(_("Developer mode is now disabled."));
3878 case LFUN_TOOLBAR_TOGGLE: {
3879 string const name = cmd.getArg(0);
3880 if (GuiToolbar * t = toolbar(name))
3885 case LFUN_TOOLBAR_MOVABLE: {
3886 string const name = cmd.getArg(0);
3888 // toggle (all) toolbars movablility
3889 toolbarsMovable_ = !toolbarsMovable_;
3890 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3891 GuiToolbar * tb = toolbar(ti.name);
3892 if (tb && tb->isMovable() != toolbarsMovable_)
3893 // toggle toolbar movablity if it does not fit lock
3894 // (all) toolbars positions state silent = true, since
3895 // status bar notifications are slow
3898 if (toolbarsMovable_)
3899 dr.setMessage(_("Toolbars unlocked."));
3901 dr.setMessage(_("Toolbars locked."));
3902 } else if (GuiToolbar * t = toolbar(name)) {
3903 // toggle current toolbar movablity
3905 // update lock (all) toolbars positions
3906 updateLockToolbars();
3911 case LFUN_ICON_SIZE: {
3912 QSize size = d.iconSize(cmd.argument());
3914 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3915 size.width(), size.height()));
3919 case LFUN_DIALOG_UPDATE: {
3920 string const name = to_utf8(cmd.argument());
3921 if (name == "prefs" || name == "document")
3922 updateDialog(name, string());
3923 else if (name == "paragraph")
3924 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3925 else if (currentBufferView()) {
3926 Inset * inset = currentBufferView()->editedInset(name);
3927 // Can only update a dialog connected to an existing inset
3929 // FIXME: get rid of this indirection; GuiView ask the inset
3930 // if he is kind enough to update itself...
3931 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3932 //FIXME: pass DispatchResult here?
3933 inset->dispatch(currentBufferView()->cursor(), fr);
3939 case LFUN_DIALOG_TOGGLE: {
3940 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3941 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3942 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3946 case LFUN_DIALOG_DISCONNECT_INSET:
3947 disconnectDialog(to_utf8(cmd.argument()));
3950 case LFUN_DIALOG_HIDE: {
3951 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3955 case LFUN_DIALOG_SHOW: {
3956 string const name = cmd.getArg(0);
3957 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3959 if (name == "character") {
3960 data = freefont2string();
3962 showDialog("character", data);
3963 } else if (name == "latexlog") {
3964 // getStatus checks that
3965 LATTEST(doc_buffer);
3966 Buffer::LogType type;
3967 string const logfile = doc_buffer->logName(&type);
3969 case Buffer::latexlog:
3972 case Buffer::buildlog:
3976 data += Lexer::quoteString(logfile);
3977 showDialog("log", data);
3978 } else if (name == "vclog") {
3979 // getStatus checks that
3980 LATTEST(doc_buffer);
3981 string const data = "vc " +
3982 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3983 showDialog("log", data);
3984 } else if (name == "symbols") {
3985 data = bv->cursor().getEncoding()->name();
3987 showDialog("symbols", data);
3989 } else if (name == "prefs" && isFullScreen()) {
3990 lfunUiToggle("fullscreen");
3991 showDialog("prefs", data);
3993 showDialog(name, data);
3998 dr.setMessage(cmd.argument());
4001 case LFUN_UI_TOGGLE: {
4002 string arg = cmd.getArg(0);
4003 if (!lfunUiToggle(arg)) {
4004 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4005 dr.setMessage(bformat(msg, from_utf8(arg)));
4007 // Make sure the keyboard focus stays in the work area.
4012 case LFUN_VIEW_SPLIT: {
4013 LASSERT(doc_buffer, break);
4014 string const orientation = cmd.getArg(0);
4015 d.splitter_->setOrientation(orientation == "vertical"
4016 ? Qt::Vertical : Qt::Horizontal);
4017 TabWorkArea * twa = addTabWorkArea();
4018 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4019 setCurrentWorkArea(wa);
4022 case LFUN_TAB_GROUP_CLOSE:
4023 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4024 closeTabWorkArea(twa);
4025 d.current_work_area_ = 0;
4026 twa = d.currentTabWorkArea();
4027 // Switch to the next GuiWorkArea in the found TabWorkArea.
4029 // Make sure the work area is up to date.
4030 setCurrentWorkArea(twa->currentWorkArea());
4032 setCurrentWorkArea(0);
4037 case LFUN_VIEW_CLOSE:
4038 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4039 closeWorkArea(twa->currentWorkArea());
4040 d.current_work_area_ = 0;
4041 twa = d.currentTabWorkArea();
4042 // Switch to the next GuiWorkArea in the found TabWorkArea.
4044 // Make sure the work area is up to date.
4045 setCurrentWorkArea(twa->currentWorkArea());
4047 setCurrentWorkArea(0);
4052 case LFUN_COMPLETION_INLINE:
4053 if (d.current_work_area_)
4054 d.current_work_area_->completer().showInline();
4057 case LFUN_COMPLETION_POPUP:
4058 if (d.current_work_area_)
4059 d.current_work_area_->completer().showPopup();
4064 if (d.current_work_area_)
4065 d.current_work_area_->completer().tab();
4068 case LFUN_COMPLETION_CANCEL:
4069 if (d.current_work_area_) {
4070 if (d.current_work_area_->completer().popupVisible())
4071 d.current_work_area_->completer().hidePopup();
4073 d.current_work_area_->completer().hideInline();
4077 case LFUN_COMPLETION_ACCEPT:
4078 if (d.current_work_area_)
4079 d.current_work_area_->completer().activate();
4082 case LFUN_BUFFER_ZOOM_IN:
4083 case LFUN_BUFFER_ZOOM_OUT:
4084 case LFUN_BUFFER_ZOOM: {
4085 // use a signed temp to avoid overflow
4086 int zoom = lyxrc.currentZoom;
4087 if (cmd.argument().empty()) {
4088 if (cmd.action() == LFUN_BUFFER_ZOOM)
4090 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4095 if (cmd.action() == LFUN_BUFFER_ZOOM)
4096 zoom = convert<int>(cmd.argument());
4097 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4098 zoom += convert<int>(cmd.argument());
4100 zoom -= convert<int>(cmd.argument());
4103 if (zoom < static_cast<int>(zoom_min_))
4106 lyxrc.currentZoom = zoom;
4108 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4110 // The global QPixmapCache is used in GuiPainter to cache text
4111 // painting so we must reset it.
4112 QPixmapCache::clear();
4113 guiApp->fontLoader().update();
4114 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4118 case LFUN_VC_REGISTER:
4119 case LFUN_VC_RENAME:
4121 case LFUN_VC_CHECK_IN:
4122 case LFUN_VC_CHECK_OUT:
4123 case LFUN_VC_REPO_UPDATE:
4124 case LFUN_VC_LOCKING_TOGGLE:
4125 case LFUN_VC_REVERT:
4126 case LFUN_VC_UNDO_LAST:
4127 case LFUN_VC_COMMAND:
4128 case LFUN_VC_COMPARE:
4129 dispatchVC(cmd, dr);
4132 case LFUN_SERVER_GOTO_FILE_ROW:
4133 if(goToFileRow(to_utf8(cmd.argument())))
4134 dr.screenUpdate(Update::Force | Update::FitCursor);
4137 case LFUN_LYX_ACTIVATE:
4141 case LFUN_FORWARD_SEARCH: {
4142 // it seems safe to assume we have a document buffer, since
4143 // getStatus wants one.
4144 LATTEST(doc_buffer);
4145 Buffer const * doc_master = doc_buffer->masterBuffer();
4146 FileName const path(doc_master->temppath());
4147 string const texname = doc_master->isChild(doc_buffer)
4148 ? DocFileName(changeExtension(
4149 doc_buffer->absFileName(),
4150 "tex")).mangledFileName()
4151 : doc_buffer->latexName();
4152 string const fulltexname =
4153 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4154 string const mastername =
4155 removeExtension(doc_master->latexName());
4156 FileName const dviname(addName(path.absFileName(),
4157 addExtension(mastername, "dvi")));
4158 FileName const pdfname(addName(path.absFileName(),
4159 addExtension(mastername, "pdf")));
4160 bool const have_dvi = dviname.exists();
4161 bool const have_pdf = pdfname.exists();
4162 if (!have_dvi && !have_pdf) {
4163 dr.setMessage(_("Please, preview the document first."));
4166 string outname = dviname.onlyFileName();
4167 string command = lyxrc.forward_search_dvi;
4168 if (!have_dvi || (have_pdf &&
4169 pdfname.lastModified() > dviname.lastModified())) {
4170 outname = pdfname.onlyFileName();
4171 command = lyxrc.forward_search_pdf;
4174 DocIterator cur = bv->cursor();
4175 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4176 LYXERR(Debug::ACTION, "Forward search: row:" << row
4178 if (row == -1 || command.empty()) {
4179 dr.setMessage(_("Couldn't proceed."));
4182 string texrow = convert<string>(row);
4184 command = subst(command, "$$n", texrow);
4185 command = subst(command, "$$f", fulltexname);
4186 command = subst(command, "$$t", texname);
4187 command = subst(command, "$$o", outname);
4189 PathChanger p(path);
4191 one.startscript(Systemcall::DontWait, command);
4195 case LFUN_SPELLING_CONTINUOUSLY:
4196 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4197 dr.screenUpdate(Update::Force);
4201 // The LFUN must be for one of BufferView, Buffer or Cursor;
4203 dispatchToBufferView(cmd, dr);
4207 // Part of automatic menu appearance feature.
4208 if (isFullScreen()) {
4209 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4213 // Need to update bv because many LFUNs here might have destroyed it
4214 bv = currentBufferView();
4216 // Clear non-empty selections
4217 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4219 Cursor & cur = bv->cursor();
4220 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4221 cur.clearSelection();
4227 bool GuiView::lfunUiToggle(string const & ui_component)
4229 if (ui_component == "scrollbar") {
4230 // hide() is of no help
4231 if (d.current_work_area_->verticalScrollBarPolicy() ==
4232 Qt::ScrollBarAlwaysOff)
4234 d.current_work_area_->setVerticalScrollBarPolicy(
4235 Qt::ScrollBarAsNeeded);
4237 d.current_work_area_->setVerticalScrollBarPolicy(
4238 Qt::ScrollBarAlwaysOff);
4239 } else if (ui_component == "statusbar") {
4240 statusBar()->setVisible(!statusBar()->isVisible());
4241 } else if (ui_component == "menubar") {
4242 menuBar()->setVisible(!menuBar()->isVisible());
4244 if (ui_component == "frame") {
4246 getContentsMargins(&l, &t, &r, &b);
4247 //are the frames in default state?
4248 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4250 setContentsMargins(-2, -2, -2, -2);
4252 setContentsMargins(0, 0, 0, 0);
4255 if (ui_component == "fullscreen") {
4263 void GuiView::toggleFullScreen()
4265 if (isFullScreen()) {
4266 for (int i = 0; i != d.splitter_->count(); ++i)
4267 d.tabWorkArea(i)->setFullScreen(false);
4268 setContentsMargins(0, 0, 0, 0);
4269 setWindowState(windowState() ^ Qt::WindowFullScreen);
4272 statusBar()->show();
4275 hideDialogs("prefs", 0);
4276 for (int i = 0; i != d.splitter_->count(); ++i)
4277 d.tabWorkArea(i)->setFullScreen(true);
4278 setContentsMargins(-2, -2, -2, -2);
4280 setWindowState(windowState() ^ Qt::WindowFullScreen);
4281 if (lyxrc.full_screen_statusbar)
4282 statusBar()->hide();
4283 if (lyxrc.full_screen_menubar)
4285 if (lyxrc.full_screen_toolbars) {
4286 ToolbarMap::iterator end = d.toolbars_.end();
4287 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4292 // give dialogs like the TOC a chance to adapt
4297 Buffer const * GuiView::updateInset(Inset const * inset)
4302 Buffer const * inset_buffer = &(inset->buffer());
4304 for (int i = 0; i != d.splitter_->count(); ++i) {
4305 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4308 Buffer const * buffer = &(wa->bufferView().buffer());
4309 if (inset_buffer == buffer)
4310 wa->scheduleRedraw();
4312 return inset_buffer;
4316 void GuiView::restartCursor()
4318 /* When we move around, or type, it's nice to be able to see
4319 * the cursor immediately after the keypress.
4321 if (d.current_work_area_)
4322 d.current_work_area_->startBlinkingCursor();
4324 // Take this occasion to update the other GUI elements.
4330 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4332 if (d.current_work_area_)
4333 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4338 // This list should be kept in sync with the list of insets in
4339 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4340 // dialog should have the same name as the inset.
4341 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4342 // docs in LyXAction.cpp.
4344 char const * const dialognames[] = {
4346 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4347 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4348 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4349 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4350 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4351 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4352 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4353 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4355 char const * const * const end_dialognames =
4356 dialognames + (sizeof(dialognames) / sizeof(char *));
4360 cmpCStr(char const * name) : name_(name) {}
4361 bool operator()(char const * other) {
4362 return strcmp(other, name_) == 0;
4369 bool isValidName(string const & name)
4371 return find_if(dialognames, end_dialognames,
4372 cmpCStr(name.c_str())) != end_dialognames;
4378 void GuiView::resetDialogs()
4380 // Make sure that no LFUN uses any GuiView.
4381 guiApp->setCurrentView(0);
4385 constructToolbars();
4386 guiApp->menus().fillMenuBar(menuBar(), this, false);
4387 d.layout_->updateContents(true);
4388 // Now update controls with current buffer.
4389 guiApp->setCurrentView(this);
4395 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4397 if (!isValidName(name))
4400 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4402 if (it != d.dialogs_.end()) {
4404 it->second->hideView();
4405 return it->second.get();
4408 Dialog * dialog = build(name);
4409 d.dialogs_[name].reset(dialog);
4410 if (lyxrc.allow_geometry_session)
4411 dialog->restoreSession();
4418 void GuiView::showDialog(string const & name, string const & data,
4421 triggerShowDialog(toqstr(name), toqstr(data), inset);
4425 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4431 const string name = fromqstr(qname);
4432 const string data = fromqstr(qdata);
4436 Dialog * dialog = findOrBuild(name, false);
4438 bool const visible = dialog->isVisibleView();
4439 dialog->showData(data);
4440 if (inset && currentBufferView())
4441 currentBufferView()->editInset(name, inset);
4442 // We only set the focus to the new dialog if it was not yet
4443 // visible in order not to change the existing previous behaviour
4445 // activateWindow is needed for floating dockviews
4446 dialog->asQWidget()->raise();
4447 dialog->asQWidget()->activateWindow();
4448 dialog->asQWidget()->setFocus();
4452 catch (ExceptionMessage const & ex) {
4460 bool GuiView::isDialogVisible(string const & name) const
4462 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4463 if (it == d.dialogs_.end())
4465 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4469 void GuiView::hideDialog(string const & name, Inset * inset)
4471 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4472 if (it == d.dialogs_.end())
4476 if (!currentBufferView())
4478 if (inset != currentBufferView()->editedInset(name))
4482 Dialog * const dialog = it->second.get();
4483 if (dialog->isVisibleView())
4485 if (currentBufferView())
4486 currentBufferView()->editInset(name, 0);
4490 void GuiView::disconnectDialog(string const & name)
4492 if (!isValidName(name))
4494 if (currentBufferView())
4495 currentBufferView()->editInset(name, 0);
4499 void GuiView::hideAll() const
4501 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4502 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4504 for(; it != end; ++it)
4505 it->second->hideView();
4509 void GuiView::updateDialogs()
4511 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4512 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4514 for(; it != end; ++it) {
4515 Dialog * dialog = it->second.get();
4517 if (dialog->needBufferOpen() && !documentBufferView())
4518 hideDialog(fromqstr(dialog->name()), 0);
4519 else if (dialog->isVisibleView())
4520 dialog->checkStatus();
4527 Dialog * createDialog(GuiView & lv, string const & name);
4529 // will be replaced by a proper factory...
4530 Dialog * createGuiAbout(GuiView & lv);
4531 Dialog * createGuiBibtex(GuiView & lv);
4532 Dialog * createGuiChanges(GuiView & lv);
4533 Dialog * createGuiCharacter(GuiView & lv);
4534 Dialog * createGuiCitation(GuiView & lv);
4535 Dialog * createGuiCompare(GuiView & lv);
4536 Dialog * createGuiCompareHistory(GuiView & lv);
4537 Dialog * createGuiDelimiter(GuiView & lv);
4538 Dialog * createGuiDocument(GuiView & lv);
4539 Dialog * createGuiErrorList(GuiView & lv);
4540 Dialog * createGuiExternal(GuiView & lv);
4541 Dialog * createGuiGraphics(GuiView & lv);
4542 Dialog * createGuiInclude(GuiView & lv);
4543 Dialog * createGuiIndex(GuiView & lv);
4544 Dialog * createGuiListings(GuiView & lv);
4545 Dialog * createGuiLog(GuiView & lv);
4546 Dialog * createGuiMathMatrix(GuiView & lv);
4547 Dialog * createGuiNote(GuiView & lv);
4548 Dialog * createGuiParagraph(GuiView & lv);
4549 Dialog * createGuiPhantom(GuiView & lv);
4550 Dialog * createGuiPreferences(GuiView & lv);
4551 Dialog * createGuiPrint(GuiView & lv);
4552 Dialog * createGuiPrintindex(GuiView & lv);
4553 Dialog * createGuiRef(GuiView & lv);
4554 Dialog * createGuiSearch(GuiView & lv);
4555 Dialog * createGuiSearchAdv(GuiView & lv);
4556 Dialog * createGuiSendTo(GuiView & lv);
4557 Dialog * createGuiShowFile(GuiView & lv);
4558 Dialog * createGuiSpellchecker(GuiView & lv);
4559 Dialog * createGuiSymbols(GuiView & lv);
4560 Dialog * createGuiTabularCreate(GuiView & lv);
4561 Dialog * createGuiTexInfo(GuiView & lv);
4562 Dialog * createGuiToc(GuiView & lv);
4563 Dialog * createGuiThesaurus(GuiView & lv);
4564 Dialog * createGuiViewSource(GuiView & lv);
4565 Dialog * createGuiWrap(GuiView & lv);
4566 Dialog * createGuiProgressView(GuiView & lv);
4570 Dialog * GuiView::build(string const & name)
4572 LASSERT(isValidName(name), return 0);
4574 Dialog * dialog = createDialog(*this, name);
4578 if (name == "aboutlyx")
4579 return createGuiAbout(*this);
4580 if (name == "bibtex")
4581 return createGuiBibtex(*this);
4582 if (name == "changes")
4583 return createGuiChanges(*this);
4584 if (name == "character")
4585 return createGuiCharacter(*this);
4586 if (name == "citation")
4587 return createGuiCitation(*this);
4588 if (name == "compare")
4589 return createGuiCompare(*this);
4590 if (name == "comparehistory")
4591 return createGuiCompareHistory(*this);
4592 if (name == "document")
4593 return createGuiDocument(*this);
4594 if (name == "errorlist")
4595 return createGuiErrorList(*this);
4596 if (name == "external")
4597 return createGuiExternal(*this);
4599 return createGuiShowFile(*this);
4600 if (name == "findreplace")
4601 return createGuiSearch(*this);
4602 if (name == "findreplaceadv")
4603 return createGuiSearchAdv(*this);
4604 if (name == "graphics")
4605 return createGuiGraphics(*this);
4606 if (name == "include")
4607 return createGuiInclude(*this);
4608 if (name == "index")
4609 return createGuiIndex(*this);
4610 if (name == "index_print")
4611 return createGuiPrintindex(*this);
4612 if (name == "listings")
4613 return createGuiListings(*this);
4615 return createGuiLog(*this);
4616 if (name == "mathdelimiter")
4617 return createGuiDelimiter(*this);
4618 if (name == "mathmatrix")
4619 return createGuiMathMatrix(*this);
4621 return createGuiNote(*this);
4622 if (name == "paragraph")
4623 return createGuiParagraph(*this);
4624 if (name == "phantom")
4625 return createGuiPhantom(*this);
4626 if (name == "prefs")
4627 return createGuiPreferences(*this);
4629 return createGuiRef(*this);
4630 if (name == "sendto")
4631 return createGuiSendTo(*this);
4632 if (name == "spellchecker")
4633 return createGuiSpellchecker(*this);
4634 if (name == "symbols")
4635 return createGuiSymbols(*this);
4636 if (name == "tabularcreate")
4637 return createGuiTabularCreate(*this);
4638 if (name == "texinfo")
4639 return createGuiTexInfo(*this);
4640 if (name == "thesaurus")
4641 return createGuiThesaurus(*this);
4643 return createGuiToc(*this);
4644 if (name == "view-source")
4645 return createGuiViewSource(*this);
4647 return createGuiWrap(*this);
4648 if (name == "progress")
4649 return createGuiProgressView(*this);
4655 } // namespace frontend
4658 #include "moc_GuiView.cpp"