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_EXPORT: {
1917 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1921 return doc_buffer->getStatus(cmd, flag);
1925 case LFUN_BUFFER_EXPORT_AS:
1926 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1931 case LFUN_BUFFER_WRITE_AS:
1932 enable = doc_buffer != 0;
1935 case LFUN_BUFFER_CLOSE:
1936 case LFUN_VIEW_CLOSE:
1937 enable = doc_buffer != 0;
1940 case LFUN_BUFFER_CLOSE_ALL:
1941 enable = theBufferList().last() != theBufferList().first();
1944 case LFUN_BUFFER_CHKTEX: {
1945 // hide if we have no checktex command
1946 if (lyxrc.chktex_command.empty()) {
1947 flag.setUnknown(true);
1951 if (!doc_buffer || !doc_buffer->params().isLatex()
1952 || d.processing_thread_watcher_.isRunning()) {
1953 // grey out, don't hide
1961 case LFUN_VIEW_SPLIT:
1962 if (cmd.getArg(0) == "vertical")
1963 enable = doc_buffer && (d.splitter_->count() == 1 ||
1964 d.splitter_->orientation() == Qt::Vertical);
1966 enable = doc_buffer && (d.splitter_->count() == 1 ||
1967 d.splitter_->orientation() == Qt::Horizontal);
1970 case LFUN_TAB_GROUP_CLOSE:
1971 enable = d.tabWorkAreaCount() > 1;
1974 case LFUN_DEVEL_MODE_TOGGLE:
1975 flag.setOnOff(devel_mode_);
1978 case LFUN_TOOLBAR_TOGGLE: {
1979 string const name = cmd.getArg(0);
1980 if (GuiToolbar * t = toolbar(name))
1981 flag.setOnOff(t->isVisible());
1984 docstring const msg =
1985 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1991 case LFUN_TOOLBAR_MOVABLE: {
1992 string const name = cmd.getArg(0);
1993 // use negation since locked == !movable
1995 // toolbar name * locks all toolbars
1996 flag.setOnOff(!toolbarsMovable_);
1997 else if (GuiToolbar * t = toolbar(name))
1998 flag.setOnOff(!(t->isMovable()));
2001 docstring const msg =
2002 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
2008 case LFUN_ICON_SIZE:
2009 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
2012 case LFUN_DROP_LAYOUTS_CHOICE:
2016 case LFUN_UI_TOGGLE:
2017 flag.setOnOff(isFullScreen());
2020 case LFUN_DIALOG_DISCONNECT_INSET:
2023 case LFUN_DIALOG_HIDE:
2024 // FIXME: should we check if the dialog is shown?
2027 case LFUN_DIALOG_TOGGLE:
2028 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
2031 case LFUN_DIALOG_SHOW: {
2032 string const name = cmd.getArg(0);
2034 enable = name == "aboutlyx"
2035 || name == "file" //FIXME: should be removed.
2037 || name == "texinfo"
2038 || name == "progress"
2039 || name == "compare";
2040 else if (name == "character" || name == "symbols"
2041 || name == "mathdelimiter" || name == "mathmatrix") {
2042 if (!buf || buf->isReadonly())
2045 Cursor const & cur = currentBufferView()->cursor();
2046 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2049 else if (name == "latexlog")
2050 enable = FileName(doc_buffer->logName()).isReadableFile();
2051 else if (name == "spellchecker")
2052 enable = theSpellChecker()
2053 && !doc_buffer->isReadonly()
2054 && !doc_buffer->text().empty();
2055 else if (name == "vclog")
2056 enable = doc_buffer->lyxvc().inUse();
2060 case LFUN_DIALOG_UPDATE: {
2061 string const name = cmd.getArg(0);
2063 enable = name == "prefs";
2067 case LFUN_COMMAND_EXECUTE:
2069 case LFUN_MENU_OPEN:
2070 // Nothing to check.
2073 case LFUN_COMPLETION_INLINE:
2074 if (!d.current_work_area_
2075 || !d.current_work_area_->completer().inlinePossible(
2076 currentBufferView()->cursor()))
2080 case LFUN_COMPLETION_POPUP:
2081 if (!d.current_work_area_
2082 || !d.current_work_area_->completer().popupPossible(
2083 currentBufferView()->cursor()))
2088 if (!d.current_work_area_
2089 || !d.current_work_area_->completer().inlinePossible(
2090 currentBufferView()->cursor()))
2094 case LFUN_COMPLETION_ACCEPT:
2095 if (!d.current_work_area_
2096 || (!d.current_work_area_->completer().popupVisible()
2097 && !d.current_work_area_->completer().inlineVisible()
2098 && !d.current_work_area_->completer().completionAvailable()))
2102 case LFUN_COMPLETION_CANCEL:
2103 if (!d.current_work_area_
2104 || (!d.current_work_area_->completer().popupVisible()
2105 && !d.current_work_area_->completer().inlineVisible()))
2109 case LFUN_BUFFER_ZOOM_OUT:
2110 case LFUN_BUFFER_ZOOM_IN: {
2111 // only diff between these two is that the default for ZOOM_OUT
2113 bool const neg_zoom =
2114 convert<int>(cmd.argument()) < 0 ||
2115 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2116 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2117 docstring const msg =
2118 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2122 enable = doc_buffer;
2126 case LFUN_BUFFER_ZOOM: {
2127 bool const less_than_min_zoom =
2128 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2129 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2130 docstring const msg =
2131 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2136 enable = doc_buffer;
2140 case LFUN_BUFFER_MOVE_NEXT:
2141 case LFUN_BUFFER_MOVE_PREVIOUS:
2142 // we do not cycle when moving
2143 case LFUN_BUFFER_NEXT:
2144 case LFUN_BUFFER_PREVIOUS:
2145 // because we cycle, it doesn't matter whether on first or last
2146 enable = (d.currentTabWorkArea()->count() > 1);
2148 case LFUN_BUFFER_SWITCH:
2149 // toggle on the current buffer, but do not toggle off
2150 // the other ones (is that a good idea?)
2152 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2153 flag.setOnOff(true);
2156 case LFUN_VC_REGISTER:
2157 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2159 case LFUN_VC_RENAME:
2160 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2163 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2165 case LFUN_VC_CHECK_IN:
2166 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2168 case LFUN_VC_CHECK_OUT:
2169 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2171 case LFUN_VC_LOCKING_TOGGLE:
2172 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2173 && doc_buffer->lyxvc().lockingToggleEnabled();
2174 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2176 case LFUN_VC_REVERT:
2177 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2178 && !doc_buffer->hasReadonlyFlag();
2180 case LFUN_VC_UNDO_LAST:
2181 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2183 case LFUN_VC_REPO_UPDATE:
2184 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2186 case LFUN_VC_COMMAND: {
2187 if (cmd.argument().empty())
2189 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2193 case LFUN_VC_COMPARE:
2194 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2197 case LFUN_SERVER_GOTO_FILE_ROW:
2198 case LFUN_LYX_ACTIVATE:
2200 case LFUN_FORWARD_SEARCH:
2201 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2204 case LFUN_FILE_INSERT_PLAINTEXT:
2205 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2206 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2209 case LFUN_SPELLING_CONTINUOUSLY:
2210 flag.setOnOff(lyxrc.spellcheck_continuously);
2218 flag.setEnabled(false);
2224 static FileName selectTemplateFile()
2226 FileDialog dlg(qt_("Select template file"));
2227 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2228 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2230 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2231 QStringList(qt_("LyX Documents (*.lyx)")));
2233 if (result.first == FileDialog::Later)
2235 if (result.second.isEmpty())
2237 return FileName(fromqstr(result.second));
2241 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2245 Buffer * newBuffer = 0;
2247 newBuffer = checkAndLoadLyXFile(filename);
2248 } catch (ExceptionMessage const & e) {
2255 message(_("Document not loaded."));
2259 setBuffer(newBuffer);
2260 newBuffer->errors("Parse");
2263 theSession().lastFiles().add(filename);
2269 void GuiView::openDocument(string const & fname)
2271 string initpath = lyxrc.document_path;
2273 if (documentBufferView()) {
2274 string const trypath = documentBufferView()->buffer().filePath();
2275 // If directory is writeable, use this as default.
2276 if (FileName(trypath).isDirWritable())
2282 if (fname.empty()) {
2283 FileDialog dlg(qt_("Select document to open"));
2284 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2285 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2287 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2288 FileDialog::Result result =
2289 dlg.open(toqstr(initpath), filter);
2291 if (result.first == FileDialog::Later)
2294 filename = fromqstr(result.second);
2296 // check selected filename
2297 if (filename.empty()) {
2298 message(_("Canceled."));
2304 // get absolute path of file and add ".lyx" to the filename if
2306 FileName const fullname =
2307 fileSearch(string(), filename, "lyx", support::may_not_exist);
2308 if (!fullname.empty())
2309 filename = fullname.absFileName();
2311 if (!fullname.onlyPath().isDirectory()) {
2312 Alert::warning(_("Invalid filename"),
2313 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2314 from_utf8(fullname.absFileName())));
2318 // if the file doesn't exist and isn't already open (bug 6645),
2319 // let the user create one
2320 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2321 !LyXVC::file_not_found_hook(fullname)) {
2322 // the user specifically chose this name. Believe him.
2323 Buffer * const b = newFile(filename, string(), true);
2329 docstring const disp_fn = makeDisplayPath(filename);
2330 message(bformat(_("Opening document %1$s..."), disp_fn));
2333 Buffer * buf = loadDocument(fullname);
2335 str2 = bformat(_("Document %1$s opened."), disp_fn);
2336 if (buf->lyxvc().inUse())
2337 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2338 " " + _("Version control detected.");
2340 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2345 // FIXME: clean that
2346 static bool import(GuiView * lv, FileName const & filename,
2347 string const & format, ErrorList & errorList)
2349 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2351 string loader_format;
2352 vector<string> loaders = theConverters().loaders();
2353 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2354 vector<string>::const_iterator it = loaders.begin();
2355 vector<string>::const_iterator en = loaders.end();
2356 for (; it != en; ++it) {
2357 if (!theConverters().isReachable(format, *it))
2360 string const tofile =
2361 support::changeExtension(filename.absFileName(),
2362 theFormats().extension(*it));
2363 if (!theConverters().convert(0, filename, FileName(tofile),
2364 filename, format, *it, errorList))
2366 loader_format = *it;
2369 if (loader_format.empty()) {
2370 frontend::Alert::error(_("Couldn't import file"),
2371 bformat(_("No information for importing the format %1$s."),
2372 theFormats().prettyName(format)));
2376 loader_format = format;
2378 if (loader_format == "lyx") {
2379 Buffer * buf = lv->loadDocument(lyxfile);
2383 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2387 bool as_paragraphs = loader_format == "textparagraph";
2388 string filename2 = (loader_format == format) ? filename.absFileName()
2389 : support::changeExtension(filename.absFileName(),
2390 theFormats().extension(loader_format));
2391 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2393 guiApp->setCurrentView(lv);
2394 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2401 void GuiView::importDocument(string const & argument)
2404 string filename = split(argument, format, ' ');
2406 LYXERR(Debug::INFO, format << " file: " << filename);
2408 // need user interaction
2409 if (filename.empty()) {
2410 string initpath = lyxrc.document_path;
2411 if (documentBufferView()) {
2412 string const trypath = documentBufferView()->buffer().filePath();
2413 // If directory is writeable, use this as default.
2414 if (FileName(trypath).isDirWritable())
2418 docstring const text = bformat(_("Select %1$s file to import"),
2419 theFormats().prettyName(format));
2421 FileDialog dlg(toqstr(text));
2422 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2423 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2425 docstring filter = theFormats().prettyName(format);
2428 filter += from_utf8(theFormats().extensions(format));
2431 FileDialog::Result result =
2432 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2434 if (result.first == FileDialog::Later)
2437 filename = fromqstr(result.second);
2439 // check selected filename
2440 if (filename.empty())
2441 message(_("Canceled."));
2444 if (filename.empty())
2447 // get absolute path of file
2448 FileName const fullname(support::makeAbsPath(filename));
2450 // Can happen if the user entered a path into the dialog
2452 if (fullname.onlyFileName().empty()) {
2453 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2454 "Aborting import."),
2455 from_utf8(fullname.absFileName()));
2456 frontend::Alert::error(_("File name error"), msg);
2457 message(_("Canceled."));
2462 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2464 // Check if the document already is open
2465 Buffer * buf = theBufferList().getBuffer(lyxfile);
2468 if (!closeBuffer()) {
2469 message(_("Canceled."));
2474 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2476 // if the file exists already, and we didn't do
2477 // -i lyx thefile.lyx, warn
2478 if (lyxfile.exists() && fullname != lyxfile) {
2480 docstring text = bformat(_("The document %1$s already exists.\n\n"
2481 "Do you want to overwrite that document?"), displaypath);
2482 int const ret = Alert::prompt(_("Overwrite document?"),
2483 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2486 message(_("Canceled."));
2491 message(bformat(_("Importing %1$s..."), displaypath));
2492 ErrorList errorList;
2493 if (import(this, fullname, format, errorList))
2494 message(_("imported."));
2496 message(_("file not imported!"));
2498 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2502 void GuiView::newDocument(string const & filename, bool from_template)
2504 FileName initpath(lyxrc.document_path);
2505 if (documentBufferView()) {
2506 FileName const trypath(documentBufferView()->buffer().filePath());
2507 // If directory is writeable, use this as default.
2508 if (trypath.isDirWritable())
2512 string templatefile;
2513 if (from_template) {
2514 templatefile = selectTemplateFile().absFileName();
2515 if (templatefile.empty())
2520 if (filename.empty())
2521 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2523 b = newFile(filename, templatefile, true);
2528 // If no new document could be created, it is unsure
2529 // whether there is a valid BufferView.
2530 if (currentBufferView())
2531 // Ensure the cursor is correctly positioned on screen.
2532 currentBufferView()->showCursor();
2536 void GuiView::insertLyXFile(docstring const & fname)
2538 BufferView * bv = documentBufferView();
2543 FileName filename(to_utf8(fname));
2544 if (filename.empty()) {
2545 // Launch a file browser
2547 string initpath = lyxrc.document_path;
2548 string const trypath = bv->buffer().filePath();
2549 // If directory is writeable, use this as default.
2550 if (FileName(trypath).isDirWritable())
2554 FileDialog dlg(qt_("Select LyX document to insert"));
2555 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2556 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2558 FileDialog::Result result = dlg.open(toqstr(initpath),
2559 QStringList(qt_("LyX Documents (*.lyx)")));
2561 if (result.first == FileDialog::Later)
2565 filename.set(fromqstr(result.second));
2567 // check selected filename
2568 if (filename.empty()) {
2569 // emit message signal.
2570 message(_("Canceled."));
2575 bv->insertLyXFile(filename);
2576 bv->buffer().errors("Parse");
2580 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2582 FileName fname = b.fileName();
2583 FileName const oldname = fname;
2585 if (!newname.empty()) {
2587 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2589 // Switch to this Buffer.
2592 // No argument? Ask user through dialog.
2594 FileDialog dlg(qt_("Choose a filename to save document as"));
2595 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2596 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2598 if (!isLyXFileName(fname.absFileName()))
2599 fname.changeExtension(".lyx");
2601 FileDialog::Result result =
2602 dlg.save(toqstr(fname.onlyPath().absFileName()),
2603 QStringList(qt_("LyX Documents (*.lyx)")),
2604 toqstr(fname.onlyFileName()));
2606 if (result.first == FileDialog::Later)
2609 fname.set(fromqstr(result.second));
2614 if (!isLyXFileName(fname.absFileName()))
2615 fname.changeExtension(".lyx");
2618 // fname is now the new Buffer location.
2620 // if there is already a Buffer open with this name, we do not want
2621 // to have another one. (the second test makes sure we're not just
2622 // trying to overwrite ourselves, which is fine.)
2623 if (theBufferList().exists(fname) && fname != oldname
2624 && theBufferList().getBuffer(fname) != &b) {
2625 docstring const text =
2626 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2627 "Please close it before attempting to overwrite it.\n"
2628 "Do you want to choose a new filename?"),
2629 from_utf8(fname.absFileName()));
2630 int const ret = Alert::prompt(_("Chosen File Already Open"),
2631 text, 0, 1, _("&Rename"), _("&Cancel"));
2633 case 0: return renameBuffer(b, docstring(), kind);
2634 case 1: return false;
2639 bool const existsLocal = fname.exists();
2640 bool const existsInVC = LyXVC::fileInVC(fname);
2641 if (existsLocal || existsInVC) {
2642 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2643 if (kind != LV_WRITE_AS && existsInVC) {
2644 // renaming to a name that is already in VC
2646 docstring text = bformat(_("The document %1$s "
2647 "is already registered.\n\n"
2648 "Do you want to choose a new name?"),
2650 docstring const title = (kind == LV_VC_RENAME) ?
2651 _("Rename document?") : _("Copy document?");
2652 docstring const button = (kind == LV_VC_RENAME) ?
2653 _("&Rename") : _("&Copy");
2654 int const ret = Alert::prompt(title, text, 0, 1,
2655 button, _("&Cancel"));
2657 case 0: return renameBuffer(b, docstring(), kind);
2658 case 1: return false;
2663 docstring text = bformat(_("The document %1$s "
2664 "already exists.\n\n"
2665 "Do you want to overwrite that document?"),
2667 int const ret = Alert::prompt(_("Overwrite document?"),
2668 text, 0, 2, _("&Overwrite"),
2669 _("&Rename"), _("&Cancel"));
2672 case 1: return renameBuffer(b, docstring(), kind);
2673 case 2: return false;
2679 case LV_VC_RENAME: {
2680 string msg = b.lyxvc().rename(fname);
2683 message(from_utf8(msg));
2687 string msg = b.lyxvc().copy(fname);
2690 message(from_utf8(msg));
2696 // LyXVC created the file already in case of LV_VC_RENAME or
2697 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2698 // relative paths of included stuff right if we moved e.g. from
2699 // /a/b.lyx to /a/c/b.lyx.
2701 bool const saved = saveBuffer(b, fname);
2708 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2710 FileName fname = b.fileName();
2712 FileDialog dlg(qt_("Choose a filename to export the document as"));
2713 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2716 QString const anyformat = qt_("Guess from extension (*.*)");
2719 vector<Format const *> export_formats;
2720 for (Format const & f : theFormats())
2721 if (f.documentFormat())
2722 export_formats.push_back(&f);
2723 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2724 map<QString, string> fmap;
2727 for (Format const * f : export_formats) {
2728 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2729 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2731 from_ascii(f->extension())));
2732 types << loc_filter;
2733 fmap[loc_filter] = f->name();
2734 if (from_ascii(f->name()) == iformat) {
2735 filter = loc_filter;
2736 ext = f->extension();
2739 string ofname = fname.onlyFileName();
2741 ofname = support::changeExtension(ofname, ext);
2742 FileDialog::Result result =
2743 dlg.save(toqstr(fname.onlyPath().absFileName()),
2747 if (result.first != FileDialog::Chosen)
2751 fname.set(fromqstr(result.second));
2752 if (filter == anyformat)
2753 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2755 fmt_name = fmap[filter];
2756 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2757 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2759 if (fmt_name.empty() || fname.empty())
2762 // fname is now the new Buffer location.
2763 if (FileName(fname).exists()) {
2764 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2765 docstring text = bformat(_("The document %1$s already "
2766 "exists.\n\nDo you want to "
2767 "overwrite that document?"),
2769 int const ret = Alert::prompt(_("Overwrite document?"),
2770 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2773 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2774 case 2: return false;
2778 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2781 return dr.dispatched();
2785 bool GuiView::saveBuffer(Buffer & b)
2787 return saveBuffer(b, FileName());
2791 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2793 if (workArea(b) && workArea(b)->inDialogMode())
2796 if (fn.empty() && b.isUnnamed())
2797 return renameBuffer(b, docstring());
2799 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2801 theSession().lastFiles().add(b.fileName());
2805 // Switch to this Buffer.
2808 // FIXME: we don't tell the user *WHY* the save failed !!
2809 docstring const file = makeDisplayPath(b.absFileName(), 30);
2810 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2811 "Do you want to rename the document and "
2812 "try again?"), file);
2813 int const ret = Alert::prompt(_("Rename and save?"),
2814 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2817 if (!renameBuffer(b, docstring()))
2826 return saveBuffer(b, fn);
2830 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2832 return closeWorkArea(wa, false);
2836 // We only want to close the buffer if it is not visible in other workareas
2837 // of the same view, nor in other views, and if this is not a child
2838 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2840 Buffer & buf = wa->bufferView().buffer();
2842 bool last_wa = d.countWorkAreasOf(buf) == 1
2843 && !inOtherView(buf) && !buf.parent();
2845 bool close_buffer = last_wa;
2848 if (lyxrc.close_buffer_with_last_view == "yes")
2850 else if (lyxrc.close_buffer_with_last_view == "no")
2851 close_buffer = false;
2854 if (buf.isUnnamed())
2855 file = from_utf8(buf.fileName().onlyFileName());
2857 file = buf.fileName().displayName(30);
2858 docstring const text = bformat(
2859 _("Last view on document %1$s is being closed.\n"
2860 "Would you like to close or hide the document?\n"
2862 "Hidden documents can be displayed back through\n"
2863 "the menu: View->Hidden->...\n"
2865 "To remove this question, set your preference in:\n"
2866 " Tools->Preferences->Look&Feel->UserInterface\n"
2868 int ret = Alert::prompt(_("Close or hide document?"),
2869 text, 0, 1, _("&Close"), _("&Hide"));
2870 close_buffer = (ret == 0);
2874 return closeWorkArea(wa, close_buffer);
2878 bool GuiView::closeBuffer()
2880 GuiWorkArea * wa = currentMainWorkArea();
2881 // coverity complained about this
2882 // it seems unnecessary, but perhaps is worth the check
2883 LASSERT(wa, return false);
2885 setCurrentWorkArea(wa);
2886 Buffer & buf = wa->bufferView().buffer();
2887 return closeWorkArea(wa, !buf.parent());
2891 void GuiView::writeSession() const {
2892 GuiWorkArea const * active_wa = currentMainWorkArea();
2893 for (int i = 0; i < d.splitter_->count(); ++i) {
2894 TabWorkArea * twa = d.tabWorkArea(i);
2895 for (int j = 0; j < twa->count(); ++j) {
2896 GuiWorkArea * wa = twa->workArea(j);
2897 Buffer & buf = wa->bufferView().buffer();
2898 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2904 bool GuiView::closeBufferAll()
2906 // Close the workareas in all other views
2907 QList<int> const ids = guiApp->viewIds();
2908 for (int i = 0; i != ids.size(); ++i) {
2909 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2913 // Close our own workareas
2914 if (!closeWorkAreaAll())
2917 // Now close the hidden buffers. We prevent hidden buffers from being
2918 // dirty, so we can just close them.
2919 theBufferList().closeAll();
2924 bool GuiView::closeWorkAreaAll()
2926 setCurrentWorkArea(currentMainWorkArea());
2928 // We might be in a situation that there is still a tabWorkArea, but
2929 // there are no tabs anymore. This can happen when we get here after a
2930 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2931 // many TabWorkArea's have no documents anymore.
2934 // We have to call count() each time, because it can happen that
2935 // more than one splitter will disappear in one iteration (bug 5998).
2936 while (d.splitter_->count() > empty_twa) {
2937 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2939 if (twa->count() == 0)
2942 setCurrentWorkArea(twa->currentWorkArea());
2943 if (!closeTabWorkArea(twa))
2951 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2956 Buffer & buf = wa->bufferView().buffer();
2958 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2959 Alert::warning(_("Close document"),
2960 _("Document could not be closed because it is being processed by LyX."));
2965 return closeBuffer(buf);
2967 if (!inMultiTabs(wa))
2968 if (!saveBufferIfNeeded(buf, true))
2976 bool GuiView::closeBuffer(Buffer & buf)
2978 // If we are in a close_event all children will be closed in some time,
2979 // so no need to do it here. This will ensure that the children end up
2980 // in the session file in the correct order. If we close the master
2981 // buffer, we can close or release the child buffers here too.
2982 bool success = true;
2984 ListOfBuffers clist = buf.getChildren();
2985 ListOfBuffers::const_iterator it = clist.begin();
2986 ListOfBuffers::const_iterator const bend = clist.end();
2987 for (; it != bend; ++it) {
2988 Buffer * child_buf = *it;
2989 if (theBufferList().isOthersChild(&buf, child_buf)) {
2990 child_buf->setParent(0);
2994 // FIXME: should we look in other tabworkareas?
2995 // ANSWER: I don't think so. I've tested, and if the child is
2996 // open in some other window, it closes without a problem.
2997 GuiWorkArea * child_wa = workArea(*child_buf);
2999 success = closeWorkArea(child_wa, true);
3003 // In this case the child buffer is open but hidden.
3004 // It therefore should not (MUST NOT) be dirty!
3005 LATTEST(child_buf->isClean());
3006 theBufferList().release(child_buf);
3011 // goto bookmark to update bookmark pit.
3012 // FIXME: we should update only the bookmarks related to this buffer!
3013 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3014 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3015 guiApp->gotoBookmark(i+1, false, false);
3017 if (saveBufferIfNeeded(buf, false)) {
3018 buf.removeAutosaveFile();
3019 theBufferList().release(&buf);
3023 // open all children again to avoid a crash because of dangling
3024 // pointers (bug 6603)
3030 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3032 while (twa == d.currentTabWorkArea()) {
3033 twa->setCurrentIndex(twa->count() - 1);
3035 GuiWorkArea * wa = twa->currentWorkArea();
3036 Buffer & b = wa->bufferView().buffer();
3038 // We only want to close the buffer if the same buffer is not visible
3039 // in another view, and if this is not a child and if we are closing
3040 // a view (not a tabgroup).
3041 bool const close_buffer =
3042 !inOtherView(b) && !b.parent() && closing_;
3044 if (!closeWorkArea(wa, close_buffer))
3051 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3053 if (buf.isClean() || buf.paragraphs().empty())
3056 // Switch to this Buffer.
3062 if (buf.isUnnamed()) {
3063 file = from_utf8(buf.fileName().onlyFileName());
3066 FileName filename = buf.fileName();
3068 file = filename.displayName(30);
3069 exists = filename.exists();
3072 // Bring this window to top before asking questions.
3077 if (hiding && buf.isUnnamed()) {
3078 docstring const text = bformat(_("The document %1$s has not been "
3079 "saved yet.\n\nDo you want to save "
3080 "the document?"), file);
3081 ret = Alert::prompt(_("Save new document?"),
3082 text, 0, 1, _("&Save"), _("&Cancel"));
3086 docstring const text = exists ?
3087 bformat(_("The document %1$s has unsaved changes."
3088 "\n\nDo you want to save the document or "
3089 "discard the changes?"), file) :
3090 bformat(_("The document %1$s has not been saved yet."
3091 "\n\nDo you want to save the document or "
3092 "discard it entirely?"), file);
3093 docstring const title = exists ?
3094 _("Save changed document?") : _("Save document?");
3095 ret = Alert::prompt(title, text, 0, 2,
3096 _("&Save"), _("&Discard"), _("&Cancel"));
3101 if (!saveBuffer(buf))
3105 // If we crash after this we could have no autosave file
3106 // but I guess this is really improbable (Jug).
3107 // Sometimes improbable things happen:
3108 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3109 // buf.removeAutosaveFile();
3111 // revert all changes
3122 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3124 Buffer & buf = wa->bufferView().buffer();
3126 for (int i = 0; i != d.splitter_->count(); ++i) {
3127 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3128 if (wa_ && wa_ != wa)
3131 return inOtherView(buf);
3135 bool GuiView::inOtherView(Buffer & buf)
3137 QList<int> const ids = guiApp->viewIds();
3139 for (int i = 0; i != ids.size(); ++i) {
3143 if (guiApp->view(ids[i]).workArea(buf))
3150 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3152 if (!documentBufferView())
3155 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3156 Buffer * const curbuf = &documentBufferView()->buffer();
3157 int nwa = twa->count();
3158 for (int i = 0; i < nwa; ++i) {
3159 if (&workArea(i)->bufferView().buffer() == curbuf) {
3161 if (np == NEXTBUFFER)
3162 next_index = (i == nwa - 1 ? 0 : i + 1);
3164 next_index = (i == 0 ? nwa - 1 : i - 1);
3166 twa->moveTab(i, next_index);
3168 setBuffer(&workArea(next_index)->bufferView().buffer());
3176 /// make sure the document is saved
3177 static bool ensureBufferClean(Buffer * buffer)
3179 LASSERT(buffer, return false);
3180 if (buffer->isClean() && !buffer->isUnnamed())
3183 docstring const file = buffer->fileName().displayName(30);
3186 if (!buffer->isUnnamed()) {
3187 text = bformat(_("The document %1$s has unsaved "
3188 "changes.\n\nDo you want to save "
3189 "the document?"), file);
3190 title = _("Save changed document?");
3193 text = bformat(_("The document %1$s has not been "
3194 "saved yet.\n\nDo you want to save "
3195 "the document?"), file);
3196 title = _("Save new document?");
3198 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3201 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3203 return buffer->isClean() && !buffer->isUnnamed();
3207 bool GuiView::reloadBuffer(Buffer & buf)
3209 currentBufferView()->cursor().reset();
3210 Buffer::ReadStatus status = buf.reload();
3211 return status == Buffer::ReadSuccess;
3215 void GuiView::checkExternallyModifiedBuffers()
3217 BufferList::iterator bit = theBufferList().begin();
3218 BufferList::iterator const bend = theBufferList().end();
3219 for (; bit != bend; ++bit) {
3220 Buffer * buf = *bit;
3221 if (buf->fileName().exists() && buf->isChecksumModified()) {
3222 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3223 " Reload now? Any local changes will be lost."),
3224 from_utf8(buf->absFileName()));
3225 int const ret = Alert::prompt(_("Reload externally changed document?"),
3226 text, 0, 1, _("&Reload"), _("&Cancel"));
3234 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3236 Buffer * buffer = documentBufferView()
3237 ? &(documentBufferView()->buffer()) : 0;
3239 switch (cmd.action()) {
3240 case LFUN_VC_REGISTER:
3241 if (!buffer || !ensureBufferClean(buffer))
3243 if (!buffer->lyxvc().inUse()) {
3244 if (buffer->lyxvc().registrer()) {
3245 reloadBuffer(*buffer);
3246 dr.clearMessageUpdate();
3251 case LFUN_VC_RENAME:
3252 case LFUN_VC_COPY: {
3253 if (!buffer || !ensureBufferClean(buffer))
3255 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3256 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3257 // Some changes are not yet committed.
3258 // We test here and not in getStatus(), since
3259 // this test is expensive.
3261 LyXVC::CommandResult ret =
3262 buffer->lyxvc().checkIn(log);
3264 if (ret == LyXVC::ErrorCommand ||
3265 ret == LyXVC::VCSuccess)
3266 reloadBuffer(*buffer);
3267 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3268 frontend::Alert::error(
3269 _("Revision control error."),
3270 _("Document could not be checked in."));
3274 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3275 LV_VC_RENAME : LV_VC_COPY;
3276 renameBuffer(*buffer, cmd.argument(), kind);
3281 case LFUN_VC_CHECK_IN:
3282 if (!buffer || !ensureBufferClean(buffer))
3284 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3286 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3288 // Only skip reloading if the checkin was cancelled or
3289 // an error occurred before the real checkin VCS command
3290 // was executed, since the VCS might have changed the
3291 // file even if it could not checkin successfully.
3292 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3293 reloadBuffer(*buffer);
3297 case LFUN_VC_CHECK_OUT:
3298 if (!buffer || !ensureBufferClean(buffer))
3300 if (buffer->lyxvc().inUse()) {
3301 dr.setMessage(buffer->lyxvc().checkOut());
3302 reloadBuffer(*buffer);
3306 case LFUN_VC_LOCKING_TOGGLE:
3307 LASSERT(buffer, return);
3308 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3310 if (buffer->lyxvc().inUse()) {
3311 string res = buffer->lyxvc().lockingToggle();
3313 frontend::Alert::error(_("Revision control error."),
3314 _("Error when setting the locking property."));
3317 reloadBuffer(*buffer);
3322 case LFUN_VC_REVERT:
3323 LASSERT(buffer, return);
3324 if (buffer->lyxvc().revert()) {
3325 reloadBuffer(*buffer);
3326 dr.clearMessageUpdate();
3330 case LFUN_VC_UNDO_LAST:
3331 LASSERT(buffer, return);
3332 buffer->lyxvc().undoLast();
3333 reloadBuffer(*buffer);
3334 dr.clearMessageUpdate();
3337 case LFUN_VC_REPO_UPDATE:
3338 LASSERT(buffer, return);
3339 if (ensureBufferClean(buffer)) {
3340 dr.setMessage(buffer->lyxvc().repoUpdate());
3341 checkExternallyModifiedBuffers();
3345 case LFUN_VC_COMMAND: {
3346 string flag = cmd.getArg(0);
3347 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3350 if (contains(flag, 'M')) {
3351 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3354 string path = cmd.getArg(1);
3355 if (contains(path, "$$p") && buffer)
3356 path = subst(path, "$$p", buffer->filePath());
3357 LYXERR(Debug::LYXVC, "Directory: " << path);
3359 if (!pp.isReadableDirectory()) {
3360 lyxerr << _("Directory is not accessible.") << endl;
3363 support::PathChanger p(pp);
3365 string command = cmd.getArg(2);
3366 if (command.empty())
3369 command = subst(command, "$$i", buffer->absFileName());
3370 command = subst(command, "$$p", buffer->filePath());
3372 command = subst(command, "$$m", to_utf8(message));
3373 LYXERR(Debug::LYXVC, "Command: " << command);
3375 one.startscript(Systemcall::Wait, command);
3379 if (contains(flag, 'I'))
3380 buffer->markDirty();
3381 if (contains(flag, 'R'))
3382 reloadBuffer(*buffer);
3387 case LFUN_VC_COMPARE: {
3388 if (cmd.argument().empty()) {
3389 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3393 string rev1 = cmd.getArg(0);
3398 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3401 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3402 f2 = buffer->absFileName();
3404 string rev2 = cmd.getArg(1);
3408 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3412 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3413 f1 << "\n" << f2 << "\n" );
3414 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3415 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3425 void GuiView::openChildDocument(string const & fname)
3427 LASSERT(documentBufferView(), return);
3428 Buffer & buffer = documentBufferView()->buffer();
3429 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3430 documentBufferView()->saveBookmark(false);
3432 if (theBufferList().exists(filename)) {
3433 child = theBufferList().getBuffer(filename);
3436 message(bformat(_("Opening child document %1$s..."),
3437 makeDisplayPath(filename.absFileName())));
3438 child = loadDocument(filename, false);
3440 // Set the parent name of the child document.
3441 // This makes insertion of citations and references in the child work,
3442 // when the target is in the parent or another child document.
3444 child->setParent(&buffer);
3448 bool GuiView::goToFileRow(string const & argument)
3452 size_t i = argument.find_last_of(' ');
3453 if (i != string::npos) {
3454 file_name = os::internal_path(trim(argument.substr(0, i)));
3455 istringstream is(argument.substr(i + 1));
3460 if (i == string::npos) {
3461 LYXERR0("Wrong argument: " << argument);
3465 string const abstmp = package().temp_dir().absFileName();
3466 string const realtmp = package().temp_dir().realPath();
3467 // We have to use os::path_prefix_is() here, instead of
3468 // simply prefixIs(), because the file name comes from
3469 // an external application and may need case adjustment.
3470 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3471 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3472 // Needed by inverse dvi search. If it is a file
3473 // in tmpdir, call the apropriated function.
3474 // If tmpdir is a symlink, we may have the real
3475 // path passed back, so we correct for that.
3476 if (!prefixIs(file_name, abstmp))
3477 file_name = subst(file_name, realtmp, abstmp);
3478 buf = theBufferList().getBufferFromTmp(file_name);
3480 // Must replace extension of the file to be .lyx
3481 // and get full path
3482 FileName const s = fileSearch(string(),
3483 support::changeExtension(file_name, ".lyx"), "lyx");
3484 // Either change buffer or load the file
3485 if (theBufferList().exists(s))
3486 buf = theBufferList().getBuffer(s);
3487 else if (s.exists()) {
3488 buf = loadDocument(s);
3493 _("File does not exist: %1$s"),
3494 makeDisplayPath(file_name)));
3500 _("No buffer for file: %1$s."),
3501 makeDisplayPath(file_name))
3506 bool success = documentBufferView()->setCursorFromRow(row);
3508 LYXERR(Debug::LATEX,
3509 "setCursorFromRow: invalid position for row " << row);
3510 frontend::Alert::error(_("Inverse Search Failed"),
3511 _("Invalid position requested by inverse search.\n"
3512 "You may need to update the viewed document."));
3518 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3520 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3521 menu->exec(QCursor::pos());
3526 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3528 Buffer::ExportStatus const status = func(format);
3530 // the cloning operation will have produced a clone of the entire set of
3531 // documents, starting from the master. so we must delete those.
3532 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3534 busyBuffers.remove(orig);
3539 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3541 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3542 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3546 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3548 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3549 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3553 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3555 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3556 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3560 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3561 string const & argument,
3562 Buffer const * used_buffer,
3563 docstring const & msg,
3564 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3565 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3566 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3571 string format = argument;
3573 format = used_buffer->params().getDefaultOutputFormat();
3574 processing_format = format;
3576 progress_->clearMessages();
3579 #if EXPORT_in_THREAD
3580 GuiViewPrivate::busyBuffers.insert(used_buffer);
3581 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3582 if (!cloned_buffer) {
3583 Alert::error(_("Export Error"),
3584 _("Error cloning the Buffer."));
3587 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3592 setPreviewFuture(f);
3593 last_export_format = used_buffer->params().bufferFormat();
3596 // We are asynchronous, so we don't know here anything about the success
3599 Buffer::ExportStatus status;
3601 status = (used_buffer->*syncFunc)(format, true);
3602 } else if (previewFunc) {
3603 status = (used_buffer->*previewFunc)(format);
3606 handleExportStatus(gv_, status, format);
3608 return (status == Buffer::ExportSuccess
3609 || status == Buffer::PreviewSuccess);
3613 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3615 BufferView * bv = currentBufferView();
3616 LASSERT(bv, return);
3618 // Let the current BufferView dispatch its own actions.
3619 bv->dispatch(cmd, dr);
3620 if (dr.dispatched())
3623 // Try with the document BufferView dispatch if any.
3624 BufferView * doc_bv = documentBufferView();
3625 if (doc_bv && doc_bv != bv) {
3626 doc_bv->dispatch(cmd, dr);
3627 if (dr.dispatched())
3631 // Then let the current Cursor dispatch its own actions.
3632 bv->cursor().dispatch(cmd);
3634 // update completion. We do it here and not in
3635 // processKeySym to avoid another redraw just for a
3636 // changed inline completion
3637 if (cmd.origin() == FuncRequest::KEYBOARD) {
3638 if (cmd.action() == LFUN_SELF_INSERT
3639 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3640 updateCompletion(bv->cursor(), true, true);
3641 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3642 updateCompletion(bv->cursor(), false, true);
3644 updateCompletion(bv->cursor(), false, false);
3647 dr = bv->cursor().result();
3651 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3653 BufferView * bv = currentBufferView();
3654 // By default we won't need any update.
3655 dr.screenUpdate(Update::None);
3656 // assume cmd will be dispatched
3657 dr.dispatched(true);
3659 Buffer * doc_buffer = documentBufferView()
3660 ? &(documentBufferView()->buffer()) : 0;
3662 if (cmd.origin() == FuncRequest::TOC) {
3663 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3664 // FIXME: do we need to pass a DispatchResult object here?
3665 toc->doDispatch(bv->cursor(), cmd);
3669 string const argument = to_utf8(cmd.argument());
3671 switch(cmd.action()) {
3672 case LFUN_BUFFER_CHILD_OPEN:
3673 openChildDocument(to_utf8(cmd.argument()));
3676 case LFUN_BUFFER_IMPORT:
3677 importDocument(to_utf8(cmd.argument()));
3680 case LFUN_BUFFER_EXPORT: {
3683 // GCC only sees strfwd.h when building merged
3684 if (::lyx::operator==(cmd.argument(), "custom")) {
3685 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3689 string const dest = cmd.getArg(1);
3690 FileName target_dir;
3691 if (!dest.empty() && FileName::isAbsolute(dest))
3692 target_dir = FileName(support::onlyPath(dest));
3694 target_dir = doc_buffer->fileName().onlyPath();
3696 string const format = (argument.empty() || argument == "default") ?
3697 doc_buffer->params().getDefaultOutputFormat() : argument;
3699 if ((dest.empty() && doc_buffer->isUnnamed())
3700 || !target_dir.isDirWritable()) {
3701 exportBufferAs(*doc_buffer, from_utf8(format));
3704 /* TODO/Review: Is it a problem to also export the children?
3705 See the update_unincluded flag */
3706 d.asyncBufferProcessing(format,
3709 &GuiViewPrivate::exportAndDestroy,
3712 // TODO Inform user about success
3716 case LFUN_BUFFER_EXPORT_AS: {
3717 LASSERT(doc_buffer, break);
3718 docstring f = cmd.argument();
3720 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3721 exportBufferAs(*doc_buffer, f);
3725 case LFUN_BUFFER_UPDATE: {
3726 d.asyncBufferProcessing(argument,
3729 &GuiViewPrivate::compileAndDestroy,
3734 case LFUN_BUFFER_VIEW: {
3735 d.asyncBufferProcessing(argument,
3737 _("Previewing ..."),
3738 &GuiViewPrivate::previewAndDestroy,
3743 case LFUN_MASTER_BUFFER_UPDATE: {
3744 d.asyncBufferProcessing(argument,
3745 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3747 &GuiViewPrivate::compileAndDestroy,
3752 case LFUN_MASTER_BUFFER_VIEW: {
3753 d.asyncBufferProcessing(argument,
3754 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3756 &GuiViewPrivate::previewAndDestroy,
3757 0, &Buffer::preview);
3760 case LFUN_BUFFER_SWITCH: {
3761 string const file_name = to_utf8(cmd.argument());
3762 if (!FileName::isAbsolute(file_name)) {
3764 dr.setMessage(_("Absolute filename expected."));
3768 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3771 dr.setMessage(_("Document not loaded"));
3775 // Do we open or switch to the buffer in this view ?
3776 if (workArea(*buffer)
3777 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3782 // Look for the buffer in other views
3783 QList<int> const ids = guiApp->viewIds();
3785 for (; i != ids.size(); ++i) {
3786 GuiView & gv = guiApp->view(ids[i]);
3787 if (gv.workArea(*buffer)) {
3789 gv.activateWindow();
3791 gv.setBuffer(buffer);
3796 // If necessary, open a new window as a last resort
3797 if (i == ids.size()) {
3798 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3804 case LFUN_BUFFER_NEXT:
3805 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3808 case LFUN_BUFFER_MOVE_NEXT:
3809 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3812 case LFUN_BUFFER_PREVIOUS:
3813 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3816 case LFUN_BUFFER_MOVE_PREVIOUS:
3817 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3820 case LFUN_BUFFER_CHKTEX:
3821 LASSERT(doc_buffer, break);
3822 doc_buffer->runChktex();
3825 case LFUN_COMMAND_EXECUTE: {
3826 command_execute_ = true;
3827 minibuffer_focus_ = true;
3830 case LFUN_DROP_LAYOUTS_CHOICE:
3831 d.layout_->showPopup();
3834 case LFUN_MENU_OPEN:
3835 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3836 menu->exec(QCursor::pos());
3839 case LFUN_FILE_INSERT:
3840 insertLyXFile(cmd.argument());
3843 case LFUN_FILE_INSERT_PLAINTEXT:
3844 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3845 string const fname = to_utf8(cmd.argument());
3846 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3847 dr.setMessage(_("Absolute filename expected."));
3851 FileName filename(fname);
3852 if (fname.empty()) {
3853 FileDialog dlg(qt_("Select file to insert"));
3855 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3856 QStringList(qt_("All Files (*)")));
3858 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3859 dr.setMessage(_("Canceled."));
3863 filename.set(fromqstr(result.second));
3867 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3868 bv->dispatch(new_cmd, dr);
3873 case LFUN_BUFFER_RELOAD: {
3874 LASSERT(doc_buffer, break);
3877 if (!doc_buffer->isClean()) {
3878 docstring const file =
3879 makeDisplayPath(doc_buffer->absFileName(), 20);
3880 if (doc_buffer->notifiesExternalModification()) {
3881 docstring text = _("The current version will be lost. "
3882 "Are you sure you want to load the version on disk "
3883 "of the document %1$s?");
3884 ret = Alert::prompt(_("Reload saved document?"),
3885 bformat(text, file), 1, 1,
3886 _("&Reload"), _("&Cancel"));
3888 docstring text = _("Any changes will be lost. "
3889 "Are you sure you want to revert to the saved version "
3890 "of the document %1$s?");
3891 ret = Alert::prompt(_("Revert to saved document?"),
3892 bformat(text, file), 1, 1,
3893 _("&Revert"), _("&Cancel"));
3898 doc_buffer->markClean();
3899 reloadBuffer(*doc_buffer);
3900 dr.forceBufferUpdate();
3905 case LFUN_BUFFER_WRITE:
3906 LASSERT(doc_buffer, break);
3907 saveBuffer(*doc_buffer);
3910 case LFUN_BUFFER_WRITE_AS:
3911 LASSERT(doc_buffer, break);
3912 renameBuffer(*doc_buffer, cmd.argument());
3915 case LFUN_BUFFER_WRITE_ALL: {
3916 Buffer * first = theBufferList().first();
3919 message(_("Saving all documents..."));
3920 // We cannot use a for loop as the buffer list cycles.
3923 if (!b->isClean()) {
3925 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3927 b = theBufferList().next(b);
3928 } while (b != first);
3929 dr.setMessage(_("All documents saved."));
3933 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3934 LASSERT(doc_buffer, break);
3935 doc_buffer->clearExternalModification();
3938 case LFUN_BUFFER_CLOSE:
3942 case LFUN_BUFFER_CLOSE_ALL:
3946 case LFUN_DEVEL_MODE_TOGGLE:
3947 devel_mode_ = !devel_mode_;
3949 dr.setMessage(_("Developer mode is now enabled."));
3951 dr.setMessage(_("Developer mode is now disabled."));
3954 case LFUN_TOOLBAR_TOGGLE: {
3955 string const name = cmd.getArg(0);
3956 if (GuiToolbar * t = toolbar(name))
3961 case LFUN_TOOLBAR_MOVABLE: {
3962 string const name = cmd.getArg(0);
3964 // toggle (all) toolbars movablility
3965 toolbarsMovable_ = !toolbarsMovable_;
3966 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3967 GuiToolbar * tb = toolbar(ti.name);
3968 if (tb && tb->isMovable() != toolbarsMovable_)
3969 // toggle toolbar movablity if it does not fit lock
3970 // (all) toolbars positions state silent = true, since
3971 // status bar notifications are slow
3974 if (toolbarsMovable_)
3975 dr.setMessage(_("Toolbars unlocked."));
3977 dr.setMessage(_("Toolbars locked."));
3978 } else if (GuiToolbar * t = toolbar(name)) {
3979 // toggle current toolbar movablity
3981 // update lock (all) toolbars positions
3982 updateLockToolbars();
3987 case LFUN_ICON_SIZE: {
3988 QSize size = d.iconSize(cmd.argument());
3990 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3991 size.width(), size.height()));
3995 case LFUN_DIALOG_UPDATE: {
3996 string const name = to_utf8(cmd.argument());
3997 if (name == "prefs" || name == "document")
3998 updateDialog(name, string());
3999 else if (name == "paragraph")
4000 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4001 else if (currentBufferView()) {
4002 Inset * inset = currentBufferView()->editedInset(name);
4003 // Can only update a dialog connected to an existing inset
4005 // FIXME: get rid of this indirection; GuiView ask the inset
4006 // if he is kind enough to update itself...
4007 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4008 //FIXME: pass DispatchResult here?
4009 inset->dispatch(currentBufferView()->cursor(), fr);
4015 case LFUN_DIALOG_TOGGLE: {
4016 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4017 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4018 dispatch(FuncRequest(func_code, cmd.argument()), dr);
4022 case LFUN_DIALOG_DISCONNECT_INSET:
4023 disconnectDialog(to_utf8(cmd.argument()));
4026 case LFUN_DIALOG_HIDE: {
4027 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4031 case LFUN_DIALOG_SHOW: {
4032 string const name = cmd.getArg(0);
4033 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4035 if (name == "character") {
4036 data = freefont2string();
4038 showDialog("character", data);
4039 } else if (name == "latexlog") {
4040 // getStatus checks that
4041 LATTEST(doc_buffer);
4042 Buffer::LogType type;
4043 string const logfile = doc_buffer->logName(&type);
4045 case Buffer::latexlog:
4048 case Buffer::buildlog:
4052 data += Lexer::quoteString(logfile);
4053 showDialog("log", data);
4054 } else if (name == "vclog") {
4055 // getStatus checks that
4056 LATTEST(doc_buffer);
4057 string const data = "vc " +
4058 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4059 showDialog("log", data);
4060 } else if (name == "symbols") {
4061 data = bv->cursor().getEncoding()->name();
4063 showDialog("symbols", data);
4065 } else if (name == "prefs" && isFullScreen()) {
4066 lfunUiToggle("fullscreen");
4067 showDialog("prefs", data);
4069 showDialog(name, data);
4074 dr.setMessage(cmd.argument());
4077 case LFUN_UI_TOGGLE: {
4078 string arg = cmd.getArg(0);
4079 if (!lfunUiToggle(arg)) {
4080 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4081 dr.setMessage(bformat(msg, from_utf8(arg)));
4083 // Make sure the keyboard focus stays in the work area.
4088 case LFUN_VIEW_SPLIT: {
4089 LASSERT(doc_buffer, break);
4090 string const orientation = cmd.getArg(0);
4091 d.splitter_->setOrientation(orientation == "vertical"
4092 ? Qt::Vertical : Qt::Horizontal);
4093 TabWorkArea * twa = addTabWorkArea();
4094 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4095 setCurrentWorkArea(wa);
4098 case LFUN_TAB_GROUP_CLOSE:
4099 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4100 closeTabWorkArea(twa);
4101 d.current_work_area_ = 0;
4102 twa = d.currentTabWorkArea();
4103 // Switch to the next GuiWorkArea in the found TabWorkArea.
4105 // Make sure the work area is up to date.
4106 setCurrentWorkArea(twa->currentWorkArea());
4108 setCurrentWorkArea(0);
4113 case LFUN_VIEW_CLOSE:
4114 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4115 closeWorkArea(twa->currentWorkArea());
4116 d.current_work_area_ = 0;
4117 twa = d.currentTabWorkArea();
4118 // Switch to the next GuiWorkArea in the found TabWorkArea.
4120 // Make sure the work area is up to date.
4121 setCurrentWorkArea(twa->currentWorkArea());
4123 setCurrentWorkArea(0);
4128 case LFUN_COMPLETION_INLINE:
4129 if (d.current_work_area_)
4130 d.current_work_area_->completer().showInline();
4133 case LFUN_COMPLETION_POPUP:
4134 if (d.current_work_area_)
4135 d.current_work_area_->completer().showPopup();
4140 if (d.current_work_area_)
4141 d.current_work_area_->completer().tab();
4144 case LFUN_COMPLETION_CANCEL:
4145 if (d.current_work_area_) {
4146 if (d.current_work_area_->completer().popupVisible())
4147 d.current_work_area_->completer().hidePopup();
4149 d.current_work_area_->completer().hideInline();
4153 case LFUN_COMPLETION_ACCEPT:
4154 if (d.current_work_area_)
4155 d.current_work_area_->completer().activate();
4158 case LFUN_BUFFER_ZOOM_IN:
4159 case LFUN_BUFFER_ZOOM_OUT:
4160 case LFUN_BUFFER_ZOOM: {
4161 if (cmd.argument().empty()) {
4162 if (cmd.action() == LFUN_BUFFER_ZOOM)
4164 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4169 if (cmd.action() == LFUN_BUFFER_ZOOM)
4170 zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4171 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4172 zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4174 zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4177 // Actual zoom value: default zoom + fractional extra value
4178 int zoom = lyxrc.defaultZoom * zoom_ratio_;
4179 if (zoom < static_cast<int>(zoom_min_))
4182 lyxrc.currentZoom = zoom;
4184 dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4185 lyxrc.currentZoom, lyxrc.defaultZoom));
4187 // The global QPixmapCache is used in GuiPainter to cache text
4188 // painting so we must reset it.
4189 QPixmapCache::clear();
4190 guiApp->fontLoader().update();
4191 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4195 case LFUN_VC_REGISTER:
4196 case LFUN_VC_RENAME:
4198 case LFUN_VC_CHECK_IN:
4199 case LFUN_VC_CHECK_OUT:
4200 case LFUN_VC_REPO_UPDATE:
4201 case LFUN_VC_LOCKING_TOGGLE:
4202 case LFUN_VC_REVERT:
4203 case LFUN_VC_UNDO_LAST:
4204 case LFUN_VC_COMMAND:
4205 case LFUN_VC_COMPARE:
4206 dispatchVC(cmd, dr);
4209 case LFUN_SERVER_GOTO_FILE_ROW:
4210 if(goToFileRow(to_utf8(cmd.argument())))
4211 dr.screenUpdate(Update::Force | Update::FitCursor);
4214 case LFUN_LYX_ACTIVATE:
4218 case LFUN_FORWARD_SEARCH: {
4219 // it seems safe to assume we have a document buffer, since
4220 // getStatus wants one.
4221 LATTEST(doc_buffer);
4222 Buffer const * doc_master = doc_buffer->masterBuffer();
4223 FileName const path(doc_master->temppath());
4224 string const texname = doc_master->isChild(doc_buffer)
4225 ? DocFileName(changeExtension(
4226 doc_buffer->absFileName(),
4227 "tex")).mangledFileName()
4228 : doc_buffer->latexName();
4229 string const fulltexname =
4230 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4231 string const mastername =
4232 removeExtension(doc_master->latexName());
4233 FileName const dviname(addName(path.absFileName(),
4234 addExtension(mastername, "dvi")));
4235 FileName const pdfname(addName(path.absFileName(),
4236 addExtension(mastername, "pdf")));
4237 bool const have_dvi = dviname.exists();
4238 bool const have_pdf = pdfname.exists();
4239 if (!have_dvi && !have_pdf) {
4240 dr.setMessage(_("Please, preview the document first."));
4243 string outname = dviname.onlyFileName();
4244 string command = lyxrc.forward_search_dvi;
4245 if (!have_dvi || (have_pdf &&
4246 pdfname.lastModified() > dviname.lastModified())) {
4247 outname = pdfname.onlyFileName();
4248 command = lyxrc.forward_search_pdf;
4251 DocIterator cur = bv->cursor();
4252 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4253 LYXERR(Debug::ACTION, "Forward search: row:" << row
4255 if (row == -1 || command.empty()) {
4256 dr.setMessage(_("Couldn't proceed."));
4259 string texrow = convert<string>(row);
4261 command = subst(command, "$$n", texrow);
4262 command = subst(command, "$$f", fulltexname);
4263 command = subst(command, "$$t", texname);
4264 command = subst(command, "$$o", outname);
4266 PathChanger p(path);
4268 one.startscript(Systemcall::DontWait, command);
4272 case LFUN_SPELLING_CONTINUOUSLY:
4273 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4274 dr.screenUpdate(Update::Force);
4278 // The LFUN must be for one of BufferView, Buffer or Cursor;
4280 dispatchToBufferView(cmd, dr);
4284 // Part of automatic menu appearance feature.
4285 if (isFullScreen()) {
4286 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4290 // Need to update bv because many LFUNs here might have destroyed it
4291 bv = currentBufferView();
4293 // Clear non-empty selections
4294 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4296 Cursor & cur = bv->cursor();
4297 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4298 cur.clearSelection();
4304 bool GuiView::lfunUiToggle(string const & ui_component)
4306 if (ui_component == "scrollbar") {
4307 // hide() is of no help
4308 if (d.current_work_area_->verticalScrollBarPolicy() ==
4309 Qt::ScrollBarAlwaysOff)
4311 d.current_work_area_->setVerticalScrollBarPolicy(
4312 Qt::ScrollBarAsNeeded);
4314 d.current_work_area_->setVerticalScrollBarPolicy(
4315 Qt::ScrollBarAlwaysOff);
4316 } else if (ui_component == "statusbar") {
4317 statusBar()->setVisible(!statusBar()->isVisible());
4318 } else if (ui_component == "menubar") {
4319 menuBar()->setVisible(!menuBar()->isVisible());
4321 if (ui_component == "frame") {
4323 getContentsMargins(&l, &t, &r, &b);
4324 //are the frames in default state?
4325 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4327 setContentsMargins(-2, -2, -2, -2);
4329 setContentsMargins(0, 0, 0, 0);
4332 if (ui_component == "fullscreen") {
4340 void GuiView::toggleFullScreen()
4342 if (isFullScreen()) {
4343 for (int i = 0; i != d.splitter_->count(); ++i)
4344 d.tabWorkArea(i)->setFullScreen(false);
4345 setContentsMargins(0, 0, 0, 0);
4346 setWindowState(windowState() ^ Qt::WindowFullScreen);
4349 statusBar()->show();
4352 hideDialogs("prefs", 0);
4353 for (int i = 0; i != d.splitter_->count(); ++i)
4354 d.tabWorkArea(i)->setFullScreen(true);
4355 setContentsMargins(-2, -2, -2, -2);
4357 setWindowState(windowState() ^ Qt::WindowFullScreen);
4358 if (lyxrc.full_screen_statusbar)
4359 statusBar()->hide();
4360 if (lyxrc.full_screen_menubar)
4362 if (lyxrc.full_screen_toolbars) {
4363 ToolbarMap::iterator end = d.toolbars_.end();
4364 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4369 // give dialogs like the TOC a chance to adapt
4374 Buffer const * GuiView::updateInset(Inset const * inset)
4379 Buffer const * inset_buffer = &(inset->buffer());
4381 for (int i = 0; i != d.splitter_->count(); ++i) {
4382 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4385 Buffer const * buffer = &(wa->bufferView().buffer());
4386 if (inset_buffer == buffer)
4387 wa->scheduleRedraw();
4389 return inset_buffer;
4393 void GuiView::restartCursor()
4395 /* When we move around, or type, it's nice to be able to see
4396 * the cursor immediately after the keypress.
4398 if (d.current_work_area_)
4399 d.current_work_area_->startBlinkingCursor();
4401 // Take this occasion to update the other GUI elements.
4407 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4409 if (d.current_work_area_)
4410 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4415 // This list should be kept in sync with the list of insets in
4416 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4417 // dialog should have the same name as the inset.
4418 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4419 // docs in LyXAction.cpp.
4421 char const * const dialognames[] = {
4423 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4424 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4425 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4426 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4427 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4428 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4429 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4430 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4432 char const * const * const end_dialognames =
4433 dialognames + (sizeof(dialognames) / sizeof(char *));
4437 cmpCStr(char const * name) : name_(name) {}
4438 bool operator()(char const * other) {
4439 return strcmp(other, name_) == 0;
4446 bool isValidName(string const & name)
4448 return find_if(dialognames, end_dialognames,
4449 cmpCStr(name.c_str())) != end_dialognames;
4455 void GuiView::resetDialogs()
4457 // Make sure that no LFUN uses any GuiView.
4458 guiApp->setCurrentView(0);
4462 constructToolbars();
4463 guiApp->menus().fillMenuBar(menuBar(), this, false);
4464 d.layout_->updateContents(true);
4465 // Now update controls with current buffer.
4466 guiApp->setCurrentView(this);
4472 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4474 if (!isValidName(name))
4477 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4479 if (it != d.dialogs_.end()) {
4481 it->second->hideView();
4482 return it->second.get();
4485 Dialog * dialog = build(name);
4486 d.dialogs_[name].reset(dialog);
4487 if (lyxrc.allow_geometry_session)
4488 dialog->restoreSession();
4495 void GuiView::showDialog(string const & name, string const & data,
4498 triggerShowDialog(toqstr(name), toqstr(data), inset);
4502 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4508 const string name = fromqstr(qname);
4509 const string data = fromqstr(qdata);
4513 Dialog * dialog = findOrBuild(name, false);
4515 bool const visible = dialog->isVisibleView();
4516 dialog->showData(data);
4517 if (inset && currentBufferView())
4518 currentBufferView()->editInset(name, inset);
4519 // We only set the focus to the new dialog if it was not yet
4520 // visible in order not to change the existing previous behaviour
4522 // activateWindow is needed for floating dockviews
4523 dialog->asQWidget()->raise();
4524 dialog->asQWidget()->activateWindow();
4525 dialog->asQWidget()->setFocus();
4529 catch (ExceptionMessage const & ex) {
4537 bool GuiView::isDialogVisible(string const & name) const
4539 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4540 if (it == d.dialogs_.end())
4542 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4546 void GuiView::hideDialog(string const & name, Inset * inset)
4548 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4549 if (it == d.dialogs_.end())
4553 if (!currentBufferView())
4555 if (inset != currentBufferView()->editedInset(name))
4559 Dialog * const dialog = it->second.get();
4560 if (dialog->isVisibleView())
4562 if (currentBufferView())
4563 currentBufferView()->editInset(name, 0);
4567 void GuiView::disconnectDialog(string const & name)
4569 if (!isValidName(name))
4571 if (currentBufferView())
4572 currentBufferView()->editInset(name, 0);
4576 void GuiView::hideAll() const
4578 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4579 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4581 for(; it != end; ++it)
4582 it->second->hideView();
4586 void GuiView::updateDialogs()
4588 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4589 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4591 for(; it != end; ++it) {
4592 Dialog * dialog = it->second.get();
4594 if (dialog->needBufferOpen() && !documentBufferView())
4595 hideDialog(fromqstr(dialog->name()), 0);
4596 else if (dialog->isVisibleView())
4597 dialog->checkStatus();
4604 Dialog * createDialog(GuiView & lv, string const & name);
4606 // will be replaced by a proper factory...
4607 Dialog * createGuiAbout(GuiView & lv);
4608 Dialog * createGuiBibtex(GuiView & lv);
4609 Dialog * createGuiChanges(GuiView & lv);
4610 Dialog * createGuiCharacter(GuiView & lv);
4611 Dialog * createGuiCitation(GuiView & lv);
4612 Dialog * createGuiCompare(GuiView & lv);
4613 Dialog * createGuiCompareHistory(GuiView & lv);
4614 Dialog * createGuiDelimiter(GuiView & lv);
4615 Dialog * createGuiDocument(GuiView & lv);
4616 Dialog * createGuiErrorList(GuiView & lv);
4617 Dialog * createGuiExternal(GuiView & lv);
4618 Dialog * createGuiGraphics(GuiView & lv);
4619 Dialog * createGuiInclude(GuiView & lv);
4620 Dialog * createGuiIndex(GuiView & lv);
4621 Dialog * createGuiListings(GuiView & lv);
4622 Dialog * createGuiLog(GuiView & lv);
4623 Dialog * createGuiMathMatrix(GuiView & lv);
4624 Dialog * createGuiNote(GuiView & lv);
4625 Dialog * createGuiParagraph(GuiView & lv);
4626 Dialog * createGuiPhantom(GuiView & lv);
4627 Dialog * createGuiPreferences(GuiView & lv);
4628 Dialog * createGuiPrint(GuiView & lv);
4629 Dialog * createGuiPrintindex(GuiView & lv);
4630 Dialog * createGuiRef(GuiView & lv);
4631 Dialog * createGuiSearch(GuiView & lv);
4632 Dialog * createGuiSearchAdv(GuiView & lv);
4633 Dialog * createGuiSendTo(GuiView & lv);
4634 Dialog * createGuiShowFile(GuiView & lv);
4635 Dialog * createGuiSpellchecker(GuiView & lv);
4636 Dialog * createGuiSymbols(GuiView & lv);
4637 Dialog * createGuiTabularCreate(GuiView & lv);
4638 Dialog * createGuiTexInfo(GuiView & lv);
4639 Dialog * createGuiToc(GuiView & lv);
4640 Dialog * createGuiThesaurus(GuiView & lv);
4641 Dialog * createGuiViewSource(GuiView & lv);
4642 Dialog * createGuiWrap(GuiView & lv);
4643 Dialog * createGuiProgressView(GuiView & lv);
4647 Dialog * GuiView::build(string const & name)
4649 LASSERT(isValidName(name), return 0);
4651 Dialog * dialog = createDialog(*this, name);
4655 if (name == "aboutlyx")
4656 return createGuiAbout(*this);
4657 if (name == "bibtex")
4658 return createGuiBibtex(*this);
4659 if (name == "changes")
4660 return createGuiChanges(*this);
4661 if (name == "character")
4662 return createGuiCharacter(*this);
4663 if (name == "citation")
4664 return createGuiCitation(*this);
4665 if (name == "compare")
4666 return createGuiCompare(*this);
4667 if (name == "comparehistory")
4668 return createGuiCompareHistory(*this);
4669 if (name == "document")
4670 return createGuiDocument(*this);
4671 if (name == "errorlist")
4672 return createGuiErrorList(*this);
4673 if (name == "external")
4674 return createGuiExternal(*this);
4676 return createGuiShowFile(*this);
4677 if (name == "findreplace")
4678 return createGuiSearch(*this);
4679 if (name == "findreplaceadv")
4680 return createGuiSearchAdv(*this);
4681 if (name == "graphics")
4682 return createGuiGraphics(*this);
4683 if (name == "include")
4684 return createGuiInclude(*this);
4685 if (name == "index")
4686 return createGuiIndex(*this);
4687 if (name == "index_print")
4688 return createGuiPrintindex(*this);
4689 if (name == "listings")
4690 return createGuiListings(*this);
4692 return createGuiLog(*this);
4693 if (name == "mathdelimiter")
4694 return createGuiDelimiter(*this);
4695 if (name == "mathmatrix")
4696 return createGuiMathMatrix(*this);
4698 return createGuiNote(*this);
4699 if (name == "paragraph")
4700 return createGuiParagraph(*this);
4701 if (name == "phantom")
4702 return createGuiPhantom(*this);
4703 if (name == "prefs")
4704 return createGuiPreferences(*this);
4706 return createGuiRef(*this);
4707 if (name == "sendto")
4708 return createGuiSendTo(*this);
4709 if (name == "spellchecker")
4710 return createGuiSpellchecker(*this);
4711 if (name == "symbols")
4712 return createGuiSymbols(*this);
4713 if (name == "tabularcreate")
4714 return createGuiTabularCreate(*this);
4715 if (name == "texinfo")
4716 return createGuiTexInfo(*this);
4717 if (name == "thesaurus")
4718 return createGuiThesaurus(*this);
4720 return createGuiToc(*this);
4721 if (name == "view-source")
4722 return createGuiViewSource(*this);
4724 return createGuiWrap(*this);
4725 if (name == "progress")
4726 return createGuiProgressView(*this);
4732 SEMenu::SEMenu(QWidget * parent)
4734 QAction * action = addAction(qt_("Disable Shell Escape"));
4735 connect(action, SIGNAL(triggered()),
4736 parent, SLOT(disableShellEscape()));
4740 } // namespace frontend
4743 #include "moc_GuiView.cpp"