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 iconheight = max(int(d.normalIconSize), fm.height());
581 QSize const iconsize(iconheight, iconheight);
583 QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
584 shell_escape_ = new QLabel(statusBar());
585 shell_escape_->setPixmap(shellescape);
586 shell_escape_->setScaledContents(true);
587 shell_escape_->setAlignment(Qt::AlignCenter);
588 shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
589 shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
590 "external commands for this document. "
591 "Right click to change."));
592 SEMenu * menu = new SEMenu(this);
593 connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
594 menu, SLOT(showMenu(QPoint)));
595 shell_escape_->hide();
596 statusBar()->addPermanentWidget(shell_escape_);
598 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
599 read_only_ = new QLabel(statusBar());
600 read_only_->setPixmap(readonly);
601 read_only_->setScaledContents(true);
602 read_only_->setAlignment(Qt::AlignCenter);
604 statusBar()->addPermanentWidget(read_only_);
606 version_control_ = new QLabel(statusBar());
607 version_control_->setAlignment(Qt::AlignCenter);
608 version_control_->setFrameStyle(QFrame::StyledPanel);
609 version_control_->hide();
610 statusBar()->addPermanentWidget(version_control_);
612 statusBar()->setSizeGripEnabled(true);
615 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
616 SLOT(autoSaveThreadFinished()));
618 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
619 SLOT(processingThreadStarted()));
620 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
621 SLOT(processingThreadFinished()));
623 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
624 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
626 // set custom application bars context menu, e.g. tool bar and menu bar
627 setContextMenuPolicy(Qt::CustomContextMenu);
628 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
629 SLOT(toolBarPopup(const QPoint &)));
631 // Forbid too small unresizable window because it can happen
632 // with some window manager under X11.
633 setMinimumSize(300, 200);
635 if (lyxrc.allow_geometry_session) {
636 // Now take care of session management.
641 // no session handling, default to a sane size.
642 setGeometry(50, 50, 690, 510);
645 // clear session data if any.
647 settings.remove("views");
657 void GuiView::disableShellEscape()
659 BufferView * bv = documentBufferView();
662 theSession().shellescapeFiles().remove(bv->buffer().absFileName());
663 bv->buffer().params().shell_escape = false;
664 bv->processUpdateFlags(Update::Force);
668 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
670 QVector<GuiWorkArea*> areas;
671 for (int i = 0; i < tabWorkAreaCount(); i++) {
672 TabWorkArea* ta = tabWorkArea(i);
673 for (int u = 0; u < ta->count(); u++) {
674 areas << ta->workArea(u);
680 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
681 string const & format)
683 docstring const fmt = theFormats().prettyName(format);
686 case Buffer::ExportSuccess:
687 msg = bformat(_("Successful export to format: %1$s"), fmt);
689 case Buffer::ExportCancel:
690 msg = _("Document export cancelled.");
692 case Buffer::ExportError:
693 case Buffer::ExportNoPathToFormat:
694 case Buffer::ExportTexPathHasSpaces:
695 case Buffer::ExportConverterError:
696 msg = bformat(_("Error while exporting format: %1$s"), fmt);
698 case Buffer::PreviewSuccess:
699 msg = bformat(_("Successful preview of format: %1$s"), fmt);
701 case Buffer::PreviewError:
702 msg = bformat(_("Error while previewing format: %1$s"), fmt);
709 void GuiView::processingThreadStarted()
714 void GuiView::processingThreadFinished()
716 QFutureWatcher<Buffer::ExportStatus> const * watcher =
717 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
719 Buffer::ExportStatus const status = watcher->result();
720 handleExportStatus(this, status, d.processing_format);
723 BufferView const * const bv = currentBufferView();
724 if (bv && !bv->buffer().errorList("Export").empty()) {
728 errors(d.last_export_format);
732 void GuiView::autoSaveThreadFinished()
734 QFutureWatcher<docstring> const * watcher =
735 static_cast<QFutureWatcher<docstring> const *>(sender());
736 message(watcher->result());
741 void GuiView::saveLayout() const
744 settings.setValue("zoom_ratio", zoom_ratio_);
745 settings.setValue("devel_mode", devel_mode_);
746 settings.beginGroup("views");
747 settings.beginGroup(QString::number(id_));
748 #if defined(Q_WS_X11) || defined(QPA_XCB)
749 settings.setValue("pos", pos());
750 settings.setValue("size", size());
752 settings.setValue("geometry", saveGeometry());
754 settings.setValue("layout", saveState(0));
755 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
759 void GuiView::saveUISettings() const
763 // Save the toolbar private states
764 ToolbarMap::iterator end = d.toolbars_.end();
765 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
766 it->second->saveSession(settings);
767 // Now take care of all other dialogs
768 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
769 for (; it!= d.dialogs_.end(); ++it)
770 it->second->saveSession(settings);
774 bool GuiView::restoreLayout()
777 zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
778 // Actual zoom value: default zoom + fractional offset
779 int zoom = lyxrc.defaultZoom * zoom_ratio_;
780 if (zoom < static_cast<int>(zoom_min_))
782 lyxrc.currentZoom = zoom;
783 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
784 settings.beginGroup("views");
785 settings.beginGroup(QString::number(id_));
786 QString const icon_key = "icon_size";
787 if (!settings.contains(icon_key))
790 //code below is skipped when when ~/.config/LyX is (re)created
791 setIconSize(d.iconSize(settings.value(icon_key).toString()));
793 #if defined(Q_WS_X11) || defined(QPA_XCB)
794 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
795 QSize size = settings.value("size", QSize(690, 510)).toSize();
799 // Work-around for bug #6034: the window ends up in an undetermined
800 // state when trying to restore a maximized window when it is
801 // already maximized.
802 if (!(windowState() & Qt::WindowMaximized))
803 if (!restoreGeometry(settings.value("geometry").toByteArray()))
804 setGeometry(50, 50, 690, 510);
806 // Make sure layout is correctly oriented.
807 setLayoutDirection(qApp->layoutDirection());
809 // Allow the toc and view-source dock widget to be restored if needed.
811 if ((dialog = findOrBuild("toc", true)))
812 // see bug 5082. At least setup title and enabled state.
813 // Visibility will be adjusted by restoreState below.
814 dialog->prepareView();
815 if ((dialog = findOrBuild("view-source", true)))
816 dialog->prepareView();
817 if ((dialog = findOrBuild("progress", true)))
818 dialog->prepareView();
820 if (!restoreState(settings.value("layout").toByteArray(), 0))
823 // init the toolbars that have not been restored
824 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
825 Toolbars::Infos::iterator end = guiApp->toolbars().end();
826 for (; cit != end; ++cit) {
827 GuiToolbar * tb = toolbar(cit->name);
828 if (tb && !tb->isRestored())
829 initToolbar(cit->name);
832 // update lock (all) toolbars positions
833 updateLockToolbars();
840 GuiToolbar * GuiView::toolbar(string const & name)
842 ToolbarMap::iterator it = d.toolbars_.find(name);
843 if (it != d.toolbars_.end())
846 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
851 void GuiView::updateLockToolbars()
853 toolbarsMovable_ = false;
854 for (ToolbarInfo const & info : guiApp->toolbars()) {
855 GuiToolbar * tb = toolbar(info.name);
856 if (tb && tb->isMovable())
857 toolbarsMovable_ = true;
862 void GuiView::constructToolbars()
864 ToolbarMap::iterator it = d.toolbars_.begin();
865 for (; it != d.toolbars_.end(); ++it)
869 // I don't like doing this here, but the standard toolbar
870 // destroys this object when it's destroyed itself (vfr)
871 d.layout_ = new LayoutBox(*this);
872 d.stack_widget_->addWidget(d.layout_);
873 d.layout_->move(0,0);
875 // extracts the toolbars from the backend
876 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
877 Toolbars::Infos::iterator end = guiApp->toolbars().end();
878 for (; cit != end; ++cit)
879 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
883 void GuiView::initToolbars()
885 // extracts the toolbars from the backend
886 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
887 Toolbars::Infos::iterator end = guiApp->toolbars().end();
888 for (; cit != end; ++cit)
889 initToolbar(cit->name);
893 void GuiView::initToolbar(string const & name)
895 GuiToolbar * tb = toolbar(name);
898 int const visibility = guiApp->toolbars().defaultVisibility(name);
899 bool newline = !(visibility & Toolbars::SAMEROW);
900 tb->setVisible(false);
901 tb->setVisibility(visibility);
903 if (visibility & Toolbars::TOP) {
905 addToolBarBreak(Qt::TopToolBarArea);
906 addToolBar(Qt::TopToolBarArea, tb);
909 if (visibility & Toolbars::BOTTOM) {
911 addToolBarBreak(Qt::BottomToolBarArea);
912 addToolBar(Qt::BottomToolBarArea, tb);
915 if (visibility & Toolbars::LEFT) {
917 addToolBarBreak(Qt::LeftToolBarArea);
918 addToolBar(Qt::LeftToolBarArea, tb);
921 if (visibility & Toolbars::RIGHT) {
923 addToolBarBreak(Qt::RightToolBarArea);
924 addToolBar(Qt::RightToolBarArea, tb);
927 if (visibility & Toolbars::ON)
928 tb->setVisible(true);
930 tb->setMovable(true);
934 TocModels & GuiView::tocModels()
936 return d.toc_models_;
940 void GuiView::setFocus()
942 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
943 QMainWindow::setFocus();
947 bool GuiView::hasFocus() const
949 if (currentWorkArea())
950 return currentWorkArea()->hasFocus();
951 if (currentMainWorkArea())
952 return currentMainWorkArea()->hasFocus();
953 return d.bg_widget_->hasFocus();
957 void GuiView::focusInEvent(QFocusEvent * e)
959 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
960 QMainWindow::focusInEvent(e);
961 // Make sure guiApp points to the correct view.
962 guiApp->setCurrentView(this);
963 if (currentWorkArea())
964 currentWorkArea()->setFocus();
965 else if (currentMainWorkArea())
966 currentMainWorkArea()->setFocus();
968 d.bg_widget_->setFocus();
972 void GuiView::showEvent(QShowEvent * e)
974 LYXERR(Debug::GUI, "Passed Geometry "
975 << size().height() << "x" << size().width()
976 << "+" << pos().x() << "+" << pos().y());
978 if (d.splitter_->count() == 0)
979 // No work area, switch to the background widget.
983 QMainWindow::showEvent(e);
987 bool GuiView::closeScheduled()
994 bool GuiView::prepareAllBuffersForLogout()
996 Buffer * first = theBufferList().first();
1000 // First, iterate over all buffers and ask the users if unsaved
1001 // changes should be saved.
1002 // We cannot use a for loop as the buffer list cycles.
1005 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1007 b = theBufferList().next(b);
1008 } while (b != first);
1010 // Next, save session state
1011 // When a view/window was closed before without quitting LyX, there
1012 // are already entries in the lastOpened list.
1013 theSession().lastOpened().clear();
1020 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1021 ** is responsibility of the container (e.g., dialog)
1023 void GuiView::closeEvent(QCloseEvent * close_event)
1025 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1027 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1028 Alert::warning(_("Exit LyX"),
1029 _("LyX could not be closed because documents are being processed by LyX."));
1030 close_event->setAccepted(false);
1034 // If the user pressed the x (so we didn't call closeView
1035 // programmatically), we want to clear all existing entries.
1037 theSession().lastOpened().clear();
1042 // it can happen that this event arrives without selecting the view,
1043 // e.g. when clicking the close button on a background window.
1045 if (!closeWorkAreaAll()) {
1047 close_event->ignore();
1051 // Make sure that nothing will use this to be closed View.
1052 guiApp->unregisterView(this);
1054 if (isFullScreen()) {
1055 // Switch off fullscreen before closing.
1060 // Make sure the timer time out will not trigger a statusbar update.
1061 d.statusbar_timer_.stop();
1063 // Saving fullscreen requires additional tweaks in the toolbar code.
1064 // It wouldn't also work under linux natively.
1065 if (lyxrc.allow_geometry_session) {
1070 close_event->accept();
1074 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1076 if (event->mimeData()->hasUrls())
1078 /// \todo Ask lyx-devel is this is enough:
1079 /// if (event->mimeData()->hasFormat("text/plain"))
1080 /// event->acceptProposedAction();
1084 void GuiView::dropEvent(QDropEvent * event)
1086 QList<QUrl> files = event->mimeData()->urls();
1087 if (files.isEmpty())
1090 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1091 for (int i = 0; i != files.size(); ++i) {
1092 string const file = os::internal_path(fromqstr(
1093 files.at(i).toLocalFile()));
1097 string const ext = support::getExtension(file);
1098 vector<const Format *> found_formats;
1100 // Find all formats that have the correct extension.
1101 vector<const Format *> const & import_formats
1102 = theConverters().importableFormats();
1103 vector<const Format *>::const_iterator it = import_formats.begin();
1104 for (; it != import_formats.end(); ++it)
1105 if ((*it)->hasExtension(ext))
1106 found_formats.push_back(*it);
1109 if (found_formats.size() >= 1) {
1110 if (found_formats.size() > 1) {
1111 //FIXME: show a dialog to choose the correct importable format
1112 LYXERR(Debug::FILES,
1113 "Multiple importable formats found, selecting first");
1115 string const arg = found_formats[0]->name() + " " + file;
1116 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1119 //FIXME: do we have to explicitly check whether it's a lyx file?
1120 LYXERR(Debug::FILES,
1121 "No formats found, trying to open it as a lyx file");
1122 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1124 // add the functions to the queue
1125 guiApp->addToFuncRequestQueue(cmd);
1128 // now process the collected functions. We perform the events
1129 // asynchronously. This prevents potential problems in case the
1130 // BufferView is closed within an event.
1131 guiApp->processFuncRequestQueueAsync();
1135 void GuiView::message(docstring const & str)
1137 if (ForkedProcess::iAmAChild())
1140 // call is moved to GUI-thread by GuiProgress
1141 d.progress_->appendMessage(toqstr(str));
1145 void GuiView::clearMessageText()
1147 message(docstring());
1151 void GuiView::updateStatusBarMessage(QString const & str)
1153 statusBar()->showMessage(str);
1154 d.statusbar_timer_.stop();
1155 d.statusbar_timer_.start(3000);
1159 void GuiView::clearMessage()
1161 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1162 // the hasFocus function mostly returns false, even if the focus is on
1163 // a workarea in this view.
1167 d.statusbar_timer_.stop();
1171 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1173 if (wa != d.current_work_area_
1174 || wa->bufferView().buffer().isInternal())
1176 Buffer const & buf = wa->bufferView().buffer();
1177 // Set the windows title
1178 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1179 if (buf.notifiesExternalModification()) {
1180 title = bformat(_("%1$s (modified externally)"), title);
1181 // If the external modification status has changed, then maybe the status of
1182 // buffer-save has changed too.
1186 title += from_ascii(" - LyX");
1188 setWindowTitle(toqstr(title));
1189 // Sets the path for the window: this is used by OSX to
1190 // allow a context click on the title bar showing a menu
1191 // with the path up to the file
1192 setWindowFilePath(toqstr(buf.absFileName()));
1193 // Tell Qt whether the current document is changed
1194 setWindowModified(!buf.isClean());
1196 if (buf.params().shell_escape)
1197 shell_escape_->show();
1199 shell_escape_->hide();
1201 if (buf.hasReadonlyFlag())
1206 if (buf.lyxvc().inUse()) {
1207 version_control_->show();
1208 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1210 version_control_->hide();
1214 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1216 if (d.current_work_area_)
1217 // disconnect the current work area from all slots
1218 QObject::disconnect(d.current_work_area_, 0, this, 0);
1220 disconnectBufferView();
1221 connectBufferView(wa->bufferView());
1222 connectBuffer(wa->bufferView().buffer());
1223 d.current_work_area_ = wa;
1224 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1225 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1226 QObject::connect(wa, SIGNAL(busy(bool)),
1227 this, SLOT(setBusy(bool)));
1228 // connection of a signal to a signal
1229 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1230 this, SIGNAL(bufferViewChanged()));
1231 Q_EMIT updateWindowTitle(wa);
1232 Q_EMIT bufferViewChanged();
1236 void GuiView::onBufferViewChanged()
1239 // Buffer-dependent dialogs must be updated. This is done here because
1240 // some dialogs require buffer()->text.
1245 void GuiView::on_lastWorkAreaRemoved()
1248 // We already are in a close event. Nothing more to do.
1251 if (d.splitter_->count() > 1)
1252 // We have a splitter so don't close anything.
1255 // Reset and updates the dialogs.
1256 Q_EMIT bufferViewChanged();
1261 if (lyxrc.open_buffers_in_tabs)
1262 // Nothing more to do, the window should stay open.
1265 if (guiApp->viewIds().size() > 1) {
1271 // On Mac we also close the last window because the application stay
1272 // resident in memory. On other platforms we don't close the last
1273 // window because this would quit the application.
1279 void GuiView::updateStatusBar()
1281 // let the user see the explicit message
1282 if (d.statusbar_timer_.isActive())
1289 void GuiView::showMessage()
1293 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1294 if (msg.isEmpty()) {
1295 BufferView const * bv = currentBufferView();
1297 msg = toqstr(bv->cursor().currentState(devel_mode_));
1299 msg = qt_("Welcome to LyX!");
1301 statusBar()->showMessage(msg);
1305 bool GuiView::event(QEvent * e)
1309 // Useful debug code:
1310 //case QEvent::ActivationChange:
1311 //case QEvent::WindowDeactivate:
1312 //case QEvent::Paint:
1313 //case QEvent::Enter:
1314 //case QEvent::Leave:
1315 //case QEvent::HoverEnter:
1316 //case QEvent::HoverLeave:
1317 //case QEvent::HoverMove:
1318 //case QEvent::StatusTip:
1319 //case QEvent::DragEnter:
1320 //case QEvent::DragLeave:
1321 //case QEvent::Drop:
1324 case QEvent::WindowActivate: {
1325 GuiView * old_view = guiApp->currentView();
1326 if (this == old_view) {
1328 return QMainWindow::event(e);
1330 if (old_view && old_view->currentBufferView()) {
1331 // save current selection to the selection buffer to allow
1332 // middle-button paste in this window.
1333 cap::saveSelection(old_view->currentBufferView()->cursor());
1335 guiApp->setCurrentView(this);
1336 if (d.current_work_area_)
1337 on_currentWorkAreaChanged(d.current_work_area_);
1341 return QMainWindow::event(e);
1344 case QEvent::ShortcutOverride: {
1346 if (isFullScreen() && menuBar()->isHidden()) {
1347 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1348 // FIXME: we should also try to detect special LyX shortcut such as
1349 // Alt-P and Alt-M. Right now there is a hack in
1350 // GuiWorkArea::processKeySym() that hides again the menubar for
1352 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1354 return QMainWindow::event(e);
1357 return QMainWindow::event(e);
1361 return QMainWindow::event(e);
1365 void GuiView::resetWindowTitle()
1367 setWindowTitle(qt_("LyX"));
1370 bool GuiView::focusNextPrevChild(bool /*next*/)
1377 bool GuiView::busy() const
1383 void GuiView::setBusy(bool busy)
1385 bool const busy_before = busy_ > 0;
1386 busy ? ++busy_ : --busy_;
1387 if ((busy_ > 0) == busy_before)
1388 // busy state didn't change
1392 QApplication::setOverrideCursor(Qt::WaitCursor);
1395 QApplication::restoreOverrideCursor();
1400 void GuiView::resetCommandExecute()
1402 command_execute_ = false;
1407 double GuiView::pixelRatio() const
1409 #if QT_VERSION >= 0x050000
1410 return qt_scale_factor * devicePixelRatio();
1417 GuiWorkArea * GuiView::workArea(int index)
1419 if (TabWorkArea * twa = d.currentTabWorkArea())
1420 if (index < twa->count())
1421 return twa->workArea(index);
1426 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1428 if (currentWorkArea()
1429 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1430 return currentWorkArea();
1431 if (TabWorkArea * twa = d.currentTabWorkArea())
1432 return twa->workArea(buffer);
1437 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1439 // Automatically create a TabWorkArea if there are none yet.
1440 TabWorkArea * tab_widget = d.splitter_->count()
1441 ? d.currentTabWorkArea() : addTabWorkArea();
1442 return tab_widget->addWorkArea(buffer, *this);
1446 TabWorkArea * GuiView::addTabWorkArea()
1448 TabWorkArea * twa = new TabWorkArea;
1449 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1450 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1451 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1452 this, SLOT(on_lastWorkAreaRemoved()));
1454 d.splitter_->addWidget(twa);
1455 d.stack_widget_->setCurrentWidget(d.splitter_);
1460 GuiWorkArea const * GuiView::currentWorkArea() const
1462 return d.current_work_area_;
1466 GuiWorkArea * GuiView::currentWorkArea()
1468 return d.current_work_area_;
1472 GuiWorkArea const * GuiView::currentMainWorkArea() const
1474 if (!d.currentTabWorkArea())
1476 return d.currentTabWorkArea()->currentWorkArea();
1480 GuiWorkArea * GuiView::currentMainWorkArea()
1482 if (!d.currentTabWorkArea())
1484 return d.currentTabWorkArea()->currentWorkArea();
1488 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1490 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1492 d.current_work_area_ = 0;
1494 Q_EMIT bufferViewChanged();
1498 // FIXME: I've no clue why this is here and why it accesses
1499 // theGuiApp()->currentView, which might be 0 (bug 6464).
1500 // See also 27525 (vfr).
1501 if (theGuiApp()->currentView() == this
1502 && theGuiApp()->currentView()->currentWorkArea() == wa)
1505 if (currentBufferView())
1506 cap::saveSelection(currentBufferView()->cursor());
1508 theGuiApp()->setCurrentView(this);
1509 d.current_work_area_ = wa;
1511 // We need to reset this now, because it will need to be
1512 // right if the tabWorkArea gets reset in the for loop. We
1513 // will change it back if we aren't in that case.
1514 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1515 d.current_main_work_area_ = wa;
1517 for (int i = 0; i != d.splitter_->count(); ++i) {
1518 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1519 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1520 << ", Current main wa: " << currentMainWorkArea());
1525 d.current_main_work_area_ = old_cmwa;
1527 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1528 on_currentWorkAreaChanged(wa);
1529 BufferView & bv = wa->bufferView();
1530 bv.cursor().fixIfBroken();
1532 wa->setUpdatesEnabled(true);
1533 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1537 void GuiView::removeWorkArea(GuiWorkArea * wa)
1539 LASSERT(wa, return);
1540 if (wa == d.current_work_area_) {
1542 disconnectBufferView();
1543 d.current_work_area_ = 0;
1544 d.current_main_work_area_ = 0;
1547 bool found_twa = false;
1548 for (int i = 0; i != d.splitter_->count(); ++i) {
1549 TabWorkArea * twa = d.tabWorkArea(i);
1550 if (twa->removeWorkArea(wa)) {
1551 // Found in this tab group, and deleted the GuiWorkArea.
1553 if (twa->count() != 0) {
1554 if (d.current_work_area_ == 0)
1555 // This means that we are closing the current GuiWorkArea, so
1556 // switch to the next GuiWorkArea in the found TabWorkArea.
1557 setCurrentWorkArea(twa->currentWorkArea());
1559 // No more WorkAreas in this tab group, so delete it.
1566 // It is not a tabbed work area (i.e., the search work area), so it
1567 // should be deleted by other means.
1568 LASSERT(found_twa, return);
1570 if (d.current_work_area_ == 0) {
1571 if (d.splitter_->count() != 0) {
1572 TabWorkArea * twa = d.currentTabWorkArea();
1573 setCurrentWorkArea(twa->currentWorkArea());
1575 // No more work areas, switch to the background widget.
1576 setCurrentWorkArea(0);
1582 LayoutBox * GuiView::getLayoutDialog() const
1588 void GuiView::updateLayoutList()
1591 d.layout_->updateContents(false);
1595 void GuiView::updateToolbars()
1597 ToolbarMap::iterator end = d.toolbars_.end();
1598 if (d.current_work_area_) {
1600 if (d.current_work_area_->bufferView().cursor().inMathed()
1601 && !d.current_work_area_->bufferView().cursor().inRegexped())
1602 context |= Toolbars::MATH;
1603 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1604 context |= Toolbars::TABLE;
1605 if (currentBufferView()->buffer().areChangesPresent()
1606 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1607 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1608 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1609 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1610 context |= Toolbars::REVIEW;
1611 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1612 context |= Toolbars::MATHMACROTEMPLATE;
1613 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1614 context |= Toolbars::IPA;
1615 if (command_execute_)
1616 context |= Toolbars::MINIBUFFER;
1617 if (minibuffer_focus_) {
1618 context |= Toolbars::MINIBUFFER_FOCUS;
1619 minibuffer_focus_ = false;
1622 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1623 it->second->update(context);
1625 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1626 it->second->update();
1630 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1632 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1633 LASSERT(newBuffer, return);
1635 GuiWorkArea * wa = workArea(*newBuffer);
1638 newBuffer->masterBuffer()->updateBuffer();
1640 wa = addWorkArea(*newBuffer);
1641 // scroll to the position when the BufferView was last closed
1642 if (lyxrc.use_lastfilepos) {
1643 LastFilePosSection::FilePos filepos =
1644 theSession().lastFilePos().load(newBuffer->fileName());
1645 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1648 //Disconnect the old buffer...there's no new one.
1651 connectBuffer(*newBuffer);
1652 connectBufferView(wa->bufferView());
1654 setCurrentWorkArea(wa);
1658 void GuiView::connectBuffer(Buffer & buf)
1660 buf.setGuiDelegate(this);
1664 void GuiView::disconnectBuffer()
1666 if (d.current_work_area_)
1667 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1671 void GuiView::connectBufferView(BufferView & bv)
1673 bv.setGuiDelegate(this);
1677 void GuiView::disconnectBufferView()
1679 if (d.current_work_area_)
1680 d.current_work_area_->bufferView().setGuiDelegate(0);
1684 void GuiView::errors(string const & error_type, bool from_master)
1686 BufferView const * const bv = currentBufferView();
1690 #if EXPORT_in_THREAD
1691 // We are called with from_master == false by default, so we
1692 // have to figure out whether that is the case or not.
1693 ErrorList & el = bv->buffer().errorList(error_type);
1695 el = bv->buffer().masterBuffer()->errorList(error_type);
1699 ErrorList const & el = from_master ?
1700 bv->buffer().masterBuffer()->errorList(error_type) :
1701 bv->buffer().errorList(error_type);
1707 string data = error_type;
1709 data = "from_master|" + error_type;
1710 showDialog("errorlist", data);
1714 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1716 d.toc_models_.updateItem(toqstr(type), dit);
1720 void GuiView::structureChanged()
1722 // This is called from the Buffer, which has no way to ensure that cursors
1723 // in BufferView remain valid.
1724 if (documentBufferView())
1725 documentBufferView()->cursor().sanitize();
1726 // FIXME: This is slightly expensive, though less than the tocBackend update
1727 // (#9880). This also resets the view in the Toc Widget (#6675).
1728 d.toc_models_.reset(documentBufferView());
1729 // Navigator needs more than a simple update in this case. It needs to be
1731 updateDialog("toc", "");
1735 void GuiView::updateDialog(string const & name, string const & data)
1737 if (!isDialogVisible(name))
1740 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1741 if (it == d.dialogs_.end())
1744 Dialog * const dialog = it->second.get();
1745 if (dialog->isVisibleView())
1746 dialog->initialiseParams(data);
1750 BufferView * GuiView::documentBufferView()
1752 return currentMainWorkArea()
1753 ? ¤tMainWorkArea()->bufferView()
1758 BufferView const * GuiView::documentBufferView() const
1760 return currentMainWorkArea()
1761 ? ¤tMainWorkArea()->bufferView()
1766 BufferView * GuiView::currentBufferView()
1768 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1772 BufferView const * GuiView::currentBufferView() const
1774 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1778 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1779 Buffer const * orig, Buffer * clone)
1781 bool const success = clone->autoSave();
1783 busyBuffers.remove(orig);
1785 ? _("Automatic save done.")
1786 : _("Automatic save failed!");
1790 void GuiView::autoSave()
1792 LYXERR(Debug::INFO, "Running autoSave()");
1794 Buffer * buffer = documentBufferView()
1795 ? &documentBufferView()->buffer() : 0;
1797 resetAutosaveTimers();
1801 GuiViewPrivate::busyBuffers.insert(buffer);
1802 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1803 buffer, buffer->cloneBufferOnly());
1804 d.autosave_watcher_.setFuture(f);
1805 resetAutosaveTimers();
1809 void GuiView::resetAutosaveTimers()
1812 d.autosave_timeout_.restart();
1816 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1819 Buffer * buf = currentBufferView()
1820 ? ¤tBufferView()->buffer() : 0;
1821 Buffer * doc_buffer = documentBufferView()
1822 ? &(documentBufferView()->buffer()) : 0;
1825 /* In LyX/Mac, when a dialog is open, the menus of the
1826 application can still be accessed without giving focus to
1827 the main window. In this case, we want to disable the menu
1828 entries that are buffer-related.
1829 This code must not be used on Linux and Windows, since it
1830 would disable buffer-related entries when hovering over the
1831 menu (see bug #9574).
1833 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1839 // Check whether we need a buffer
1840 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1841 // no, exit directly
1842 flag.message(from_utf8(N_("Command not allowed with"
1843 "out any document open")));
1844 flag.setEnabled(false);
1848 if (cmd.origin() == FuncRequest::TOC) {
1849 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1850 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1851 flag.setEnabled(false);
1855 switch(cmd.action()) {
1856 case LFUN_BUFFER_IMPORT:
1859 case LFUN_MASTER_BUFFER_UPDATE:
1860 case LFUN_MASTER_BUFFER_VIEW:
1862 && (doc_buffer->parent() != 0
1863 || doc_buffer->hasChildren())
1864 && !d.processing_thread_watcher_.isRunning();
1867 case LFUN_BUFFER_UPDATE:
1868 case LFUN_BUFFER_VIEW: {
1869 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1873 string format = to_utf8(cmd.argument());
1874 if (cmd.argument().empty())
1875 format = doc_buffer->params().getDefaultOutputFormat();
1876 enable = doc_buffer->params().isExportable(format, true);
1880 case LFUN_BUFFER_RELOAD:
1881 enable = doc_buffer && !doc_buffer->isUnnamed()
1882 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1885 case LFUN_BUFFER_CHILD_OPEN:
1886 enable = doc_buffer != 0;
1889 case LFUN_BUFFER_WRITE:
1890 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1893 //FIXME: This LFUN should be moved to GuiApplication.
1894 case LFUN_BUFFER_WRITE_ALL: {
1895 // We enable the command only if there are some modified buffers
1896 Buffer * first = theBufferList().first();
1901 // We cannot use a for loop as the buffer list is a cycle.
1903 if (!b->isClean()) {
1907 b = theBufferList().next(b);
1908 } while (b != first);
1912 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1913 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1916 case LFUN_BUFFER_WRITE_AS:
1917 case LFUN_BUFFER_EXPORT_AS:
1918 enable = doc_buffer != 0;
1921 case LFUN_EXPORT_CANCEL:
1922 enable = d.processing_thread_watcher_.isRunning();
1925 case LFUN_BUFFER_CLOSE:
1926 case LFUN_VIEW_CLOSE:
1927 enable = doc_buffer != 0;
1930 case LFUN_BUFFER_CLOSE_ALL:
1931 enable = theBufferList().last() != theBufferList().first();
1934 case LFUN_BUFFER_CHKTEX: {
1935 if (!doc_buffer || !doc_buffer->params().isLatex()
1936 || d.processing_thread_watcher_.isRunning()) {
1937 // grey out, don't hide
1941 // hide if we have no checktex command
1942 enable = !lyxrc.chktex_command.empty();
1943 flag.setUnknown(!enable);
1947 case LFUN_VIEW_SPLIT:
1948 if (cmd.getArg(0) == "vertical")
1949 enable = doc_buffer && (d.splitter_->count() == 1 ||
1950 d.splitter_->orientation() == Qt::Vertical);
1952 enable = doc_buffer && (d.splitter_->count() == 1 ||
1953 d.splitter_->orientation() == Qt::Horizontal);
1956 case LFUN_TAB_GROUP_CLOSE:
1957 enable = d.tabWorkAreaCount() > 1;
1960 case LFUN_DEVEL_MODE_TOGGLE:
1961 flag.setOnOff(devel_mode_);
1964 case LFUN_TOOLBAR_TOGGLE: {
1965 string const name = cmd.getArg(0);
1966 if (GuiToolbar * t = toolbar(name))
1967 flag.setOnOff(t->isVisible());
1970 docstring const msg =
1971 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1977 case LFUN_TOOLBAR_MOVABLE: {
1978 string const name = cmd.getArg(0);
1979 // use negation since locked == !movable
1981 // toolbar name * locks all toolbars
1982 flag.setOnOff(!toolbarsMovable_);
1983 else if (GuiToolbar * t = toolbar(name))
1984 flag.setOnOff(!(t->isMovable()));
1987 docstring const msg =
1988 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1994 case LFUN_ICON_SIZE:
1995 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1998 case LFUN_DROP_LAYOUTS_CHOICE:
2002 case LFUN_UI_TOGGLE:
2003 flag.setOnOff(isFullScreen());
2006 case LFUN_DIALOG_DISCONNECT_INSET:
2009 case LFUN_DIALOG_HIDE:
2010 // FIXME: should we check if the dialog is shown?
2013 case LFUN_DIALOG_TOGGLE:
2014 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
2017 case LFUN_DIALOG_SHOW: {
2018 string const name = cmd.getArg(0);
2020 enable = name == "aboutlyx"
2021 || name == "file" //FIXME: should be removed.
2023 || name == "texinfo"
2024 || name == "progress"
2025 || name == "compare";
2026 else if (name == "character" || name == "symbols"
2027 || name == "mathdelimiter" || name == "mathmatrix") {
2028 if (!buf || buf->isReadonly())
2031 Cursor const & cur = currentBufferView()->cursor();
2032 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2035 else if (name == "latexlog")
2036 enable = FileName(doc_buffer->logName()).isReadableFile();
2037 else if (name == "spellchecker")
2038 enable = theSpellChecker()
2039 && !doc_buffer->isReadonly()
2040 && !doc_buffer->text().empty();
2041 else if (name == "vclog")
2042 enable = doc_buffer->lyxvc().inUse();
2046 case LFUN_DIALOG_UPDATE: {
2047 string const name = cmd.getArg(0);
2049 enable = name == "prefs";
2053 case LFUN_COMMAND_EXECUTE:
2055 case LFUN_MENU_OPEN:
2056 // Nothing to check.
2059 case LFUN_COMPLETION_INLINE:
2060 if (!d.current_work_area_
2061 || !d.current_work_area_->completer().inlinePossible(
2062 currentBufferView()->cursor()))
2066 case LFUN_COMPLETION_POPUP:
2067 if (!d.current_work_area_
2068 || !d.current_work_area_->completer().popupPossible(
2069 currentBufferView()->cursor()))
2074 if (!d.current_work_area_
2075 || !d.current_work_area_->completer().inlinePossible(
2076 currentBufferView()->cursor()))
2080 case LFUN_COMPLETION_ACCEPT:
2081 if (!d.current_work_area_
2082 || (!d.current_work_area_->completer().popupVisible()
2083 && !d.current_work_area_->completer().inlineVisible()
2084 && !d.current_work_area_->completer().completionAvailable()))
2088 case LFUN_COMPLETION_CANCEL:
2089 if (!d.current_work_area_
2090 || (!d.current_work_area_->completer().popupVisible()
2091 && !d.current_work_area_->completer().inlineVisible()))
2095 case LFUN_BUFFER_ZOOM_OUT:
2096 case LFUN_BUFFER_ZOOM_IN: {
2097 // only diff between these two is that the default for ZOOM_OUT
2099 bool const neg_zoom =
2100 convert<int>(cmd.argument()) < 0 ||
2101 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2102 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2103 docstring const msg =
2104 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2108 enable = doc_buffer;
2112 case LFUN_BUFFER_ZOOM: {
2113 bool const less_than_min_zoom =
2114 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2115 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2116 docstring const msg =
2117 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2122 enable = doc_buffer;
2126 case LFUN_BUFFER_MOVE_NEXT:
2127 case LFUN_BUFFER_MOVE_PREVIOUS:
2128 // we do not cycle when moving
2129 case LFUN_BUFFER_NEXT:
2130 case LFUN_BUFFER_PREVIOUS:
2131 // because we cycle, it doesn't matter whether on first or last
2132 enable = (d.currentTabWorkArea()->count() > 1);
2134 case LFUN_BUFFER_SWITCH:
2135 // toggle on the current buffer, but do not toggle off
2136 // the other ones (is that a good idea?)
2138 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2139 flag.setOnOff(true);
2142 case LFUN_VC_REGISTER:
2143 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2145 case LFUN_VC_RENAME:
2146 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2149 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2151 case LFUN_VC_CHECK_IN:
2152 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2154 case LFUN_VC_CHECK_OUT:
2155 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2157 case LFUN_VC_LOCKING_TOGGLE:
2158 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2159 && doc_buffer->lyxvc().lockingToggleEnabled();
2160 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2162 case LFUN_VC_REVERT:
2163 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2164 && !doc_buffer->hasReadonlyFlag();
2166 case LFUN_VC_UNDO_LAST:
2167 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2169 case LFUN_VC_REPO_UPDATE:
2170 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2172 case LFUN_VC_COMMAND: {
2173 if (cmd.argument().empty())
2175 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2179 case LFUN_VC_COMPARE:
2180 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2183 case LFUN_SERVER_GOTO_FILE_ROW:
2184 case LFUN_LYX_ACTIVATE:
2186 case LFUN_FORWARD_SEARCH:
2187 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2190 case LFUN_FILE_INSERT_PLAINTEXT:
2191 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2192 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2195 case LFUN_SPELLING_CONTINUOUSLY:
2196 flag.setOnOff(lyxrc.spellcheck_continuously);
2204 flag.setEnabled(false);
2210 static FileName selectTemplateFile()
2212 FileDialog dlg(qt_("Select template file"));
2213 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2214 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2216 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2217 QStringList(qt_("LyX Documents (*.lyx)")));
2219 if (result.first == FileDialog::Later)
2221 if (result.second.isEmpty())
2223 return FileName(fromqstr(result.second));
2227 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2231 Buffer * newBuffer = 0;
2233 newBuffer = checkAndLoadLyXFile(filename);
2234 } catch (ExceptionMessage const & e) {
2241 message(_("Document not loaded."));
2245 setBuffer(newBuffer);
2246 newBuffer->errors("Parse");
2249 theSession().lastFiles().add(filename);
2255 void GuiView::openDocument(string const & fname)
2257 string initpath = lyxrc.document_path;
2259 if (documentBufferView()) {
2260 string const trypath = documentBufferView()->buffer().filePath();
2261 // If directory is writeable, use this as default.
2262 if (FileName(trypath).isDirWritable())
2268 if (fname.empty()) {
2269 FileDialog dlg(qt_("Select document to open"));
2270 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2271 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2273 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2274 FileDialog::Result result =
2275 dlg.open(toqstr(initpath), filter);
2277 if (result.first == FileDialog::Later)
2280 filename = fromqstr(result.second);
2282 // check selected filename
2283 if (filename.empty()) {
2284 message(_("Canceled."));
2290 // get absolute path of file and add ".lyx" to the filename if
2292 FileName const fullname =
2293 fileSearch(string(), filename, "lyx", support::may_not_exist);
2294 if (!fullname.empty())
2295 filename = fullname.absFileName();
2297 if (!fullname.onlyPath().isDirectory()) {
2298 Alert::warning(_("Invalid filename"),
2299 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2300 from_utf8(fullname.absFileName())));
2304 // if the file doesn't exist and isn't already open (bug 6645),
2305 // let the user create one
2306 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2307 !LyXVC::file_not_found_hook(fullname)) {
2308 // the user specifically chose this name. Believe him.
2309 Buffer * const b = newFile(filename, string(), true);
2315 docstring const disp_fn = makeDisplayPath(filename);
2316 message(bformat(_("Opening document %1$s..."), disp_fn));
2319 Buffer * buf = loadDocument(fullname);
2321 str2 = bformat(_("Document %1$s opened."), disp_fn);
2322 if (buf->lyxvc().inUse())
2323 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2324 " " + _("Version control detected.");
2326 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2331 // FIXME: clean that
2332 static bool import(GuiView * lv, FileName const & filename,
2333 string const & format, ErrorList & errorList)
2335 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2337 string loader_format;
2338 vector<string> loaders = theConverters().loaders();
2339 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2340 vector<string>::const_iterator it = loaders.begin();
2341 vector<string>::const_iterator en = loaders.end();
2342 for (; it != en; ++it) {
2343 if (!theConverters().isReachable(format, *it))
2346 string const tofile =
2347 support::changeExtension(filename.absFileName(),
2348 theFormats().extension(*it));
2349 if (!theConverters().convert(0, filename, FileName(tofile),
2350 filename, format, *it, errorList))
2352 loader_format = *it;
2355 if (loader_format.empty()) {
2356 frontend::Alert::error(_("Couldn't import file"),
2357 bformat(_("No information for importing the format %1$s."),
2358 theFormats().prettyName(format)));
2362 loader_format = format;
2364 if (loader_format == "lyx") {
2365 Buffer * buf = lv->loadDocument(lyxfile);
2369 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2373 bool as_paragraphs = loader_format == "textparagraph";
2374 string filename2 = (loader_format == format) ? filename.absFileName()
2375 : support::changeExtension(filename.absFileName(),
2376 theFormats().extension(loader_format));
2377 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2379 guiApp->setCurrentView(lv);
2380 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2387 void GuiView::importDocument(string const & argument)
2390 string filename = split(argument, format, ' ');
2392 LYXERR(Debug::INFO, format << " file: " << filename);
2394 // need user interaction
2395 if (filename.empty()) {
2396 string initpath = lyxrc.document_path;
2397 if (documentBufferView()) {
2398 string const trypath = documentBufferView()->buffer().filePath();
2399 // If directory is writeable, use this as default.
2400 if (FileName(trypath).isDirWritable())
2404 docstring const text = bformat(_("Select %1$s file to import"),
2405 theFormats().prettyName(format));
2407 FileDialog dlg(toqstr(text));
2408 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2409 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2411 docstring filter = theFormats().prettyName(format);
2414 filter += from_utf8(theFormats().extensions(format));
2417 FileDialog::Result result =
2418 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2420 if (result.first == FileDialog::Later)
2423 filename = fromqstr(result.second);
2425 // check selected filename
2426 if (filename.empty())
2427 message(_("Canceled."));
2430 if (filename.empty())
2433 // get absolute path of file
2434 FileName const fullname(support::makeAbsPath(filename));
2436 // Can happen if the user entered a path into the dialog
2438 if (fullname.onlyFileName().empty()) {
2439 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2440 "Aborting import."),
2441 from_utf8(fullname.absFileName()));
2442 frontend::Alert::error(_("File name error"), msg);
2443 message(_("Canceled."));
2448 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2450 // Check if the document already is open
2451 Buffer * buf = theBufferList().getBuffer(lyxfile);
2454 if (!closeBuffer()) {
2455 message(_("Canceled."));
2460 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2462 // if the file exists already, and we didn't do
2463 // -i lyx thefile.lyx, warn
2464 if (lyxfile.exists() && fullname != lyxfile) {
2466 docstring text = bformat(_("The document %1$s already exists.\n\n"
2467 "Do you want to overwrite that document?"), displaypath);
2468 int const ret = Alert::prompt(_("Overwrite document?"),
2469 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2472 message(_("Canceled."));
2477 message(bformat(_("Importing %1$s..."), displaypath));
2478 ErrorList errorList;
2479 if (import(this, fullname, format, errorList))
2480 message(_("imported."));
2482 message(_("file not imported!"));
2484 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2488 void GuiView::newDocument(string const & filename, bool from_template)
2490 FileName initpath(lyxrc.document_path);
2491 if (documentBufferView()) {
2492 FileName const trypath(documentBufferView()->buffer().filePath());
2493 // If directory is writeable, use this as default.
2494 if (trypath.isDirWritable())
2498 string templatefile;
2499 if (from_template) {
2500 templatefile = selectTemplateFile().absFileName();
2501 if (templatefile.empty())
2506 if (filename.empty())
2507 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2509 b = newFile(filename, templatefile, true);
2514 // If no new document could be created, it is unsure
2515 // whether there is a valid BufferView.
2516 if (currentBufferView())
2517 // Ensure the cursor is correctly positioned on screen.
2518 currentBufferView()->showCursor();
2522 void GuiView::insertLyXFile(docstring const & fname)
2524 BufferView * bv = documentBufferView();
2529 FileName filename(to_utf8(fname));
2530 if (filename.empty()) {
2531 // Launch a file browser
2533 string initpath = lyxrc.document_path;
2534 string const trypath = bv->buffer().filePath();
2535 // If directory is writeable, use this as default.
2536 if (FileName(trypath).isDirWritable())
2540 FileDialog dlg(qt_("Select LyX document to insert"));
2541 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2542 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
2544 FileDialog::Result result = dlg.open(toqstr(initpath),
2545 QStringList(qt_("LyX Documents (*.lyx)")));
2547 if (result.first == FileDialog::Later)
2551 filename.set(fromqstr(result.second));
2553 // check selected filename
2554 if (filename.empty()) {
2555 // emit message signal.
2556 message(_("Canceled."));
2561 bv->insertLyXFile(filename);
2562 bv->buffer().errors("Parse");
2566 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2568 FileName fname = b.fileName();
2569 FileName const oldname = fname;
2571 if (!newname.empty()) {
2573 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2575 // Switch to this Buffer.
2578 // No argument? Ask user through dialog.
2580 FileDialog dlg(qt_("Choose a filename to save document as"));
2581 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2582 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
2584 if (!isLyXFileName(fname.absFileName()))
2585 fname.changeExtension(".lyx");
2587 FileDialog::Result result =
2588 dlg.save(toqstr(fname.onlyPath().absFileName()),
2589 QStringList(qt_("LyX Documents (*.lyx)")),
2590 toqstr(fname.onlyFileName()));
2592 if (result.first == FileDialog::Later)
2595 fname.set(fromqstr(result.second));
2600 if (!isLyXFileName(fname.absFileName()))
2601 fname.changeExtension(".lyx");
2604 // fname is now the new Buffer location.
2606 // if there is already a Buffer open with this name, we do not want
2607 // to have another one. (the second test makes sure we're not just
2608 // trying to overwrite ourselves, which is fine.)
2609 if (theBufferList().exists(fname) && fname != oldname
2610 && theBufferList().getBuffer(fname) != &b) {
2611 docstring const text =
2612 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2613 "Please close it before attempting to overwrite it.\n"
2614 "Do you want to choose a new filename?"),
2615 from_utf8(fname.absFileName()));
2616 int const ret = Alert::prompt(_("Chosen File Already Open"),
2617 text, 0, 1, _("&Rename"), _("&Cancel"));
2619 case 0: return renameBuffer(b, docstring(), kind);
2620 case 1: return false;
2625 bool const existsLocal = fname.exists();
2626 bool const existsInVC = LyXVC::fileInVC(fname);
2627 if (existsLocal || existsInVC) {
2628 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2629 if (kind != LV_WRITE_AS && existsInVC) {
2630 // renaming to a name that is already in VC
2632 docstring text = bformat(_("The document %1$s "
2633 "is already registered.\n\n"
2634 "Do you want to choose a new name?"),
2636 docstring const title = (kind == LV_VC_RENAME) ?
2637 _("Rename document?") : _("Copy document?");
2638 docstring const button = (kind == LV_VC_RENAME) ?
2639 _("&Rename") : _("&Copy");
2640 int const ret = Alert::prompt(title, text, 0, 1,
2641 button, _("&Cancel"));
2643 case 0: return renameBuffer(b, docstring(), kind);
2644 case 1: return false;
2649 docstring text = bformat(_("The document %1$s "
2650 "already exists.\n\n"
2651 "Do you want to overwrite that document?"),
2653 int const ret = Alert::prompt(_("Overwrite document?"),
2654 text, 0, 2, _("&Overwrite"),
2655 _("&Rename"), _("&Cancel"));
2658 case 1: return renameBuffer(b, docstring(), kind);
2659 case 2: return false;
2665 case LV_VC_RENAME: {
2666 string msg = b.lyxvc().rename(fname);
2669 message(from_utf8(msg));
2673 string msg = b.lyxvc().copy(fname);
2676 message(from_utf8(msg));
2682 // LyXVC created the file already in case of LV_VC_RENAME or
2683 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2684 // relative paths of included stuff right if we moved e.g. from
2685 // /a/b.lyx to /a/c/b.lyx.
2687 bool const saved = saveBuffer(b, fname);
2694 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2696 FileName fname = b.fileName();
2698 FileDialog dlg(qt_("Choose a filename to export the document as"));
2699 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
2702 QString const anyformat = qt_("Guess from extension (*.*)");
2705 vector<Format const *> export_formats;
2706 for (Format const & f : theFormats())
2707 if (f.documentFormat())
2708 export_formats.push_back(&f);
2709 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2710 map<QString, string> fmap;
2713 for (Format const * f : export_formats) {
2714 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2715 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2717 from_ascii(f->extension())));
2718 types << loc_filter;
2719 fmap[loc_filter] = f->name();
2720 if (from_ascii(f->name()) == iformat) {
2721 filter = loc_filter;
2722 ext = f->extension();
2725 string ofname = fname.onlyFileName();
2727 ofname = support::changeExtension(ofname, ext);
2728 FileDialog::Result result =
2729 dlg.save(toqstr(fname.onlyPath().absFileName()),
2733 if (result.first != FileDialog::Chosen)
2737 fname.set(fromqstr(result.second));
2738 if (filter == anyformat)
2739 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2741 fmt_name = fmap[filter];
2742 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2743 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2745 if (fmt_name.empty() || fname.empty())
2748 // fname is now the new Buffer location.
2749 if (FileName(fname).exists()) {
2750 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2751 docstring text = bformat(_("The document %1$s already "
2752 "exists.\n\nDo you want to "
2753 "overwrite that document?"),
2755 int const ret = Alert::prompt(_("Overwrite document?"),
2756 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2759 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2760 case 2: return false;
2764 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2767 return dr.dispatched();
2771 bool GuiView::saveBuffer(Buffer & b)
2773 return saveBuffer(b, FileName());
2777 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2779 if (workArea(b) && workArea(b)->inDialogMode())
2782 if (fn.empty() && b.isUnnamed())
2783 return renameBuffer(b, docstring());
2785 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2787 theSession().lastFiles().add(b.fileName());
2791 // Switch to this Buffer.
2794 // FIXME: we don't tell the user *WHY* the save failed !!
2795 docstring const file = makeDisplayPath(b.absFileName(), 30);
2796 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2797 "Do you want to rename the document and "
2798 "try again?"), file);
2799 int const ret = Alert::prompt(_("Rename and save?"),
2800 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2803 if (!renameBuffer(b, docstring()))
2812 return saveBuffer(b, fn);
2816 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2818 return closeWorkArea(wa, false);
2822 // We only want to close the buffer if it is not visible in other workareas
2823 // of the same view, nor in other views, and if this is not a child
2824 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2826 Buffer & buf = wa->bufferView().buffer();
2828 bool last_wa = d.countWorkAreasOf(buf) == 1
2829 && !inOtherView(buf) && !buf.parent();
2831 bool close_buffer = last_wa;
2834 if (lyxrc.close_buffer_with_last_view == "yes")
2836 else if (lyxrc.close_buffer_with_last_view == "no")
2837 close_buffer = false;
2840 if (buf.isUnnamed())
2841 file = from_utf8(buf.fileName().onlyFileName());
2843 file = buf.fileName().displayName(30);
2844 docstring const text = bformat(
2845 _("Last view on document %1$s is being closed.\n"
2846 "Would you like to close or hide the document?\n"
2848 "Hidden documents can be displayed back through\n"
2849 "the menu: View->Hidden->...\n"
2851 "To remove this question, set your preference in:\n"
2852 " Tools->Preferences->Look&Feel->UserInterface\n"
2854 int ret = Alert::prompt(_("Close or hide document?"),
2855 text, 0, 1, _("&Close"), _("&Hide"));
2856 close_buffer = (ret == 0);
2860 return closeWorkArea(wa, close_buffer);
2864 bool GuiView::closeBuffer()
2866 GuiWorkArea * wa = currentMainWorkArea();
2867 // coverity complained about this
2868 // it seems unnecessary, but perhaps is worth the check
2869 LASSERT(wa, return false);
2871 setCurrentWorkArea(wa);
2872 Buffer & buf = wa->bufferView().buffer();
2873 return closeWorkArea(wa, !buf.parent());
2877 void GuiView::writeSession() const {
2878 GuiWorkArea const * active_wa = currentMainWorkArea();
2879 for (int i = 0; i < d.splitter_->count(); ++i) {
2880 TabWorkArea * twa = d.tabWorkArea(i);
2881 for (int j = 0; j < twa->count(); ++j) {
2882 GuiWorkArea * wa = twa->workArea(j);
2883 Buffer & buf = wa->bufferView().buffer();
2884 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2890 bool GuiView::closeBufferAll()
2892 // Close the workareas in all other views
2893 QList<int> const ids = guiApp->viewIds();
2894 for (int i = 0; i != ids.size(); ++i) {
2895 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2899 // Close our own workareas
2900 if (!closeWorkAreaAll())
2903 // Now close the hidden buffers. We prevent hidden buffers from being
2904 // dirty, so we can just close them.
2905 theBufferList().closeAll();
2910 bool GuiView::closeWorkAreaAll()
2912 setCurrentWorkArea(currentMainWorkArea());
2914 // We might be in a situation that there is still a tabWorkArea, but
2915 // there are no tabs anymore. This can happen when we get here after a
2916 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2917 // many TabWorkArea's have no documents anymore.
2920 // We have to call count() each time, because it can happen that
2921 // more than one splitter will disappear in one iteration (bug 5998).
2922 while (d.splitter_->count() > empty_twa) {
2923 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2925 if (twa->count() == 0)
2928 setCurrentWorkArea(twa->currentWorkArea());
2929 if (!closeTabWorkArea(twa))
2937 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2942 Buffer & buf = wa->bufferView().buffer();
2944 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2945 Alert::warning(_("Close document"),
2946 _("Document could not be closed because it is being processed by LyX."));
2951 return closeBuffer(buf);
2953 if (!inMultiTabs(wa))
2954 if (!saveBufferIfNeeded(buf, true))
2962 bool GuiView::closeBuffer(Buffer & buf)
2964 // If we are in a close_event all children will be closed in some time,
2965 // so no need to do it here. This will ensure that the children end up
2966 // in the session file in the correct order. If we close the master
2967 // buffer, we can close or release the child buffers here too.
2968 bool success = true;
2970 ListOfBuffers clist = buf.getChildren();
2971 ListOfBuffers::const_iterator it = clist.begin();
2972 ListOfBuffers::const_iterator const bend = clist.end();
2973 for (; it != bend; ++it) {
2974 Buffer * child_buf = *it;
2975 if (theBufferList().isOthersChild(&buf, child_buf)) {
2976 child_buf->setParent(0);
2980 // FIXME: should we look in other tabworkareas?
2981 // ANSWER: I don't think so. I've tested, and if the child is
2982 // open in some other window, it closes without a problem.
2983 GuiWorkArea * child_wa = workArea(*child_buf);
2985 success = closeWorkArea(child_wa, true);
2989 // In this case the child buffer is open but hidden.
2990 // It therefore should not (MUST NOT) be dirty!
2991 LATTEST(child_buf->isClean());
2992 theBufferList().release(child_buf);
2997 // goto bookmark to update bookmark pit.
2998 // FIXME: we should update only the bookmarks related to this buffer!
2999 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3000 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3001 guiApp->gotoBookmark(i+1, false, false);
3003 if (saveBufferIfNeeded(buf, false)) {
3004 buf.removeAutosaveFile();
3005 theBufferList().release(&buf);
3009 // open all children again to avoid a crash because of dangling
3010 // pointers (bug 6603)
3016 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3018 while (twa == d.currentTabWorkArea()) {
3019 twa->setCurrentIndex(twa->count() - 1);
3021 GuiWorkArea * wa = twa->currentWorkArea();
3022 Buffer & b = wa->bufferView().buffer();
3024 // We only want to close the buffer if the same buffer is not visible
3025 // in another view, and if this is not a child and if we are closing
3026 // a view (not a tabgroup).
3027 bool const close_buffer =
3028 !inOtherView(b) && !b.parent() && closing_;
3030 if (!closeWorkArea(wa, close_buffer))
3037 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3039 if (buf.isClean() || buf.paragraphs().empty())
3042 // Switch to this Buffer.
3048 if (buf.isUnnamed()) {
3049 file = from_utf8(buf.fileName().onlyFileName());
3052 FileName filename = buf.fileName();
3054 file = filename.displayName(30);
3055 exists = filename.exists();
3058 // Bring this window to top before asking questions.
3063 if (hiding && buf.isUnnamed()) {
3064 docstring const text = bformat(_("The document %1$s has not been "
3065 "saved yet.\n\nDo you want to save "
3066 "the document?"), file);
3067 ret = Alert::prompt(_("Save new document?"),
3068 text, 0, 1, _("&Save"), _("&Cancel"));
3072 docstring const text = exists ?
3073 bformat(_("The document %1$s has unsaved changes."
3074 "\n\nDo you want to save the document or "
3075 "discard the changes?"), file) :
3076 bformat(_("The document %1$s has not been saved yet."
3077 "\n\nDo you want to save the document or "
3078 "discard it entirely?"), file);
3079 docstring const title = exists ?
3080 _("Save changed document?") : _("Save document?");
3081 ret = Alert::prompt(title, text, 0, 2,
3082 _("&Save"), _("&Discard"), _("&Cancel"));
3087 if (!saveBuffer(buf))
3091 // If we crash after this we could have no autosave file
3092 // but I guess this is really improbable (Jug).
3093 // Sometimes improbable things happen:
3094 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3095 // buf.removeAutosaveFile();
3097 // revert all changes
3108 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3110 Buffer & buf = wa->bufferView().buffer();
3112 for (int i = 0; i != d.splitter_->count(); ++i) {
3113 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3114 if (wa_ && wa_ != wa)
3117 return inOtherView(buf);
3121 bool GuiView::inOtherView(Buffer & buf)
3123 QList<int> const ids = guiApp->viewIds();
3125 for (int i = 0; i != ids.size(); ++i) {
3129 if (guiApp->view(ids[i]).workArea(buf))
3136 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3138 if (!documentBufferView())
3141 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3142 Buffer * const curbuf = &documentBufferView()->buffer();
3143 int nwa = twa->count();
3144 for (int i = 0; i < nwa; ++i) {
3145 if (&workArea(i)->bufferView().buffer() == curbuf) {
3147 if (np == NEXTBUFFER)
3148 next_index = (i == nwa - 1 ? 0 : i + 1);
3150 next_index = (i == 0 ? nwa - 1 : i - 1);
3152 twa->moveTab(i, next_index);
3154 setBuffer(&workArea(next_index)->bufferView().buffer());
3162 /// make sure the document is saved
3163 static bool ensureBufferClean(Buffer * buffer)
3165 LASSERT(buffer, return false);
3166 if (buffer->isClean() && !buffer->isUnnamed())
3169 docstring const file = buffer->fileName().displayName(30);
3172 if (!buffer->isUnnamed()) {
3173 text = bformat(_("The document %1$s has unsaved "
3174 "changes.\n\nDo you want to save "
3175 "the document?"), file);
3176 title = _("Save changed document?");
3179 text = bformat(_("The document %1$s has not been "
3180 "saved yet.\n\nDo you want to save "
3181 "the document?"), file);
3182 title = _("Save new document?");
3184 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3187 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3189 return buffer->isClean() && !buffer->isUnnamed();
3193 bool GuiView::reloadBuffer(Buffer & buf)
3195 currentBufferView()->cursor().reset();
3196 Buffer::ReadStatus status = buf.reload();
3197 return status == Buffer::ReadSuccess;
3201 void GuiView::checkExternallyModifiedBuffers()
3203 BufferList::iterator bit = theBufferList().begin();
3204 BufferList::iterator const bend = theBufferList().end();
3205 for (; bit != bend; ++bit) {
3206 Buffer * buf = *bit;
3207 if (buf->fileName().exists() && buf->isChecksumModified()) {
3208 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3209 " Reload now? Any local changes will be lost."),
3210 from_utf8(buf->absFileName()));
3211 int const ret = Alert::prompt(_("Reload externally changed document?"),
3212 text, 0, 1, _("&Reload"), _("&Cancel"));
3220 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3222 Buffer * buffer = documentBufferView()
3223 ? &(documentBufferView()->buffer()) : 0;
3225 switch (cmd.action()) {
3226 case LFUN_VC_REGISTER:
3227 if (!buffer || !ensureBufferClean(buffer))
3229 if (!buffer->lyxvc().inUse()) {
3230 if (buffer->lyxvc().registrer()) {
3231 reloadBuffer(*buffer);
3232 dr.clearMessageUpdate();
3237 case LFUN_VC_RENAME:
3238 case LFUN_VC_COPY: {
3239 if (!buffer || !ensureBufferClean(buffer))
3241 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3242 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3243 // Some changes are not yet committed.
3244 // We test here and not in getStatus(), since
3245 // this test is expensive.
3247 LyXVC::CommandResult ret =
3248 buffer->lyxvc().checkIn(log);
3250 if (ret == LyXVC::ErrorCommand ||
3251 ret == LyXVC::VCSuccess)
3252 reloadBuffer(*buffer);
3253 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3254 frontend::Alert::error(
3255 _("Revision control error."),
3256 _("Document could not be checked in."));
3260 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3261 LV_VC_RENAME : LV_VC_COPY;
3262 renameBuffer(*buffer, cmd.argument(), kind);
3267 case LFUN_VC_CHECK_IN:
3268 if (!buffer || !ensureBufferClean(buffer))
3270 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3272 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3274 // Only skip reloading if the checkin was cancelled or
3275 // an error occurred before the real checkin VCS command
3276 // was executed, since the VCS might have changed the
3277 // file even if it could not checkin successfully.
3278 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3279 reloadBuffer(*buffer);
3283 case LFUN_VC_CHECK_OUT:
3284 if (!buffer || !ensureBufferClean(buffer))
3286 if (buffer->lyxvc().inUse()) {
3287 dr.setMessage(buffer->lyxvc().checkOut());
3288 reloadBuffer(*buffer);
3292 case LFUN_VC_LOCKING_TOGGLE:
3293 LASSERT(buffer, return);
3294 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3296 if (buffer->lyxvc().inUse()) {
3297 string res = buffer->lyxvc().lockingToggle();
3299 frontend::Alert::error(_("Revision control error."),
3300 _("Error when setting the locking property."));
3303 reloadBuffer(*buffer);
3308 case LFUN_VC_REVERT:
3309 LASSERT(buffer, return);
3310 if (buffer->lyxvc().revert()) {
3311 reloadBuffer(*buffer);
3312 dr.clearMessageUpdate();
3316 case LFUN_VC_UNDO_LAST:
3317 LASSERT(buffer, return);
3318 buffer->lyxvc().undoLast();
3319 reloadBuffer(*buffer);
3320 dr.clearMessageUpdate();
3323 case LFUN_VC_REPO_UPDATE:
3324 LASSERT(buffer, return);
3325 if (ensureBufferClean(buffer)) {
3326 dr.setMessage(buffer->lyxvc().repoUpdate());
3327 checkExternallyModifiedBuffers();
3331 case LFUN_VC_COMMAND: {
3332 string flag = cmd.getArg(0);
3333 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3336 if (contains(flag, 'M')) {
3337 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3340 string path = cmd.getArg(1);
3341 if (contains(path, "$$p") && buffer)
3342 path = subst(path, "$$p", buffer->filePath());
3343 LYXERR(Debug::LYXVC, "Directory: " << path);
3345 if (!pp.isReadableDirectory()) {
3346 lyxerr << _("Directory is not accessible.") << endl;
3349 support::PathChanger p(pp);
3351 string command = cmd.getArg(2);
3352 if (command.empty())
3355 command = subst(command, "$$i", buffer->absFileName());
3356 command = subst(command, "$$p", buffer->filePath());
3358 command = subst(command, "$$m", to_utf8(message));
3359 LYXERR(Debug::LYXVC, "Command: " << command);
3361 one.startscript(Systemcall::Wait, command);
3365 if (contains(flag, 'I'))
3366 buffer->markDirty();
3367 if (contains(flag, 'R'))
3368 reloadBuffer(*buffer);
3373 case LFUN_VC_COMPARE: {
3374 if (cmd.argument().empty()) {
3375 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3379 string rev1 = cmd.getArg(0);
3384 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3387 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3388 f2 = buffer->absFileName();
3390 string rev2 = cmd.getArg(1);
3394 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3398 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3399 f1 << "\n" << f2 << "\n" );
3400 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3401 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3411 void GuiView::openChildDocument(string const & fname)
3413 LASSERT(documentBufferView(), return);
3414 Buffer & buffer = documentBufferView()->buffer();
3415 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3416 documentBufferView()->saveBookmark(false);
3418 if (theBufferList().exists(filename)) {
3419 child = theBufferList().getBuffer(filename);
3422 message(bformat(_("Opening child document %1$s..."),
3423 makeDisplayPath(filename.absFileName())));
3424 child = loadDocument(filename, false);
3426 // Set the parent name of the child document.
3427 // This makes insertion of citations and references in the child work,
3428 // when the target is in the parent or another child document.
3430 child->setParent(&buffer);
3434 bool GuiView::goToFileRow(string const & argument)
3438 size_t i = argument.find_last_of(' ');
3439 if (i != string::npos) {
3440 file_name = os::internal_path(trim(argument.substr(0, i)));
3441 istringstream is(argument.substr(i + 1));
3446 if (i == string::npos) {
3447 LYXERR0("Wrong argument: " << argument);
3451 string const abstmp = package().temp_dir().absFileName();
3452 string const realtmp = package().temp_dir().realPath();
3453 // We have to use os::path_prefix_is() here, instead of
3454 // simply prefixIs(), because the file name comes from
3455 // an external application and may need case adjustment.
3456 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3457 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3458 // Needed by inverse dvi search. If it is a file
3459 // in tmpdir, call the apropriated function.
3460 // If tmpdir is a symlink, we may have the real
3461 // path passed back, so we correct for that.
3462 if (!prefixIs(file_name, abstmp))
3463 file_name = subst(file_name, realtmp, abstmp);
3464 buf = theBufferList().getBufferFromTmp(file_name);
3466 // Must replace extension of the file to be .lyx
3467 // and get full path
3468 FileName const s = fileSearch(string(),
3469 support::changeExtension(file_name, ".lyx"), "lyx");
3470 // Either change buffer or load the file
3471 if (theBufferList().exists(s))
3472 buf = theBufferList().getBuffer(s);
3473 else if (s.exists()) {
3474 buf = loadDocument(s);
3479 _("File does not exist: %1$s"),
3480 makeDisplayPath(file_name)));
3486 _("No buffer for file: %1$s."),
3487 makeDisplayPath(file_name))
3492 bool success = documentBufferView()->setCursorFromRow(row);
3494 LYXERR(Debug::LATEX,
3495 "setCursorFromRow: invalid position for row " << row);
3496 frontend::Alert::error(_("Inverse Search Failed"),
3497 _("Invalid position requested by inverse search.\n"
3498 "You may need to update the viewed document."));
3504 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3506 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3507 menu->exec(QCursor::pos());
3512 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3514 Buffer::ExportStatus const status = func(format);
3516 // the cloning operation will have produced a clone of the entire set of
3517 // documents, starting from the master. so we must delete those.
3518 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3520 busyBuffers.remove(orig);
3525 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3527 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3528 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3532 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3534 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3535 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3539 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3541 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3542 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3546 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3547 string const & argument,
3548 Buffer const * used_buffer,
3549 docstring const & msg,
3550 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3551 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3552 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3557 string format = argument;
3559 format = used_buffer->params().getDefaultOutputFormat();
3560 processing_format = format;
3562 progress_->clearMessages();
3565 #if EXPORT_in_THREAD
3566 GuiViewPrivate::busyBuffers.insert(used_buffer);
3567 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3568 if (!cloned_buffer) {
3569 Alert::error(_("Export Error"),
3570 _("Error cloning the Buffer."));
3573 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3578 setPreviewFuture(f);
3579 last_export_format = used_buffer->params().bufferFormat();
3582 // We are asynchronous, so we don't know here anything about the success
3585 Buffer::ExportStatus status;
3587 status = (used_buffer->*syncFunc)(format, true);
3588 } else if (previewFunc) {
3589 status = (used_buffer->*previewFunc)(format);
3592 handleExportStatus(gv_, status, format);
3594 return (status == Buffer::ExportSuccess
3595 || status == Buffer::PreviewSuccess);
3599 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3601 BufferView * bv = currentBufferView();
3602 LASSERT(bv, return);
3604 // Let the current BufferView dispatch its own actions.
3605 bv->dispatch(cmd, dr);
3606 if (dr.dispatched())
3609 // Try with the document BufferView dispatch if any.
3610 BufferView * doc_bv = documentBufferView();
3611 if (doc_bv && doc_bv != bv) {
3612 doc_bv->dispatch(cmd, dr);
3613 if (dr.dispatched())
3617 // Then let the current Cursor dispatch its own actions.
3618 bv->cursor().dispatch(cmd);
3620 // update completion. We do it here and not in
3621 // processKeySym to avoid another redraw just for a
3622 // changed inline completion
3623 if (cmd.origin() == FuncRequest::KEYBOARD) {
3624 if (cmd.action() == LFUN_SELF_INSERT
3625 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3626 updateCompletion(bv->cursor(), true, true);
3627 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3628 updateCompletion(bv->cursor(), false, true);
3630 updateCompletion(bv->cursor(), false, false);
3633 dr = bv->cursor().result();
3637 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3639 BufferView * bv = currentBufferView();
3640 // By default we won't need any update.
3641 dr.screenUpdate(Update::None);
3642 // assume cmd will be dispatched
3643 dr.dispatched(true);
3645 Buffer * doc_buffer = documentBufferView()
3646 ? &(documentBufferView()->buffer()) : 0;
3648 if (cmd.origin() == FuncRequest::TOC) {
3649 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3650 // FIXME: do we need to pass a DispatchResult object here?
3651 toc->doDispatch(bv->cursor(), cmd);
3655 string const argument = to_utf8(cmd.argument());
3657 switch(cmd.action()) {
3658 case LFUN_BUFFER_CHILD_OPEN:
3659 openChildDocument(to_utf8(cmd.argument()));
3662 case LFUN_BUFFER_IMPORT:
3663 importDocument(to_utf8(cmd.argument()));
3666 case LFUN_BUFFER_EXPORT: {
3669 // GCC only sees strfwd.h when building merged
3670 if (::lyx::operator==(cmd.argument(), "custom")) {
3671 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3675 string const dest = cmd.getArg(1);
3676 FileName target_dir;
3677 if (!dest.empty() && FileName::isAbsolute(dest))
3678 target_dir = FileName(support::onlyPath(dest));
3680 target_dir = doc_buffer->fileName().onlyPath();
3682 string const format = (argument.empty() || argument == "default") ?
3683 doc_buffer->params().getDefaultOutputFormat() : argument;
3685 if ((dest.empty() && doc_buffer->isUnnamed())
3686 || !target_dir.isDirWritable()) {
3687 exportBufferAs(*doc_buffer, from_utf8(format));
3690 /* TODO/Review: Is it a problem to also export the children?
3691 See the update_unincluded flag */
3692 d.asyncBufferProcessing(format,
3695 &GuiViewPrivate::exportAndDestroy,
3698 // TODO Inform user about success
3702 case LFUN_BUFFER_EXPORT_AS: {
3703 LASSERT(doc_buffer, break);
3704 docstring f = cmd.argument();
3706 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3707 exportBufferAs(*doc_buffer, f);
3711 case LFUN_BUFFER_UPDATE: {
3712 d.asyncBufferProcessing(argument,
3715 &GuiViewPrivate::compileAndDestroy,
3720 case LFUN_BUFFER_VIEW: {
3721 d.asyncBufferProcessing(argument,
3723 _("Previewing ..."),
3724 &GuiViewPrivate::previewAndDestroy,
3729 case LFUN_MASTER_BUFFER_UPDATE: {
3730 d.asyncBufferProcessing(argument,
3731 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3733 &GuiViewPrivate::compileAndDestroy,
3738 case LFUN_MASTER_BUFFER_VIEW: {
3739 d.asyncBufferProcessing(argument,
3740 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3742 &GuiViewPrivate::previewAndDestroy,
3743 0, &Buffer::preview);
3746 case LFUN_EXPORT_CANCEL: {
3747 Systemcall::killscript();
3750 case LFUN_BUFFER_SWITCH: {
3751 string const file_name = to_utf8(cmd.argument());
3752 if (!FileName::isAbsolute(file_name)) {
3754 dr.setMessage(_("Absolute filename expected."));
3758 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3761 dr.setMessage(_("Document not loaded"));
3765 // Do we open or switch to the buffer in this view ?
3766 if (workArea(*buffer)
3767 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3772 // Look for the buffer in other views
3773 QList<int> const ids = guiApp->viewIds();
3775 for (; i != ids.size(); ++i) {
3776 GuiView & gv = guiApp->view(ids[i]);
3777 if (gv.workArea(*buffer)) {
3779 gv.activateWindow();
3781 gv.setBuffer(buffer);
3786 // If necessary, open a new window as a last resort
3787 if (i == ids.size()) {
3788 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3794 case LFUN_BUFFER_NEXT:
3795 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3798 case LFUN_BUFFER_MOVE_NEXT:
3799 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3802 case LFUN_BUFFER_PREVIOUS:
3803 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3806 case LFUN_BUFFER_MOVE_PREVIOUS:
3807 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3810 case LFUN_BUFFER_CHKTEX:
3811 LASSERT(doc_buffer, break);
3812 doc_buffer->runChktex();
3815 case LFUN_COMMAND_EXECUTE: {
3816 command_execute_ = true;
3817 minibuffer_focus_ = true;
3820 case LFUN_DROP_LAYOUTS_CHOICE:
3821 d.layout_->showPopup();
3824 case LFUN_MENU_OPEN:
3825 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3826 menu->exec(QCursor::pos());
3829 case LFUN_FILE_INSERT:
3830 insertLyXFile(cmd.argument());
3833 case LFUN_FILE_INSERT_PLAINTEXT:
3834 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3835 string const fname = to_utf8(cmd.argument());
3836 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3837 dr.setMessage(_("Absolute filename expected."));
3841 FileName filename(fname);
3842 if (fname.empty()) {
3843 FileDialog dlg(qt_("Select file to insert"));
3845 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3846 QStringList(qt_("All Files (*)")));
3848 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3849 dr.setMessage(_("Canceled."));
3853 filename.set(fromqstr(result.second));
3857 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3858 bv->dispatch(new_cmd, dr);
3863 case LFUN_BUFFER_RELOAD: {
3864 LASSERT(doc_buffer, break);
3867 bool drop = (cmd.argument()=="dump");
3870 if (!drop && !doc_buffer->isClean()) {
3871 docstring const file =
3872 makeDisplayPath(doc_buffer->absFileName(), 20);
3873 if (doc_buffer->notifiesExternalModification()) {
3874 docstring text = _("The current version will be lost. "
3875 "Are you sure you want to load the version on disk "
3876 "of the document %1$s?");
3877 ret = Alert::prompt(_("Reload saved document?"),
3878 bformat(text, file), 1, 1,
3879 _("&Reload"), _("&Cancel"));
3881 docstring text = _("Any changes will be lost. "
3882 "Are you sure you want to revert to the saved version "
3883 "of the document %1$s?");
3884 ret = Alert::prompt(_("Revert to saved document?"),
3885 bformat(text, file), 1, 1,
3886 _("&Revert"), _("&Cancel"));
3891 doc_buffer->markClean();
3892 reloadBuffer(*doc_buffer);
3893 dr.forceBufferUpdate();
3898 case LFUN_BUFFER_WRITE:
3899 LASSERT(doc_buffer, break);
3900 saveBuffer(*doc_buffer);
3903 case LFUN_BUFFER_WRITE_AS:
3904 LASSERT(doc_buffer, break);
3905 renameBuffer(*doc_buffer, cmd.argument());
3908 case LFUN_BUFFER_WRITE_ALL: {
3909 Buffer * first = theBufferList().first();
3912 message(_("Saving all documents..."));
3913 // We cannot use a for loop as the buffer list cycles.
3916 if (!b->isClean()) {
3918 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3920 b = theBufferList().next(b);
3921 } while (b != first);
3922 dr.setMessage(_("All documents saved."));
3926 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3927 LASSERT(doc_buffer, break);
3928 doc_buffer->clearExternalModification();
3931 case LFUN_BUFFER_CLOSE:
3935 case LFUN_BUFFER_CLOSE_ALL:
3939 case LFUN_DEVEL_MODE_TOGGLE:
3940 devel_mode_ = !devel_mode_;
3942 dr.setMessage(_("Developer mode is now enabled."));
3944 dr.setMessage(_("Developer mode is now disabled."));
3947 case LFUN_TOOLBAR_TOGGLE: {
3948 string const name = cmd.getArg(0);
3949 if (GuiToolbar * t = toolbar(name))
3954 case LFUN_TOOLBAR_MOVABLE: {
3955 string const name = cmd.getArg(0);
3957 // toggle (all) toolbars movablility
3958 toolbarsMovable_ = !toolbarsMovable_;
3959 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3960 GuiToolbar * tb = toolbar(ti.name);
3961 if (tb && tb->isMovable() != toolbarsMovable_)
3962 // toggle toolbar movablity if it does not fit lock
3963 // (all) toolbars positions state silent = true, since
3964 // status bar notifications are slow
3967 if (toolbarsMovable_)
3968 dr.setMessage(_("Toolbars unlocked."));
3970 dr.setMessage(_("Toolbars locked."));
3971 } else if (GuiToolbar * t = toolbar(name)) {
3972 // toggle current toolbar movablity
3974 // update lock (all) toolbars positions
3975 updateLockToolbars();
3980 case LFUN_ICON_SIZE: {
3981 QSize size = d.iconSize(cmd.argument());
3983 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3984 size.width(), size.height()));
3988 case LFUN_DIALOG_UPDATE: {
3989 string const name = to_utf8(cmd.argument());
3990 if (name == "prefs" || name == "document")
3991 updateDialog(name, string());
3992 else if (name == "paragraph")
3993 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3994 else if (currentBufferView()) {
3995 Inset * inset = currentBufferView()->editedInset(name);
3996 // Can only update a dialog connected to an existing inset
3998 // FIXME: get rid of this indirection; GuiView ask the inset
3999 // if he is kind enough to update itself...
4000 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4001 //FIXME: pass DispatchResult here?
4002 inset->dispatch(currentBufferView()->cursor(), fr);
4008 case LFUN_DIALOG_TOGGLE: {
4009 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4010 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4011 dispatch(FuncRequest(func_code, cmd.argument()), dr);
4015 case LFUN_DIALOG_DISCONNECT_INSET:
4016 disconnectDialog(to_utf8(cmd.argument()));
4019 case LFUN_DIALOG_HIDE: {
4020 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4024 case LFUN_DIALOG_SHOW: {
4025 string const name = cmd.getArg(0);
4026 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4028 if (name == "character") {
4029 data = freefont2string();
4031 showDialog("character", data);
4032 } else if (name == "latexlog") {
4033 // getStatus checks that
4034 LATTEST(doc_buffer);
4035 Buffer::LogType type;
4036 string const logfile = doc_buffer->logName(&type);
4038 case Buffer::latexlog:
4041 case Buffer::buildlog:
4045 data += Lexer::quoteString(logfile);
4046 showDialog("log", data);
4047 } else if (name == "vclog") {
4048 // getStatus checks that
4049 LATTEST(doc_buffer);
4050 string const data = "vc " +
4051 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4052 showDialog("log", data);
4053 } else if (name == "symbols") {
4054 data = bv->cursor().getEncoding()->name();
4056 showDialog("symbols", data);
4058 } else if (name == "prefs" && isFullScreen()) {
4059 lfunUiToggle("fullscreen");
4060 showDialog("prefs", data);
4062 showDialog(name, data);
4067 dr.setMessage(cmd.argument());
4070 case LFUN_UI_TOGGLE: {
4071 string arg = cmd.getArg(0);
4072 if (!lfunUiToggle(arg)) {
4073 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4074 dr.setMessage(bformat(msg, from_utf8(arg)));
4076 // Make sure the keyboard focus stays in the work area.
4081 case LFUN_VIEW_SPLIT: {
4082 LASSERT(doc_buffer, break);
4083 string const orientation = cmd.getArg(0);
4084 d.splitter_->setOrientation(orientation == "vertical"
4085 ? Qt::Vertical : Qt::Horizontal);
4086 TabWorkArea * twa = addTabWorkArea();
4087 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4088 setCurrentWorkArea(wa);
4091 case LFUN_TAB_GROUP_CLOSE:
4092 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4093 closeTabWorkArea(twa);
4094 d.current_work_area_ = 0;
4095 twa = d.currentTabWorkArea();
4096 // Switch to the next GuiWorkArea in the found TabWorkArea.
4098 // Make sure the work area is up to date.
4099 setCurrentWorkArea(twa->currentWorkArea());
4101 setCurrentWorkArea(0);
4106 case LFUN_VIEW_CLOSE:
4107 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4108 closeWorkArea(twa->currentWorkArea());
4109 d.current_work_area_ = 0;
4110 twa = d.currentTabWorkArea();
4111 // Switch to the next GuiWorkArea in the found TabWorkArea.
4113 // Make sure the work area is up to date.
4114 setCurrentWorkArea(twa->currentWorkArea());
4116 setCurrentWorkArea(0);
4121 case LFUN_COMPLETION_INLINE:
4122 if (d.current_work_area_)
4123 d.current_work_area_->completer().showInline();
4126 case LFUN_COMPLETION_POPUP:
4127 if (d.current_work_area_)
4128 d.current_work_area_->completer().showPopup();
4133 if (d.current_work_area_)
4134 d.current_work_area_->completer().tab();
4137 case LFUN_COMPLETION_CANCEL:
4138 if (d.current_work_area_) {
4139 if (d.current_work_area_->completer().popupVisible())
4140 d.current_work_area_->completer().hidePopup();
4142 d.current_work_area_->completer().hideInline();
4146 case LFUN_COMPLETION_ACCEPT:
4147 if (d.current_work_area_)
4148 d.current_work_area_->completer().activate();
4151 case LFUN_BUFFER_ZOOM_IN:
4152 case LFUN_BUFFER_ZOOM_OUT:
4153 case LFUN_BUFFER_ZOOM: {
4154 if (cmd.argument().empty()) {
4155 if (cmd.action() == LFUN_BUFFER_ZOOM)
4157 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4162 if (cmd.action() == LFUN_BUFFER_ZOOM)
4163 zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4164 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4165 zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4167 zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4170 // Actual zoom value: default zoom + fractional extra value
4171 int zoom = lyxrc.defaultZoom * zoom_ratio_;
4172 if (zoom < static_cast<int>(zoom_min_))
4175 lyxrc.currentZoom = zoom;
4177 dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4178 lyxrc.currentZoom, lyxrc.defaultZoom));
4180 // The global QPixmapCache is used in GuiPainter to cache text
4181 // painting so we must reset it.
4182 QPixmapCache::clear();
4183 guiApp->fontLoader().update();
4184 dr.screenUpdate(Update::Force | Update::FitCursor);
4188 case LFUN_VC_REGISTER:
4189 case LFUN_VC_RENAME:
4191 case LFUN_VC_CHECK_IN:
4192 case LFUN_VC_CHECK_OUT:
4193 case LFUN_VC_REPO_UPDATE:
4194 case LFUN_VC_LOCKING_TOGGLE:
4195 case LFUN_VC_REVERT:
4196 case LFUN_VC_UNDO_LAST:
4197 case LFUN_VC_COMMAND:
4198 case LFUN_VC_COMPARE:
4199 dispatchVC(cmd, dr);
4202 case LFUN_SERVER_GOTO_FILE_ROW:
4203 if(goToFileRow(to_utf8(cmd.argument())))
4204 dr.screenUpdate(Update::Force | Update::FitCursor);
4207 case LFUN_LYX_ACTIVATE:
4211 case LFUN_FORWARD_SEARCH: {
4212 // it seems safe to assume we have a document buffer, since
4213 // getStatus wants one.
4214 LATTEST(doc_buffer);
4215 Buffer const * doc_master = doc_buffer->masterBuffer();
4216 FileName const path(doc_master->temppath());
4217 string const texname = doc_master->isChild(doc_buffer)
4218 ? DocFileName(changeExtension(
4219 doc_buffer->absFileName(),
4220 "tex")).mangledFileName()
4221 : doc_buffer->latexName();
4222 string const fulltexname =
4223 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4224 string const mastername =
4225 removeExtension(doc_master->latexName());
4226 FileName const dviname(addName(path.absFileName(),
4227 addExtension(mastername, "dvi")));
4228 FileName const pdfname(addName(path.absFileName(),
4229 addExtension(mastername, "pdf")));
4230 bool const have_dvi = dviname.exists();
4231 bool const have_pdf = pdfname.exists();
4232 if (!have_dvi && !have_pdf) {
4233 dr.setMessage(_("Please, preview the document first."));
4236 string outname = dviname.onlyFileName();
4237 string command = lyxrc.forward_search_dvi;
4238 if (!have_dvi || (have_pdf &&
4239 pdfname.lastModified() > dviname.lastModified())) {
4240 outname = pdfname.onlyFileName();
4241 command = lyxrc.forward_search_pdf;
4244 DocIterator cur = bv->cursor();
4245 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4246 LYXERR(Debug::ACTION, "Forward search: row:" << row
4248 if (row == -1 || command.empty()) {
4249 dr.setMessage(_("Couldn't proceed."));
4252 string texrow = convert<string>(row);
4254 command = subst(command, "$$n", texrow);
4255 command = subst(command, "$$f", fulltexname);
4256 command = subst(command, "$$t", texname);
4257 command = subst(command, "$$o", outname);
4259 PathChanger p(path);
4261 one.startscript(Systemcall::DontWait, command);
4265 case LFUN_SPELLING_CONTINUOUSLY:
4266 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4267 dr.screenUpdate(Update::Force);
4271 // The LFUN must be for one of BufferView, Buffer or Cursor;
4273 dispatchToBufferView(cmd, dr);
4277 // Part of automatic menu appearance feature.
4278 if (isFullScreen()) {
4279 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4283 // Need to update bv because many LFUNs here might have destroyed it
4284 bv = currentBufferView();
4286 // Clear non-empty selections
4287 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4289 Cursor & cur = bv->cursor();
4290 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4291 cur.clearSelection();
4297 bool GuiView::lfunUiToggle(string const & ui_component)
4299 if (ui_component == "scrollbar") {
4300 // hide() is of no help
4301 if (d.current_work_area_->verticalScrollBarPolicy() ==
4302 Qt::ScrollBarAlwaysOff)
4304 d.current_work_area_->setVerticalScrollBarPolicy(
4305 Qt::ScrollBarAsNeeded);
4307 d.current_work_area_->setVerticalScrollBarPolicy(
4308 Qt::ScrollBarAlwaysOff);
4309 } else if (ui_component == "statusbar") {
4310 statusBar()->setVisible(!statusBar()->isVisible());
4311 } else if (ui_component == "menubar") {
4312 menuBar()->setVisible(!menuBar()->isVisible());
4314 if (ui_component == "frame") {
4316 getContentsMargins(&l, &t, &r, &b);
4317 //are the frames in default state?
4318 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4320 setContentsMargins(-2, -2, -2, -2);
4322 setContentsMargins(0, 0, 0, 0);
4325 if (ui_component == "fullscreen") {
4333 void GuiView::toggleFullScreen()
4335 if (isFullScreen()) {
4336 for (int i = 0; i != d.splitter_->count(); ++i)
4337 d.tabWorkArea(i)->setFullScreen(false);
4338 setContentsMargins(0, 0, 0, 0);
4339 setWindowState(windowState() ^ Qt::WindowFullScreen);
4342 statusBar()->show();
4345 hideDialogs("prefs", 0);
4346 for (int i = 0; i != d.splitter_->count(); ++i)
4347 d.tabWorkArea(i)->setFullScreen(true);
4348 setContentsMargins(-2, -2, -2, -2);
4350 setWindowState(windowState() ^ Qt::WindowFullScreen);
4351 if (lyxrc.full_screen_statusbar)
4352 statusBar()->hide();
4353 if (lyxrc.full_screen_menubar)
4355 if (lyxrc.full_screen_toolbars) {
4356 ToolbarMap::iterator end = d.toolbars_.end();
4357 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4362 // give dialogs like the TOC a chance to adapt
4367 Buffer const * GuiView::updateInset(Inset const * inset)
4372 Buffer const * inset_buffer = &(inset->buffer());
4374 for (int i = 0; i != d.splitter_->count(); ++i) {
4375 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4378 Buffer const * buffer = &(wa->bufferView().buffer());
4379 if (inset_buffer == buffer)
4380 wa->scheduleRedraw(true);
4382 return inset_buffer;
4386 void GuiView::restartCaret()
4388 /* When we move around, or type, it's nice to be able to see
4389 * the caret immediately after the keypress.
4391 if (d.current_work_area_)
4392 d.current_work_area_->startBlinkingCaret();
4394 // Take this occasion to update the other GUI elements.
4400 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4402 if (d.current_work_area_)
4403 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4408 // This list should be kept in sync with the list of insets in
4409 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4410 // dialog should have the same name as the inset.
4411 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4412 // docs in LyXAction.cpp.
4414 char const * const dialognames[] = {
4416 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4417 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4418 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4419 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4420 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4421 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4422 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4423 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4425 char const * const * const end_dialognames =
4426 dialognames + (sizeof(dialognames) / sizeof(char *));
4430 cmpCStr(char const * name) : name_(name) {}
4431 bool operator()(char const * other) {
4432 return strcmp(other, name_) == 0;
4439 bool isValidName(string const & name)
4441 return find_if(dialognames, end_dialognames,
4442 cmpCStr(name.c_str())) != end_dialognames;
4448 void GuiView::resetDialogs()
4450 // Make sure that no LFUN uses any GuiView.
4451 guiApp->setCurrentView(0);
4455 constructToolbars();
4456 guiApp->menus().fillMenuBar(menuBar(), this, false);
4457 d.layout_->updateContents(true);
4458 // Now update controls with current buffer.
4459 guiApp->setCurrentView(this);
4465 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4467 if (!isValidName(name))
4470 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4472 if (it != d.dialogs_.end()) {
4474 it->second->hideView();
4475 return it->second.get();
4478 Dialog * dialog = build(name);
4479 d.dialogs_[name].reset(dialog);
4480 if (lyxrc.allow_geometry_session)
4481 dialog->restoreSession();
4488 void GuiView::showDialog(string const & name, string const & data,
4491 triggerShowDialog(toqstr(name), toqstr(data), inset);
4495 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4501 const string name = fromqstr(qname);
4502 const string data = fromqstr(qdata);
4506 Dialog * dialog = findOrBuild(name, false);
4508 bool const visible = dialog->isVisibleView();
4509 dialog->showData(data);
4510 if (inset && currentBufferView())
4511 currentBufferView()->editInset(name, inset);
4512 // We only set the focus to the new dialog if it was not yet
4513 // visible in order not to change the existing previous behaviour
4515 // activateWindow is needed for floating dockviews
4516 dialog->asQWidget()->raise();
4517 dialog->asQWidget()->activateWindow();
4518 dialog->asQWidget()->setFocus();
4522 catch (ExceptionMessage const & ex) {
4530 bool GuiView::isDialogVisible(string const & name) const
4532 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4533 if (it == d.dialogs_.end())
4535 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4539 void GuiView::hideDialog(string const & name, Inset * inset)
4541 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4542 if (it == d.dialogs_.end())
4546 if (!currentBufferView())
4548 if (inset != currentBufferView()->editedInset(name))
4552 Dialog * const dialog = it->second.get();
4553 if (dialog->isVisibleView())
4555 if (currentBufferView())
4556 currentBufferView()->editInset(name, 0);
4560 void GuiView::disconnectDialog(string const & name)
4562 if (!isValidName(name))
4564 if (currentBufferView())
4565 currentBufferView()->editInset(name, 0);
4569 void GuiView::hideAll() const
4571 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4572 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4574 for(; it != end; ++it)
4575 it->second->hideView();
4579 void GuiView::updateDialogs()
4581 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4582 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4584 for(; it != end; ++it) {
4585 Dialog * dialog = it->second.get();
4587 if (dialog->needBufferOpen() && !documentBufferView())
4588 hideDialog(fromqstr(dialog->name()), 0);
4589 else if (dialog->isVisibleView())
4590 dialog->checkStatus();
4597 Dialog * createDialog(GuiView & lv, string const & name);
4599 // will be replaced by a proper factory...
4600 Dialog * createGuiAbout(GuiView & lv);
4601 Dialog * createGuiBibtex(GuiView & lv);
4602 Dialog * createGuiChanges(GuiView & lv);
4603 Dialog * createGuiCharacter(GuiView & lv);
4604 Dialog * createGuiCitation(GuiView & lv);
4605 Dialog * createGuiCompare(GuiView & lv);
4606 Dialog * createGuiCompareHistory(GuiView & lv);
4607 Dialog * createGuiDelimiter(GuiView & lv);
4608 Dialog * createGuiDocument(GuiView & lv);
4609 Dialog * createGuiErrorList(GuiView & lv);
4610 Dialog * createGuiExternal(GuiView & lv);
4611 Dialog * createGuiGraphics(GuiView & lv);
4612 Dialog * createGuiInclude(GuiView & lv);
4613 Dialog * createGuiIndex(GuiView & lv);
4614 Dialog * createGuiListings(GuiView & lv);
4615 Dialog * createGuiLog(GuiView & lv);
4616 Dialog * createGuiMathMatrix(GuiView & lv);
4617 Dialog * createGuiNote(GuiView & lv);
4618 Dialog * createGuiParagraph(GuiView & lv);
4619 Dialog * createGuiPhantom(GuiView & lv);
4620 Dialog * createGuiPreferences(GuiView & lv);
4621 Dialog * createGuiPrint(GuiView & lv);
4622 Dialog * createGuiPrintindex(GuiView & lv);
4623 Dialog * createGuiRef(GuiView & lv);
4624 Dialog * createGuiSearch(GuiView & lv);
4625 Dialog * createGuiSearchAdv(GuiView & lv);
4626 Dialog * createGuiSendTo(GuiView & lv);
4627 Dialog * createGuiShowFile(GuiView & lv);
4628 Dialog * createGuiSpellchecker(GuiView & lv);
4629 Dialog * createGuiSymbols(GuiView & lv);
4630 Dialog * createGuiTabularCreate(GuiView & lv);
4631 Dialog * createGuiTexInfo(GuiView & lv);
4632 Dialog * createGuiToc(GuiView & lv);
4633 Dialog * createGuiThesaurus(GuiView & lv);
4634 Dialog * createGuiViewSource(GuiView & lv);
4635 Dialog * createGuiWrap(GuiView & lv);
4636 Dialog * createGuiProgressView(GuiView & lv);
4640 Dialog * GuiView::build(string const & name)
4642 LASSERT(isValidName(name), return 0);
4644 Dialog * dialog = createDialog(*this, name);
4648 if (name == "aboutlyx")
4649 return createGuiAbout(*this);
4650 if (name == "bibtex")
4651 return createGuiBibtex(*this);
4652 if (name == "changes")
4653 return createGuiChanges(*this);
4654 if (name == "character")
4655 return createGuiCharacter(*this);
4656 if (name == "citation")
4657 return createGuiCitation(*this);
4658 if (name == "compare")
4659 return createGuiCompare(*this);
4660 if (name == "comparehistory")
4661 return createGuiCompareHistory(*this);
4662 if (name == "document")
4663 return createGuiDocument(*this);
4664 if (name == "errorlist")
4665 return createGuiErrorList(*this);
4666 if (name == "external")
4667 return createGuiExternal(*this);
4669 return createGuiShowFile(*this);
4670 if (name == "findreplace")
4671 return createGuiSearch(*this);
4672 if (name == "findreplaceadv")
4673 return createGuiSearchAdv(*this);
4674 if (name == "graphics")
4675 return createGuiGraphics(*this);
4676 if (name == "include")
4677 return createGuiInclude(*this);
4678 if (name == "index")
4679 return createGuiIndex(*this);
4680 if (name == "index_print")
4681 return createGuiPrintindex(*this);
4682 if (name == "listings")
4683 return createGuiListings(*this);
4685 return createGuiLog(*this);
4686 if (name == "mathdelimiter")
4687 return createGuiDelimiter(*this);
4688 if (name == "mathmatrix")
4689 return createGuiMathMatrix(*this);
4691 return createGuiNote(*this);
4692 if (name == "paragraph")
4693 return createGuiParagraph(*this);
4694 if (name == "phantom")
4695 return createGuiPhantom(*this);
4696 if (name == "prefs")
4697 return createGuiPreferences(*this);
4699 return createGuiRef(*this);
4700 if (name == "sendto")
4701 return createGuiSendTo(*this);
4702 if (name == "spellchecker")
4703 return createGuiSpellchecker(*this);
4704 if (name == "symbols")
4705 return createGuiSymbols(*this);
4706 if (name == "tabularcreate")
4707 return createGuiTabularCreate(*this);
4708 if (name == "texinfo")
4709 return createGuiTexInfo(*this);
4710 if (name == "thesaurus")
4711 return createGuiThesaurus(*this);
4713 return createGuiToc(*this);
4714 if (name == "view-source")
4715 return createGuiViewSource(*this);
4717 return createGuiWrap(*this);
4718 if (name == "progress")
4719 return createGuiProgressView(*this);
4725 SEMenu::SEMenu(QWidget * parent)
4727 QAction * action = addAction(qt_("Disable Shell Escape"));
4728 connect(action, SIGNAL(triggered()),
4729 parent, SLOT(disableShellEscape()));
4732 } // namespace frontend
4735 #include "moc_GuiView.cpp"