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", lyxrc.currentZoom);
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
761 // Save the toolbar private states
762 ToolbarMap::iterator end = d.toolbars_.end();
763 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
764 it->second->saveSession();
765 // Now take care of all other dialogs
766 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
767 for (; it!= d.dialogs_.end(); ++it)
768 it->second->saveSession();
772 bool GuiView::restoreLayout()
775 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
776 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
777 settings.beginGroup("views");
778 settings.beginGroup(QString::number(id_));
779 QString const icon_key = "icon_size";
780 if (!settings.contains(icon_key))
783 //code below is skipped when when ~/.config/LyX is (re)created
784 setIconSize(d.iconSize(settings.value(icon_key).toString()));
786 #if defined(Q_WS_X11) || defined(QPA_XCB)
787 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
788 QSize size = settings.value("size", QSize(690, 510)).toSize();
792 // Work-around for bug #6034: the window ends up in an undetermined
793 // state when trying to restore a maximized window when it is
794 // already maximized.
795 if (!(windowState() & Qt::WindowMaximized))
796 if (!restoreGeometry(settings.value("geometry").toByteArray()))
797 setGeometry(50, 50, 690, 510);
799 // Make sure layout is correctly oriented.
800 setLayoutDirection(qApp->layoutDirection());
802 // Allow the toc and view-source dock widget to be restored if needed.
804 if ((dialog = findOrBuild("toc", true)))
805 // see bug 5082. At least setup title and enabled state.
806 // Visibility will be adjusted by restoreState below.
807 dialog->prepareView();
808 if ((dialog = findOrBuild("view-source", true)))
809 dialog->prepareView();
810 if ((dialog = findOrBuild("progress", true)))
811 dialog->prepareView();
813 if (!restoreState(settings.value("layout").toByteArray(), 0))
816 // init the toolbars that have not been restored
817 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
818 Toolbars::Infos::iterator end = guiApp->toolbars().end();
819 for (; cit != end; ++cit) {
820 GuiToolbar * tb = toolbar(cit->name);
821 if (tb && !tb->isRestored())
822 initToolbar(cit->name);
825 // update lock (all) toolbars positions
826 updateLockToolbars();
833 GuiToolbar * GuiView::toolbar(string const & name)
835 ToolbarMap::iterator it = d.toolbars_.find(name);
836 if (it != d.toolbars_.end())
839 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
844 void GuiView::updateLockToolbars()
846 toolbarsMovable_ = false;
847 for (ToolbarInfo const & info : guiApp->toolbars()) {
848 GuiToolbar * tb = toolbar(info.name);
849 if (tb && tb->isMovable())
850 toolbarsMovable_ = true;
855 void GuiView::constructToolbars()
857 ToolbarMap::iterator it = d.toolbars_.begin();
858 for (; it != d.toolbars_.end(); ++it)
862 // I don't like doing this here, but the standard toolbar
863 // destroys this object when it's destroyed itself (vfr)
864 d.layout_ = new LayoutBox(*this);
865 d.stack_widget_->addWidget(d.layout_);
866 d.layout_->move(0,0);
868 // extracts the toolbars from the backend
869 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
870 Toolbars::Infos::iterator end = guiApp->toolbars().end();
871 for (; cit != end; ++cit)
872 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
876 void GuiView::initToolbars()
878 // extracts the toolbars from the backend
879 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
880 Toolbars::Infos::iterator end = guiApp->toolbars().end();
881 for (; cit != end; ++cit)
882 initToolbar(cit->name);
886 void GuiView::initToolbar(string const & name)
888 GuiToolbar * tb = toolbar(name);
891 int const visibility = guiApp->toolbars().defaultVisibility(name);
892 bool newline = !(visibility & Toolbars::SAMEROW);
893 tb->setVisible(false);
894 tb->setVisibility(visibility);
896 if (visibility & Toolbars::TOP) {
898 addToolBarBreak(Qt::TopToolBarArea);
899 addToolBar(Qt::TopToolBarArea, tb);
902 if (visibility & Toolbars::BOTTOM) {
904 addToolBarBreak(Qt::BottomToolBarArea);
905 addToolBar(Qt::BottomToolBarArea, tb);
908 if (visibility & Toolbars::LEFT) {
910 addToolBarBreak(Qt::LeftToolBarArea);
911 addToolBar(Qt::LeftToolBarArea, tb);
914 if (visibility & Toolbars::RIGHT) {
916 addToolBarBreak(Qt::RightToolBarArea);
917 addToolBar(Qt::RightToolBarArea, tb);
920 if (visibility & Toolbars::ON)
921 tb->setVisible(true);
923 tb->setMovable(true);
927 TocModels & GuiView::tocModels()
929 return d.toc_models_;
933 void GuiView::setFocus()
935 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
936 QMainWindow::setFocus();
940 bool GuiView::hasFocus() const
942 if (currentWorkArea())
943 return currentWorkArea()->hasFocus();
944 if (currentMainWorkArea())
945 return currentMainWorkArea()->hasFocus();
946 return d.bg_widget_->hasFocus();
950 void GuiView::focusInEvent(QFocusEvent * e)
952 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
953 QMainWindow::focusInEvent(e);
954 // Make sure guiApp points to the correct view.
955 guiApp->setCurrentView(this);
956 if (currentWorkArea())
957 currentWorkArea()->setFocus();
958 else if (currentMainWorkArea())
959 currentMainWorkArea()->setFocus();
961 d.bg_widget_->setFocus();
965 void GuiView::showEvent(QShowEvent * e)
967 LYXERR(Debug::GUI, "Passed Geometry "
968 << size().height() << "x" << size().width()
969 << "+" << pos().x() << "+" << pos().y());
971 if (d.splitter_->count() == 0)
972 // No work area, switch to the background widget.
976 QMainWindow::showEvent(e);
980 bool GuiView::closeScheduled()
987 bool GuiView::prepareAllBuffersForLogout()
989 Buffer * first = theBufferList().first();
993 // First, iterate over all buffers and ask the users if unsaved
994 // changes should be saved.
995 // We cannot use a for loop as the buffer list cycles.
998 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1000 b = theBufferList().next(b);
1001 } while (b != first);
1003 // Next, save session state
1004 // When a view/window was closed before without quitting LyX, there
1005 // are already entries in the lastOpened list.
1006 theSession().lastOpened().clear();
1013 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1014 ** is responsibility of the container (e.g., dialog)
1016 void GuiView::closeEvent(QCloseEvent * close_event)
1018 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1020 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1021 Alert::warning(_("Exit LyX"),
1022 _("LyX could not be closed because documents are being processed by LyX."));
1023 close_event->setAccepted(false);
1027 // If the user pressed the x (so we didn't call closeView
1028 // programmatically), we want to clear all existing entries.
1030 theSession().lastOpened().clear();
1035 // it can happen that this event arrives without selecting the view,
1036 // e.g. when clicking the close button on a background window.
1038 if (!closeWorkAreaAll()) {
1040 close_event->ignore();
1044 // Make sure that nothing will use this to be closed View.
1045 guiApp->unregisterView(this);
1047 if (isFullScreen()) {
1048 // Switch off fullscreen before closing.
1053 // Make sure the timer time out will not trigger a statusbar update.
1054 d.statusbar_timer_.stop();
1056 // Saving fullscreen requires additional tweaks in the toolbar code.
1057 // It wouldn't also work under linux natively.
1058 if (lyxrc.allow_geometry_session) {
1063 close_event->accept();
1067 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1069 if (event->mimeData()->hasUrls())
1071 /// \todo Ask lyx-devel is this is enough:
1072 /// if (event->mimeData()->hasFormat("text/plain"))
1073 /// event->acceptProposedAction();
1077 void GuiView::dropEvent(QDropEvent * event)
1079 QList<QUrl> files = event->mimeData()->urls();
1080 if (files.isEmpty())
1083 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1084 for (int i = 0; i != files.size(); ++i) {
1085 string const file = os::internal_path(fromqstr(
1086 files.at(i).toLocalFile()));
1090 string const ext = support::getExtension(file);
1091 vector<const Format *> found_formats;
1093 // Find all formats that have the correct extension.
1094 vector<const Format *> const & import_formats
1095 = theConverters().importableFormats();
1096 vector<const Format *>::const_iterator it = import_formats.begin();
1097 for (; it != import_formats.end(); ++it)
1098 if ((*it)->hasExtension(ext))
1099 found_formats.push_back(*it);
1102 if (found_formats.size() >= 1) {
1103 if (found_formats.size() > 1) {
1104 //FIXME: show a dialog to choose the correct importable format
1105 LYXERR(Debug::FILES,
1106 "Multiple importable formats found, selecting first");
1108 string const arg = found_formats[0]->name() + " " + file;
1109 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1112 //FIXME: do we have to explicitly check whether it's a lyx file?
1113 LYXERR(Debug::FILES,
1114 "No formats found, trying to open it as a lyx file");
1115 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1117 // add the functions to the queue
1118 guiApp->addToFuncRequestQueue(cmd);
1121 // now process the collected functions. We perform the events
1122 // asynchronously. This prevents potential problems in case the
1123 // BufferView is closed within an event.
1124 guiApp->processFuncRequestQueueAsync();
1128 void GuiView::message(docstring const & str)
1130 if (ForkedProcess::iAmAChild())
1133 // call is moved to GUI-thread by GuiProgress
1134 d.progress_->appendMessage(toqstr(str));
1138 void GuiView::clearMessageText()
1140 message(docstring());
1144 void GuiView::updateStatusBarMessage(QString const & str)
1146 statusBar()->showMessage(str);
1147 d.statusbar_timer_.stop();
1148 d.statusbar_timer_.start(3000);
1152 void GuiView::clearMessage()
1154 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1155 // the hasFocus function mostly returns false, even if the focus is on
1156 // a workarea in this view.
1160 d.statusbar_timer_.stop();
1164 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1166 if (wa != d.current_work_area_
1167 || wa->bufferView().buffer().isInternal())
1169 Buffer const & buf = wa->bufferView().buffer();
1170 // Set the windows title
1171 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1172 if (buf.notifiesExternalModification()) {
1173 title = bformat(_("%1$s (modified externally)"), title);
1174 // If the external modification status has changed, then maybe the status of
1175 // buffer-save has changed too.
1179 title += from_ascii(" - LyX");
1181 setWindowTitle(toqstr(title));
1182 // Sets the path for the window: this is used by OSX to
1183 // allow a context click on the title bar showing a menu
1184 // with the path up to the file
1185 setWindowFilePath(toqstr(buf.absFileName()));
1186 // Tell Qt whether the current document is changed
1187 setWindowModified(!buf.isClean());
1189 if (buf.params().shell_escape)
1190 shell_escape_->show();
1192 shell_escape_->hide();
1194 if (buf.hasReadonlyFlag())
1199 if (buf.lyxvc().inUse()) {
1200 version_control_->show();
1201 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1203 version_control_->hide();
1207 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1209 if (d.current_work_area_)
1210 // disconnect the current work area from all slots
1211 QObject::disconnect(d.current_work_area_, 0, this, 0);
1213 disconnectBufferView();
1214 connectBufferView(wa->bufferView());
1215 connectBuffer(wa->bufferView().buffer());
1216 d.current_work_area_ = wa;
1217 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1218 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1219 QObject::connect(wa, SIGNAL(busy(bool)),
1220 this, SLOT(setBusy(bool)));
1221 // connection of a signal to a signal
1222 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1223 this, SIGNAL(bufferViewChanged()));
1224 Q_EMIT updateWindowTitle(wa);
1225 Q_EMIT bufferViewChanged();
1229 void GuiView::onBufferViewChanged()
1232 // Buffer-dependent dialogs must be updated. This is done here because
1233 // some dialogs require buffer()->text.
1238 void GuiView::on_lastWorkAreaRemoved()
1241 // We already are in a close event. Nothing more to do.
1244 if (d.splitter_->count() > 1)
1245 // We have a splitter so don't close anything.
1248 // Reset and updates the dialogs.
1249 Q_EMIT bufferViewChanged();
1254 if (lyxrc.open_buffers_in_tabs)
1255 // Nothing more to do, the window should stay open.
1258 if (guiApp->viewIds().size() > 1) {
1264 // On Mac we also close the last window because the application stay
1265 // resident in memory. On other platforms we don't close the last
1266 // window because this would quit the application.
1272 void GuiView::updateStatusBar()
1274 // let the user see the explicit message
1275 if (d.statusbar_timer_.isActive())
1282 void GuiView::showMessage()
1286 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1287 if (msg.isEmpty()) {
1288 BufferView const * bv = currentBufferView();
1290 msg = toqstr(bv->cursor().currentState(devel_mode_));
1292 msg = qt_("Welcome to LyX!");
1294 statusBar()->showMessage(msg);
1298 bool GuiView::event(QEvent * e)
1302 // Useful debug code:
1303 //case QEvent::ActivationChange:
1304 //case QEvent::WindowDeactivate:
1305 //case QEvent::Paint:
1306 //case QEvent::Enter:
1307 //case QEvent::Leave:
1308 //case QEvent::HoverEnter:
1309 //case QEvent::HoverLeave:
1310 //case QEvent::HoverMove:
1311 //case QEvent::StatusTip:
1312 //case QEvent::DragEnter:
1313 //case QEvent::DragLeave:
1314 //case QEvent::Drop:
1317 case QEvent::WindowActivate: {
1318 GuiView * old_view = guiApp->currentView();
1319 if (this == old_view) {
1321 return QMainWindow::event(e);
1323 if (old_view && old_view->currentBufferView()) {
1324 // save current selection to the selection buffer to allow
1325 // middle-button paste in this window.
1326 cap::saveSelection(old_view->currentBufferView()->cursor());
1328 guiApp->setCurrentView(this);
1329 if (d.current_work_area_)
1330 on_currentWorkAreaChanged(d.current_work_area_);
1334 return QMainWindow::event(e);
1337 case QEvent::ShortcutOverride: {
1339 if (isFullScreen() && menuBar()->isHidden()) {
1340 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1341 // FIXME: we should also try to detect special LyX shortcut such as
1342 // Alt-P and Alt-M. Right now there is a hack in
1343 // GuiWorkArea::processKeySym() that hides again the menubar for
1345 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1347 return QMainWindow::event(e);
1350 return QMainWindow::event(e);
1354 return QMainWindow::event(e);
1358 void GuiView::resetWindowTitle()
1360 setWindowTitle(qt_("LyX"));
1363 bool GuiView::focusNextPrevChild(bool /*next*/)
1370 bool GuiView::busy() const
1376 void GuiView::setBusy(bool busy)
1378 bool const busy_before = busy_ > 0;
1379 busy ? ++busy_ : --busy_;
1380 if ((busy_ > 0) == busy_before)
1381 // busy state didn't change
1385 QApplication::setOverrideCursor(Qt::WaitCursor);
1388 QApplication::restoreOverrideCursor();
1393 void GuiView::resetCommandExecute()
1395 command_execute_ = false;
1400 double GuiView::pixelRatio() const
1402 #if QT_VERSION >= 0x050000
1403 return qt_scale_factor * devicePixelRatio();
1410 GuiWorkArea * GuiView::workArea(int index)
1412 if (TabWorkArea * twa = d.currentTabWorkArea())
1413 if (index < twa->count())
1414 return twa->workArea(index);
1419 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1421 if (currentWorkArea()
1422 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1423 return currentWorkArea();
1424 if (TabWorkArea * twa = d.currentTabWorkArea())
1425 return twa->workArea(buffer);
1430 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1432 // Automatically create a TabWorkArea if there are none yet.
1433 TabWorkArea * tab_widget = d.splitter_->count()
1434 ? d.currentTabWorkArea() : addTabWorkArea();
1435 return tab_widget->addWorkArea(buffer, *this);
1439 TabWorkArea * GuiView::addTabWorkArea()
1441 TabWorkArea * twa = new TabWorkArea;
1442 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1443 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1444 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1445 this, SLOT(on_lastWorkAreaRemoved()));
1447 d.splitter_->addWidget(twa);
1448 d.stack_widget_->setCurrentWidget(d.splitter_);
1453 GuiWorkArea const * GuiView::currentWorkArea() const
1455 return d.current_work_area_;
1459 GuiWorkArea * GuiView::currentWorkArea()
1461 return d.current_work_area_;
1465 GuiWorkArea const * GuiView::currentMainWorkArea() const
1467 if (!d.currentTabWorkArea())
1469 return d.currentTabWorkArea()->currentWorkArea();
1473 GuiWorkArea * GuiView::currentMainWorkArea()
1475 if (!d.currentTabWorkArea())
1477 return d.currentTabWorkArea()->currentWorkArea();
1481 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1483 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1485 d.current_work_area_ = 0;
1487 Q_EMIT bufferViewChanged();
1491 // FIXME: I've no clue why this is here and why it accesses
1492 // theGuiApp()->currentView, which might be 0 (bug 6464).
1493 // See also 27525 (vfr).
1494 if (theGuiApp()->currentView() == this
1495 && theGuiApp()->currentView()->currentWorkArea() == wa)
1498 if (currentBufferView())
1499 cap::saveSelection(currentBufferView()->cursor());
1501 theGuiApp()->setCurrentView(this);
1502 d.current_work_area_ = wa;
1504 // We need to reset this now, because it will need to be
1505 // right if the tabWorkArea gets reset in the for loop. We
1506 // will change it back if we aren't in that case.
1507 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1508 d.current_main_work_area_ = wa;
1510 for (int i = 0; i != d.splitter_->count(); ++i) {
1511 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1512 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1513 << ", Current main wa: " << currentMainWorkArea());
1518 d.current_main_work_area_ = old_cmwa;
1520 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1521 on_currentWorkAreaChanged(wa);
1522 BufferView & bv = wa->bufferView();
1523 bv.cursor().fixIfBroken();
1525 wa->setUpdatesEnabled(true);
1526 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1530 void GuiView::removeWorkArea(GuiWorkArea * wa)
1532 LASSERT(wa, return);
1533 if (wa == d.current_work_area_) {
1535 disconnectBufferView();
1536 d.current_work_area_ = 0;
1537 d.current_main_work_area_ = 0;
1540 bool found_twa = false;
1541 for (int i = 0; i != d.splitter_->count(); ++i) {
1542 TabWorkArea * twa = d.tabWorkArea(i);
1543 if (twa->removeWorkArea(wa)) {
1544 // Found in this tab group, and deleted the GuiWorkArea.
1546 if (twa->count() != 0) {
1547 if (d.current_work_area_ == 0)
1548 // This means that we are closing the current GuiWorkArea, so
1549 // switch to the next GuiWorkArea in the found TabWorkArea.
1550 setCurrentWorkArea(twa->currentWorkArea());
1552 // No more WorkAreas in this tab group, so delete it.
1559 // It is not a tabbed work area (i.e., the search work area), so it
1560 // should be deleted by other means.
1561 LASSERT(found_twa, return);
1563 if (d.current_work_area_ == 0) {
1564 if (d.splitter_->count() != 0) {
1565 TabWorkArea * twa = d.currentTabWorkArea();
1566 setCurrentWorkArea(twa->currentWorkArea());
1568 // No more work areas, switch to the background widget.
1569 setCurrentWorkArea(0);
1575 LayoutBox * GuiView::getLayoutDialog() const
1581 void GuiView::updateLayoutList()
1584 d.layout_->updateContents(false);
1588 void GuiView::updateToolbars()
1590 ToolbarMap::iterator end = d.toolbars_.end();
1591 if (d.current_work_area_) {
1593 if (d.current_work_area_->bufferView().cursor().inMathed()
1594 && !d.current_work_area_->bufferView().cursor().inRegexped())
1595 context |= Toolbars::MATH;
1596 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1597 context |= Toolbars::TABLE;
1598 if (currentBufferView()->buffer().areChangesPresent()
1599 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1600 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1601 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1602 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1603 context |= Toolbars::REVIEW;
1604 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1605 context |= Toolbars::MATHMACROTEMPLATE;
1606 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1607 context |= Toolbars::IPA;
1608 if (command_execute_)
1609 context |= Toolbars::MINIBUFFER;
1610 if (minibuffer_focus_) {
1611 context |= Toolbars::MINIBUFFER_FOCUS;
1612 minibuffer_focus_ = false;
1615 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1616 it->second->update(context);
1618 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1619 it->second->update();
1623 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1625 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1626 LASSERT(newBuffer, return);
1628 GuiWorkArea * wa = workArea(*newBuffer);
1631 newBuffer->masterBuffer()->updateBuffer();
1633 wa = addWorkArea(*newBuffer);
1634 // scroll to the position when the BufferView was last closed
1635 if (lyxrc.use_lastfilepos) {
1636 LastFilePosSection::FilePos filepos =
1637 theSession().lastFilePos().load(newBuffer->fileName());
1638 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1641 //Disconnect the old buffer...there's no new one.
1644 connectBuffer(*newBuffer);
1645 connectBufferView(wa->bufferView());
1647 setCurrentWorkArea(wa);
1651 void GuiView::connectBuffer(Buffer & buf)
1653 buf.setGuiDelegate(this);
1657 void GuiView::disconnectBuffer()
1659 if (d.current_work_area_)
1660 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1664 void GuiView::connectBufferView(BufferView & bv)
1666 bv.setGuiDelegate(this);
1670 void GuiView::disconnectBufferView()
1672 if (d.current_work_area_)
1673 d.current_work_area_->bufferView().setGuiDelegate(0);
1677 void GuiView::errors(string const & error_type, bool from_master)
1679 BufferView const * const bv = currentBufferView();
1683 #if EXPORT_in_THREAD
1684 // We are called with from_master == false by default, so we
1685 // have to figure out whether that is the case or not.
1686 ErrorList & el = bv->buffer().errorList(error_type);
1688 el = bv->buffer().masterBuffer()->errorList(error_type);
1692 ErrorList const & el = from_master ?
1693 bv->buffer().masterBuffer()->errorList(error_type) :
1694 bv->buffer().errorList(error_type);
1700 string data = error_type;
1702 data = "from_master|" + error_type;
1703 showDialog("errorlist", data);
1707 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1709 d.toc_models_.updateItem(toqstr(type), dit);
1713 void GuiView::structureChanged()
1715 // This is called from the Buffer, which has no way to ensure that cursors
1716 // in BufferView remain valid.
1717 if (documentBufferView())
1718 documentBufferView()->cursor().sanitize();
1719 // FIXME: This is slightly expensive, though less than the tocBackend update
1720 // (#9880). This also resets the view in the Toc Widget (#6675).
1721 d.toc_models_.reset(documentBufferView());
1722 // Navigator needs more than a simple update in this case. It needs to be
1724 updateDialog("toc", "");
1728 void GuiView::updateDialog(string const & name, string const & data)
1730 if (!isDialogVisible(name))
1733 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1734 if (it == d.dialogs_.end())
1737 Dialog * const dialog = it->second.get();
1738 if (dialog->isVisibleView())
1739 dialog->initialiseParams(data);
1743 BufferView * GuiView::documentBufferView()
1745 return currentMainWorkArea()
1746 ? ¤tMainWorkArea()->bufferView()
1751 BufferView const * GuiView::documentBufferView() const
1753 return currentMainWorkArea()
1754 ? ¤tMainWorkArea()->bufferView()
1759 BufferView * GuiView::currentBufferView()
1761 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1765 BufferView const * GuiView::currentBufferView() const
1767 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1771 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1772 Buffer const * orig, Buffer * clone)
1774 bool const success = clone->autoSave();
1776 busyBuffers.remove(orig);
1778 ? _("Automatic save done.")
1779 : _("Automatic save failed!");
1783 void GuiView::autoSave()
1785 LYXERR(Debug::INFO, "Running autoSave()");
1787 Buffer * buffer = documentBufferView()
1788 ? &documentBufferView()->buffer() : 0;
1790 resetAutosaveTimers();
1794 GuiViewPrivate::busyBuffers.insert(buffer);
1795 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1796 buffer, buffer->cloneBufferOnly());
1797 d.autosave_watcher_.setFuture(f);
1798 resetAutosaveTimers();
1802 void GuiView::resetAutosaveTimers()
1805 d.autosave_timeout_.restart();
1809 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1812 Buffer * buf = currentBufferView()
1813 ? ¤tBufferView()->buffer() : 0;
1814 Buffer * doc_buffer = documentBufferView()
1815 ? &(documentBufferView()->buffer()) : 0;
1818 /* In LyX/Mac, when a dialog is open, the menus of the
1819 application can still be accessed without giving focus to
1820 the main window. In this case, we want to disable the menu
1821 entries that are buffer-related.
1822 This code must not be used on Linux and Windows, since it
1823 would disable buffer-related entries when hovering over the
1824 menu (see bug #9574).
1826 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1832 // Check whether we need a buffer
1833 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1834 // no, exit directly
1835 flag.message(from_utf8(N_("Command not allowed with"
1836 "out any document open")));
1837 flag.setEnabled(false);
1841 if (cmd.origin() == FuncRequest::TOC) {
1842 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1843 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1844 flag.setEnabled(false);
1848 switch(cmd.action()) {
1849 case LFUN_BUFFER_IMPORT:
1852 case LFUN_MASTER_BUFFER_UPDATE:
1853 case LFUN_MASTER_BUFFER_VIEW:
1855 && (doc_buffer->parent() != 0
1856 || doc_buffer->hasChildren())
1857 && !d.processing_thread_watcher_.isRunning();
1860 case LFUN_BUFFER_UPDATE:
1861 case LFUN_BUFFER_VIEW: {
1862 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1866 string format = to_utf8(cmd.argument());
1867 if (cmd.argument().empty())
1868 format = doc_buffer->params().getDefaultOutputFormat();
1869 enable = doc_buffer->params().isExportable(format, true);
1873 case LFUN_BUFFER_RELOAD:
1874 enable = doc_buffer && !doc_buffer->isUnnamed()
1875 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1878 case LFUN_BUFFER_CHILD_OPEN:
1879 enable = doc_buffer != 0;
1882 case LFUN_BUFFER_WRITE:
1883 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1886 //FIXME: This LFUN should be moved to GuiApplication.
1887 case LFUN_BUFFER_WRITE_ALL: {
1888 // We enable the command only if there are some modified buffers
1889 Buffer * first = theBufferList().first();
1894 // We cannot use a for loop as the buffer list is a cycle.
1896 if (!b->isClean()) {
1900 b = theBufferList().next(b);
1901 } while (b != first);
1905 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1906 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1909 case LFUN_BUFFER_WRITE_AS:
1910 case LFUN_BUFFER_EXPORT_AS:
1911 enable = doc_buffer != 0;
1914 case LFUN_BUFFER_CLOSE:
1915 case LFUN_VIEW_CLOSE:
1916 enable = doc_buffer != 0;
1919 case LFUN_BUFFER_CLOSE_ALL:
1920 enable = theBufferList().last() != theBufferList().first();
1923 case LFUN_VIEW_SPLIT:
1924 if (cmd.getArg(0) == "vertical")
1925 enable = doc_buffer && (d.splitter_->count() == 1 ||
1926 d.splitter_->orientation() == Qt::Vertical);
1928 enable = doc_buffer && (d.splitter_->count() == 1 ||
1929 d.splitter_->orientation() == Qt::Horizontal);
1932 case LFUN_TAB_GROUP_CLOSE:
1933 enable = d.tabWorkAreaCount() > 1;
1936 case LFUN_DEVEL_MODE_TOGGLE:
1937 flag.setOnOff(devel_mode_);
1940 case LFUN_TOOLBAR_TOGGLE: {
1941 string const name = cmd.getArg(0);
1942 if (GuiToolbar * t = toolbar(name))
1943 flag.setOnOff(t->isVisible());
1946 docstring const msg =
1947 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1953 case LFUN_TOOLBAR_MOVABLE: {
1954 string const name = cmd.getArg(0);
1955 // use negation since locked == !movable
1957 // toolbar name * locks all toolbars
1958 flag.setOnOff(!toolbarsMovable_);
1959 else if (GuiToolbar * t = toolbar(name))
1960 flag.setOnOff(!(t->isMovable()));
1963 docstring const msg =
1964 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1970 case LFUN_ICON_SIZE:
1971 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1974 case LFUN_DROP_LAYOUTS_CHOICE:
1978 case LFUN_UI_TOGGLE:
1979 flag.setOnOff(isFullScreen());
1982 case LFUN_DIALOG_DISCONNECT_INSET:
1985 case LFUN_DIALOG_HIDE:
1986 // FIXME: should we check if the dialog is shown?
1989 case LFUN_DIALOG_TOGGLE:
1990 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1993 case LFUN_DIALOG_SHOW: {
1994 string const name = cmd.getArg(0);
1996 enable = name == "aboutlyx"
1997 || name == "file" //FIXME: should be removed.
1999 || name == "texinfo"
2000 || name == "progress"
2001 || name == "compare";
2002 else if (name == "character" || name == "symbols"
2003 || name == "mathdelimiter" || name == "mathmatrix") {
2004 if (!buf || buf->isReadonly())
2007 Cursor const & cur = currentBufferView()->cursor();
2008 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2011 else if (name == "latexlog")
2012 enable = FileName(doc_buffer->logName()).isReadableFile();
2013 else if (name == "spellchecker")
2014 enable = theSpellChecker()
2015 && !doc_buffer->isReadonly()
2016 && !doc_buffer->text().empty();
2017 else if (name == "vclog")
2018 enable = doc_buffer->lyxvc().inUse();
2022 case LFUN_DIALOG_UPDATE: {
2023 string const name = cmd.getArg(0);
2025 enable = name == "prefs";
2029 case LFUN_COMMAND_EXECUTE:
2031 case LFUN_MENU_OPEN:
2032 // Nothing to check.
2035 case LFUN_COMPLETION_INLINE:
2036 if (!d.current_work_area_
2037 || !d.current_work_area_->completer().inlinePossible(
2038 currentBufferView()->cursor()))
2042 case LFUN_COMPLETION_POPUP:
2043 if (!d.current_work_area_
2044 || !d.current_work_area_->completer().popupPossible(
2045 currentBufferView()->cursor()))
2050 if (!d.current_work_area_
2051 || !d.current_work_area_->completer().inlinePossible(
2052 currentBufferView()->cursor()))
2056 case LFUN_COMPLETION_ACCEPT:
2057 if (!d.current_work_area_
2058 || (!d.current_work_area_->completer().popupVisible()
2059 && !d.current_work_area_->completer().inlineVisible()
2060 && !d.current_work_area_->completer().completionAvailable()))
2064 case LFUN_COMPLETION_CANCEL:
2065 if (!d.current_work_area_
2066 || (!d.current_work_area_->completer().popupVisible()
2067 && !d.current_work_area_->completer().inlineVisible()))
2071 case LFUN_BUFFER_ZOOM_OUT:
2072 case LFUN_BUFFER_ZOOM_IN: {
2073 // only diff between these two is that the default for ZOOM_OUT
2075 bool const neg_zoom =
2076 convert<int>(cmd.argument()) < 0 ||
2077 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2078 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2079 docstring const msg =
2080 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2084 enable = doc_buffer;
2088 case LFUN_BUFFER_ZOOM: {
2089 bool const less_than_min_zoom =
2090 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2091 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2092 docstring const msg =
2093 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2098 enable = doc_buffer;
2102 case LFUN_BUFFER_MOVE_NEXT:
2103 case LFUN_BUFFER_MOVE_PREVIOUS:
2104 // we do not cycle when moving
2105 case LFUN_BUFFER_NEXT:
2106 case LFUN_BUFFER_PREVIOUS:
2107 // because we cycle, it doesn't matter whether on first or last
2108 enable = (d.currentTabWorkArea()->count() > 1);
2110 case LFUN_BUFFER_SWITCH:
2111 // toggle on the current buffer, but do not toggle off
2112 // the other ones (is that a good idea?)
2114 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2115 flag.setOnOff(true);
2118 case LFUN_VC_REGISTER:
2119 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2121 case LFUN_VC_RENAME:
2122 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2125 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2127 case LFUN_VC_CHECK_IN:
2128 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2130 case LFUN_VC_CHECK_OUT:
2131 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2133 case LFUN_VC_LOCKING_TOGGLE:
2134 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2135 && doc_buffer->lyxvc().lockingToggleEnabled();
2136 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2138 case LFUN_VC_REVERT:
2139 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2140 && !doc_buffer->hasReadonlyFlag();
2142 case LFUN_VC_UNDO_LAST:
2143 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2145 case LFUN_VC_REPO_UPDATE:
2146 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2148 case LFUN_VC_COMMAND: {
2149 if (cmd.argument().empty())
2151 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2155 case LFUN_VC_COMPARE:
2156 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2159 case LFUN_SERVER_GOTO_FILE_ROW:
2160 case LFUN_LYX_ACTIVATE:
2162 case LFUN_FORWARD_SEARCH:
2163 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2166 case LFUN_FILE_INSERT_PLAINTEXT:
2167 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2168 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2171 case LFUN_SPELLING_CONTINUOUSLY:
2172 flag.setOnOff(lyxrc.spellcheck_continuously);
2180 flag.setEnabled(false);
2186 static FileName selectTemplateFile()
2188 FileDialog dlg(qt_("Select template file"));
2189 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2190 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2192 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2193 QStringList(qt_("LyX Documents (*.lyx)")));
2195 if (result.first == FileDialog::Later)
2197 if (result.second.isEmpty())
2199 return FileName(fromqstr(result.second));
2203 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2207 Buffer * newBuffer = 0;
2209 newBuffer = checkAndLoadLyXFile(filename);
2210 } catch (ExceptionMessage const & e) {
2217 message(_("Document not loaded."));
2221 setBuffer(newBuffer);
2222 newBuffer->errors("Parse");
2225 theSession().lastFiles().add(filename);
2231 void GuiView::openDocument(string const & fname)
2233 string initpath = lyxrc.document_path;
2235 if (documentBufferView()) {
2236 string const trypath = documentBufferView()->buffer().filePath();
2237 // If directory is writeable, use this as default.
2238 if (FileName(trypath).isDirWritable())
2244 if (fname.empty()) {
2245 FileDialog dlg(qt_("Select document to open"));
2246 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2247 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2249 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2250 FileDialog::Result result =
2251 dlg.open(toqstr(initpath), filter);
2253 if (result.first == FileDialog::Later)
2256 filename = fromqstr(result.second);
2258 // check selected filename
2259 if (filename.empty()) {
2260 message(_("Canceled."));
2266 // get absolute path of file and add ".lyx" to the filename if
2268 FileName const fullname =
2269 fileSearch(string(), filename, "lyx", support::may_not_exist);
2270 if (!fullname.empty())
2271 filename = fullname.absFileName();
2273 if (!fullname.onlyPath().isDirectory()) {
2274 Alert::warning(_("Invalid filename"),
2275 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2276 from_utf8(fullname.absFileName())));
2280 // if the file doesn't exist and isn't already open (bug 6645),
2281 // let the user create one
2282 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2283 !LyXVC::file_not_found_hook(fullname)) {
2284 // the user specifically chose this name. Believe him.
2285 Buffer * const b = newFile(filename, string(), true);
2291 docstring const disp_fn = makeDisplayPath(filename);
2292 message(bformat(_("Opening document %1$s..."), disp_fn));
2295 Buffer * buf = loadDocument(fullname);
2297 str2 = bformat(_("Document %1$s opened."), disp_fn);
2298 if (buf->lyxvc().inUse())
2299 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2300 " " + _("Version control detected.");
2302 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2307 // FIXME: clean that
2308 static bool import(GuiView * lv, FileName const & filename,
2309 string const & format, ErrorList & errorList)
2311 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2313 string loader_format;
2314 vector<string> loaders = theConverters().loaders();
2315 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2316 vector<string>::const_iterator it = loaders.begin();
2317 vector<string>::const_iterator en = loaders.end();
2318 for (; it != en; ++it) {
2319 if (!theConverters().isReachable(format, *it))
2322 string const tofile =
2323 support::changeExtension(filename.absFileName(),
2324 theFormats().extension(*it));
2325 if (!theConverters().convert(0, filename, FileName(tofile),
2326 filename, format, *it, errorList))
2328 loader_format = *it;
2331 if (loader_format.empty()) {
2332 frontend::Alert::error(_("Couldn't import file"),
2333 bformat(_("No information for importing the format %1$s."),
2334 theFormats().prettyName(format)));
2338 loader_format = format;
2340 if (loader_format == "lyx") {
2341 Buffer * buf = lv->loadDocument(lyxfile);
2345 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2349 bool as_paragraphs = loader_format == "textparagraph";
2350 string filename2 = (loader_format == format) ? filename.absFileName()
2351 : support::changeExtension(filename.absFileName(),
2352 theFormats().extension(loader_format));
2353 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2355 guiApp->setCurrentView(lv);
2356 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2363 void GuiView::importDocument(string const & argument)
2366 string filename = split(argument, format, ' ');
2368 LYXERR(Debug::INFO, format << " file: " << filename);
2370 // need user interaction
2371 if (filename.empty()) {
2372 string initpath = lyxrc.document_path;
2373 if (documentBufferView()) {
2374 string const trypath = documentBufferView()->buffer().filePath();
2375 // If directory is writeable, use this as default.
2376 if (FileName(trypath).isDirWritable())
2380 docstring const text = bformat(_("Select %1$s file to import"),
2381 theFormats().prettyName(format));
2383 FileDialog dlg(toqstr(text));
2384 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2385 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2387 docstring filter = theFormats().prettyName(format);
2390 filter += from_utf8(theFormats().extensions(format));
2393 FileDialog::Result result =
2394 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2396 if (result.first == FileDialog::Later)
2399 filename = fromqstr(result.second);
2401 // check selected filename
2402 if (filename.empty())
2403 message(_("Canceled."));
2406 if (filename.empty())
2409 // get absolute path of file
2410 FileName const fullname(support::makeAbsPath(filename));
2412 // Can happen if the user entered a path into the dialog
2414 if (fullname.onlyFileName().empty()) {
2415 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2416 "Aborting import."),
2417 from_utf8(fullname.absFileName()));
2418 frontend::Alert::error(_("File name error"), msg);
2419 message(_("Canceled."));
2424 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2426 // Check if the document already is open
2427 Buffer * buf = theBufferList().getBuffer(lyxfile);
2430 if (!closeBuffer()) {
2431 message(_("Canceled."));
2436 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2438 // if the file exists already, and we didn't do
2439 // -i lyx thefile.lyx, warn
2440 if (lyxfile.exists() && fullname != lyxfile) {
2442 docstring text = bformat(_("The document %1$s already exists.\n\n"
2443 "Do you want to overwrite that document?"), displaypath);
2444 int const ret = Alert::prompt(_("Overwrite document?"),
2445 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2448 message(_("Canceled."));
2453 message(bformat(_("Importing %1$s..."), displaypath));
2454 ErrorList errorList;
2455 if (import(this, fullname, format, errorList))
2456 message(_("imported."));
2458 message(_("file not imported!"));
2460 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2464 void GuiView::newDocument(string const & filename, bool from_template)
2466 FileName initpath(lyxrc.document_path);
2467 if (documentBufferView()) {
2468 FileName const trypath(documentBufferView()->buffer().filePath());
2469 // If directory is writeable, use this as default.
2470 if (trypath.isDirWritable())
2474 string templatefile;
2475 if (from_template) {
2476 templatefile = selectTemplateFile().absFileName();
2477 if (templatefile.empty())
2482 if (filename.empty())
2483 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2485 b = newFile(filename, templatefile, true);
2490 // If no new document could be created, it is unsure
2491 // whether there is a valid BufferView.
2492 if (currentBufferView())
2493 // Ensure the cursor is correctly positioned on screen.
2494 currentBufferView()->showCursor();
2498 void GuiView::insertLyXFile(docstring const & fname)
2500 BufferView * bv = documentBufferView();
2505 FileName filename(to_utf8(fname));
2506 if (filename.empty()) {
2507 // Launch a file browser
2509 string initpath = lyxrc.document_path;
2510 string const trypath = bv->buffer().filePath();
2511 // If directory is writeable, use this as default.
2512 if (FileName(trypath).isDirWritable())
2516 FileDialog dlg(qt_("Select LyX document to insert"));
2517 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2518 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2520 FileDialog::Result result = dlg.open(toqstr(initpath),
2521 QStringList(qt_("LyX Documents (*.lyx)")));
2523 if (result.first == FileDialog::Later)
2527 filename.set(fromqstr(result.second));
2529 // check selected filename
2530 if (filename.empty()) {
2531 // emit message signal.
2532 message(_("Canceled."));
2537 bv->insertLyXFile(filename);
2538 bv->buffer().errors("Parse");
2542 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2544 FileName fname = b.fileName();
2545 FileName const oldname = fname;
2547 if (!newname.empty()) {
2549 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2551 // Switch to this Buffer.
2554 // No argument? Ask user through dialog.
2556 FileDialog dlg(qt_("Choose a filename to save document as"));
2557 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2558 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2560 if (!isLyXFileName(fname.absFileName()))
2561 fname.changeExtension(".lyx");
2563 FileDialog::Result result =
2564 dlg.save(toqstr(fname.onlyPath().absFileName()),
2565 QStringList(qt_("LyX Documents (*.lyx)")),
2566 toqstr(fname.onlyFileName()));
2568 if (result.first == FileDialog::Later)
2571 fname.set(fromqstr(result.second));
2576 if (!isLyXFileName(fname.absFileName()))
2577 fname.changeExtension(".lyx");
2580 // fname is now the new Buffer location.
2582 // if there is already a Buffer open with this name, we do not want
2583 // to have another one. (the second test makes sure we're not just
2584 // trying to overwrite ourselves, which is fine.)
2585 if (theBufferList().exists(fname) && fname != oldname
2586 && theBufferList().getBuffer(fname) != &b) {
2587 docstring const text =
2588 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2589 "Please close it before attempting to overwrite it.\n"
2590 "Do you want to choose a new filename?"),
2591 from_utf8(fname.absFileName()));
2592 int const ret = Alert::prompt(_("Chosen File Already Open"),
2593 text, 0, 1, _("&Rename"), _("&Cancel"));
2595 case 0: return renameBuffer(b, docstring(), kind);
2596 case 1: return false;
2601 bool const existsLocal = fname.exists();
2602 bool const existsInVC = LyXVC::fileInVC(fname);
2603 if (existsLocal || existsInVC) {
2604 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2605 if (kind != LV_WRITE_AS && existsInVC) {
2606 // renaming to a name that is already in VC
2608 docstring text = bformat(_("The document %1$s "
2609 "is already registered.\n\n"
2610 "Do you want to choose a new name?"),
2612 docstring const title = (kind == LV_VC_RENAME) ?
2613 _("Rename document?") : _("Copy document?");
2614 docstring const button = (kind == LV_VC_RENAME) ?
2615 _("&Rename") : _("&Copy");
2616 int const ret = Alert::prompt(title, text, 0, 1,
2617 button, _("&Cancel"));
2619 case 0: return renameBuffer(b, docstring(), kind);
2620 case 1: return false;
2625 docstring text = bformat(_("The document %1$s "
2626 "already exists.\n\n"
2627 "Do you want to overwrite that document?"),
2629 int const ret = Alert::prompt(_("Overwrite document?"),
2630 text, 0, 2, _("&Overwrite"),
2631 _("&Rename"), _("&Cancel"));
2634 case 1: return renameBuffer(b, docstring(), kind);
2635 case 2: return false;
2641 case LV_VC_RENAME: {
2642 string msg = b.lyxvc().rename(fname);
2645 message(from_utf8(msg));
2649 string msg = b.lyxvc().copy(fname);
2652 message(from_utf8(msg));
2658 // LyXVC created the file already in case of LV_VC_RENAME or
2659 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2660 // relative paths of included stuff right if we moved e.g. from
2661 // /a/b.lyx to /a/c/b.lyx.
2663 bool const saved = saveBuffer(b, fname);
2670 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2672 FileName fname = b.fileName();
2674 FileDialog dlg(qt_("Choose a filename to export the document as"));
2675 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2678 QString const anyformat = qt_("Guess from extension (*.*)");
2681 vector<Format const *> export_formats;
2682 for (Format const & f : theFormats())
2683 if (f.documentFormat())
2684 export_formats.push_back(&f);
2685 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2686 map<QString, string> fmap;
2689 for (Format const * f : export_formats) {
2690 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2691 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2693 from_ascii(f->extension())));
2694 types << loc_filter;
2695 fmap[loc_filter] = f->name();
2696 if (from_ascii(f->name()) == iformat) {
2697 filter = loc_filter;
2698 ext = f->extension();
2701 string ofname = fname.onlyFileName();
2703 ofname = support::changeExtension(ofname, ext);
2704 FileDialog::Result result =
2705 dlg.save(toqstr(fname.onlyPath().absFileName()),
2709 if (result.first != FileDialog::Chosen)
2713 fname.set(fromqstr(result.second));
2714 if (filter == anyformat)
2715 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2717 fmt_name = fmap[filter];
2718 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2719 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2721 if (fmt_name.empty() || fname.empty())
2724 // fname is now the new Buffer location.
2725 if (FileName(fname).exists()) {
2726 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2727 docstring text = bformat(_("The document %1$s already "
2728 "exists.\n\nDo you want to "
2729 "overwrite that document?"),
2731 int const ret = Alert::prompt(_("Overwrite document?"),
2732 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2735 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2736 case 2: return false;
2740 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2743 return dr.dispatched();
2747 bool GuiView::saveBuffer(Buffer & b)
2749 return saveBuffer(b, FileName());
2753 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2755 if (workArea(b) && workArea(b)->inDialogMode())
2758 if (fn.empty() && b.isUnnamed())
2759 return renameBuffer(b, docstring());
2761 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2763 theSession().lastFiles().add(b.fileName());
2767 // Switch to this Buffer.
2770 // FIXME: we don't tell the user *WHY* the save failed !!
2771 docstring const file = makeDisplayPath(b.absFileName(), 30);
2772 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2773 "Do you want to rename the document and "
2774 "try again?"), file);
2775 int const ret = Alert::prompt(_("Rename and save?"),
2776 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2779 if (!renameBuffer(b, docstring()))
2788 return saveBuffer(b, fn);
2792 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2794 return closeWorkArea(wa, false);
2798 // We only want to close the buffer if it is not visible in other workareas
2799 // of the same view, nor in other views, and if this is not a child
2800 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2802 Buffer & buf = wa->bufferView().buffer();
2804 bool last_wa = d.countWorkAreasOf(buf) == 1
2805 && !inOtherView(buf) && !buf.parent();
2807 bool close_buffer = last_wa;
2810 if (lyxrc.close_buffer_with_last_view == "yes")
2812 else if (lyxrc.close_buffer_with_last_view == "no")
2813 close_buffer = false;
2816 if (buf.isUnnamed())
2817 file = from_utf8(buf.fileName().onlyFileName());
2819 file = buf.fileName().displayName(30);
2820 docstring const text = bformat(
2821 _("Last view on document %1$s is being closed.\n"
2822 "Would you like to close or hide the document?\n"
2824 "Hidden documents can be displayed back through\n"
2825 "the menu: View->Hidden->...\n"
2827 "To remove this question, set your preference in:\n"
2828 " Tools->Preferences->Look&Feel->UserInterface\n"
2830 int ret = Alert::prompt(_("Close or hide document?"),
2831 text, 0, 1, _("&Close"), _("&Hide"));
2832 close_buffer = (ret == 0);
2836 return closeWorkArea(wa, close_buffer);
2840 bool GuiView::closeBuffer()
2842 GuiWorkArea * wa = currentMainWorkArea();
2843 // coverity complained about this
2844 // it seems unnecessary, but perhaps is worth the check
2845 LASSERT(wa, return false);
2847 setCurrentWorkArea(wa);
2848 Buffer & buf = wa->bufferView().buffer();
2849 return closeWorkArea(wa, !buf.parent());
2853 void GuiView::writeSession() const {
2854 GuiWorkArea const * active_wa = currentMainWorkArea();
2855 for (int i = 0; i < d.splitter_->count(); ++i) {
2856 TabWorkArea * twa = d.tabWorkArea(i);
2857 for (int j = 0; j < twa->count(); ++j) {
2858 GuiWorkArea * wa = twa->workArea(j);
2859 Buffer & buf = wa->bufferView().buffer();
2860 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2866 bool GuiView::closeBufferAll()
2868 // Close the workareas in all other views
2869 QList<int> const ids = guiApp->viewIds();
2870 for (int i = 0; i != ids.size(); ++i) {
2871 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2875 // Close our own workareas
2876 if (!closeWorkAreaAll())
2879 // Now close the hidden buffers. We prevent hidden buffers from being
2880 // dirty, so we can just close them.
2881 theBufferList().closeAll();
2886 bool GuiView::closeWorkAreaAll()
2888 setCurrentWorkArea(currentMainWorkArea());
2890 // We might be in a situation that there is still a tabWorkArea, but
2891 // there are no tabs anymore. This can happen when we get here after a
2892 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2893 // many TabWorkArea's have no documents anymore.
2896 // We have to call count() each time, because it can happen that
2897 // more than one splitter will disappear in one iteration (bug 5998).
2898 while (d.splitter_->count() > empty_twa) {
2899 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2901 if (twa->count() == 0)
2904 setCurrentWorkArea(twa->currentWorkArea());
2905 if (!closeTabWorkArea(twa))
2913 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2918 Buffer & buf = wa->bufferView().buffer();
2920 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2921 Alert::warning(_("Close document"),
2922 _("Document could not be closed because it is being processed by LyX."));
2927 return closeBuffer(buf);
2929 if (!inMultiTabs(wa))
2930 if (!saveBufferIfNeeded(buf, true))
2938 bool GuiView::closeBuffer(Buffer & buf)
2940 // If we are in a close_event all children will be closed in some time,
2941 // so no need to do it here. This will ensure that the children end up
2942 // in the session file in the correct order. If we close the master
2943 // buffer, we can close or release the child buffers here too.
2944 bool success = true;
2946 ListOfBuffers clist = buf.getChildren();
2947 ListOfBuffers::const_iterator it = clist.begin();
2948 ListOfBuffers::const_iterator const bend = clist.end();
2949 for (; it != bend; ++it) {
2950 Buffer * child_buf = *it;
2951 if (theBufferList().isOthersChild(&buf, child_buf)) {
2952 child_buf->setParent(0);
2956 // FIXME: should we look in other tabworkareas?
2957 // ANSWER: I don't think so. I've tested, and if the child is
2958 // open in some other window, it closes without a problem.
2959 GuiWorkArea * child_wa = workArea(*child_buf);
2961 success = closeWorkArea(child_wa, true);
2965 // In this case the child buffer is open but hidden.
2966 // It therefore should not (MUST NOT) be dirty!
2967 LATTEST(child_buf->isClean());
2968 theBufferList().release(child_buf);
2973 // goto bookmark to update bookmark pit.
2974 // FIXME: we should update only the bookmarks related to this buffer!
2975 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2976 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2977 guiApp->gotoBookmark(i+1, false, false);
2979 if (saveBufferIfNeeded(buf, false)) {
2980 buf.removeAutosaveFile();
2981 theBufferList().release(&buf);
2985 // open all children again to avoid a crash because of dangling
2986 // pointers (bug 6603)
2992 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2994 while (twa == d.currentTabWorkArea()) {
2995 twa->setCurrentIndex(twa->count() - 1);
2997 GuiWorkArea * wa = twa->currentWorkArea();
2998 Buffer & b = wa->bufferView().buffer();
3000 // We only want to close the buffer if the same buffer is not visible
3001 // in another view, and if this is not a child and if we are closing
3002 // a view (not a tabgroup).
3003 bool const close_buffer =
3004 !inOtherView(b) && !b.parent() && closing_;
3006 if (!closeWorkArea(wa, close_buffer))
3013 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3015 if (buf.isClean() || buf.paragraphs().empty())
3018 // Switch to this Buffer.
3024 if (buf.isUnnamed()) {
3025 file = from_utf8(buf.fileName().onlyFileName());
3028 FileName filename = buf.fileName();
3030 file = filename.displayName(30);
3031 exists = filename.exists();
3034 // Bring this window to top before asking questions.
3039 if (hiding && buf.isUnnamed()) {
3040 docstring const text = bformat(_("The document %1$s has not been "
3041 "saved yet.\n\nDo you want to save "
3042 "the document?"), file);
3043 ret = Alert::prompt(_("Save new document?"),
3044 text, 0, 1, _("&Save"), _("&Cancel"));
3048 docstring const text = exists ?
3049 bformat(_("The document %1$s has unsaved changes."
3050 "\n\nDo you want to save the document or "
3051 "discard the changes?"), file) :
3052 bformat(_("The document %1$s has not been saved yet."
3053 "\n\nDo you want to save the document or "
3054 "discard it entirely?"), file);
3055 docstring const title = exists ?
3056 _("Save changed document?") : _("Save document?");
3057 ret = Alert::prompt(title, text, 0, 2,
3058 _("&Save"), _("&Discard"), _("&Cancel"));
3063 if (!saveBuffer(buf))
3067 // If we crash after this we could have no autosave file
3068 // but I guess this is really improbable (Jug).
3069 // Sometimes improbable things happen:
3070 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3071 // buf.removeAutosaveFile();
3073 // revert all changes
3084 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3086 Buffer & buf = wa->bufferView().buffer();
3088 for (int i = 0; i != d.splitter_->count(); ++i) {
3089 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3090 if (wa_ && wa_ != wa)
3093 return inOtherView(buf);
3097 bool GuiView::inOtherView(Buffer & buf)
3099 QList<int> const ids = guiApp->viewIds();
3101 for (int i = 0; i != ids.size(); ++i) {
3105 if (guiApp->view(ids[i]).workArea(buf))
3112 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3114 if (!documentBufferView())
3117 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3118 Buffer * const curbuf = &documentBufferView()->buffer();
3119 int nwa = twa->count();
3120 for (int i = 0; i < nwa; ++i) {
3121 if (&workArea(i)->bufferView().buffer() == curbuf) {
3123 if (np == NEXTBUFFER)
3124 next_index = (i == nwa - 1 ? 0 : i + 1);
3126 next_index = (i == 0 ? nwa - 1 : i - 1);
3128 twa->moveTab(i, next_index);
3130 setBuffer(&workArea(next_index)->bufferView().buffer());
3138 /// make sure the document is saved
3139 static bool ensureBufferClean(Buffer * buffer)
3141 LASSERT(buffer, return false);
3142 if (buffer->isClean() && !buffer->isUnnamed())
3145 docstring const file = buffer->fileName().displayName(30);
3148 if (!buffer->isUnnamed()) {
3149 text = bformat(_("The document %1$s has unsaved "
3150 "changes.\n\nDo you want to save "
3151 "the document?"), file);
3152 title = _("Save changed document?");
3155 text = bformat(_("The document %1$s has not been "
3156 "saved yet.\n\nDo you want to save "
3157 "the document?"), file);
3158 title = _("Save new document?");
3160 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3163 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3165 return buffer->isClean() && !buffer->isUnnamed();
3169 bool GuiView::reloadBuffer(Buffer & buf)
3171 Buffer::ReadStatus status = buf.reload();
3172 return status == Buffer::ReadSuccess;
3176 void GuiView::checkExternallyModifiedBuffers()
3178 BufferList::iterator bit = theBufferList().begin();
3179 BufferList::iterator const bend = theBufferList().end();
3180 for (; bit != bend; ++bit) {
3181 Buffer * buf = *bit;
3182 if (buf->fileName().exists() && buf->isChecksumModified()) {
3183 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3184 " Reload now? Any local changes will be lost."),
3185 from_utf8(buf->absFileName()));
3186 int const ret = Alert::prompt(_("Reload externally changed document?"),
3187 text, 0, 1, _("&Reload"), _("&Cancel"));
3195 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3197 Buffer * buffer = documentBufferView()
3198 ? &(documentBufferView()->buffer()) : 0;
3200 switch (cmd.action()) {
3201 case LFUN_VC_REGISTER:
3202 if (!buffer || !ensureBufferClean(buffer))
3204 if (!buffer->lyxvc().inUse()) {
3205 if (buffer->lyxvc().registrer()) {
3206 reloadBuffer(*buffer);
3207 dr.clearMessageUpdate();
3212 case LFUN_VC_RENAME:
3213 case LFUN_VC_COPY: {
3214 if (!buffer || !ensureBufferClean(buffer))
3216 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3217 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3218 // Some changes are not yet committed.
3219 // We test here and not in getStatus(), since
3220 // this test is expensive.
3222 LyXVC::CommandResult ret =
3223 buffer->lyxvc().checkIn(log);
3225 if (ret == LyXVC::ErrorCommand ||
3226 ret == LyXVC::VCSuccess)
3227 reloadBuffer(*buffer);
3228 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3229 frontend::Alert::error(
3230 _("Revision control error."),
3231 _("Document could not be checked in."));
3235 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3236 LV_VC_RENAME : LV_VC_COPY;
3237 renameBuffer(*buffer, cmd.argument(), kind);
3242 case LFUN_VC_CHECK_IN:
3243 if (!buffer || !ensureBufferClean(buffer))
3245 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3247 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3249 // Only skip reloading if the checkin was cancelled or
3250 // an error occurred before the real checkin VCS command
3251 // was executed, since the VCS might have changed the
3252 // file even if it could not checkin successfully.
3253 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3254 reloadBuffer(*buffer);
3258 case LFUN_VC_CHECK_OUT:
3259 if (!buffer || !ensureBufferClean(buffer))
3261 if (buffer->lyxvc().inUse()) {
3262 dr.setMessage(buffer->lyxvc().checkOut());
3263 reloadBuffer(*buffer);
3267 case LFUN_VC_LOCKING_TOGGLE:
3268 LASSERT(buffer, return);
3269 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3271 if (buffer->lyxvc().inUse()) {
3272 string res = buffer->lyxvc().lockingToggle();
3274 frontend::Alert::error(_("Revision control error."),
3275 _("Error when setting the locking property."));
3278 reloadBuffer(*buffer);
3283 case LFUN_VC_REVERT:
3284 LASSERT(buffer, return);
3285 if (buffer->lyxvc().revert()) {
3286 reloadBuffer(*buffer);
3287 dr.clearMessageUpdate();
3291 case LFUN_VC_UNDO_LAST:
3292 LASSERT(buffer, return);
3293 buffer->lyxvc().undoLast();
3294 reloadBuffer(*buffer);
3295 dr.clearMessageUpdate();
3298 case LFUN_VC_REPO_UPDATE:
3299 LASSERT(buffer, return);
3300 if (ensureBufferClean(buffer)) {
3301 dr.setMessage(buffer->lyxvc().repoUpdate());
3302 checkExternallyModifiedBuffers();
3306 case LFUN_VC_COMMAND: {
3307 string flag = cmd.getArg(0);
3308 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3311 if (contains(flag, 'M')) {
3312 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3315 string path = cmd.getArg(1);
3316 if (contains(path, "$$p") && buffer)
3317 path = subst(path, "$$p", buffer->filePath());
3318 LYXERR(Debug::LYXVC, "Directory: " << path);
3320 if (!pp.isReadableDirectory()) {
3321 lyxerr << _("Directory is not accessible.") << endl;
3324 support::PathChanger p(pp);
3326 string command = cmd.getArg(2);
3327 if (command.empty())
3330 command = subst(command, "$$i", buffer->absFileName());
3331 command = subst(command, "$$p", buffer->filePath());
3333 command = subst(command, "$$m", to_utf8(message));
3334 LYXERR(Debug::LYXVC, "Command: " << command);
3336 one.startscript(Systemcall::Wait, command);
3340 if (contains(flag, 'I'))
3341 buffer->markDirty();
3342 if (contains(flag, 'R'))
3343 reloadBuffer(*buffer);
3348 case LFUN_VC_COMPARE: {
3349 if (cmd.argument().empty()) {
3350 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3354 string rev1 = cmd.getArg(0);
3359 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3362 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3363 f2 = buffer->absFileName();
3365 string rev2 = cmd.getArg(1);
3369 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3373 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3374 f1 << "\n" << f2 << "\n" );
3375 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3376 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3386 void GuiView::openChildDocument(string const & fname)
3388 LASSERT(documentBufferView(), return);
3389 Buffer & buffer = documentBufferView()->buffer();
3390 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3391 documentBufferView()->saveBookmark(false);
3393 if (theBufferList().exists(filename)) {
3394 child = theBufferList().getBuffer(filename);
3397 message(bformat(_("Opening child document %1$s..."),
3398 makeDisplayPath(filename.absFileName())));
3399 child = loadDocument(filename, false);
3401 // Set the parent name of the child document.
3402 // This makes insertion of citations and references in the child work,
3403 // when the target is in the parent or another child document.
3405 child->setParent(&buffer);
3409 bool GuiView::goToFileRow(string const & argument)
3413 size_t i = argument.find_last_of(' ');
3414 if (i != string::npos) {
3415 file_name = os::internal_path(trim(argument.substr(0, i)));
3416 istringstream is(argument.substr(i + 1));
3421 if (i == string::npos) {
3422 LYXERR0("Wrong argument: " << argument);
3426 string const abstmp = package().temp_dir().absFileName();
3427 string const realtmp = package().temp_dir().realPath();
3428 // We have to use os::path_prefix_is() here, instead of
3429 // simply prefixIs(), because the file name comes from
3430 // an external application and may need case adjustment.
3431 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3432 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3433 // Needed by inverse dvi search. If it is a file
3434 // in tmpdir, call the apropriated function.
3435 // If tmpdir is a symlink, we may have the real
3436 // path passed back, so we correct for that.
3437 if (!prefixIs(file_name, abstmp))
3438 file_name = subst(file_name, realtmp, abstmp);
3439 buf = theBufferList().getBufferFromTmp(file_name);
3441 // Must replace extension of the file to be .lyx
3442 // and get full path
3443 FileName const s = fileSearch(string(),
3444 support::changeExtension(file_name, ".lyx"), "lyx");
3445 // Either change buffer or load the file
3446 if (theBufferList().exists(s))
3447 buf = theBufferList().getBuffer(s);
3448 else if (s.exists()) {
3449 buf = loadDocument(s);
3454 _("File does not exist: %1$s"),
3455 makeDisplayPath(file_name)));
3461 _("No buffer for file: %1$s."),
3462 makeDisplayPath(file_name))
3467 bool success = documentBufferView()->setCursorFromRow(row);
3469 LYXERR(Debug::LATEX,
3470 "setCursorFromRow: invalid position for row " << row);
3471 frontend::Alert::error(_("Inverse Search Failed"),
3472 _("Invalid position requested by inverse search.\n"
3473 "You may need to update the viewed document."));
3479 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3481 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3482 menu->exec(QCursor::pos());
3487 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3489 Buffer::ExportStatus const status = func(format);
3491 // the cloning operation will have produced a clone of the entire set of
3492 // documents, starting from the master. so we must delete those.
3493 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3495 busyBuffers.remove(orig);
3500 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3502 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3503 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3507 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3509 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3510 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3514 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3516 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3517 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3521 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3522 string const & argument,
3523 Buffer const * used_buffer,
3524 docstring const & msg,
3525 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3526 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3527 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3532 string format = argument;
3534 format = used_buffer->params().getDefaultOutputFormat();
3535 processing_format = format;
3537 progress_->clearMessages();
3540 #if EXPORT_in_THREAD
3541 GuiViewPrivate::busyBuffers.insert(used_buffer);
3542 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3543 if (!cloned_buffer) {
3544 Alert::error(_("Export Error"),
3545 _("Error cloning the Buffer."));
3548 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3553 setPreviewFuture(f);
3554 last_export_format = used_buffer->params().bufferFormat();
3557 // We are asynchronous, so we don't know here anything about the success
3560 Buffer::ExportStatus status;
3562 status = (used_buffer->*syncFunc)(format, true);
3563 } else if (previewFunc) {
3564 status = (used_buffer->*previewFunc)(format);
3567 handleExportStatus(gv_, status, format);
3569 return (status == Buffer::ExportSuccess
3570 || status == Buffer::PreviewSuccess);
3574 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3576 BufferView * bv = currentBufferView();
3577 LASSERT(bv, return);
3579 // Let the current BufferView dispatch its own actions.
3580 bv->dispatch(cmd, dr);
3581 if (dr.dispatched())
3584 // Try with the document BufferView dispatch if any.
3585 BufferView * doc_bv = documentBufferView();
3586 if (doc_bv && doc_bv != bv) {
3587 doc_bv->dispatch(cmd, dr);
3588 if (dr.dispatched())
3592 // Then let the current Cursor dispatch its own actions.
3593 bv->cursor().dispatch(cmd);
3595 // update completion. We do it here and not in
3596 // processKeySym to avoid another redraw just for a
3597 // changed inline completion
3598 if (cmd.origin() == FuncRequest::KEYBOARD) {
3599 if (cmd.action() == LFUN_SELF_INSERT
3600 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3601 updateCompletion(bv->cursor(), true, true);
3602 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3603 updateCompletion(bv->cursor(), false, true);
3605 updateCompletion(bv->cursor(), false, false);
3608 dr = bv->cursor().result();
3612 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3614 BufferView * bv = currentBufferView();
3615 // By default we won't need any update.
3616 dr.screenUpdate(Update::None);
3617 // assume cmd will be dispatched
3618 dr.dispatched(true);
3620 Buffer * doc_buffer = documentBufferView()
3621 ? &(documentBufferView()->buffer()) : 0;
3623 if (cmd.origin() == FuncRequest::TOC) {
3624 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3625 // FIXME: do we need to pass a DispatchResult object here?
3626 toc->doDispatch(bv->cursor(), cmd);
3630 string const argument = to_utf8(cmd.argument());
3632 switch(cmd.action()) {
3633 case LFUN_BUFFER_CHILD_OPEN:
3634 openChildDocument(to_utf8(cmd.argument()));
3637 case LFUN_BUFFER_IMPORT:
3638 importDocument(to_utf8(cmd.argument()));
3641 case LFUN_BUFFER_EXPORT: {
3644 // GCC only sees strfwd.h when building merged
3645 if (::lyx::operator==(cmd.argument(), "custom")) {
3646 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3650 string const dest = cmd.getArg(1);
3651 FileName target_dir;
3652 if (!dest.empty() && FileName::isAbsolute(dest))
3653 target_dir = FileName(support::onlyPath(dest));
3655 target_dir = doc_buffer->fileName().onlyPath();
3657 string const format = (argument.empty() || argument == "default") ?
3658 doc_buffer->params().getDefaultOutputFormat() : argument;
3660 if ((dest.empty() && doc_buffer->isUnnamed())
3661 || !target_dir.isDirWritable()) {
3662 exportBufferAs(*doc_buffer, from_utf8(format));
3665 /* TODO/Review: Is it a problem to also export the children?
3666 See the update_unincluded flag */
3667 d.asyncBufferProcessing(format,
3670 &GuiViewPrivate::exportAndDestroy,
3673 // TODO Inform user about success
3677 case LFUN_BUFFER_EXPORT_AS: {
3678 LASSERT(doc_buffer, break);
3679 docstring f = cmd.argument();
3681 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3682 exportBufferAs(*doc_buffer, f);
3686 case LFUN_BUFFER_UPDATE: {
3687 d.asyncBufferProcessing(argument,
3690 &GuiViewPrivate::compileAndDestroy,
3695 case LFUN_BUFFER_VIEW: {
3696 d.asyncBufferProcessing(argument,
3698 _("Previewing ..."),
3699 &GuiViewPrivate::previewAndDestroy,
3704 case LFUN_MASTER_BUFFER_UPDATE: {
3705 d.asyncBufferProcessing(argument,
3706 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3708 &GuiViewPrivate::compileAndDestroy,
3713 case LFUN_MASTER_BUFFER_VIEW: {
3714 d.asyncBufferProcessing(argument,
3715 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3717 &GuiViewPrivate::previewAndDestroy,
3718 0, &Buffer::preview);
3721 case LFUN_BUFFER_SWITCH: {
3722 string const file_name = to_utf8(cmd.argument());
3723 if (!FileName::isAbsolute(file_name)) {
3725 dr.setMessage(_("Absolute filename expected."));
3729 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3732 dr.setMessage(_("Document not loaded"));
3736 // Do we open or switch to the buffer in this view ?
3737 if (workArea(*buffer)
3738 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3743 // Look for the buffer in other views
3744 QList<int> const ids = guiApp->viewIds();
3746 for (; i != ids.size(); ++i) {
3747 GuiView & gv = guiApp->view(ids[i]);
3748 if (gv.workArea(*buffer)) {
3750 gv.activateWindow();
3752 gv.setBuffer(buffer);
3757 // If necessary, open a new window as a last resort
3758 if (i == ids.size()) {
3759 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3765 case LFUN_BUFFER_NEXT:
3766 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3769 case LFUN_BUFFER_MOVE_NEXT:
3770 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3773 case LFUN_BUFFER_PREVIOUS:
3774 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3777 case LFUN_BUFFER_MOVE_PREVIOUS:
3778 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3781 case LFUN_COMMAND_EXECUTE: {
3782 command_execute_ = true;
3783 minibuffer_focus_ = true;
3786 case LFUN_DROP_LAYOUTS_CHOICE:
3787 d.layout_->showPopup();
3790 case LFUN_MENU_OPEN:
3791 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3792 menu->exec(QCursor::pos());
3795 case LFUN_FILE_INSERT:
3796 insertLyXFile(cmd.argument());
3799 case LFUN_FILE_INSERT_PLAINTEXT:
3800 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3801 string const fname = to_utf8(cmd.argument());
3802 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3803 dr.setMessage(_("Absolute filename expected."));
3807 FileName filename(fname);
3808 if (fname.empty()) {
3809 FileDialog dlg(qt_("Select file to insert"));
3811 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3812 QStringList(qt_("All Files (*)")));
3814 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3815 dr.setMessage(_("Canceled."));
3819 filename.set(fromqstr(result.second));
3823 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3824 bv->dispatch(new_cmd, dr);
3829 case LFUN_BUFFER_RELOAD: {
3830 LASSERT(doc_buffer, break);
3833 if (!doc_buffer->isClean()) {
3834 docstring const file =
3835 makeDisplayPath(doc_buffer->absFileName(), 20);
3836 if (doc_buffer->notifiesExternalModification()) {
3837 docstring text = _("The current version will be lost. "
3838 "Are you sure you want to load the version on disk "
3839 "of the document %1$s?");
3840 ret = Alert::prompt(_("Reload saved document?"),
3841 bformat(text, file), 1, 1,
3842 _("&Reload"), _("&Cancel"));
3844 docstring text = _("Any changes will be lost. "
3845 "Are you sure you want to revert to the saved version "
3846 "of the document %1$s?");
3847 ret = Alert::prompt(_("Revert to saved document?"),
3848 bformat(text, file), 1, 1,
3849 _("&Revert"), _("&Cancel"));
3854 doc_buffer->markClean();
3855 reloadBuffer(*doc_buffer);
3856 dr.forceBufferUpdate();
3861 case LFUN_BUFFER_WRITE:
3862 LASSERT(doc_buffer, break);
3863 saveBuffer(*doc_buffer);
3866 case LFUN_BUFFER_WRITE_AS:
3867 LASSERT(doc_buffer, break);
3868 renameBuffer(*doc_buffer, cmd.argument());
3871 case LFUN_BUFFER_WRITE_ALL: {
3872 Buffer * first = theBufferList().first();
3875 message(_("Saving all documents..."));
3876 // We cannot use a for loop as the buffer list cycles.
3879 if (!b->isClean()) {
3881 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3883 b = theBufferList().next(b);
3884 } while (b != first);
3885 dr.setMessage(_("All documents saved."));
3889 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3890 LASSERT(doc_buffer, break);
3891 doc_buffer->clearExternalModification();
3894 case LFUN_BUFFER_CLOSE:
3898 case LFUN_BUFFER_CLOSE_ALL:
3902 case LFUN_DEVEL_MODE_TOGGLE:
3903 devel_mode_ = !devel_mode_;
3905 dr.setMessage(_("Developer mode is now enabled."));
3907 dr.setMessage(_("Developer mode is now disabled."));
3910 case LFUN_TOOLBAR_TOGGLE: {
3911 string const name = cmd.getArg(0);
3912 if (GuiToolbar * t = toolbar(name))
3917 case LFUN_TOOLBAR_MOVABLE: {
3918 string const name = cmd.getArg(0);
3920 // toggle (all) toolbars movablility
3921 toolbarsMovable_ = !toolbarsMovable_;
3922 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3923 GuiToolbar * tb = toolbar(ti.name);
3924 if (tb && tb->isMovable() != toolbarsMovable_)
3925 // toggle toolbar movablity if it does not fit lock
3926 // (all) toolbars positions state silent = true, since
3927 // status bar notifications are slow
3930 if (toolbarsMovable_)
3931 dr.setMessage(_("Toolbars unlocked."));
3933 dr.setMessage(_("Toolbars locked."));
3934 } else if (GuiToolbar * t = toolbar(name)) {
3935 // toggle current toolbar movablity
3937 // update lock (all) toolbars positions
3938 updateLockToolbars();
3943 case LFUN_ICON_SIZE: {
3944 QSize size = d.iconSize(cmd.argument());
3946 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3947 size.width(), size.height()));
3951 case LFUN_DIALOG_UPDATE: {
3952 string const name = to_utf8(cmd.argument());
3953 if (name == "prefs" || name == "document")
3954 updateDialog(name, string());
3955 else if (name == "paragraph")
3956 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3957 else if (currentBufferView()) {
3958 Inset * inset = currentBufferView()->editedInset(name);
3959 // Can only update a dialog connected to an existing inset
3961 // FIXME: get rid of this indirection; GuiView ask the inset
3962 // if he is kind enough to update itself...
3963 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3964 //FIXME: pass DispatchResult here?
3965 inset->dispatch(currentBufferView()->cursor(), fr);
3971 case LFUN_DIALOG_TOGGLE: {
3972 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3973 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3974 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3978 case LFUN_DIALOG_DISCONNECT_INSET:
3979 disconnectDialog(to_utf8(cmd.argument()));
3982 case LFUN_DIALOG_HIDE: {
3983 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3987 case LFUN_DIALOG_SHOW: {
3988 string const name = cmd.getArg(0);
3989 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3991 if (name == "character") {
3992 data = freefont2string();
3994 showDialog("character", data);
3995 } else if (name == "latexlog") {
3996 // getStatus checks that
3997 LATTEST(doc_buffer);
3998 Buffer::LogType type;
3999 string const logfile = doc_buffer->logName(&type);
4001 case Buffer::latexlog:
4004 case Buffer::buildlog:
4008 data += Lexer::quoteString(logfile);
4009 showDialog("log", data);
4010 } else if (name == "vclog") {
4011 // getStatus checks that
4012 LATTEST(doc_buffer);
4013 string const data = "vc " +
4014 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4015 showDialog("log", data);
4016 } else if (name == "symbols") {
4017 data = bv->cursor().getEncoding()->name();
4019 showDialog("symbols", data);
4021 } else if (name == "prefs" && isFullScreen()) {
4022 lfunUiToggle("fullscreen");
4023 showDialog("prefs", data);
4025 showDialog(name, data);
4030 dr.setMessage(cmd.argument());
4033 case LFUN_UI_TOGGLE: {
4034 string arg = cmd.getArg(0);
4035 if (!lfunUiToggle(arg)) {
4036 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4037 dr.setMessage(bformat(msg, from_utf8(arg)));
4039 // Make sure the keyboard focus stays in the work area.
4044 case LFUN_VIEW_SPLIT: {
4045 LASSERT(doc_buffer, break);
4046 string const orientation = cmd.getArg(0);
4047 d.splitter_->setOrientation(orientation == "vertical"
4048 ? Qt::Vertical : Qt::Horizontal);
4049 TabWorkArea * twa = addTabWorkArea();
4050 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4051 setCurrentWorkArea(wa);
4054 case LFUN_TAB_GROUP_CLOSE:
4055 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4056 closeTabWorkArea(twa);
4057 d.current_work_area_ = 0;
4058 twa = d.currentTabWorkArea();
4059 // Switch to the next GuiWorkArea in the found TabWorkArea.
4061 // Make sure the work area is up to date.
4062 setCurrentWorkArea(twa->currentWorkArea());
4064 setCurrentWorkArea(0);
4069 case LFUN_VIEW_CLOSE:
4070 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4071 closeWorkArea(twa->currentWorkArea());
4072 d.current_work_area_ = 0;
4073 twa = d.currentTabWorkArea();
4074 // Switch to the next GuiWorkArea in the found TabWorkArea.
4076 // Make sure the work area is up to date.
4077 setCurrentWorkArea(twa->currentWorkArea());
4079 setCurrentWorkArea(0);
4084 case LFUN_COMPLETION_INLINE:
4085 if (d.current_work_area_)
4086 d.current_work_area_->completer().showInline();
4089 case LFUN_COMPLETION_POPUP:
4090 if (d.current_work_area_)
4091 d.current_work_area_->completer().showPopup();
4096 if (d.current_work_area_)
4097 d.current_work_area_->completer().tab();
4100 case LFUN_COMPLETION_CANCEL:
4101 if (d.current_work_area_) {
4102 if (d.current_work_area_->completer().popupVisible())
4103 d.current_work_area_->completer().hidePopup();
4105 d.current_work_area_->completer().hideInline();
4109 case LFUN_COMPLETION_ACCEPT:
4110 if (d.current_work_area_)
4111 d.current_work_area_->completer().activate();
4114 case LFUN_BUFFER_ZOOM_IN:
4115 case LFUN_BUFFER_ZOOM_OUT:
4116 case LFUN_BUFFER_ZOOM: {
4117 // use a signed temp to avoid overflow
4118 int zoom = lyxrc.currentZoom;
4119 if (cmd.argument().empty()) {
4120 if (cmd.action() == LFUN_BUFFER_ZOOM)
4122 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4127 if (cmd.action() == LFUN_BUFFER_ZOOM)
4128 zoom = convert<int>(cmd.argument());
4129 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4130 zoom += convert<int>(cmd.argument());
4132 zoom -= convert<int>(cmd.argument());
4135 if (zoom < static_cast<int>(zoom_min_))
4138 lyxrc.currentZoom = zoom;
4140 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4142 // The global QPixmapCache is used in GuiPainter to cache text
4143 // painting so we must reset it.
4144 QPixmapCache::clear();
4145 guiApp->fontLoader().update();
4146 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4150 case LFUN_VC_REGISTER:
4151 case LFUN_VC_RENAME:
4153 case LFUN_VC_CHECK_IN:
4154 case LFUN_VC_CHECK_OUT:
4155 case LFUN_VC_REPO_UPDATE:
4156 case LFUN_VC_LOCKING_TOGGLE:
4157 case LFUN_VC_REVERT:
4158 case LFUN_VC_UNDO_LAST:
4159 case LFUN_VC_COMMAND:
4160 case LFUN_VC_COMPARE:
4161 dispatchVC(cmd, dr);
4164 case LFUN_SERVER_GOTO_FILE_ROW:
4165 if(goToFileRow(to_utf8(cmd.argument())))
4166 dr.screenUpdate(Update::Force | Update::FitCursor);
4169 case LFUN_LYX_ACTIVATE:
4173 case LFUN_FORWARD_SEARCH: {
4174 // it seems safe to assume we have a document buffer, since
4175 // getStatus wants one.
4176 LATTEST(doc_buffer);
4177 Buffer const * doc_master = doc_buffer->masterBuffer();
4178 FileName const path(doc_master->temppath());
4179 string const texname = doc_master->isChild(doc_buffer)
4180 ? DocFileName(changeExtension(
4181 doc_buffer->absFileName(),
4182 "tex")).mangledFileName()
4183 : doc_buffer->latexName();
4184 string const fulltexname =
4185 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4186 string const mastername =
4187 removeExtension(doc_master->latexName());
4188 FileName const dviname(addName(path.absFileName(),
4189 addExtension(mastername, "dvi")));
4190 FileName const pdfname(addName(path.absFileName(),
4191 addExtension(mastername, "pdf")));
4192 bool const have_dvi = dviname.exists();
4193 bool const have_pdf = pdfname.exists();
4194 if (!have_dvi && !have_pdf) {
4195 dr.setMessage(_("Please, preview the document first."));
4198 string outname = dviname.onlyFileName();
4199 string command = lyxrc.forward_search_dvi;
4200 if (!have_dvi || (have_pdf &&
4201 pdfname.lastModified() > dviname.lastModified())) {
4202 outname = pdfname.onlyFileName();
4203 command = lyxrc.forward_search_pdf;
4206 DocIterator cur = bv->cursor();
4207 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4208 LYXERR(Debug::ACTION, "Forward search: row:" << row
4210 if (row == -1 || command.empty()) {
4211 dr.setMessage(_("Couldn't proceed."));
4214 string texrow = convert<string>(row);
4216 command = subst(command, "$$n", texrow);
4217 command = subst(command, "$$f", fulltexname);
4218 command = subst(command, "$$t", texname);
4219 command = subst(command, "$$o", outname);
4221 PathChanger p(path);
4223 one.startscript(Systemcall::DontWait, command);
4227 case LFUN_SPELLING_CONTINUOUSLY:
4228 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4229 dr.screenUpdate(Update::Force);
4233 // The LFUN must be for one of BufferView, Buffer or Cursor;
4235 dispatchToBufferView(cmd, dr);
4239 // Part of automatic menu appearance feature.
4240 if (isFullScreen()) {
4241 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4245 // Need to update bv because many LFUNs here might have destroyed it
4246 bv = currentBufferView();
4248 // Clear non-empty selections
4249 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4251 Cursor & cur = bv->cursor();
4252 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4253 cur.clearSelection();
4259 bool GuiView::lfunUiToggle(string const & ui_component)
4261 if (ui_component == "scrollbar") {
4262 // hide() is of no help
4263 if (d.current_work_area_->verticalScrollBarPolicy() ==
4264 Qt::ScrollBarAlwaysOff)
4266 d.current_work_area_->setVerticalScrollBarPolicy(
4267 Qt::ScrollBarAsNeeded);
4269 d.current_work_area_->setVerticalScrollBarPolicy(
4270 Qt::ScrollBarAlwaysOff);
4271 } else if (ui_component == "statusbar") {
4272 statusBar()->setVisible(!statusBar()->isVisible());
4273 } else if (ui_component == "menubar") {
4274 menuBar()->setVisible(!menuBar()->isVisible());
4276 if (ui_component == "frame") {
4278 getContentsMargins(&l, &t, &r, &b);
4279 //are the frames in default state?
4280 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4282 setContentsMargins(-2, -2, -2, -2);
4284 setContentsMargins(0, 0, 0, 0);
4287 if (ui_component == "fullscreen") {
4295 void GuiView::toggleFullScreen()
4297 if (isFullScreen()) {
4298 for (int i = 0; i != d.splitter_->count(); ++i)
4299 d.tabWorkArea(i)->setFullScreen(false);
4300 setContentsMargins(0, 0, 0, 0);
4301 setWindowState(windowState() ^ Qt::WindowFullScreen);
4304 statusBar()->show();
4307 hideDialogs("prefs", 0);
4308 for (int i = 0; i != d.splitter_->count(); ++i)
4309 d.tabWorkArea(i)->setFullScreen(true);
4310 setContentsMargins(-2, -2, -2, -2);
4312 setWindowState(windowState() ^ Qt::WindowFullScreen);
4313 if (lyxrc.full_screen_statusbar)
4314 statusBar()->hide();
4315 if (lyxrc.full_screen_menubar)
4317 if (lyxrc.full_screen_toolbars) {
4318 ToolbarMap::iterator end = d.toolbars_.end();
4319 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4324 // give dialogs like the TOC a chance to adapt
4329 Buffer const * GuiView::updateInset(Inset const * inset)
4334 Buffer const * inset_buffer = &(inset->buffer());
4336 for (int i = 0; i != d.splitter_->count(); ++i) {
4337 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4340 Buffer const * buffer = &(wa->bufferView().buffer());
4341 if (inset_buffer == buffer)
4342 wa->scheduleRedraw();
4344 return inset_buffer;
4348 void GuiView::restartCursor()
4350 /* When we move around, or type, it's nice to be able to see
4351 * the cursor immediately after the keypress.
4353 if (d.current_work_area_)
4354 d.current_work_area_->startBlinkingCursor();
4356 // Take this occasion to update the other GUI elements.
4362 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4364 if (d.current_work_area_)
4365 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4370 // This list should be kept in sync with the list of insets in
4371 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4372 // dialog should have the same name as the inset.
4373 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4374 // docs in LyXAction.cpp.
4376 char const * const dialognames[] = {
4378 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4379 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4380 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4381 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4382 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4383 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4384 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4385 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4387 char const * const * const end_dialognames =
4388 dialognames + (sizeof(dialognames) / sizeof(char *));
4392 cmpCStr(char const * name) : name_(name) {}
4393 bool operator()(char const * other) {
4394 return strcmp(other, name_) == 0;
4401 bool isValidName(string const & name)
4403 return find_if(dialognames, end_dialognames,
4404 cmpCStr(name.c_str())) != end_dialognames;
4410 void GuiView::resetDialogs()
4412 // Make sure that no LFUN uses any GuiView.
4413 guiApp->setCurrentView(0);
4417 constructToolbars();
4418 guiApp->menus().fillMenuBar(menuBar(), this, false);
4419 d.layout_->updateContents(true);
4420 // Now update controls with current buffer.
4421 guiApp->setCurrentView(this);
4427 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4429 if (!isValidName(name))
4432 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4434 if (it != d.dialogs_.end()) {
4436 it->second->hideView();
4437 return it->second.get();
4440 Dialog * dialog = build(name);
4441 d.dialogs_[name].reset(dialog);
4442 if (lyxrc.allow_geometry_session)
4443 dialog->restoreSession();
4450 void GuiView::showDialog(string const & name, string const & data,
4453 triggerShowDialog(toqstr(name), toqstr(data), inset);
4457 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4463 const string name = fromqstr(qname);
4464 const string data = fromqstr(qdata);
4468 Dialog * dialog = findOrBuild(name, false);
4470 bool const visible = dialog->isVisibleView();
4471 dialog->showData(data);
4472 if (inset && currentBufferView())
4473 currentBufferView()->editInset(name, inset);
4474 // We only set the focus to the new dialog if it was not yet
4475 // visible in order not to change the existing previous behaviour
4477 // activateWindow is needed for floating dockviews
4478 dialog->asQWidget()->raise();
4479 dialog->asQWidget()->activateWindow();
4480 dialog->asQWidget()->setFocus();
4484 catch (ExceptionMessage const & ex) {
4492 bool GuiView::isDialogVisible(string const & name) const
4494 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4495 if (it == d.dialogs_.end())
4497 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4501 void GuiView::hideDialog(string const & name, Inset * inset)
4503 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4504 if (it == d.dialogs_.end())
4508 if (!currentBufferView())
4510 if (inset != currentBufferView()->editedInset(name))
4514 Dialog * const dialog = it->second.get();
4515 if (dialog->isVisibleView())
4517 if (currentBufferView())
4518 currentBufferView()->editInset(name, 0);
4522 void GuiView::disconnectDialog(string const & name)
4524 if (!isValidName(name))
4526 if (currentBufferView())
4527 currentBufferView()->editInset(name, 0);
4531 void GuiView::hideAll() const
4533 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4534 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4536 for(; it != end; ++it)
4537 it->second->hideView();
4541 void GuiView::updateDialogs()
4543 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4544 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4546 for(; it != end; ++it) {
4547 Dialog * dialog = it->second.get();
4549 if (dialog->needBufferOpen() && !documentBufferView())
4550 hideDialog(fromqstr(dialog->name()), 0);
4551 else if (dialog->isVisibleView())
4552 dialog->checkStatus();
4559 Dialog * createDialog(GuiView & lv, string const & name);
4561 // will be replaced by a proper factory...
4562 Dialog * createGuiAbout(GuiView & lv);
4563 Dialog * createGuiBibtex(GuiView & lv);
4564 Dialog * createGuiChanges(GuiView & lv);
4565 Dialog * createGuiCharacter(GuiView & lv);
4566 Dialog * createGuiCitation(GuiView & lv);
4567 Dialog * createGuiCompare(GuiView & lv);
4568 Dialog * createGuiCompareHistory(GuiView & lv);
4569 Dialog * createGuiDelimiter(GuiView & lv);
4570 Dialog * createGuiDocument(GuiView & lv);
4571 Dialog * createGuiErrorList(GuiView & lv);
4572 Dialog * createGuiExternal(GuiView & lv);
4573 Dialog * createGuiGraphics(GuiView & lv);
4574 Dialog * createGuiInclude(GuiView & lv);
4575 Dialog * createGuiIndex(GuiView & lv);
4576 Dialog * createGuiListings(GuiView & lv);
4577 Dialog * createGuiLog(GuiView & lv);
4578 Dialog * createGuiMathMatrix(GuiView & lv);
4579 Dialog * createGuiNote(GuiView & lv);
4580 Dialog * createGuiParagraph(GuiView & lv);
4581 Dialog * createGuiPhantom(GuiView & lv);
4582 Dialog * createGuiPreferences(GuiView & lv);
4583 Dialog * createGuiPrint(GuiView & lv);
4584 Dialog * createGuiPrintindex(GuiView & lv);
4585 Dialog * createGuiRef(GuiView & lv);
4586 Dialog * createGuiSearch(GuiView & lv);
4587 Dialog * createGuiSearchAdv(GuiView & lv);
4588 Dialog * createGuiSendTo(GuiView & lv);
4589 Dialog * createGuiShowFile(GuiView & lv);
4590 Dialog * createGuiSpellchecker(GuiView & lv);
4591 Dialog * createGuiSymbols(GuiView & lv);
4592 Dialog * createGuiTabularCreate(GuiView & lv);
4593 Dialog * createGuiTexInfo(GuiView & lv);
4594 Dialog * createGuiToc(GuiView & lv);
4595 Dialog * createGuiThesaurus(GuiView & lv);
4596 Dialog * createGuiViewSource(GuiView & lv);
4597 Dialog * createGuiWrap(GuiView & lv);
4598 Dialog * createGuiProgressView(GuiView & lv);
4602 Dialog * GuiView::build(string const & name)
4604 LASSERT(isValidName(name), return 0);
4606 Dialog * dialog = createDialog(*this, name);
4610 if (name == "aboutlyx")
4611 return createGuiAbout(*this);
4612 if (name == "bibtex")
4613 return createGuiBibtex(*this);
4614 if (name == "changes")
4615 return createGuiChanges(*this);
4616 if (name == "character")
4617 return createGuiCharacter(*this);
4618 if (name == "citation")
4619 return createGuiCitation(*this);
4620 if (name == "compare")
4621 return createGuiCompare(*this);
4622 if (name == "comparehistory")
4623 return createGuiCompareHistory(*this);
4624 if (name == "document")
4625 return createGuiDocument(*this);
4626 if (name == "errorlist")
4627 return createGuiErrorList(*this);
4628 if (name == "external")
4629 return createGuiExternal(*this);
4631 return createGuiShowFile(*this);
4632 if (name == "findreplace")
4633 return createGuiSearch(*this);
4634 if (name == "findreplaceadv")
4635 return createGuiSearchAdv(*this);
4636 if (name == "graphics")
4637 return createGuiGraphics(*this);
4638 if (name == "include")
4639 return createGuiInclude(*this);
4640 if (name == "index")
4641 return createGuiIndex(*this);
4642 if (name == "index_print")
4643 return createGuiPrintindex(*this);
4644 if (name == "listings")
4645 return createGuiListings(*this);
4647 return createGuiLog(*this);
4648 if (name == "mathdelimiter")
4649 return createGuiDelimiter(*this);
4650 if (name == "mathmatrix")
4651 return createGuiMathMatrix(*this);
4653 return createGuiNote(*this);
4654 if (name == "paragraph")
4655 return createGuiParagraph(*this);
4656 if (name == "phantom")
4657 return createGuiPhantom(*this);
4658 if (name == "prefs")
4659 return createGuiPreferences(*this);
4661 return createGuiRef(*this);
4662 if (name == "sendto")
4663 return createGuiSendTo(*this);
4664 if (name == "spellchecker")
4665 return createGuiSpellchecker(*this);
4666 if (name == "symbols")
4667 return createGuiSymbols(*this);
4668 if (name == "tabularcreate")
4669 return createGuiTabularCreate(*this);
4670 if (name == "texinfo")
4671 return createGuiTexInfo(*this);
4672 if (name == "thesaurus")
4673 return createGuiThesaurus(*this);
4675 return createGuiToc(*this);
4676 if (name == "view-source")
4677 return createGuiViewSource(*this);
4679 return createGuiWrap(*this);
4680 if (name == "progress")
4681 return createGuiProgressView(*this);
4687 SEMenu::SEMenu(QWidget * parent)
4689 QAction * action = addAction(qt_("Disable Shell Escape"));
4690 connect(action, SIGNAL(triggered()),
4691 parent, SLOT(disableShellEscape()));
4695 } // namespace frontend
4698 #include "moc_GuiView.cpp"