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 if (status != Buffer::ExportSuccess && status != Buffer::PreviewSuccess &&
729 status != Buffer::ExportCancel) {
730 errors(d.last_export_format);
735 void GuiView::autoSaveThreadFinished()
737 QFutureWatcher<docstring> const * watcher =
738 static_cast<QFutureWatcher<docstring> const *>(sender());
739 message(watcher->result());
744 void GuiView::saveLayout() const
747 settings.setValue("zoom_ratio", zoom_ratio_);
748 settings.setValue("devel_mode", devel_mode_);
749 settings.beginGroup("views");
750 settings.beginGroup(QString::number(id_));
751 #if defined(Q_WS_X11) || defined(QPA_XCB)
752 settings.setValue("pos", pos());
753 settings.setValue("size", size());
755 settings.setValue("geometry", saveGeometry());
757 settings.setValue("layout", saveState(0));
758 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
762 void GuiView::saveUISettings() const
766 // Save the toolbar private states
767 ToolbarMap::iterator end = d.toolbars_.end();
768 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
769 it->second->saveSession(settings);
770 // Now take care of all other dialogs
771 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
772 for (; it!= d.dialogs_.end(); ++it)
773 it->second->saveSession(settings);
777 bool GuiView::restoreLayout()
780 zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
781 // Actual zoom value: default zoom + fractional offset
782 int zoom = lyxrc.defaultZoom * zoom_ratio_;
783 if (zoom < static_cast<int>(zoom_min_))
785 lyxrc.currentZoom = zoom;
786 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
787 settings.beginGroup("views");
788 settings.beginGroup(QString::number(id_));
789 QString const icon_key = "icon_size";
790 if (!settings.contains(icon_key))
793 //code below is skipped when when ~/.config/LyX is (re)created
794 setIconSize(d.iconSize(settings.value(icon_key).toString()));
796 #if defined(Q_WS_X11) || defined(QPA_XCB)
797 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
798 QSize size = settings.value("size", QSize(690, 510)).toSize();
802 // Work-around for bug #6034: the window ends up in an undetermined
803 // state when trying to restore a maximized window when it is
804 // already maximized.
805 if (!(windowState() & Qt::WindowMaximized))
806 if (!restoreGeometry(settings.value("geometry").toByteArray()))
807 setGeometry(50, 50, 690, 510);
809 // Make sure layout is correctly oriented.
810 setLayoutDirection(qApp->layoutDirection());
812 // Allow the toc and view-source dock widget to be restored if needed.
814 if ((dialog = findOrBuild("toc", true)))
815 // see bug 5082. At least setup title and enabled state.
816 // Visibility will be adjusted by restoreState below.
817 dialog->prepareView();
818 if ((dialog = findOrBuild("view-source", true)))
819 dialog->prepareView();
820 if ((dialog = findOrBuild("progress", true)))
821 dialog->prepareView();
823 if (!restoreState(settings.value("layout").toByteArray(), 0))
826 // init the toolbars that have not been restored
827 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
828 Toolbars::Infos::iterator end = guiApp->toolbars().end();
829 for (; cit != end; ++cit) {
830 GuiToolbar * tb = toolbar(cit->name);
831 if (tb && !tb->isRestored())
832 initToolbar(cit->name);
835 // update lock (all) toolbars positions
836 updateLockToolbars();
843 GuiToolbar * GuiView::toolbar(string const & name)
845 ToolbarMap::iterator it = d.toolbars_.find(name);
846 if (it != d.toolbars_.end())
849 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
854 void GuiView::updateLockToolbars()
856 toolbarsMovable_ = false;
857 for (ToolbarInfo const & info : guiApp->toolbars()) {
858 GuiToolbar * tb = toolbar(info.name);
859 if (tb && tb->isMovable())
860 toolbarsMovable_ = true;
865 void GuiView::constructToolbars()
867 ToolbarMap::iterator it = d.toolbars_.begin();
868 for (; it != d.toolbars_.end(); ++it)
872 // I don't like doing this here, but the standard toolbar
873 // destroys this object when it's destroyed itself (vfr)
874 d.layout_ = new LayoutBox(*this);
875 d.stack_widget_->addWidget(d.layout_);
876 d.layout_->move(0,0);
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 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
886 void GuiView::initToolbars()
888 // extracts the toolbars from the backend
889 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
890 Toolbars::Infos::iterator end = guiApp->toolbars().end();
891 for (; cit != end; ++cit)
892 initToolbar(cit->name);
896 void GuiView::initToolbar(string const & name)
898 GuiToolbar * tb = toolbar(name);
901 int const visibility = guiApp->toolbars().defaultVisibility(name);
902 bool newline = !(visibility & Toolbars::SAMEROW);
903 tb->setVisible(false);
904 tb->setVisibility(visibility);
906 if (visibility & Toolbars::TOP) {
908 addToolBarBreak(Qt::TopToolBarArea);
909 addToolBar(Qt::TopToolBarArea, tb);
912 if (visibility & Toolbars::BOTTOM) {
914 addToolBarBreak(Qt::BottomToolBarArea);
915 addToolBar(Qt::BottomToolBarArea, tb);
918 if (visibility & Toolbars::LEFT) {
920 addToolBarBreak(Qt::LeftToolBarArea);
921 addToolBar(Qt::LeftToolBarArea, tb);
924 if (visibility & Toolbars::RIGHT) {
926 addToolBarBreak(Qt::RightToolBarArea);
927 addToolBar(Qt::RightToolBarArea, tb);
930 if (visibility & Toolbars::ON)
931 tb->setVisible(true);
933 tb->setMovable(true);
937 TocModels & GuiView::tocModels()
939 return d.toc_models_;
943 void GuiView::setFocus()
945 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
946 QMainWindow::setFocus();
950 bool GuiView::hasFocus() const
952 if (currentWorkArea())
953 return currentWorkArea()->hasFocus();
954 if (currentMainWorkArea())
955 return currentMainWorkArea()->hasFocus();
956 return d.bg_widget_->hasFocus();
960 void GuiView::focusInEvent(QFocusEvent * e)
962 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
963 QMainWindow::focusInEvent(e);
964 // Make sure guiApp points to the correct view.
965 guiApp->setCurrentView(this);
966 if (currentWorkArea())
967 currentWorkArea()->setFocus();
968 else if (currentMainWorkArea())
969 currentMainWorkArea()->setFocus();
971 d.bg_widget_->setFocus();
975 void GuiView::showEvent(QShowEvent * e)
977 LYXERR(Debug::GUI, "Passed Geometry "
978 << size().height() << "x" << size().width()
979 << "+" << pos().x() << "+" << pos().y());
981 if (d.splitter_->count() == 0)
982 // No work area, switch to the background widget.
986 QMainWindow::showEvent(e);
990 bool GuiView::closeScheduled()
997 bool GuiView::prepareAllBuffersForLogout()
999 Buffer * first = theBufferList().first();
1003 // First, iterate over all buffers and ask the users if unsaved
1004 // changes should be saved.
1005 // We cannot use a for loop as the buffer list cycles.
1008 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1010 b = theBufferList().next(b);
1011 } while (b != first);
1013 // Next, save session state
1014 // When a view/window was closed before without quitting LyX, there
1015 // are already entries in the lastOpened list.
1016 theSession().lastOpened().clear();
1023 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1024 ** is responsibility of the container (e.g., dialog)
1026 void GuiView::closeEvent(QCloseEvent * close_event)
1028 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1030 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1031 Alert::warning(_("Exit LyX"),
1032 _("LyX could not be closed because documents are being processed by LyX."));
1033 close_event->setAccepted(false);
1037 // If the user pressed the x (so we didn't call closeView
1038 // programmatically), we want to clear all existing entries.
1040 theSession().lastOpened().clear();
1045 // it can happen that this event arrives without selecting the view,
1046 // e.g. when clicking the close button on a background window.
1048 if (!closeWorkAreaAll()) {
1050 close_event->ignore();
1054 // Make sure that nothing will use this to be closed View.
1055 guiApp->unregisterView(this);
1057 if (isFullScreen()) {
1058 // Switch off fullscreen before closing.
1063 // Make sure the timer time out will not trigger a statusbar update.
1064 d.statusbar_timer_.stop();
1066 // Saving fullscreen requires additional tweaks in the toolbar code.
1067 // It wouldn't also work under linux natively.
1068 if (lyxrc.allow_geometry_session) {
1073 close_event->accept();
1077 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1079 if (event->mimeData()->hasUrls())
1081 /// \todo Ask lyx-devel is this is enough:
1082 /// if (event->mimeData()->hasFormat("text/plain"))
1083 /// event->acceptProposedAction();
1087 void GuiView::dropEvent(QDropEvent * event)
1089 QList<QUrl> files = event->mimeData()->urls();
1090 if (files.isEmpty())
1093 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1094 for (int i = 0; i != files.size(); ++i) {
1095 string const file = os::internal_path(fromqstr(
1096 files.at(i).toLocalFile()));
1100 string const ext = support::getExtension(file);
1101 vector<const Format *> found_formats;
1103 // Find all formats that have the correct extension.
1104 vector<const Format *> const & import_formats
1105 = theConverters().importableFormats();
1106 vector<const Format *>::const_iterator it = import_formats.begin();
1107 for (; it != import_formats.end(); ++it)
1108 if ((*it)->hasExtension(ext))
1109 found_formats.push_back(*it);
1112 if (found_formats.size() >= 1) {
1113 if (found_formats.size() > 1) {
1114 //FIXME: show a dialog to choose the correct importable format
1115 LYXERR(Debug::FILES,
1116 "Multiple importable formats found, selecting first");
1118 string const arg = found_formats[0]->name() + " " + file;
1119 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1122 //FIXME: do we have to explicitly check whether it's a lyx file?
1123 LYXERR(Debug::FILES,
1124 "No formats found, trying to open it as a lyx file");
1125 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1127 // add the functions to the queue
1128 guiApp->addToFuncRequestQueue(cmd);
1131 // now process the collected functions. We perform the events
1132 // asynchronously. This prevents potential problems in case the
1133 // BufferView is closed within an event.
1134 guiApp->processFuncRequestQueueAsync();
1138 void GuiView::message(docstring const & str)
1140 if (ForkedProcess::iAmAChild())
1143 // call is moved to GUI-thread by GuiProgress
1144 d.progress_->appendMessage(toqstr(str));
1148 void GuiView::clearMessageText()
1150 message(docstring());
1154 void GuiView::updateStatusBarMessage(QString const & str)
1156 statusBar()->showMessage(str);
1157 d.statusbar_timer_.stop();
1158 d.statusbar_timer_.start(3000);
1162 void GuiView::clearMessage()
1164 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1165 // the hasFocus function mostly returns false, even if the focus is on
1166 // a workarea in this view.
1170 d.statusbar_timer_.stop();
1174 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1176 if (wa != d.current_work_area_
1177 || wa->bufferView().buffer().isInternal())
1179 Buffer const & buf = wa->bufferView().buffer();
1180 // Set the windows title
1181 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1182 if (buf.notifiesExternalModification()) {
1183 title = bformat(_("%1$s (modified externally)"), title);
1184 // If the external modification status has changed, then maybe the status of
1185 // buffer-save has changed too.
1189 title += from_ascii(" - LyX");
1191 setWindowTitle(toqstr(title));
1192 // Sets the path for the window: this is used by OSX to
1193 // allow a context click on the title bar showing a menu
1194 // with the path up to the file
1195 setWindowFilePath(toqstr(buf.absFileName()));
1196 // Tell Qt whether the current document is changed
1197 setWindowModified(!buf.isClean());
1199 if (buf.params().shell_escape)
1200 shell_escape_->show();
1202 shell_escape_->hide();
1204 if (buf.hasReadonlyFlag())
1209 if (buf.lyxvc().inUse()) {
1210 version_control_->show();
1211 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1213 version_control_->hide();
1217 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1219 if (d.current_work_area_)
1220 // disconnect the current work area from all slots
1221 QObject::disconnect(d.current_work_area_, 0, this, 0);
1223 disconnectBufferView();
1224 connectBufferView(wa->bufferView());
1225 connectBuffer(wa->bufferView().buffer());
1226 d.current_work_area_ = wa;
1227 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1228 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1229 QObject::connect(wa, SIGNAL(busy(bool)),
1230 this, SLOT(setBusy(bool)));
1231 // connection of a signal to a signal
1232 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1233 this, SIGNAL(bufferViewChanged()));
1234 Q_EMIT updateWindowTitle(wa);
1235 Q_EMIT bufferViewChanged();
1239 void GuiView::onBufferViewChanged()
1242 // Buffer-dependent dialogs must be updated. This is done here because
1243 // some dialogs require buffer()->text.
1248 void GuiView::on_lastWorkAreaRemoved()
1251 // We already are in a close event. Nothing more to do.
1254 if (d.splitter_->count() > 1)
1255 // We have a splitter so don't close anything.
1258 // Reset and updates the dialogs.
1259 Q_EMIT bufferViewChanged();
1264 if (lyxrc.open_buffers_in_tabs)
1265 // Nothing more to do, the window should stay open.
1268 if (guiApp->viewIds().size() > 1) {
1274 // On Mac we also close the last window because the application stay
1275 // resident in memory. On other platforms we don't close the last
1276 // window because this would quit the application.
1282 void GuiView::updateStatusBar()
1284 // let the user see the explicit message
1285 if (d.statusbar_timer_.isActive())
1292 void GuiView::showMessage()
1296 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1297 if (msg.isEmpty()) {
1298 BufferView const * bv = currentBufferView();
1300 msg = toqstr(bv->cursor().currentState(devel_mode_));
1302 msg = qt_("Welcome to LyX!");
1304 statusBar()->showMessage(msg);
1308 bool GuiView::event(QEvent * e)
1312 // Useful debug code:
1313 //case QEvent::ActivationChange:
1314 //case QEvent::WindowDeactivate:
1315 //case QEvent::Paint:
1316 //case QEvent::Enter:
1317 //case QEvent::Leave:
1318 //case QEvent::HoverEnter:
1319 //case QEvent::HoverLeave:
1320 //case QEvent::HoverMove:
1321 //case QEvent::StatusTip:
1322 //case QEvent::DragEnter:
1323 //case QEvent::DragLeave:
1324 //case QEvent::Drop:
1327 case QEvent::WindowActivate: {
1328 GuiView * old_view = guiApp->currentView();
1329 if (this == old_view) {
1331 return QMainWindow::event(e);
1333 if (old_view && old_view->currentBufferView()) {
1334 // save current selection to the selection buffer to allow
1335 // middle-button paste in this window.
1336 cap::saveSelection(old_view->currentBufferView()->cursor());
1338 guiApp->setCurrentView(this);
1339 if (d.current_work_area_)
1340 on_currentWorkAreaChanged(d.current_work_area_);
1344 return QMainWindow::event(e);
1347 case QEvent::ShortcutOverride: {
1349 if (isFullScreen() && menuBar()->isHidden()) {
1350 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1351 // FIXME: we should also try to detect special LyX shortcut such as
1352 // Alt-P and Alt-M. Right now there is a hack in
1353 // GuiWorkArea::processKeySym() that hides again the menubar for
1355 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1357 return QMainWindow::event(e);
1360 return QMainWindow::event(e);
1364 return QMainWindow::event(e);
1368 void GuiView::resetWindowTitle()
1370 setWindowTitle(qt_("LyX"));
1373 bool GuiView::focusNextPrevChild(bool /*next*/)
1380 bool GuiView::busy() const
1386 void GuiView::setBusy(bool busy)
1388 bool const busy_before = busy_ > 0;
1389 busy ? ++busy_ : --busy_;
1390 if ((busy_ > 0) == busy_before)
1391 // busy state didn't change
1395 QApplication::setOverrideCursor(Qt::WaitCursor);
1398 QApplication::restoreOverrideCursor();
1403 void GuiView::resetCommandExecute()
1405 command_execute_ = false;
1410 double GuiView::pixelRatio() const
1412 #if QT_VERSION >= 0x050000
1413 return qt_scale_factor * devicePixelRatio();
1420 GuiWorkArea * GuiView::workArea(int index)
1422 if (TabWorkArea * twa = d.currentTabWorkArea())
1423 if (index < twa->count())
1424 return twa->workArea(index);
1429 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1431 if (currentWorkArea()
1432 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1433 return currentWorkArea();
1434 if (TabWorkArea * twa = d.currentTabWorkArea())
1435 return twa->workArea(buffer);
1440 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1442 // Automatically create a TabWorkArea if there are none yet.
1443 TabWorkArea * tab_widget = d.splitter_->count()
1444 ? d.currentTabWorkArea() : addTabWorkArea();
1445 return tab_widget->addWorkArea(buffer, *this);
1449 TabWorkArea * GuiView::addTabWorkArea()
1451 TabWorkArea * twa = new TabWorkArea;
1452 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1453 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1454 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1455 this, SLOT(on_lastWorkAreaRemoved()));
1457 d.splitter_->addWidget(twa);
1458 d.stack_widget_->setCurrentWidget(d.splitter_);
1463 GuiWorkArea const * GuiView::currentWorkArea() const
1465 return d.current_work_area_;
1469 GuiWorkArea * GuiView::currentWorkArea()
1471 return d.current_work_area_;
1475 GuiWorkArea const * GuiView::currentMainWorkArea() const
1477 if (!d.currentTabWorkArea())
1479 return d.currentTabWorkArea()->currentWorkArea();
1483 GuiWorkArea * GuiView::currentMainWorkArea()
1485 if (!d.currentTabWorkArea())
1487 return d.currentTabWorkArea()->currentWorkArea();
1491 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1493 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1495 d.current_work_area_ = 0;
1497 Q_EMIT bufferViewChanged();
1501 // FIXME: I've no clue why this is here and why it accesses
1502 // theGuiApp()->currentView, which might be 0 (bug 6464).
1503 // See also 27525 (vfr).
1504 if (theGuiApp()->currentView() == this
1505 && theGuiApp()->currentView()->currentWorkArea() == wa)
1508 if (currentBufferView())
1509 cap::saveSelection(currentBufferView()->cursor());
1511 theGuiApp()->setCurrentView(this);
1512 d.current_work_area_ = wa;
1514 // We need to reset this now, because it will need to be
1515 // right if the tabWorkArea gets reset in the for loop. We
1516 // will change it back if we aren't in that case.
1517 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1518 d.current_main_work_area_ = wa;
1520 for (int i = 0; i != d.splitter_->count(); ++i) {
1521 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1522 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1523 << ", Current main wa: " << currentMainWorkArea());
1528 d.current_main_work_area_ = old_cmwa;
1530 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1531 on_currentWorkAreaChanged(wa);
1532 BufferView & bv = wa->bufferView();
1533 bv.cursor().fixIfBroken();
1535 wa->setUpdatesEnabled(true);
1536 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1540 void GuiView::removeWorkArea(GuiWorkArea * wa)
1542 LASSERT(wa, return);
1543 if (wa == d.current_work_area_) {
1545 disconnectBufferView();
1546 d.current_work_area_ = 0;
1547 d.current_main_work_area_ = 0;
1550 bool found_twa = false;
1551 for (int i = 0; i != d.splitter_->count(); ++i) {
1552 TabWorkArea * twa = d.tabWorkArea(i);
1553 if (twa->removeWorkArea(wa)) {
1554 // Found in this tab group, and deleted the GuiWorkArea.
1556 if (twa->count() != 0) {
1557 if (d.current_work_area_ == 0)
1558 // This means that we are closing the current GuiWorkArea, so
1559 // switch to the next GuiWorkArea in the found TabWorkArea.
1560 setCurrentWorkArea(twa->currentWorkArea());
1562 // No more WorkAreas in this tab group, so delete it.
1569 // It is not a tabbed work area (i.e., the search work area), so it
1570 // should be deleted by other means.
1571 LASSERT(found_twa, return);
1573 if (d.current_work_area_ == 0) {
1574 if (d.splitter_->count() != 0) {
1575 TabWorkArea * twa = d.currentTabWorkArea();
1576 setCurrentWorkArea(twa->currentWorkArea());
1578 // No more work areas, switch to the background widget.
1579 setCurrentWorkArea(0);
1585 LayoutBox * GuiView::getLayoutDialog() const
1591 void GuiView::updateLayoutList()
1594 d.layout_->updateContents(false);
1598 void GuiView::updateToolbars()
1600 ToolbarMap::iterator end = d.toolbars_.end();
1601 if (d.current_work_area_) {
1603 if (d.current_work_area_->bufferView().cursor().inMathed()
1604 && !d.current_work_area_->bufferView().cursor().inRegexped())
1605 context |= Toolbars::MATH;
1606 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1607 context |= Toolbars::TABLE;
1608 if (currentBufferView()->buffer().areChangesPresent()
1609 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1610 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1611 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1612 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1613 context |= Toolbars::REVIEW;
1614 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1615 context |= Toolbars::MATHMACROTEMPLATE;
1616 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1617 context |= Toolbars::IPA;
1618 if (command_execute_)
1619 context |= Toolbars::MINIBUFFER;
1620 if (minibuffer_focus_) {
1621 context |= Toolbars::MINIBUFFER_FOCUS;
1622 minibuffer_focus_ = false;
1625 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1626 it->second->update(context);
1628 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1629 it->second->update();
1633 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1635 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1636 LASSERT(newBuffer, return);
1638 GuiWorkArea * wa = workArea(*newBuffer);
1641 newBuffer->masterBuffer()->updateBuffer();
1643 wa = addWorkArea(*newBuffer);
1644 // scroll to the position when the BufferView was last closed
1645 if (lyxrc.use_lastfilepos) {
1646 LastFilePosSection::FilePos filepos =
1647 theSession().lastFilePos().load(newBuffer->fileName());
1648 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1651 //Disconnect the old buffer...there's no new one.
1654 connectBuffer(*newBuffer);
1655 connectBufferView(wa->bufferView());
1657 setCurrentWorkArea(wa);
1661 void GuiView::connectBuffer(Buffer & buf)
1663 buf.setGuiDelegate(this);
1667 void GuiView::disconnectBuffer()
1669 if (d.current_work_area_)
1670 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1674 void GuiView::connectBufferView(BufferView & bv)
1676 bv.setGuiDelegate(this);
1680 void GuiView::disconnectBufferView()
1682 if (d.current_work_area_)
1683 d.current_work_area_->bufferView().setGuiDelegate(0);
1687 void GuiView::errors(string const & error_type, bool from_master)
1689 BufferView const * const bv = currentBufferView();
1693 #if EXPORT_in_THREAD
1694 // We are called with from_master == false by default, so we
1695 // have to figure out whether that is the case or not.
1696 ErrorList & el = bv->buffer().errorList(error_type);
1698 el = bv->buffer().masterBuffer()->errorList(error_type);
1702 ErrorList const & el = from_master ?
1703 bv->buffer().masterBuffer()->errorList(error_type) :
1704 bv->buffer().errorList(error_type);
1710 string data = error_type;
1712 data = "from_master|" + error_type;
1713 showDialog("errorlist", data);
1717 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1719 d.toc_models_.updateItem(toqstr(type), dit);
1723 void GuiView::structureChanged()
1725 // This is called from the Buffer, which has no way to ensure that cursors
1726 // in BufferView remain valid.
1727 if (documentBufferView())
1728 documentBufferView()->cursor().sanitize();
1729 // FIXME: This is slightly expensive, though less than the tocBackend update
1730 // (#9880). This also resets the view in the Toc Widget (#6675).
1731 d.toc_models_.reset(documentBufferView());
1732 // Navigator needs more than a simple update in this case. It needs to be
1734 updateDialog("toc", "");
1738 void GuiView::updateDialog(string const & name, string const & data)
1740 if (!isDialogVisible(name))
1743 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1744 if (it == d.dialogs_.end())
1747 Dialog * const dialog = it->second.get();
1748 if (dialog->isVisibleView())
1749 dialog->initialiseParams(data);
1753 BufferView * GuiView::documentBufferView()
1755 return currentMainWorkArea()
1756 ? ¤tMainWorkArea()->bufferView()
1761 BufferView const * GuiView::documentBufferView() const
1763 return currentMainWorkArea()
1764 ? ¤tMainWorkArea()->bufferView()
1769 BufferView * GuiView::currentBufferView()
1771 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1775 BufferView const * GuiView::currentBufferView() const
1777 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1781 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1782 Buffer const * orig, Buffer * clone)
1784 bool const success = clone->autoSave();
1786 busyBuffers.remove(orig);
1788 ? _("Automatic save done.")
1789 : _("Automatic save failed!");
1793 void GuiView::autoSave()
1795 LYXERR(Debug::INFO, "Running autoSave()");
1797 Buffer * buffer = documentBufferView()
1798 ? &documentBufferView()->buffer() : 0;
1800 resetAutosaveTimers();
1804 GuiViewPrivate::busyBuffers.insert(buffer);
1805 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1806 buffer, buffer->cloneBufferOnly());
1807 d.autosave_watcher_.setFuture(f);
1808 resetAutosaveTimers();
1812 void GuiView::resetAutosaveTimers()
1815 d.autosave_timeout_.restart();
1819 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1822 Buffer * buf = currentBufferView()
1823 ? ¤tBufferView()->buffer() : 0;
1824 Buffer * doc_buffer = documentBufferView()
1825 ? &(documentBufferView()->buffer()) : 0;
1828 /* In LyX/Mac, when a dialog is open, the menus of the
1829 application can still be accessed without giving focus to
1830 the main window. In this case, we want to disable the menu
1831 entries that are buffer-related.
1832 This code must not be used on Linux and Windows, since it
1833 would disable buffer-related entries when hovering over the
1834 menu (see bug #9574).
1836 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1842 // Check whether we need a buffer
1843 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1844 // no, exit directly
1845 flag.message(from_utf8(N_("Command not allowed with"
1846 "out any document open")));
1847 flag.setEnabled(false);
1851 if (cmd.origin() == FuncRequest::TOC) {
1852 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1853 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1854 flag.setEnabled(false);
1858 switch(cmd.action()) {
1859 case LFUN_BUFFER_IMPORT:
1862 case LFUN_MASTER_BUFFER_UPDATE:
1863 case LFUN_MASTER_BUFFER_VIEW:
1865 && (doc_buffer->parent() != 0
1866 || doc_buffer->hasChildren())
1867 && !d.processing_thread_watcher_.isRunning();
1870 case LFUN_BUFFER_UPDATE:
1871 case LFUN_BUFFER_VIEW: {
1872 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1876 string format = to_utf8(cmd.argument());
1877 if (cmd.argument().empty())
1878 format = doc_buffer->params().getDefaultOutputFormat();
1879 enable = doc_buffer->params().isExportable(format, true);
1883 case LFUN_BUFFER_RELOAD:
1884 enable = doc_buffer && !doc_buffer->isUnnamed()
1885 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1888 case LFUN_BUFFER_CHILD_OPEN:
1889 enable = doc_buffer != 0;
1892 case LFUN_BUFFER_WRITE:
1893 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1896 //FIXME: This LFUN should be moved to GuiApplication.
1897 case LFUN_BUFFER_WRITE_ALL: {
1898 // We enable the command only if there are some modified buffers
1899 Buffer * first = theBufferList().first();
1904 // We cannot use a for loop as the buffer list is a cycle.
1906 if (!b->isClean()) {
1910 b = theBufferList().next(b);
1911 } while (b != first);
1915 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1916 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1919 case LFUN_BUFFER_EXPORT: {
1920 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1924 return doc_buffer->getStatus(cmd, flag);
1928 case LFUN_BUFFER_EXPORT_AS:
1929 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1934 case LFUN_BUFFER_WRITE_AS:
1935 enable = doc_buffer != 0;
1938 case LFUN_BUFFER_CLOSE:
1939 case LFUN_VIEW_CLOSE:
1940 enable = doc_buffer != 0;
1943 case LFUN_BUFFER_CLOSE_ALL:
1944 enable = theBufferList().last() != theBufferList().first();
1947 case LFUN_BUFFER_CHKTEX: {
1948 // hide if we have no checktex command
1949 if (lyxrc.chktex_command.empty()) {
1950 flag.setUnknown(true);
1954 if (!doc_buffer || !doc_buffer->params().isLatex()
1955 || d.processing_thread_watcher_.isRunning()) {
1956 // grey out, don't hide
1964 case LFUN_VIEW_SPLIT:
1965 if (cmd.getArg(0) == "vertical")
1966 enable = doc_buffer && (d.splitter_->count() == 1 ||
1967 d.splitter_->orientation() == Qt::Vertical);
1969 enable = doc_buffer && (d.splitter_->count() == 1 ||
1970 d.splitter_->orientation() == Qt::Horizontal);
1973 case LFUN_TAB_GROUP_CLOSE:
1974 enable = d.tabWorkAreaCount() > 1;
1977 case LFUN_DEVEL_MODE_TOGGLE:
1978 flag.setOnOff(devel_mode_);
1981 case LFUN_TOOLBAR_TOGGLE: {
1982 string const name = cmd.getArg(0);
1983 if (GuiToolbar * t = toolbar(name))
1984 flag.setOnOff(t->isVisible());
1987 docstring const msg =
1988 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1994 case LFUN_TOOLBAR_MOVABLE: {
1995 string const name = cmd.getArg(0);
1996 // use negation since locked == !movable
1998 // toolbar name * locks all toolbars
1999 flag.setOnOff(!toolbarsMovable_);
2000 else if (GuiToolbar * t = toolbar(name))
2001 flag.setOnOff(!(t->isMovable()));
2004 docstring const msg =
2005 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
2011 case LFUN_ICON_SIZE:
2012 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
2015 case LFUN_DROP_LAYOUTS_CHOICE:
2019 case LFUN_UI_TOGGLE:
2020 flag.setOnOff(isFullScreen());
2023 case LFUN_DIALOG_DISCONNECT_INSET:
2026 case LFUN_DIALOG_HIDE:
2027 // FIXME: should we check if the dialog is shown?
2030 case LFUN_DIALOG_TOGGLE:
2031 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
2034 case LFUN_DIALOG_SHOW: {
2035 string const name = cmd.getArg(0);
2037 enable = name == "aboutlyx"
2038 || name == "file" //FIXME: should be removed.
2040 || name == "texinfo"
2041 || name == "progress"
2042 || name == "compare";
2043 else if (name == "character" || name == "symbols"
2044 || name == "mathdelimiter" || name == "mathmatrix") {
2045 if (!buf || buf->isReadonly())
2048 Cursor const & cur = currentBufferView()->cursor();
2049 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2052 else if (name == "latexlog")
2053 enable = FileName(doc_buffer->logName()).isReadableFile();
2054 else if (name == "spellchecker")
2055 enable = theSpellChecker()
2056 && !doc_buffer->isReadonly()
2057 && !doc_buffer->text().empty();
2058 else if (name == "vclog")
2059 enable = doc_buffer->lyxvc().inUse();
2063 case LFUN_DIALOG_UPDATE: {
2064 string const name = cmd.getArg(0);
2066 enable = name == "prefs";
2070 case LFUN_COMMAND_EXECUTE:
2072 case LFUN_MENU_OPEN:
2073 // Nothing to check.
2076 case LFUN_COMPLETION_INLINE:
2077 if (!d.current_work_area_
2078 || !d.current_work_area_->completer().inlinePossible(
2079 currentBufferView()->cursor()))
2083 case LFUN_COMPLETION_POPUP:
2084 if (!d.current_work_area_
2085 || !d.current_work_area_->completer().popupPossible(
2086 currentBufferView()->cursor()))
2091 if (!d.current_work_area_
2092 || !d.current_work_area_->completer().inlinePossible(
2093 currentBufferView()->cursor()))
2097 case LFUN_COMPLETION_ACCEPT:
2098 if (!d.current_work_area_
2099 || (!d.current_work_area_->completer().popupVisible()
2100 && !d.current_work_area_->completer().inlineVisible()
2101 && !d.current_work_area_->completer().completionAvailable()))
2105 case LFUN_COMPLETION_CANCEL:
2106 if (!d.current_work_area_
2107 || (!d.current_work_area_->completer().popupVisible()
2108 && !d.current_work_area_->completer().inlineVisible()))
2112 case LFUN_BUFFER_ZOOM_OUT:
2113 case LFUN_BUFFER_ZOOM_IN: {
2114 // only diff between these two is that the default for ZOOM_OUT
2116 bool const neg_zoom =
2117 convert<int>(cmd.argument()) < 0 ||
2118 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2119 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2120 docstring const msg =
2121 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2125 enable = doc_buffer;
2129 case LFUN_BUFFER_ZOOM: {
2130 bool const less_than_min_zoom =
2131 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2132 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2133 docstring const msg =
2134 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2139 enable = doc_buffer;
2143 case LFUN_BUFFER_MOVE_NEXT:
2144 case LFUN_BUFFER_MOVE_PREVIOUS:
2145 // we do not cycle when moving
2146 case LFUN_BUFFER_NEXT:
2147 case LFUN_BUFFER_PREVIOUS:
2148 // because we cycle, it doesn't matter whether on first or last
2149 enable = (d.currentTabWorkArea()->count() > 1);
2151 case LFUN_BUFFER_SWITCH:
2152 // toggle on the current buffer, but do not toggle off
2153 // the other ones (is that a good idea?)
2155 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2156 flag.setOnOff(true);
2159 case LFUN_VC_REGISTER:
2160 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2162 case LFUN_VC_RENAME:
2163 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2166 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2168 case LFUN_VC_CHECK_IN:
2169 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2171 case LFUN_VC_CHECK_OUT:
2172 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2174 case LFUN_VC_LOCKING_TOGGLE:
2175 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2176 && doc_buffer->lyxvc().lockingToggleEnabled();
2177 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2179 case LFUN_VC_REVERT:
2180 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2181 && !doc_buffer->hasReadonlyFlag();
2183 case LFUN_VC_UNDO_LAST:
2184 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2186 case LFUN_VC_REPO_UPDATE:
2187 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2189 case LFUN_VC_COMMAND: {
2190 if (cmd.argument().empty())
2192 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2196 case LFUN_VC_COMPARE:
2197 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2200 case LFUN_SERVER_GOTO_FILE_ROW:
2201 case LFUN_LYX_ACTIVATE:
2203 case LFUN_FORWARD_SEARCH:
2204 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2207 case LFUN_FILE_INSERT_PLAINTEXT:
2208 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2209 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2212 case LFUN_SPELLING_CONTINUOUSLY:
2213 flag.setOnOff(lyxrc.spellcheck_continuously);
2221 flag.setEnabled(false);
2227 static FileName selectTemplateFile()
2229 FileDialog dlg(qt_("Select template file"));
2230 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2231 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2233 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2234 QStringList(qt_("LyX Documents (*.lyx)")));
2236 if (result.first == FileDialog::Later)
2238 if (result.second.isEmpty())
2240 return FileName(fromqstr(result.second));
2244 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2248 Buffer * newBuffer = 0;
2250 newBuffer = checkAndLoadLyXFile(filename);
2251 } catch (ExceptionMessage const & e) {
2258 message(_("Document not loaded."));
2262 setBuffer(newBuffer);
2263 newBuffer->errors("Parse");
2266 theSession().lastFiles().add(filename);
2272 void GuiView::openDocument(string const & fname)
2274 string initpath = lyxrc.document_path;
2276 if (documentBufferView()) {
2277 string const trypath = documentBufferView()->buffer().filePath();
2278 // If directory is writeable, use this as default.
2279 if (FileName(trypath).isDirWritable())
2285 if (fname.empty()) {
2286 FileDialog dlg(qt_("Select document to open"));
2287 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2288 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2290 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2291 FileDialog::Result result =
2292 dlg.open(toqstr(initpath), filter);
2294 if (result.first == FileDialog::Later)
2297 filename = fromqstr(result.second);
2299 // check selected filename
2300 if (filename.empty()) {
2301 message(_("Canceled."));
2307 // get absolute path of file and add ".lyx" to the filename if
2309 FileName const fullname =
2310 fileSearch(string(), filename, "lyx", support::may_not_exist);
2311 if (!fullname.empty())
2312 filename = fullname.absFileName();
2314 if (!fullname.onlyPath().isDirectory()) {
2315 Alert::warning(_("Invalid filename"),
2316 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2317 from_utf8(fullname.absFileName())));
2321 // if the file doesn't exist and isn't already open (bug 6645),
2322 // let the user create one
2323 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2324 !LyXVC::file_not_found_hook(fullname)) {
2325 // the user specifically chose this name. Believe him.
2326 Buffer * const b = newFile(filename, string(), true);
2332 docstring const disp_fn = makeDisplayPath(filename);
2333 message(bformat(_("Opening document %1$s..."), disp_fn));
2336 Buffer * buf = loadDocument(fullname);
2338 str2 = bformat(_("Document %1$s opened."), disp_fn);
2339 if (buf->lyxvc().inUse())
2340 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2341 " " + _("Version control detected.");
2343 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2348 // FIXME: clean that
2349 static bool import(GuiView * lv, FileName const & filename,
2350 string const & format, ErrorList & errorList)
2352 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2354 string loader_format;
2355 vector<string> loaders = theConverters().loaders();
2356 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2357 vector<string>::const_iterator it = loaders.begin();
2358 vector<string>::const_iterator en = loaders.end();
2359 for (; it != en; ++it) {
2360 if (!theConverters().isReachable(format, *it))
2363 string const tofile =
2364 support::changeExtension(filename.absFileName(),
2365 theFormats().extension(*it));
2366 if (!theConverters().convert(0, filename, FileName(tofile),
2367 filename, format, *it, errorList))
2369 loader_format = *it;
2372 if (loader_format.empty()) {
2373 frontend::Alert::error(_("Couldn't import file"),
2374 bformat(_("No information for importing the format %1$s."),
2375 theFormats().prettyName(format)));
2379 loader_format = format;
2381 if (loader_format == "lyx") {
2382 Buffer * buf = lv->loadDocument(lyxfile);
2386 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2390 bool as_paragraphs = loader_format == "textparagraph";
2391 string filename2 = (loader_format == format) ? filename.absFileName()
2392 : support::changeExtension(filename.absFileName(),
2393 theFormats().extension(loader_format));
2394 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2396 guiApp->setCurrentView(lv);
2397 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2404 void GuiView::importDocument(string const & argument)
2407 string filename = split(argument, format, ' ');
2409 LYXERR(Debug::INFO, format << " file: " << filename);
2411 // need user interaction
2412 if (filename.empty()) {
2413 string initpath = lyxrc.document_path;
2414 if (documentBufferView()) {
2415 string const trypath = documentBufferView()->buffer().filePath();
2416 // If directory is writeable, use this as default.
2417 if (FileName(trypath).isDirWritable())
2421 docstring const text = bformat(_("Select %1$s file to import"),
2422 theFormats().prettyName(format));
2424 FileDialog dlg(toqstr(text));
2425 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2426 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2428 docstring filter = theFormats().prettyName(format);
2431 filter += from_utf8(theFormats().extensions(format));
2434 FileDialog::Result result =
2435 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2437 if (result.first == FileDialog::Later)
2440 filename = fromqstr(result.second);
2442 // check selected filename
2443 if (filename.empty())
2444 message(_("Canceled."));
2447 if (filename.empty())
2450 // get absolute path of file
2451 FileName const fullname(support::makeAbsPath(filename));
2453 // Can happen if the user entered a path into the dialog
2455 if (fullname.onlyFileName().empty()) {
2456 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2457 "Aborting import."),
2458 from_utf8(fullname.absFileName()));
2459 frontend::Alert::error(_("File name error"), msg);
2460 message(_("Canceled."));
2465 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2467 // Check if the document already is open
2468 Buffer * buf = theBufferList().getBuffer(lyxfile);
2471 if (!closeBuffer()) {
2472 message(_("Canceled."));
2477 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2479 // if the file exists already, and we didn't do
2480 // -i lyx thefile.lyx, warn
2481 if (lyxfile.exists() && fullname != lyxfile) {
2483 docstring text = bformat(_("The document %1$s already exists.\n\n"
2484 "Do you want to overwrite that document?"), displaypath);
2485 int const ret = Alert::prompt(_("Overwrite document?"),
2486 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2489 message(_("Canceled."));
2494 message(bformat(_("Importing %1$s..."), displaypath));
2495 ErrorList errorList;
2496 if (import(this, fullname, format, errorList))
2497 message(_("imported."));
2499 message(_("file not imported!"));
2501 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2505 void GuiView::newDocument(string const & filename, bool from_template)
2507 FileName initpath(lyxrc.document_path);
2508 if (documentBufferView()) {
2509 FileName const trypath(documentBufferView()->buffer().filePath());
2510 // If directory is writeable, use this as default.
2511 if (trypath.isDirWritable())
2515 string templatefile;
2516 if (from_template) {
2517 templatefile = selectTemplateFile().absFileName();
2518 if (templatefile.empty())
2523 if (filename.empty())
2524 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2526 b = newFile(filename, templatefile, true);
2531 // If no new document could be created, it is unsure
2532 // whether there is a valid BufferView.
2533 if (currentBufferView())
2534 // Ensure the cursor is correctly positioned on screen.
2535 currentBufferView()->showCursor();
2539 void GuiView::insertLyXFile(docstring const & fname)
2541 BufferView * bv = documentBufferView();
2546 FileName filename(to_utf8(fname));
2547 if (filename.empty()) {
2548 // Launch a file browser
2550 string initpath = lyxrc.document_path;
2551 string const trypath = bv->buffer().filePath();
2552 // If directory is writeable, use this as default.
2553 if (FileName(trypath).isDirWritable())
2557 FileDialog dlg(qt_("Select LyX document to insert"));
2558 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2559 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2561 FileDialog::Result result = dlg.open(toqstr(initpath),
2562 QStringList(qt_("LyX Documents (*.lyx)")));
2564 if (result.first == FileDialog::Later)
2568 filename.set(fromqstr(result.second));
2570 // check selected filename
2571 if (filename.empty()) {
2572 // emit message signal.
2573 message(_("Canceled."));
2578 bv->insertLyXFile(filename);
2579 bv->buffer().errors("Parse");
2583 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2585 FileName fname = b.fileName();
2586 FileName const oldname = fname;
2588 if (!newname.empty()) {
2590 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2592 // Switch to this Buffer.
2595 // No argument? Ask user through dialog.
2597 FileDialog dlg(qt_("Choose a filename to save document as"));
2598 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2599 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2601 if (!isLyXFileName(fname.absFileName()))
2602 fname.changeExtension(".lyx");
2604 FileDialog::Result result =
2605 dlg.save(toqstr(fname.onlyPath().absFileName()),
2606 QStringList(qt_("LyX Documents (*.lyx)")),
2607 toqstr(fname.onlyFileName()));
2609 if (result.first == FileDialog::Later)
2612 fname.set(fromqstr(result.second));
2617 if (!isLyXFileName(fname.absFileName()))
2618 fname.changeExtension(".lyx");
2621 // fname is now the new Buffer location.
2623 // if there is already a Buffer open with this name, we do not want
2624 // to have another one. (the second test makes sure we're not just
2625 // trying to overwrite ourselves, which is fine.)
2626 if (theBufferList().exists(fname) && fname != oldname
2627 && theBufferList().getBuffer(fname) != &b) {
2628 docstring const text =
2629 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2630 "Please close it before attempting to overwrite it.\n"
2631 "Do you want to choose a new filename?"),
2632 from_utf8(fname.absFileName()));
2633 int const ret = Alert::prompt(_("Chosen File Already Open"),
2634 text, 0, 1, _("&Rename"), _("&Cancel"));
2636 case 0: return renameBuffer(b, docstring(), kind);
2637 case 1: return false;
2642 bool const existsLocal = fname.exists();
2643 bool const existsInVC = LyXVC::fileInVC(fname);
2644 if (existsLocal || existsInVC) {
2645 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2646 if (kind != LV_WRITE_AS && existsInVC) {
2647 // renaming to a name that is already in VC
2649 docstring text = bformat(_("The document %1$s "
2650 "is already registered.\n\n"
2651 "Do you want to choose a new name?"),
2653 docstring const title = (kind == LV_VC_RENAME) ?
2654 _("Rename document?") : _("Copy document?");
2655 docstring const button = (kind == LV_VC_RENAME) ?
2656 _("&Rename") : _("&Copy");
2657 int const ret = Alert::prompt(title, text, 0, 1,
2658 button, _("&Cancel"));
2660 case 0: return renameBuffer(b, docstring(), kind);
2661 case 1: return false;
2666 docstring text = bformat(_("The document %1$s "
2667 "already exists.\n\n"
2668 "Do you want to overwrite that document?"),
2670 int const ret = Alert::prompt(_("Overwrite document?"),
2671 text, 0, 2, _("&Overwrite"),
2672 _("&Rename"), _("&Cancel"));
2675 case 1: return renameBuffer(b, docstring(), kind);
2676 case 2: return false;
2682 case LV_VC_RENAME: {
2683 string msg = b.lyxvc().rename(fname);
2686 message(from_utf8(msg));
2690 string msg = b.lyxvc().copy(fname);
2693 message(from_utf8(msg));
2699 // LyXVC created the file already in case of LV_VC_RENAME or
2700 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2701 // relative paths of included stuff right if we moved e.g. from
2702 // /a/b.lyx to /a/c/b.lyx.
2704 bool const saved = saveBuffer(b, fname);
2711 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2713 FileName fname = b.fileName();
2715 FileDialog dlg(qt_("Choose a filename to export the document as"));
2716 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2719 QString const anyformat = qt_("Guess from extension (*.*)");
2722 vector<Format const *> export_formats;
2723 for (Format const & f : theFormats())
2724 if (f.documentFormat())
2725 export_formats.push_back(&f);
2726 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2727 map<QString, string> fmap;
2730 for (Format const * f : export_formats) {
2731 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2732 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2734 from_ascii(f->extension())));
2735 types << loc_filter;
2736 fmap[loc_filter] = f->name();
2737 if (from_ascii(f->name()) == iformat) {
2738 filter = loc_filter;
2739 ext = f->extension();
2742 string ofname = fname.onlyFileName();
2744 ofname = support::changeExtension(ofname, ext);
2745 FileDialog::Result result =
2746 dlg.save(toqstr(fname.onlyPath().absFileName()),
2750 if (result.first != FileDialog::Chosen)
2754 fname.set(fromqstr(result.second));
2755 if (filter == anyformat)
2756 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2758 fmt_name = fmap[filter];
2759 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2760 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2762 if (fmt_name.empty() || fname.empty())
2765 // fname is now the new Buffer location.
2766 if (FileName(fname).exists()) {
2767 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2768 docstring text = bformat(_("The document %1$s already "
2769 "exists.\n\nDo you want to "
2770 "overwrite that document?"),
2772 int const ret = Alert::prompt(_("Overwrite document?"),
2773 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2776 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2777 case 2: return false;
2781 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2784 return dr.dispatched();
2788 bool GuiView::saveBuffer(Buffer & b)
2790 return saveBuffer(b, FileName());
2794 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2796 if (workArea(b) && workArea(b)->inDialogMode())
2799 if (fn.empty() && b.isUnnamed())
2800 return renameBuffer(b, docstring());
2802 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2804 theSession().lastFiles().add(b.fileName());
2808 // Switch to this Buffer.
2811 // FIXME: we don't tell the user *WHY* the save failed !!
2812 docstring const file = makeDisplayPath(b.absFileName(), 30);
2813 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2814 "Do you want to rename the document and "
2815 "try again?"), file);
2816 int const ret = Alert::prompt(_("Rename and save?"),
2817 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2820 if (!renameBuffer(b, docstring()))
2829 return saveBuffer(b, fn);
2833 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2835 return closeWorkArea(wa, false);
2839 // We only want to close the buffer if it is not visible in other workareas
2840 // of the same view, nor in other views, and if this is not a child
2841 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2843 Buffer & buf = wa->bufferView().buffer();
2845 bool last_wa = d.countWorkAreasOf(buf) == 1
2846 && !inOtherView(buf) && !buf.parent();
2848 bool close_buffer = last_wa;
2851 if (lyxrc.close_buffer_with_last_view == "yes")
2853 else if (lyxrc.close_buffer_with_last_view == "no")
2854 close_buffer = false;
2857 if (buf.isUnnamed())
2858 file = from_utf8(buf.fileName().onlyFileName());
2860 file = buf.fileName().displayName(30);
2861 docstring const text = bformat(
2862 _("Last view on document %1$s is being closed.\n"
2863 "Would you like to close or hide the document?\n"
2865 "Hidden documents can be displayed back through\n"
2866 "the menu: View->Hidden->...\n"
2868 "To remove this question, set your preference in:\n"
2869 " Tools->Preferences->Look&Feel->UserInterface\n"
2871 int ret = Alert::prompt(_("Close or hide document?"),
2872 text, 0, 1, _("&Close"), _("&Hide"));
2873 close_buffer = (ret == 0);
2877 return closeWorkArea(wa, close_buffer);
2881 bool GuiView::closeBuffer()
2883 GuiWorkArea * wa = currentMainWorkArea();
2884 // coverity complained about this
2885 // it seems unnecessary, but perhaps is worth the check
2886 LASSERT(wa, return false);
2888 setCurrentWorkArea(wa);
2889 Buffer & buf = wa->bufferView().buffer();
2890 return closeWorkArea(wa, !buf.parent());
2894 void GuiView::writeSession() const {
2895 GuiWorkArea const * active_wa = currentMainWorkArea();
2896 for (int i = 0; i < d.splitter_->count(); ++i) {
2897 TabWorkArea * twa = d.tabWorkArea(i);
2898 for (int j = 0; j < twa->count(); ++j) {
2899 GuiWorkArea * wa = twa->workArea(j);
2900 Buffer & buf = wa->bufferView().buffer();
2901 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2907 bool GuiView::closeBufferAll()
2909 // Close the workareas in all other views
2910 QList<int> const ids = guiApp->viewIds();
2911 for (int i = 0; i != ids.size(); ++i) {
2912 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2916 // Close our own workareas
2917 if (!closeWorkAreaAll())
2920 // Now close the hidden buffers. We prevent hidden buffers from being
2921 // dirty, so we can just close them.
2922 theBufferList().closeAll();
2927 bool GuiView::closeWorkAreaAll()
2929 setCurrentWorkArea(currentMainWorkArea());
2931 // We might be in a situation that there is still a tabWorkArea, but
2932 // there are no tabs anymore. This can happen when we get here after a
2933 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2934 // many TabWorkArea's have no documents anymore.
2937 // We have to call count() each time, because it can happen that
2938 // more than one splitter will disappear in one iteration (bug 5998).
2939 while (d.splitter_->count() > empty_twa) {
2940 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2942 if (twa->count() == 0)
2945 setCurrentWorkArea(twa->currentWorkArea());
2946 if (!closeTabWorkArea(twa))
2954 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2959 Buffer & buf = wa->bufferView().buffer();
2961 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2962 Alert::warning(_("Close document"),
2963 _("Document could not be closed because it is being processed by LyX."));
2968 return closeBuffer(buf);
2970 if (!inMultiTabs(wa))
2971 if (!saveBufferIfNeeded(buf, true))
2979 bool GuiView::closeBuffer(Buffer & buf)
2981 // If we are in a close_event all children will be closed in some time,
2982 // so no need to do it here. This will ensure that the children end up
2983 // in the session file in the correct order. If we close the master
2984 // buffer, we can close or release the child buffers here too.
2985 bool success = true;
2987 ListOfBuffers clist = buf.getChildren();
2988 ListOfBuffers::const_iterator it = clist.begin();
2989 ListOfBuffers::const_iterator const bend = clist.end();
2990 for (; it != bend; ++it) {
2991 Buffer * child_buf = *it;
2992 if (theBufferList().isOthersChild(&buf, child_buf)) {
2993 child_buf->setParent(0);
2997 // FIXME: should we look in other tabworkareas?
2998 // ANSWER: I don't think so. I've tested, and if the child is
2999 // open in some other window, it closes without a problem.
3000 GuiWorkArea * child_wa = workArea(*child_buf);
3002 success = closeWorkArea(child_wa, true);
3006 // In this case the child buffer is open but hidden.
3007 // It therefore should not (MUST NOT) be dirty!
3008 LATTEST(child_buf->isClean());
3009 theBufferList().release(child_buf);
3014 // goto bookmark to update bookmark pit.
3015 // FIXME: we should update only the bookmarks related to this buffer!
3016 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3017 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3018 guiApp->gotoBookmark(i+1, false, false);
3020 if (saveBufferIfNeeded(buf, false)) {
3021 buf.removeAutosaveFile();
3022 theBufferList().release(&buf);
3026 // open all children again to avoid a crash because of dangling
3027 // pointers (bug 6603)
3033 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3035 while (twa == d.currentTabWorkArea()) {
3036 twa->setCurrentIndex(twa->count() - 1);
3038 GuiWorkArea * wa = twa->currentWorkArea();
3039 Buffer & b = wa->bufferView().buffer();
3041 // We only want to close the buffer if the same buffer is not visible
3042 // in another view, and if this is not a child and if we are closing
3043 // a view (not a tabgroup).
3044 bool const close_buffer =
3045 !inOtherView(b) && !b.parent() && closing_;
3047 if (!closeWorkArea(wa, close_buffer))
3054 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3056 if (buf.isClean() || buf.paragraphs().empty())
3059 // Switch to this Buffer.
3065 if (buf.isUnnamed()) {
3066 file = from_utf8(buf.fileName().onlyFileName());
3069 FileName filename = buf.fileName();
3071 file = filename.displayName(30);
3072 exists = filename.exists();
3075 // Bring this window to top before asking questions.
3080 if (hiding && buf.isUnnamed()) {
3081 docstring const text = bformat(_("The document %1$s has not been "
3082 "saved yet.\n\nDo you want to save "
3083 "the document?"), file);
3084 ret = Alert::prompt(_("Save new document?"),
3085 text, 0, 1, _("&Save"), _("&Cancel"));
3089 docstring const text = exists ?
3090 bformat(_("The document %1$s has unsaved changes."
3091 "\n\nDo you want to save the document or "
3092 "discard the changes?"), file) :
3093 bformat(_("The document %1$s has not been saved yet."
3094 "\n\nDo you want to save the document or "
3095 "discard it entirely?"), file);
3096 docstring const title = exists ?
3097 _("Save changed document?") : _("Save document?");
3098 ret = Alert::prompt(title, text, 0, 2,
3099 _("&Save"), _("&Discard"), _("&Cancel"));
3104 if (!saveBuffer(buf))
3108 // If we crash after this we could have no autosave file
3109 // but I guess this is really improbable (Jug).
3110 // Sometimes improbable things happen:
3111 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3112 // buf.removeAutosaveFile();
3114 // revert all changes
3125 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3127 Buffer & buf = wa->bufferView().buffer();
3129 for (int i = 0; i != d.splitter_->count(); ++i) {
3130 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3131 if (wa_ && wa_ != wa)
3134 return inOtherView(buf);
3138 bool GuiView::inOtherView(Buffer & buf)
3140 QList<int> const ids = guiApp->viewIds();
3142 for (int i = 0; i != ids.size(); ++i) {
3146 if (guiApp->view(ids[i]).workArea(buf))
3153 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3155 if (!documentBufferView())
3158 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3159 Buffer * const curbuf = &documentBufferView()->buffer();
3160 int nwa = twa->count();
3161 for (int i = 0; i < nwa; ++i) {
3162 if (&workArea(i)->bufferView().buffer() == curbuf) {
3164 if (np == NEXTBUFFER)
3165 next_index = (i == nwa - 1 ? 0 : i + 1);
3167 next_index = (i == 0 ? nwa - 1 : i - 1);
3169 twa->moveTab(i, next_index);
3171 setBuffer(&workArea(next_index)->bufferView().buffer());
3179 /// make sure the document is saved
3180 static bool ensureBufferClean(Buffer * buffer)
3182 LASSERT(buffer, return false);
3183 if (buffer->isClean() && !buffer->isUnnamed())
3186 docstring const file = buffer->fileName().displayName(30);
3189 if (!buffer->isUnnamed()) {
3190 text = bformat(_("The document %1$s has unsaved "
3191 "changes.\n\nDo you want to save "
3192 "the document?"), file);
3193 title = _("Save changed document?");
3196 text = bformat(_("The document %1$s has not been "
3197 "saved yet.\n\nDo you want to save "
3198 "the document?"), file);
3199 title = _("Save new document?");
3201 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3204 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3206 return buffer->isClean() && !buffer->isUnnamed();
3210 bool GuiView::reloadBuffer(Buffer & buf)
3212 currentBufferView()->cursor().reset();
3213 Buffer::ReadStatus status = buf.reload();
3214 return status == Buffer::ReadSuccess;
3218 void GuiView::checkExternallyModifiedBuffers()
3220 BufferList::iterator bit = theBufferList().begin();
3221 BufferList::iterator const bend = theBufferList().end();
3222 for (; bit != bend; ++bit) {
3223 Buffer * buf = *bit;
3224 if (buf->fileName().exists() && buf->isChecksumModified()) {
3225 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3226 " Reload now? Any local changes will be lost."),
3227 from_utf8(buf->absFileName()));
3228 int const ret = Alert::prompt(_("Reload externally changed document?"),
3229 text, 0, 1, _("&Reload"), _("&Cancel"));
3237 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3239 Buffer * buffer = documentBufferView()
3240 ? &(documentBufferView()->buffer()) : 0;
3242 switch (cmd.action()) {
3243 case LFUN_VC_REGISTER:
3244 if (!buffer || !ensureBufferClean(buffer))
3246 if (!buffer->lyxvc().inUse()) {
3247 if (buffer->lyxvc().registrer()) {
3248 reloadBuffer(*buffer);
3249 dr.clearMessageUpdate();
3254 case LFUN_VC_RENAME:
3255 case LFUN_VC_COPY: {
3256 if (!buffer || !ensureBufferClean(buffer))
3258 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3259 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3260 // Some changes are not yet committed.
3261 // We test here and not in getStatus(), since
3262 // this test is expensive.
3264 LyXVC::CommandResult ret =
3265 buffer->lyxvc().checkIn(log);
3267 if (ret == LyXVC::ErrorCommand ||
3268 ret == LyXVC::VCSuccess)
3269 reloadBuffer(*buffer);
3270 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3271 frontend::Alert::error(
3272 _("Revision control error."),
3273 _("Document could not be checked in."));
3277 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3278 LV_VC_RENAME : LV_VC_COPY;
3279 renameBuffer(*buffer, cmd.argument(), kind);
3284 case LFUN_VC_CHECK_IN:
3285 if (!buffer || !ensureBufferClean(buffer))
3287 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3289 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3291 // Only skip reloading if the checkin was cancelled or
3292 // an error occurred before the real checkin VCS command
3293 // was executed, since the VCS might have changed the
3294 // file even if it could not checkin successfully.
3295 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3296 reloadBuffer(*buffer);
3300 case LFUN_VC_CHECK_OUT:
3301 if (!buffer || !ensureBufferClean(buffer))
3303 if (buffer->lyxvc().inUse()) {
3304 dr.setMessage(buffer->lyxvc().checkOut());
3305 reloadBuffer(*buffer);
3309 case LFUN_VC_LOCKING_TOGGLE:
3310 LASSERT(buffer, return);
3311 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3313 if (buffer->lyxvc().inUse()) {
3314 string res = buffer->lyxvc().lockingToggle();
3316 frontend::Alert::error(_("Revision control error."),
3317 _("Error when setting the locking property."));
3320 reloadBuffer(*buffer);
3325 case LFUN_VC_REVERT:
3326 LASSERT(buffer, return);
3327 if (buffer->lyxvc().revert()) {
3328 reloadBuffer(*buffer);
3329 dr.clearMessageUpdate();
3333 case LFUN_VC_UNDO_LAST:
3334 LASSERT(buffer, return);
3335 buffer->lyxvc().undoLast();
3336 reloadBuffer(*buffer);
3337 dr.clearMessageUpdate();
3340 case LFUN_VC_REPO_UPDATE:
3341 LASSERT(buffer, return);
3342 if (ensureBufferClean(buffer)) {
3343 dr.setMessage(buffer->lyxvc().repoUpdate());
3344 checkExternallyModifiedBuffers();
3348 case LFUN_VC_COMMAND: {
3349 string flag = cmd.getArg(0);
3350 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3353 if (contains(flag, 'M')) {
3354 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3357 string path = cmd.getArg(1);
3358 if (contains(path, "$$p") && buffer)
3359 path = subst(path, "$$p", buffer->filePath());
3360 LYXERR(Debug::LYXVC, "Directory: " << path);
3362 if (!pp.isReadableDirectory()) {
3363 lyxerr << _("Directory is not accessible.") << endl;
3366 support::PathChanger p(pp);
3368 string command = cmd.getArg(2);
3369 if (command.empty())
3372 command = subst(command, "$$i", buffer->absFileName());
3373 command = subst(command, "$$p", buffer->filePath());
3375 command = subst(command, "$$m", to_utf8(message));
3376 LYXERR(Debug::LYXVC, "Command: " << command);
3378 one.startscript(Systemcall::Wait, command);
3382 if (contains(flag, 'I'))
3383 buffer->markDirty();
3384 if (contains(flag, 'R'))
3385 reloadBuffer(*buffer);
3390 case LFUN_VC_COMPARE: {
3391 if (cmd.argument().empty()) {
3392 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3396 string rev1 = cmd.getArg(0);
3401 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3404 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3405 f2 = buffer->absFileName();
3407 string rev2 = cmd.getArg(1);
3411 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3415 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3416 f1 << "\n" << f2 << "\n" );
3417 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3418 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3428 void GuiView::openChildDocument(string const & fname)
3430 LASSERT(documentBufferView(), return);
3431 Buffer & buffer = documentBufferView()->buffer();
3432 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3433 documentBufferView()->saveBookmark(false);
3435 if (theBufferList().exists(filename)) {
3436 child = theBufferList().getBuffer(filename);
3439 message(bformat(_("Opening child document %1$s..."),
3440 makeDisplayPath(filename.absFileName())));
3441 child = loadDocument(filename, false);
3443 // Set the parent name of the child document.
3444 // This makes insertion of citations and references in the child work,
3445 // when the target is in the parent or another child document.
3447 child->setParent(&buffer);
3451 bool GuiView::goToFileRow(string const & argument)
3455 size_t i = argument.find_last_of(' ');
3456 if (i != string::npos) {
3457 file_name = os::internal_path(trim(argument.substr(0, i)));
3458 istringstream is(argument.substr(i + 1));
3463 if (i == string::npos) {
3464 LYXERR0("Wrong argument: " << argument);
3468 string const abstmp = package().temp_dir().absFileName();
3469 string const realtmp = package().temp_dir().realPath();
3470 // We have to use os::path_prefix_is() here, instead of
3471 // simply prefixIs(), because the file name comes from
3472 // an external application and may need case adjustment.
3473 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3474 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3475 // Needed by inverse dvi search. If it is a file
3476 // in tmpdir, call the apropriated function.
3477 // If tmpdir is a symlink, we may have the real
3478 // path passed back, so we correct for that.
3479 if (!prefixIs(file_name, abstmp))
3480 file_name = subst(file_name, realtmp, abstmp);
3481 buf = theBufferList().getBufferFromTmp(file_name);
3483 // Must replace extension of the file to be .lyx
3484 // and get full path
3485 FileName const s = fileSearch(string(),
3486 support::changeExtension(file_name, ".lyx"), "lyx");
3487 // Either change buffer or load the file
3488 if (theBufferList().exists(s))
3489 buf = theBufferList().getBuffer(s);
3490 else if (s.exists()) {
3491 buf = loadDocument(s);
3496 _("File does not exist: %1$s"),
3497 makeDisplayPath(file_name)));
3503 _("No buffer for file: %1$s."),
3504 makeDisplayPath(file_name))
3509 bool success = documentBufferView()->setCursorFromRow(row);
3511 LYXERR(Debug::LATEX,
3512 "setCursorFromRow: invalid position for row " << row);
3513 frontend::Alert::error(_("Inverse Search Failed"),
3514 _("Invalid position requested by inverse search.\n"
3515 "You may need to update the viewed document."));
3521 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3523 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3524 menu->exec(QCursor::pos());
3529 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3531 Buffer::ExportStatus const status = func(format);
3533 // the cloning operation will have produced a clone of the entire set of
3534 // documents, starting from the master. so we must delete those.
3535 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3537 busyBuffers.remove(orig);
3542 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3544 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3545 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3549 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3551 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3552 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3556 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3558 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3559 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3563 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3564 string const & argument,
3565 Buffer const * used_buffer,
3566 docstring const & msg,
3567 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3568 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3569 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3574 string format = argument;
3576 format = used_buffer->params().getDefaultOutputFormat();
3577 processing_format = format;
3579 progress_->clearMessages();
3582 #if EXPORT_in_THREAD
3583 GuiViewPrivate::busyBuffers.insert(used_buffer);
3584 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3585 if (!cloned_buffer) {
3586 Alert::error(_("Export Error"),
3587 _("Error cloning the Buffer."));
3590 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3595 setPreviewFuture(f);
3596 last_export_format = used_buffer->params().bufferFormat();
3599 // We are asynchronous, so we don't know here anything about the success
3602 Buffer::ExportStatus status;
3604 status = (used_buffer->*syncFunc)(format, true);
3605 } else if (previewFunc) {
3606 status = (used_buffer->*previewFunc)(format);
3609 handleExportStatus(gv_, status, format);
3611 return (status == Buffer::ExportSuccess
3612 || status == Buffer::PreviewSuccess);
3616 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3618 BufferView * bv = currentBufferView();
3619 LASSERT(bv, return);
3621 // Let the current BufferView dispatch its own actions.
3622 bv->dispatch(cmd, dr);
3623 if (dr.dispatched())
3626 // Try with the document BufferView dispatch if any.
3627 BufferView * doc_bv = documentBufferView();
3628 if (doc_bv && doc_bv != bv) {
3629 doc_bv->dispatch(cmd, dr);
3630 if (dr.dispatched())
3634 // Then let the current Cursor dispatch its own actions.
3635 bv->cursor().dispatch(cmd);
3637 // update completion. We do it here and not in
3638 // processKeySym to avoid another redraw just for a
3639 // changed inline completion
3640 if (cmd.origin() == FuncRequest::KEYBOARD) {
3641 if (cmd.action() == LFUN_SELF_INSERT
3642 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3643 updateCompletion(bv->cursor(), true, true);
3644 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3645 updateCompletion(bv->cursor(), false, true);
3647 updateCompletion(bv->cursor(), false, false);
3650 dr = bv->cursor().result();
3654 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3656 BufferView * bv = currentBufferView();
3657 // By default we won't need any update.
3658 dr.screenUpdate(Update::None);
3659 // assume cmd will be dispatched
3660 dr.dispatched(true);
3662 Buffer * doc_buffer = documentBufferView()
3663 ? &(documentBufferView()->buffer()) : 0;
3665 if (cmd.origin() == FuncRequest::TOC) {
3666 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3667 // FIXME: do we need to pass a DispatchResult object here?
3668 toc->doDispatch(bv->cursor(), cmd);
3672 string const argument = to_utf8(cmd.argument());
3674 switch(cmd.action()) {
3675 case LFUN_BUFFER_CHILD_OPEN:
3676 openChildDocument(to_utf8(cmd.argument()));
3679 case LFUN_BUFFER_IMPORT:
3680 importDocument(to_utf8(cmd.argument()));
3683 case LFUN_BUFFER_EXPORT: {
3686 // GCC only sees strfwd.h when building merged
3687 if (::lyx::operator==(cmd.argument(), "custom")) {
3688 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3692 string const dest = cmd.getArg(1);
3693 FileName target_dir;
3694 if (!dest.empty() && FileName::isAbsolute(dest))
3695 target_dir = FileName(support::onlyPath(dest));
3697 target_dir = doc_buffer->fileName().onlyPath();
3699 string const format = (argument.empty() || argument == "default") ?
3700 doc_buffer->params().getDefaultOutputFormat() : argument;
3702 if ((dest.empty() && doc_buffer->isUnnamed())
3703 || !target_dir.isDirWritable()) {
3704 exportBufferAs(*doc_buffer, from_utf8(format));
3707 /* TODO/Review: Is it a problem to also export the children?
3708 See the update_unincluded flag */
3709 d.asyncBufferProcessing(format,
3712 &GuiViewPrivate::exportAndDestroy,
3715 // TODO Inform user about success
3719 case LFUN_BUFFER_EXPORT_AS: {
3720 LASSERT(doc_buffer, break);
3721 docstring f = cmd.argument();
3723 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3724 exportBufferAs(*doc_buffer, f);
3728 case LFUN_BUFFER_UPDATE: {
3729 d.asyncBufferProcessing(argument,
3732 &GuiViewPrivate::compileAndDestroy,
3737 case LFUN_BUFFER_VIEW: {
3738 d.asyncBufferProcessing(argument,
3740 _("Previewing ..."),
3741 &GuiViewPrivate::previewAndDestroy,
3746 case LFUN_MASTER_BUFFER_UPDATE: {
3747 d.asyncBufferProcessing(argument,
3748 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3750 &GuiViewPrivate::compileAndDestroy,
3755 case LFUN_MASTER_BUFFER_VIEW: {
3756 d.asyncBufferProcessing(argument,
3757 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3759 &GuiViewPrivate::previewAndDestroy,
3760 0, &Buffer::preview);
3763 case LFUN_BUFFER_SWITCH: {
3764 string const file_name = to_utf8(cmd.argument());
3765 if (!FileName::isAbsolute(file_name)) {
3767 dr.setMessage(_("Absolute filename expected."));
3771 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3774 dr.setMessage(_("Document not loaded"));
3778 // Do we open or switch to the buffer in this view ?
3779 if (workArea(*buffer)
3780 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3785 // Look for the buffer in other views
3786 QList<int> const ids = guiApp->viewIds();
3788 for (; i != ids.size(); ++i) {
3789 GuiView & gv = guiApp->view(ids[i]);
3790 if (gv.workArea(*buffer)) {
3792 gv.activateWindow();
3794 gv.setBuffer(buffer);
3799 // If necessary, open a new window as a last resort
3800 if (i == ids.size()) {
3801 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3807 case LFUN_BUFFER_NEXT:
3808 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3811 case LFUN_BUFFER_MOVE_NEXT:
3812 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3815 case LFUN_BUFFER_PREVIOUS:
3816 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3819 case LFUN_BUFFER_MOVE_PREVIOUS:
3820 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3823 case LFUN_BUFFER_CHKTEX:
3824 LASSERT(doc_buffer, break);
3825 doc_buffer->runChktex();
3828 case LFUN_COMMAND_EXECUTE: {
3829 command_execute_ = true;
3830 minibuffer_focus_ = true;
3833 case LFUN_DROP_LAYOUTS_CHOICE:
3834 d.layout_->showPopup();
3837 case LFUN_MENU_OPEN:
3838 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3839 menu->exec(QCursor::pos());
3842 case LFUN_FILE_INSERT:
3843 insertLyXFile(cmd.argument());
3846 case LFUN_FILE_INSERT_PLAINTEXT:
3847 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3848 string const fname = to_utf8(cmd.argument());
3849 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3850 dr.setMessage(_("Absolute filename expected."));
3854 FileName filename(fname);
3855 if (fname.empty()) {
3856 FileDialog dlg(qt_("Select file to insert"));
3858 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3859 QStringList(qt_("All Files (*)")));
3861 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3862 dr.setMessage(_("Canceled."));
3866 filename.set(fromqstr(result.second));
3870 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3871 bv->dispatch(new_cmd, dr);
3876 case LFUN_BUFFER_RELOAD: {
3877 LASSERT(doc_buffer, break);
3880 if (!doc_buffer->isClean()) {
3881 docstring const file =
3882 makeDisplayPath(doc_buffer->absFileName(), 20);
3883 if (doc_buffer->notifiesExternalModification()) {
3884 docstring text = _("The current version will be lost. "
3885 "Are you sure you want to load the version on disk "
3886 "of the document %1$s?");
3887 ret = Alert::prompt(_("Reload saved document?"),
3888 bformat(text, file), 1, 1,
3889 _("&Reload"), _("&Cancel"));
3891 docstring text = _("Any changes will be lost. "
3892 "Are you sure you want to revert to the saved version "
3893 "of the document %1$s?");
3894 ret = Alert::prompt(_("Revert to saved document?"),
3895 bformat(text, file), 1, 1,
3896 _("&Revert"), _("&Cancel"));
3901 doc_buffer->markClean();
3902 reloadBuffer(*doc_buffer);
3903 dr.forceBufferUpdate();
3908 case LFUN_BUFFER_WRITE:
3909 LASSERT(doc_buffer, break);
3910 saveBuffer(*doc_buffer);
3913 case LFUN_BUFFER_WRITE_AS:
3914 LASSERT(doc_buffer, break);
3915 renameBuffer(*doc_buffer, cmd.argument());
3918 case LFUN_BUFFER_WRITE_ALL: {
3919 Buffer * first = theBufferList().first();
3922 message(_("Saving all documents..."));
3923 // We cannot use a for loop as the buffer list cycles.
3926 if (!b->isClean()) {
3928 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3930 b = theBufferList().next(b);
3931 } while (b != first);
3932 dr.setMessage(_("All documents saved."));
3936 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3937 LASSERT(doc_buffer, break);
3938 doc_buffer->clearExternalModification();
3941 case LFUN_BUFFER_CLOSE:
3945 case LFUN_BUFFER_CLOSE_ALL:
3949 case LFUN_DEVEL_MODE_TOGGLE:
3950 devel_mode_ = !devel_mode_;
3952 dr.setMessage(_("Developer mode is now enabled."));
3954 dr.setMessage(_("Developer mode is now disabled."));
3957 case LFUN_TOOLBAR_TOGGLE: {
3958 string const name = cmd.getArg(0);
3959 if (GuiToolbar * t = toolbar(name))
3964 case LFUN_TOOLBAR_MOVABLE: {
3965 string const name = cmd.getArg(0);
3967 // toggle (all) toolbars movablility
3968 toolbarsMovable_ = !toolbarsMovable_;
3969 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3970 GuiToolbar * tb = toolbar(ti.name);
3971 if (tb && tb->isMovable() != toolbarsMovable_)
3972 // toggle toolbar movablity if it does not fit lock
3973 // (all) toolbars positions state silent = true, since
3974 // status bar notifications are slow
3977 if (toolbarsMovable_)
3978 dr.setMessage(_("Toolbars unlocked."));
3980 dr.setMessage(_("Toolbars locked."));
3981 } else if (GuiToolbar * t = toolbar(name)) {
3982 // toggle current toolbar movablity
3984 // update lock (all) toolbars positions
3985 updateLockToolbars();
3990 case LFUN_ICON_SIZE: {
3991 QSize size = d.iconSize(cmd.argument());
3993 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3994 size.width(), size.height()));
3998 case LFUN_DIALOG_UPDATE: {
3999 string const name = to_utf8(cmd.argument());
4000 if (name == "prefs" || name == "document")
4001 updateDialog(name, string());
4002 else if (name == "paragraph")
4003 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4004 else if (currentBufferView()) {
4005 Inset * inset = currentBufferView()->editedInset(name);
4006 // Can only update a dialog connected to an existing inset
4008 // FIXME: get rid of this indirection; GuiView ask the inset
4009 // if he is kind enough to update itself...
4010 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4011 //FIXME: pass DispatchResult here?
4012 inset->dispatch(currentBufferView()->cursor(), fr);
4018 case LFUN_DIALOG_TOGGLE: {
4019 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4020 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4021 dispatch(FuncRequest(func_code, cmd.argument()), dr);
4025 case LFUN_DIALOG_DISCONNECT_INSET:
4026 disconnectDialog(to_utf8(cmd.argument()));
4029 case LFUN_DIALOG_HIDE: {
4030 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4034 case LFUN_DIALOG_SHOW: {
4035 string const name = cmd.getArg(0);
4036 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4038 if (name == "character") {
4039 data = freefont2string();
4041 showDialog("character", data);
4042 } else if (name == "latexlog") {
4043 // getStatus checks that
4044 LATTEST(doc_buffer);
4045 Buffer::LogType type;
4046 string const logfile = doc_buffer->logName(&type);
4048 case Buffer::latexlog:
4051 case Buffer::buildlog:
4055 data += Lexer::quoteString(logfile);
4056 showDialog("log", data);
4057 } else if (name == "vclog") {
4058 // getStatus checks that
4059 LATTEST(doc_buffer);
4060 string const data = "vc " +
4061 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4062 showDialog("log", data);
4063 } else if (name == "symbols") {
4064 data = bv->cursor().getEncoding()->name();
4066 showDialog("symbols", data);
4068 } else if (name == "prefs" && isFullScreen()) {
4069 lfunUiToggle("fullscreen");
4070 showDialog("prefs", data);
4072 showDialog(name, data);
4077 dr.setMessage(cmd.argument());
4080 case LFUN_UI_TOGGLE: {
4081 string arg = cmd.getArg(0);
4082 if (!lfunUiToggle(arg)) {
4083 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4084 dr.setMessage(bformat(msg, from_utf8(arg)));
4086 // Make sure the keyboard focus stays in the work area.
4091 case LFUN_VIEW_SPLIT: {
4092 LASSERT(doc_buffer, break);
4093 string const orientation = cmd.getArg(0);
4094 d.splitter_->setOrientation(orientation == "vertical"
4095 ? Qt::Vertical : Qt::Horizontal);
4096 TabWorkArea * twa = addTabWorkArea();
4097 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4098 setCurrentWorkArea(wa);
4101 case LFUN_TAB_GROUP_CLOSE:
4102 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4103 closeTabWorkArea(twa);
4104 d.current_work_area_ = 0;
4105 twa = d.currentTabWorkArea();
4106 // Switch to the next GuiWorkArea in the found TabWorkArea.
4108 // Make sure the work area is up to date.
4109 setCurrentWorkArea(twa->currentWorkArea());
4111 setCurrentWorkArea(0);
4116 case LFUN_VIEW_CLOSE:
4117 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4118 closeWorkArea(twa->currentWorkArea());
4119 d.current_work_area_ = 0;
4120 twa = d.currentTabWorkArea();
4121 // Switch to the next GuiWorkArea in the found TabWorkArea.
4123 // Make sure the work area is up to date.
4124 setCurrentWorkArea(twa->currentWorkArea());
4126 setCurrentWorkArea(0);
4131 case LFUN_COMPLETION_INLINE:
4132 if (d.current_work_area_)
4133 d.current_work_area_->completer().showInline();
4136 case LFUN_COMPLETION_POPUP:
4137 if (d.current_work_area_)
4138 d.current_work_area_->completer().showPopup();
4143 if (d.current_work_area_)
4144 d.current_work_area_->completer().tab();
4147 case LFUN_COMPLETION_CANCEL:
4148 if (d.current_work_area_) {
4149 if (d.current_work_area_->completer().popupVisible())
4150 d.current_work_area_->completer().hidePopup();
4152 d.current_work_area_->completer().hideInline();
4156 case LFUN_COMPLETION_ACCEPT:
4157 if (d.current_work_area_)
4158 d.current_work_area_->completer().activate();
4161 case LFUN_BUFFER_ZOOM_IN:
4162 case LFUN_BUFFER_ZOOM_OUT:
4163 case LFUN_BUFFER_ZOOM: {
4164 if (cmd.argument().empty()) {
4165 if (cmd.action() == LFUN_BUFFER_ZOOM)
4167 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4172 if (cmd.action() == LFUN_BUFFER_ZOOM)
4173 zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4174 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4175 zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4177 zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4180 // Actual zoom value: default zoom + fractional extra value
4181 int zoom = lyxrc.defaultZoom * zoom_ratio_;
4182 if (zoom < static_cast<int>(zoom_min_))
4185 lyxrc.currentZoom = zoom;
4187 dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4188 lyxrc.currentZoom, lyxrc.defaultZoom));
4190 // The global QPixmapCache is used in GuiPainter to cache text
4191 // painting so we must reset it.
4192 QPixmapCache::clear();
4193 guiApp->fontLoader().update();
4194 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4198 case LFUN_VC_REGISTER:
4199 case LFUN_VC_RENAME:
4201 case LFUN_VC_CHECK_IN:
4202 case LFUN_VC_CHECK_OUT:
4203 case LFUN_VC_REPO_UPDATE:
4204 case LFUN_VC_LOCKING_TOGGLE:
4205 case LFUN_VC_REVERT:
4206 case LFUN_VC_UNDO_LAST:
4207 case LFUN_VC_COMMAND:
4208 case LFUN_VC_COMPARE:
4209 dispatchVC(cmd, dr);
4212 case LFUN_SERVER_GOTO_FILE_ROW:
4213 if(goToFileRow(to_utf8(cmd.argument())))
4214 dr.screenUpdate(Update::Force | Update::FitCursor);
4217 case LFUN_LYX_ACTIVATE:
4221 case LFUN_FORWARD_SEARCH: {
4222 // it seems safe to assume we have a document buffer, since
4223 // getStatus wants one.
4224 LATTEST(doc_buffer);
4225 Buffer const * doc_master = doc_buffer->masterBuffer();
4226 FileName const path(doc_master->temppath());
4227 string const texname = doc_master->isChild(doc_buffer)
4228 ? DocFileName(changeExtension(
4229 doc_buffer->absFileName(),
4230 "tex")).mangledFileName()
4231 : doc_buffer->latexName();
4232 string const fulltexname =
4233 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4234 string const mastername =
4235 removeExtension(doc_master->latexName());
4236 FileName const dviname(addName(path.absFileName(),
4237 addExtension(mastername, "dvi")));
4238 FileName const pdfname(addName(path.absFileName(),
4239 addExtension(mastername, "pdf")));
4240 bool const have_dvi = dviname.exists();
4241 bool const have_pdf = pdfname.exists();
4242 if (!have_dvi && !have_pdf) {
4243 dr.setMessage(_("Please, preview the document first."));
4246 string outname = dviname.onlyFileName();
4247 string command = lyxrc.forward_search_dvi;
4248 if (!have_dvi || (have_pdf &&
4249 pdfname.lastModified() > dviname.lastModified())) {
4250 outname = pdfname.onlyFileName();
4251 command = lyxrc.forward_search_pdf;
4254 DocIterator cur = bv->cursor();
4255 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4256 LYXERR(Debug::ACTION, "Forward search: row:" << row
4258 if (row == -1 || command.empty()) {
4259 dr.setMessage(_("Couldn't proceed."));
4262 string texrow = convert<string>(row);
4264 command = subst(command, "$$n", texrow);
4265 command = subst(command, "$$f", fulltexname);
4266 command = subst(command, "$$t", texname);
4267 command = subst(command, "$$o", outname);
4269 PathChanger p(path);
4271 one.startscript(Systemcall::DontWait, command);
4275 case LFUN_SPELLING_CONTINUOUSLY:
4276 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4277 dr.screenUpdate(Update::Force);
4281 // The LFUN must be for one of BufferView, Buffer or Cursor;
4283 dispatchToBufferView(cmd, dr);
4287 // Part of automatic menu appearance feature.
4288 if (isFullScreen()) {
4289 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4293 // Need to update bv because many LFUNs here might have destroyed it
4294 bv = currentBufferView();
4296 // Clear non-empty selections
4297 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4299 Cursor & cur = bv->cursor();
4300 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4301 cur.clearSelection();
4307 bool GuiView::lfunUiToggle(string const & ui_component)
4309 if (ui_component == "scrollbar") {
4310 // hide() is of no help
4311 if (d.current_work_area_->verticalScrollBarPolicy() ==
4312 Qt::ScrollBarAlwaysOff)
4314 d.current_work_area_->setVerticalScrollBarPolicy(
4315 Qt::ScrollBarAsNeeded);
4317 d.current_work_area_->setVerticalScrollBarPolicy(
4318 Qt::ScrollBarAlwaysOff);
4319 } else if (ui_component == "statusbar") {
4320 statusBar()->setVisible(!statusBar()->isVisible());
4321 } else if (ui_component == "menubar") {
4322 menuBar()->setVisible(!menuBar()->isVisible());
4324 if (ui_component == "frame") {
4326 getContentsMargins(&l, &t, &r, &b);
4327 //are the frames in default state?
4328 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4330 setContentsMargins(-2, -2, -2, -2);
4332 setContentsMargins(0, 0, 0, 0);
4335 if (ui_component == "fullscreen") {
4343 void GuiView::toggleFullScreen()
4345 if (isFullScreen()) {
4346 for (int i = 0; i != d.splitter_->count(); ++i)
4347 d.tabWorkArea(i)->setFullScreen(false);
4348 setContentsMargins(0, 0, 0, 0);
4349 setWindowState(windowState() ^ Qt::WindowFullScreen);
4352 statusBar()->show();
4355 hideDialogs("prefs", 0);
4356 for (int i = 0; i != d.splitter_->count(); ++i)
4357 d.tabWorkArea(i)->setFullScreen(true);
4358 setContentsMargins(-2, -2, -2, -2);
4360 setWindowState(windowState() ^ Qt::WindowFullScreen);
4361 if (lyxrc.full_screen_statusbar)
4362 statusBar()->hide();
4363 if (lyxrc.full_screen_menubar)
4365 if (lyxrc.full_screen_toolbars) {
4366 ToolbarMap::iterator end = d.toolbars_.end();
4367 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4372 // give dialogs like the TOC a chance to adapt
4377 Buffer const * GuiView::updateInset(Inset const * inset)
4382 Buffer const * inset_buffer = &(inset->buffer());
4384 for (int i = 0; i != d.splitter_->count(); ++i) {
4385 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4388 Buffer const * buffer = &(wa->bufferView().buffer());
4389 if (inset_buffer == buffer)
4390 wa->scheduleRedraw();
4392 return inset_buffer;
4396 void GuiView::restartCursor()
4398 /* When we move around, or type, it's nice to be able to see
4399 * the cursor immediately after the keypress.
4401 if (d.current_work_area_)
4402 d.current_work_area_->startBlinkingCursor();
4404 // Take this occasion to update the other GUI elements.
4410 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4412 if (d.current_work_area_)
4413 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4418 // This list should be kept in sync with the list of insets in
4419 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4420 // dialog should have the same name as the inset.
4421 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4422 // docs in LyXAction.cpp.
4424 char const * const dialognames[] = {
4426 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4427 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4428 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4429 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4430 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4431 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4432 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4433 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4435 char const * const * const end_dialognames =
4436 dialognames + (sizeof(dialognames) / sizeof(char *));
4440 cmpCStr(char const * name) : name_(name) {}
4441 bool operator()(char const * other) {
4442 return strcmp(other, name_) == 0;
4449 bool isValidName(string const & name)
4451 return find_if(dialognames, end_dialognames,
4452 cmpCStr(name.c_str())) != end_dialognames;
4458 void GuiView::resetDialogs()
4460 // Make sure that no LFUN uses any GuiView.
4461 guiApp->setCurrentView(0);
4465 constructToolbars();
4466 guiApp->menus().fillMenuBar(menuBar(), this, false);
4467 d.layout_->updateContents(true);
4468 // Now update controls with current buffer.
4469 guiApp->setCurrentView(this);
4475 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4477 if (!isValidName(name))
4480 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4482 if (it != d.dialogs_.end()) {
4484 it->second->hideView();
4485 return it->second.get();
4488 Dialog * dialog = build(name);
4489 d.dialogs_[name].reset(dialog);
4490 if (lyxrc.allow_geometry_session)
4491 dialog->restoreSession();
4498 void GuiView::showDialog(string const & name, string const & data,
4501 triggerShowDialog(toqstr(name), toqstr(data), inset);
4505 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4511 const string name = fromqstr(qname);
4512 const string data = fromqstr(qdata);
4516 Dialog * dialog = findOrBuild(name, false);
4518 bool const visible = dialog->isVisibleView();
4519 dialog->showData(data);
4520 if (inset && currentBufferView())
4521 currentBufferView()->editInset(name, inset);
4522 // We only set the focus to the new dialog if it was not yet
4523 // visible in order not to change the existing previous behaviour
4525 // activateWindow is needed for floating dockviews
4526 dialog->asQWidget()->raise();
4527 dialog->asQWidget()->activateWindow();
4528 dialog->asQWidget()->setFocus();
4532 catch (ExceptionMessage const & ex) {
4540 bool GuiView::isDialogVisible(string const & name) const
4542 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4543 if (it == d.dialogs_.end())
4545 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4549 void GuiView::hideDialog(string const & name, Inset * inset)
4551 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4552 if (it == d.dialogs_.end())
4556 if (!currentBufferView())
4558 if (inset != currentBufferView()->editedInset(name))
4562 Dialog * const dialog = it->second.get();
4563 if (dialog->isVisibleView())
4565 if (currentBufferView())
4566 currentBufferView()->editInset(name, 0);
4570 void GuiView::disconnectDialog(string const & name)
4572 if (!isValidName(name))
4574 if (currentBufferView())
4575 currentBufferView()->editInset(name, 0);
4579 void GuiView::hideAll() const
4581 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4582 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4584 for(; it != end; ++it)
4585 it->second->hideView();
4589 void GuiView::updateDialogs()
4591 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4592 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4594 for(; it != end; ++it) {
4595 Dialog * dialog = it->second.get();
4597 if (dialog->needBufferOpen() && !documentBufferView())
4598 hideDialog(fromqstr(dialog->name()), 0);
4599 else if (dialog->isVisibleView())
4600 dialog->checkStatus();
4607 Dialog * createDialog(GuiView & lv, string const & name);
4609 // will be replaced by a proper factory...
4610 Dialog * createGuiAbout(GuiView & lv);
4611 Dialog * createGuiBibtex(GuiView & lv);
4612 Dialog * createGuiChanges(GuiView & lv);
4613 Dialog * createGuiCharacter(GuiView & lv);
4614 Dialog * createGuiCitation(GuiView & lv);
4615 Dialog * createGuiCompare(GuiView & lv);
4616 Dialog * createGuiCompareHistory(GuiView & lv);
4617 Dialog * createGuiDelimiter(GuiView & lv);
4618 Dialog * createGuiDocument(GuiView & lv);
4619 Dialog * createGuiErrorList(GuiView & lv);
4620 Dialog * createGuiExternal(GuiView & lv);
4621 Dialog * createGuiGraphics(GuiView & lv);
4622 Dialog * createGuiInclude(GuiView & lv);
4623 Dialog * createGuiIndex(GuiView & lv);
4624 Dialog * createGuiListings(GuiView & lv);
4625 Dialog * createGuiLog(GuiView & lv);
4626 Dialog * createGuiMathMatrix(GuiView & lv);
4627 Dialog * createGuiNote(GuiView & lv);
4628 Dialog * createGuiParagraph(GuiView & lv);
4629 Dialog * createGuiPhantom(GuiView & lv);
4630 Dialog * createGuiPreferences(GuiView & lv);
4631 Dialog * createGuiPrint(GuiView & lv);
4632 Dialog * createGuiPrintindex(GuiView & lv);
4633 Dialog * createGuiRef(GuiView & lv);
4634 Dialog * createGuiSearch(GuiView & lv);
4635 Dialog * createGuiSearchAdv(GuiView & lv);
4636 Dialog * createGuiSendTo(GuiView & lv);
4637 Dialog * createGuiShowFile(GuiView & lv);
4638 Dialog * createGuiSpellchecker(GuiView & lv);
4639 Dialog * createGuiSymbols(GuiView & lv);
4640 Dialog * createGuiTabularCreate(GuiView & lv);
4641 Dialog * createGuiTexInfo(GuiView & lv);
4642 Dialog * createGuiToc(GuiView & lv);
4643 Dialog * createGuiThesaurus(GuiView & lv);
4644 Dialog * createGuiViewSource(GuiView & lv);
4645 Dialog * createGuiWrap(GuiView & lv);
4646 Dialog * createGuiProgressView(GuiView & lv);
4650 Dialog * GuiView::build(string const & name)
4652 LASSERT(isValidName(name), return 0);
4654 Dialog * dialog = createDialog(*this, name);
4658 if (name == "aboutlyx")
4659 return createGuiAbout(*this);
4660 if (name == "bibtex")
4661 return createGuiBibtex(*this);
4662 if (name == "changes")
4663 return createGuiChanges(*this);
4664 if (name == "character")
4665 return createGuiCharacter(*this);
4666 if (name == "citation")
4667 return createGuiCitation(*this);
4668 if (name == "compare")
4669 return createGuiCompare(*this);
4670 if (name == "comparehistory")
4671 return createGuiCompareHistory(*this);
4672 if (name == "document")
4673 return createGuiDocument(*this);
4674 if (name == "errorlist")
4675 return createGuiErrorList(*this);
4676 if (name == "external")
4677 return createGuiExternal(*this);
4679 return createGuiShowFile(*this);
4680 if (name == "findreplace")
4681 return createGuiSearch(*this);
4682 if (name == "findreplaceadv")
4683 return createGuiSearchAdv(*this);
4684 if (name == "graphics")
4685 return createGuiGraphics(*this);
4686 if (name == "include")
4687 return createGuiInclude(*this);
4688 if (name == "index")
4689 return createGuiIndex(*this);
4690 if (name == "index_print")
4691 return createGuiPrintindex(*this);
4692 if (name == "listings")
4693 return createGuiListings(*this);
4695 return createGuiLog(*this);
4696 if (name == "mathdelimiter")
4697 return createGuiDelimiter(*this);
4698 if (name == "mathmatrix")
4699 return createGuiMathMatrix(*this);
4701 return createGuiNote(*this);
4702 if (name == "paragraph")
4703 return createGuiParagraph(*this);
4704 if (name == "phantom")
4705 return createGuiPhantom(*this);
4706 if (name == "prefs")
4707 return createGuiPreferences(*this);
4709 return createGuiRef(*this);
4710 if (name == "sendto")
4711 return createGuiSendTo(*this);
4712 if (name == "spellchecker")
4713 return createGuiSpellchecker(*this);
4714 if (name == "symbols")
4715 return createGuiSymbols(*this);
4716 if (name == "tabularcreate")
4717 return createGuiTabularCreate(*this);
4718 if (name == "texinfo")
4719 return createGuiTexInfo(*this);
4720 if (name == "thesaurus")
4721 return createGuiThesaurus(*this);
4723 return createGuiToc(*this);
4724 if (name == "view-source")
4725 return createGuiViewSource(*this);
4727 return createGuiWrap(*this);
4728 if (name == "progress")
4729 return createGuiProgressView(*this);
4735 SEMenu::SEMenu(QWidget * parent)
4737 QAction * action = addAction(qt_("Disable Shell Escape"));
4738 connect(action, SIGNAL(triggered()),
4739 parent, SLOT(disableShellEscape()));
4743 } // namespace frontend
4746 #include "moc_GuiView.cpp"