3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
386 QSize iconSize(docstring const & icon_size)
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
395 else if (icon_size == "huge")
397 else if (icon_size == "giant")
398 size = giantIconSize;
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
402 if (size < smallIconSize)
403 size = smallIconSize;
405 return QSize(size, size);
408 QSize iconSize(QString const & icon_size)
410 return iconSize(qstring_to_ucs4(icon_size));
413 string & iconSize(QSize const & qsize)
415 LATTEST(qsize.width() == qsize.height());
417 static string icon_size;
419 unsigned int size = qsize.width();
421 if (size < smallIconSize)
422 size = smallIconSize;
424 if (size == smallIconSize)
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
430 else if (size == hugeIconSize)
432 else if (size == giantIconSize)
435 icon_size = convert<string>(size);
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
452 * \warning Don't Delete! The layout box is actually owned by
453 * whichever toolbar contains it. All the GuiView class needs is a
454 * means of accessing it.
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
462 map<string, DialogPtr> dialogs_;
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
470 QTimer statusbar_timer_;
471 /// auto-saving of buffers
472 Timeout autosave_timeout_;
473 /// flag against a race condition due to multiclicks, see bug #1119
477 TocModels toc_models_;
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
483 string last_export_format;
484 string processing_format;
486 static QSet<Buffer const *> busyBuffers;
487 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
495 // TODO syncFunc/previewFunc: use bind
496 bool asyncBufferProcessing(string const & argument,
497 Buffer const * used_buffer,
498 docstring const & msg,
499 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
503 QVector<GuiWorkArea*> guiWorkAreas();
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
509 GuiView::GuiView(int id)
510 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511 command_execute_(false), minibuffer_focus_(false), devel_mode_(false)
513 connect(this, SIGNAL(bufferViewChanged()),
514 this, SLOT(onBufferViewChanged()));
516 // GuiToolbars *must* be initialised before the menu bar.
517 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 // The connection is closed when this is destroyed.
533 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535 d.autosave_timeout_.start();
537 connect(&d.statusbar_timer_, SIGNAL(timeout()),
538 this, SLOT(clearMessage()));
540 // We don't want to keep the window in memory if it is closed.
541 setAttribute(Qt::WA_DeleteOnClose, true);
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544 // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547 // since the icon is provided in the application bundle. We use a themed
548 // version when available and use the bundled one as fallback.
549 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
551 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
557 // use tabbed dock area for multiple docks
558 // (such as "source" and "messages")
559 setDockOptions(QMainWindow::ForceTabbedDocks);
562 setAcceptDrops(true);
564 // add busy indicator to statusbar
565 QLabel * busylabel = new QLabel(statusBar());
566 statusBar()->addPermanentWidget(busylabel);
567 search_mode mode = theGuiApp()->imageSearchMode();
568 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570 busylabel->setMovie(busyanim);
574 connect(&d.processing_thread_watcher_, SIGNAL(started()),
575 busylabel, SLOT(show()));
576 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577 busylabel, SLOT(hide()));
579 QFontMetrics const fm(statusBar()->fontMetrics());
580 int const iconheight = max(int(d.normalIconSize), fm.height());
581 QSize const iconsize(iconheight, iconheight);
583 QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
584 shell_escape_ = new QLabel(statusBar());
585 shell_escape_->setPixmap(shellescape);
586 shell_escape_->setScaledContents(true);
587 shell_escape_->setAlignment(Qt::AlignCenter);
588 shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
589 shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
590 "external commands for this document. "
591 "Right click to change."));
592 SEMenu * menu = new SEMenu(this);
593 connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
594 menu, SLOT(showMenu(QPoint)));
595 shell_escape_->hide();
596 statusBar()->addPermanentWidget(shell_escape_);
598 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
599 read_only_ = new QLabel(statusBar());
600 read_only_->setPixmap(readonly);
601 read_only_->setScaledContents(true);
602 read_only_->setAlignment(Qt::AlignCenter);
604 statusBar()->addPermanentWidget(read_only_);
606 version_control_ = new QLabel(statusBar());
607 version_control_->setAlignment(Qt::AlignCenter);
608 version_control_->setFrameStyle(QFrame::StyledPanel);
609 version_control_->hide();
610 statusBar()->addPermanentWidget(version_control_);
612 statusBar()->setSizeGripEnabled(true);
615 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
616 SLOT(autoSaveThreadFinished()));
618 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
619 SLOT(processingThreadStarted()));
620 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
621 SLOT(processingThreadFinished()));
623 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
624 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
626 // set custom application bars context menu, e.g. tool bar and menu bar
627 setContextMenuPolicy(Qt::CustomContextMenu);
628 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
629 SLOT(toolBarPopup(const QPoint &)));
631 // Forbid too small unresizable window because it can happen
632 // with some window manager under X11.
633 setMinimumSize(300, 200);
635 if (lyxrc.allow_geometry_session) {
636 // Now take care of session management.
641 // no session handling, default to a sane size.
642 setGeometry(50, 50, 690, 510);
645 // clear session data if any.
647 settings.remove("views");
657 void GuiView::disableShellEscape()
659 BufferView * bv = documentBufferView();
662 theSession().shellescapeFiles().remove(bv->buffer().absFileName());
663 bv->buffer().params().shell_escape = false;
664 bv->processUpdateFlags(Update::Force);
668 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
670 QVector<GuiWorkArea*> areas;
671 for (int i = 0; i < tabWorkAreaCount(); i++) {
672 TabWorkArea* ta = tabWorkArea(i);
673 for (int u = 0; u < ta->count(); u++) {
674 areas << ta->workArea(u);
680 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
681 string const & format)
683 docstring const fmt = theFormats().prettyName(format);
686 case Buffer::ExportSuccess:
687 msg = bformat(_("Successful export to format: %1$s"), fmt);
689 case Buffer::ExportCancel:
690 msg = _("Document export cancelled.");
692 case Buffer::ExportError:
693 case Buffer::ExportNoPathToFormat:
694 case Buffer::ExportTexPathHasSpaces:
695 case Buffer::ExportConverterError:
696 msg = bformat(_("Error while exporting format: %1$s"), fmt);
698 case Buffer::PreviewSuccess:
699 msg = bformat(_("Successful preview of format: %1$s"), fmt);
701 case Buffer::PreviewError:
702 msg = bformat(_("Error while previewing format: %1$s"), fmt);
709 void GuiView::processingThreadStarted()
714 void GuiView::processingThreadFinished()
716 QFutureWatcher<Buffer::ExportStatus> const * watcher =
717 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
719 Buffer::ExportStatus const status = watcher->result();
720 handleExportStatus(this, status, d.processing_format);
723 BufferView const * const bv = currentBufferView();
724 if (bv && !bv->buffer().errorList("Export").empty()) {
728 errors(d.last_export_format);
732 void GuiView::autoSaveThreadFinished()
734 QFutureWatcher<docstring> const * watcher =
735 static_cast<QFutureWatcher<docstring> const *>(sender());
736 message(watcher->result());
741 void GuiView::saveLayout() const
744 settings.setValue("zoom", lyxrc.currentZoom);
745 settings.setValue("devel_mode", devel_mode_);
746 settings.beginGroup("views");
747 settings.beginGroup(QString::number(id_));
748 #if defined(Q_WS_X11) || defined(QPA_XCB)
749 settings.setValue("pos", pos());
750 settings.setValue("size", size());
752 settings.setValue("geometry", saveGeometry());
754 settings.setValue("layout", saveState(0));
755 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
759 void GuiView::saveUISettings() const
761 // Save the toolbar private states
762 ToolbarMap::iterator end = d.toolbars_.end();
763 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
764 it->second->saveSession();
765 // Now take care of all other dialogs
766 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
767 for (; it!= d.dialogs_.end(); ++it)
768 it->second->saveSession();
772 bool GuiView::restoreLayout()
775 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
776 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
777 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
778 settings.beginGroup("views");
779 settings.beginGroup(QString::number(id_));
780 QString const icon_key = "icon_size";
781 if (!settings.contains(icon_key))
784 //code below is skipped when when ~/.config/LyX is (re)created
785 setIconSize(d.iconSize(settings.value(icon_key).toString()));
787 #if defined(Q_WS_X11) || defined(QPA_XCB)
788 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
789 QSize size = settings.value("size", QSize(690, 510)).toSize();
793 // Work-around for bug #6034: the window ends up in an undetermined
794 // state when trying to restore a maximized window when it is
795 // already maximized.
796 if (!(windowState() & Qt::WindowMaximized))
797 if (!restoreGeometry(settings.value("geometry").toByteArray()))
798 setGeometry(50, 50, 690, 510);
800 // Make sure layout is correctly oriented.
801 setLayoutDirection(qApp->layoutDirection());
803 // Allow the toc and view-source dock widget to be restored if needed.
805 if ((dialog = findOrBuild("toc", true)))
806 // see bug 5082. At least setup title and enabled state.
807 // Visibility will be adjusted by restoreState below.
808 dialog->prepareView();
809 if ((dialog = findOrBuild("view-source", true)))
810 dialog->prepareView();
811 if ((dialog = findOrBuild("progress", true)))
812 dialog->prepareView();
814 if (!restoreState(settings.value("layout").toByteArray(), 0))
817 // init the toolbars that have not been restored
818 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
819 Toolbars::Infos::iterator end = guiApp->toolbars().end();
820 for (; cit != end; ++cit) {
821 GuiToolbar * tb = toolbar(cit->name);
822 if (tb && !tb->isRestored())
823 initToolbar(cit->name);
826 // update lock (all) toolbars positions
827 updateLockToolbars();
834 GuiToolbar * GuiView::toolbar(string const & name)
836 ToolbarMap::iterator it = d.toolbars_.find(name);
837 if (it != d.toolbars_.end())
840 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
845 void GuiView::updateLockToolbars()
847 toolbarsMovable_ = false;
848 for (ToolbarInfo const & info : guiApp->toolbars()) {
849 GuiToolbar * tb = toolbar(info.name);
850 if (tb && tb->isMovable())
851 toolbarsMovable_ = true;
856 void GuiView::constructToolbars()
858 ToolbarMap::iterator it = d.toolbars_.begin();
859 for (; it != d.toolbars_.end(); ++it)
863 // I don't like doing this here, but the standard toolbar
864 // destroys this object when it's destroyed itself (vfr)
865 d.layout_ = new LayoutBox(*this);
866 d.stack_widget_->addWidget(d.layout_);
867 d.layout_->move(0,0);
869 // extracts the toolbars from the backend
870 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
871 Toolbars::Infos::iterator end = guiApp->toolbars().end();
872 for (; cit != end; ++cit)
873 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
877 void GuiView::initToolbars()
879 // extracts the toolbars from the backend
880 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
881 Toolbars::Infos::iterator end = guiApp->toolbars().end();
882 for (; cit != end; ++cit)
883 initToolbar(cit->name);
887 void GuiView::initToolbar(string const & name)
889 GuiToolbar * tb = toolbar(name);
892 int const visibility = guiApp->toolbars().defaultVisibility(name);
893 bool newline = !(visibility & Toolbars::SAMEROW);
894 tb->setVisible(false);
895 tb->setVisibility(visibility);
897 if (visibility & Toolbars::TOP) {
899 addToolBarBreak(Qt::TopToolBarArea);
900 addToolBar(Qt::TopToolBarArea, tb);
903 if (visibility & Toolbars::BOTTOM) {
905 addToolBarBreak(Qt::BottomToolBarArea);
906 addToolBar(Qt::BottomToolBarArea, tb);
909 if (visibility & Toolbars::LEFT) {
911 addToolBarBreak(Qt::LeftToolBarArea);
912 addToolBar(Qt::LeftToolBarArea, tb);
915 if (visibility & Toolbars::RIGHT) {
917 addToolBarBreak(Qt::RightToolBarArea);
918 addToolBar(Qt::RightToolBarArea, tb);
921 if (visibility & Toolbars::ON)
922 tb->setVisible(true);
924 tb->setMovable(true);
928 TocModels & GuiView::tocModels()
930 return d.toc_models_;
934 void GuiView::setFocus()
936 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
937 QMainWindow::setFocus();
941 bool GuiView::hasFocus() const
943 if (currentWorkArea())
944 return currentWorkArea()->hasFocus();
945 if (currentMainWorkArea())
946 return currentMainWorkArea()->hasFocus();
947 return d.bg_widget_->hasFocus();
951 void GuiView::focusInEvent(QFocusEvent * e)
953 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
954 QMainWindow::focusInEvent(e);
955 // Make sure guiApp points to the correct view.
956 guiApp->setCurrentView(this);
957 if (currentWorkArea())
958 currentWorkArea()->setFocus();
959 else if (currentMainWorkArea())
960 currentMainWorkArea()->setFocus();
962 d.bg_widget_->setFocus();
966 void GuiView::showEvent(QShowEvent * e)
968 LYXERR(Debug::GUI, "Passed Geometry "
969 << size().height() << "x" << size().width()
970 << "+" << pos().x() << "+" << pos().y());
972 if (d.splitter_->count() == 0)
973 // No work area, switch to the background widget.
977 QMainWindow::showEvent(e);
981 bool GuiView::closeScheduled()
988 bool GuiView::prepareAllBuffersForLogout()
990 Buffer * first = theBufferList().first();
994 // First, iterate over all buffers and ask the users if unsaved
995 // changes should be saved.
996 // We cannot use a for loop as the buffer list cycles.
999 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1001 b = theBufferList().next(b);
1002 } while (b != first);
1004 // Next, save session state
1005 // When a view/window was closed before without quitting LyX, there
1006 // are already entries in the lastOpened list.
1007 theSession().lastOpened().clear();
1014 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1015 ** is responsibility of the container (e.g., dialog)
1017 void GuiView::closeEvent(QCloseEvent * close_event)
1019 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1021 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1022 Alert::warning(_("Exit LyX"),
1023 _("LyX could not be closed because documents are being processed by LyX."));
1024 close_event->setAccepted(false);
1028 // If the user pressed the x (so we didn't call closeView
1029 // programmatically), we want to clear all existing entries.
1031 theSession().lastOpened().clear();
1036 // it can happen that this event arrives without selecting the view,
1037 // e.g. when clicking the close button on a background window.
1039 if (!closeWorkAreaAll()) {
1041 close_event->ignore();
1045 // Make sure that nothing will use this to be closed View.
1046 guiApp->unregisterView(this);
1048 if (isFullScreen()) {
1049 // Switch off fullscreen before closing.
1054 // Make sure the timer time out will not trigger a statusbar update.
1055 d.statusbar_timer_.stop();
1057 // Saving fullscreen requires additional tweaks in the toolbar code.
1058 // It wouldn't also work under linux natively.
1059 if (lyxrc.allow_geometry_session) {
1064 close_event->accept();
1068 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1070 if (event->mimeData()->hasUrls())
1072 /// \todo Ask lyx-devel is this is enough:
1073 /// if (event->mimeData()->hasFormat("text/plain"))
1074 /// event->acceptProposedAction();
1078 void GuiView::dropEvent(QDropEvent * event)
1080 QList<QUrl> files = event->mimeData()->urls();
1081 if (files.isEmpty())
1084 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1085 for (int i = 0; i != files.size(); ++i) {
1086 string const file = os::internal_path(fromqstr(
1087 files.at(i).toLocalFile()));
1091 string const ext = support::getExtension(file);
1092 vector<const Format *> found_formats;
1094 // Find all formats that have the correct extension.
1095 vector<const Format *> const & import_formats
1096 = theConverters().importableFormats();
1097 vector<const Format *>::const_iterator it = import_formats.begin();
1098 for (; it != import_formats.end(); ++it)
1099 if ((*it)->hasExtension(ext))
1100 found_formats.push_back(*it);
1103 if (found_formats.size() >= 1) {
1104 if (found_formats.size() > 1) {
1105 //FIXME: show a dialog to choose the correct importable format
1106 LYXERR(Debug::FILES,
1107 "Multiple importable formats found, selecting first");
1109 string const arg = found_formats[0]->name() + " " + file;
1110 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1113 //FIXME: do we have to explicitly check whether it's a lyx file?
1114 LYXERR(Debug::FILES,
1115 "No formats found, trying to open it as a lyx file");
1116 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1118 // add the functions to the queue
1119 guiApp->addToFuncRequestQueue(cmd);
1122 // now process the collected functions. We perform the events
1123 // asynchronously. This prevents potential problems in case the
1124 // BufferView is closed within an event.
1125 guiApp->processFuncRequestQueueAsync();
1129 void GuiView::message(docstring const & str)
1131 if (ForkedProcess::iAmAChild())
1134 // call is moved to GUI-thread by GuiProgress
1135 d.progress_->appendMessage(toqstr(str));
1139 void GuiView::clearMessageText()
1141 message(docstring());
1145 void GuiView::updateStatusBarMessage(QString const & str)
1147 statusBar()->showMessage(str);
1148 d.statusbar_timer_.stop();
1149 d.statusbar_timer_.start(3000);
1153 void GuiView::clearMessage()
1155 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1156 // the hasFocus function mostly returns false, even if the focus is on
1157 // a workarea in this view.
1161 d.statusbar_timer_.stop();
1165 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1167 if (wa != d.current_work_area_
1168 || wa->bufferView().buffer().isInternal())
1170 Buffer const & buf = wa->bufferView().buffer();
1171 // Set the windows title
1172 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1173 if (buf.notifiesExternalModification()) {
1174 title = bformat(_("%1$s (modified externally)"), title);
1175 // If the external modification status has changed, then maybe the status of
1176 // buffer-save has changed too.
1180 title += from_ascii(" - LyX");
1182 setWindowTitle(toqstr(title));
1183 // Sets the path for the window: this is used by OSX to
1184 // allow a context click on the title bar showing a menu
1185 // with the path up to the file
1186 setWindowFilePath(toqstr(buf.absFileName()));
1187 // Tell Qt whether the current document is changed
1188 setWindowModified(!buf.isClean());
1190 if (buf.params().shell_escape)
1191 shell_escape_->show();
1193 shell_escape_->hide();
1195 if (buf.hasReadonlyFlag())
1200 if (buf.lyxvc().inUse()) {
1201 version_control_->show();
1202 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1204 version_control_->hide();
1208 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1210 if (d.current_work_area_)
1211 // disconnect the current work area from all slots
1212 QObject::disconnect(d.current_work_area_, 0, this, 0);
1214 disconnectBufferView();
1215 connectBufferView(wa->bufferView());
1216 connectBuffer(wa->bufferView().buffer());
1217 d.current_work_area_ = wa;
1218 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1219 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1220 QObject::connect(wa, SIGNAL(busy(bool)),
1221 this, SLOT(setBusy(bool)));
1222 // connection of a signal to a signal
1223 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1224 this, SIGNAL(bufferViewChanged()));
1225 Q_EMIT updateWindowTitle(wa);
1226 Q_EMIT bufferViewChanged();
1230 void GuiView::onBufferViewChanged()
1233 // Buffer-dependent dialogs must be updated. This is done here because
1234 // some dialogs require buffer()->text.
1239 void GuiView::on_lastWorkAreaRemoved()
1242 // We already are in a close event. Nothing more to do.
1245 if (d.splitter_->count() > 1)
1246 // We have a splitter so don't close anything.
1249 // Reset and updates the dialogs.
1250 Q_EMIT bufferViewChanged();
1255 if (lyxrc.open_buffers_in_tabs)
1256 // Nothing more to do, the window should stay open.
1259 if (guiApp->viewIds().size() > 1) {
1265 // On Mac we also close the last window because the application stay
1266 // resident in memory. On other platforms we don't close the last
1267 // window because this would quit the application.
1273 void GuiView::updateStatusBar()
1275 // let the user see the explicit message
1276 if (d.statusbar_timer_.isActive())
1283 void GuiView::showMessage()
1287 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1288 if (msg.isEmpty()) {
1289 BufferView const * bv = currentBufferView();
1291 msg = toqstr(bv->cursor().currentState(devel_mode_));
1293 msg = qt_("Welcome to LyX!");
1295 statusBar()->showMessage(msg);
1299 bool GuiView::event(QEvent * e)
1303 // Useful debug code:
1304 //case QEvent::ActivationChange:
1305 //case QEvent::WindowDeactivate:
1306 //case QEvent::Paint:
1307 //case QEvent::Enter:
1308 //case QEvent::Leave:
1309 //case QEvent::HoverEnter:
1310 //case QEvent::HoverLeave:
1311 //case QEvent::HoverMove:
1312 //case QEvent::StatusTip:
1313 //case QEvent::DragEnter:
1314 //case QEvent::DragLeave:
1315 //case QEvent::Drop:
1318 case QEvent::WindowActivate: {
1319 GuiView * old_view = guiApp->currentView();
1320 if (this == old_view) {
1322 return QMainWindow::event(e);
1324 if (old_view && old_view->currentBufferView()) {
1325 // save current selection to the selection buffer to allow
1326 // middle-button paste in this window.
1327 cap::saveSelection(old_view->currentBufferView()->cursor());
1329 guiApp->setCurrentView(this);
1330 if (d.current_work_area_)
1331 on_currentWorkAreaChanged(d.current_work_area_);
1335 return QMainWindow::event(e);
1338 case QEvent::ShortcutOverride: {
1340 if (isFullScreen() && menuBar()->isHidden()) {
1341 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1342 // FIXME: we should also try to detect special LyX shortcut such as
1343 // Alt-P and Alt-M. Right now there is a hack in
1344 // GuiWorkArea::processKeySym() that hides again the menubar for
1346 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1348 return QMainWindow::event(e);
1351 return QMainWindow::event(e);
1355 return QMainWindow::event(e);
1359 void GuiView::resetWindowTitle()
1361 setWindowTitle(qt_("LyX"));
1364 bool GuiView::focusNextPrevChild(bool /*next*/)
1371 bool GuiView::busy() const
1377 void GuiView::setBusy(bool busy)
1379 bool const busy_before = busy_ > 0;
1380 busy ? ++busy_ : --busy_;
1381 if ((busy_ > 0) == busy_before)
1382 // busy state didn't change
1386 QApplication::setOverrideCursor(Qt::WaitCursor);
1389 QApplication::restoreOverrideCursor();
1394 void GuiView::resetCommandExecute()
1396 command_execute_ = false;
1401 double GuiView::pixelRatio() const
1403 #if QT_VERSION >= 0x050000
1404 return qt_scale_factor * devicePixelRatio();
1411 GuiWorkArea * GuiView::workArea(int index)
1413 if (TabWorkArea * twa = d.currentTabWorkArea())
1414 if (index < twa->count())
1415 return twa->workArea(index);
1420 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1422 if (currentWorkArea()
1423 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1424 return currentWorkArea();
1425 if (TabWorkArea * twa = d.currentTabWorkArea())
1426 return twa->workArea(buffer);
1431 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1433 // Automatically create a TabWorkArea if there are none yet.
1434 TabWorkArea * tab_widget = d.splitter_->count()
1435 ? d.currentTabWorkArea() : addTabWorkArea();
1436 return tab_widget->addWorkArea(buffer, *this);
1440 TabWorkArea * GuiView::addTabWorkArea()
1442 TabWorkArea * twa = new TabWorkArea;
1443 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1444 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1445 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1446 this, SLOT(on_lastWorkAreaRemoved()));
1448 d.splitter_->addWidget(twa);
1449 d.stack_widget_->setCurrentWidget(d.splitter_);
1454 GuiWorkArea const * GuiView::currentWorkArea() const
1456 return d.current_work_area_;
1460 GuiWorkArea * GuiView::currentWorkArea()
1462 return d.current_work_area_;
1466 GuiWorkArea const * GuiView::currentMainWorkArea() const
1468 if (!d.currentTabWorkArea())
1470 return d.currentTabWorkArea()->currentWorkArea();
1474 GuiWorkArea * GuiView::currentMainWorkArea()
1476 if (!d.currentTabWorkArea())
1478 return d.currentTabWorkArea()->currentWorkArea();
1482 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1484 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1486 d.current_work_area_ = 0;
1488 Q_EMIT bufferViewChanged();
1492 // FIXME: I've no clue why this is here and why it accesses
1493 // theGuiApp()->currentView, which might be 0 (bug 6464).
1494 // See also 27525 (vfr).
1495 if (theGuiApp()->currentView() == this
1496 && theGuiApp()->currentView()->currentWorkArea() == wa)
1499 if (currentBufferView())
1500 cap::saveSelection(currentBufferView()->cursor());
1502 theGuiApp()->setCurrentView(this);
1503 d.current_work_area_ = wa;
1505 // We need to reset this now, because it will need to be
1506 // right if the tabWorkArea gets reset in the for loop. We
1507 // will change it back if we aren't in that case.
1508 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1509 d.current_main_work_area_ = wa;
1511 for (int i = 0; i != d.splitter_->count(); ++i) {
1512 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1513 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1514 << ", Current main wa: " << currentMainWorkArea());
1519 d.current_main_work_area_ = old_cmwa;
1521 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1522 on_currentWorkAreaChanged(wa);
1523 BufferView & bv = wa->bufferView();
1524 bv.cursor().fixIfBroken();
1526 wa->setUpdatesEnabled(true);
1527 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1531 void GuiView::removeWorkArea(GuiWorkArea * wa)
1533 LASSERT(wa, return);
1534 if (wa == d.current_work_area_) {
1536 disconnectBufferView();
1537 d.current_work_area_ = 0;
1538 d.current_main_work_area_ = 0;
1541 bool found_twa = false;
1542 for (int i = 0; i != d.splitter_->count(); ++i) {
1543 TabWorkArea * twa = d.tabWorkArea(i);
1544 if (twa->removeWorkArea(wa)) {
1545 // Found in this tab group, and deleted the GuiWorkArea.
1547 if (twa->count() != 0) {
1548 if (d.current_work_area_ == 0)
1549 // This means that we are closing the current GuiWorkArea, so
1550 // switch to the next GuiWorkArea in the found TabWorkArea.
1551 setCurrentWorkArea(twa->currentWorkArea());
1553 // No more WorkAreas in this tab group, so delete it.
1560 // It is not a tabbed work area (i.e., the search work area), so it
1561 // should be deleted by other means.
1562 LASSERT(found_twa, return);
1564 if (d.current_work_area_ == 0) {
1565 if (d.splitter_->count() != 0) {
1566 TabWorkArea * twa = d.currentTabWorkArea();
1567 setCurrentWorkArea(twa->currentWorkArea());
1569 // No more work areas, switch to the background widget.
1570 setCurrentWorkArea(0);
1576 LayoutBox * GuiView::getLayoutDialog() const
1582 void GuiView::updateLayoutList()
1585 d.layout_->updateContents(false);
1589 void GuiView::updateToolbars()
1591 ToolbarMap::iterator end = d.toolbars_.end();
1592 if (d.current_work_area_) {
1594 if (d.current_work_area_->bufferView().cursor().inMathed()
1595 && !d.current_work_area_->bufferView().cursor().inRegexped())
1596 context |= Toolbars::MATH;
1597 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1598 context |= Toolbars::TABLE;
1599 if (currentBufferView()->buffer().areChangesPresent()
1600 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1601 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1602 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1603 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1604 context |= Toolbars::REVIEW;
1605 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1606 context |= Toolbars::MATHMACROTEMPLATE;
1607 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1608 context |= Toolbars::IPA;
1609 if (command_execute_)
1610 context |= Toolbars::MINIBUFFER;
1611 if (minibuffer_focus_) {
1612 context |= Toolbars::MINIBUFFER_FOCUS;
1613 minibuffer_focus_ = false;
1616 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1617 it->second->update(context);
1619 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1620 it->second->update();
1624 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1626 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1627 LASSERT(newBuffer, return);
1629 GuiWorkArea * wa = workArea(*newBuffer);
1632 newBuffer->masterBuffer()->updateBuffer();
1634 wa = addWorkArea(*newBuffer);
1635 // scroll to the position when the BufferView was last closed
1636 if (lyxrc.use_lastfilepos) {
1637 LastFilePosSection::FilePos filepos =
1638 theSession().lastFilePos().load(newBuffer->fileName());
1639 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1642 //Disconnect the old buffer...there's no new one.
1645 connectBuffer(*newBuffer);
1646 connectBufferView(wa->bufferView());
1648 setCurrentWorkArea(wa);
1652 void GuiView::connectBuffer(Buffer & buf)
1654 buf.setGuiDelegate(this);
1658 void GuiView::disconnectBuffer()
1660 if (d.current_work_area_)
1661 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1665 void GuiView::connectBufferView(BufferView & bv)
1667 bv.setGuiDelegate(this);
1671 void GuiView::disconnectBufferView()
1673 if (d.current_work_area_)
1674 d.current_work_area_->bufferView().setGuiDelegate(0);
1678 void GuiView::errors(string const & error_type, bool from_master)
1680 BufferView const * const bv = currentBufferView();
1684 #if EXPORT_in_THREAD
1685 // We are called with from_master == false by default, so we
1686 // have to figure out whether that is the case or not.
1687 ErrorList & el = bv->buffer().errorList(error_type);
1689 el = bv->buffer().masterBuffer()->errorList(error_type);
1693 ErrorList const & el = from_master ?
1694 bv->buffer().masterBuffer()->errorList(error_type) :
1695 bv->buffer().errorList(error_type);
1701 string data = error_type;
1703 data = "from_master|" + error_type;
1704 showDialog("errorlist", data);
1708 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1710 d.toc_models_.updateItem(toqstr(type), dit);
1714 void GuiView::structureChanged()
1716 // This is called from the Buffer, which has no way to ensure that cursors
1717 // in BufferView remain valid.
1718 if (documentBufferView())
1719 documentBufferView()->cursor().sanitize();
1720 // FIXME: This is slightly expensive, though less than the tocBackend update
1721 // (#9880). This also resets the view in the Toc Widget (#6675).
1722 d.toc_models_.reset(documentBufferView());
1723 // Navigator needs more than a simple update in this case. It needs to be
1725 updateDialog("toc", "");
1729 void GuiView::updateDialog(string const & name, string const & data)
1731 if (!isDialogVisible(name))
1734 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1735 if (it == d.dialogs_.end())
1738 Dialog * const dialog = it->second.get();
1739 if (dialog->isVisibleView())
1740 dialog->initialiseParams(data);
1744 BufferView * GuiView::documentBufferView()
1746 return currentMainWorkArea()
1747 ? ¤tMainWorkArea()->bufferView()
1752 BufferView const * GuiView::documentBufferView() const
1754 return currentMainWorkArea()
1755 ? ¤tMainWorkArea()->bufferView()
1760 BufferView * GuiView::currentBufferView()
1762 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1766 BufferView const * GuiView::currentBufferView() const
1768 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1772 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1773 Buffer const * orig, Buffer * clone)
1775 bool const success = clone->autoSave();
1777 busyBuffers.remove(orig);
1779 ? _("Automatic save done.")
1780 : _("Automatic save failed!");
1784 void GuiView::autoSave()
1786 LYXERR(Debug::INFO, "Running autoSave()");
1788 Buffer * buffer = documentBufferView()
1789 ? &documentBufferView()->buffer() : 0;
1791 resetAutosaveTimers();
1795 GuiViewPrivate::busyBuffers.insert(buffer);
1796 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1797 buffer, buffer->cloneBufferOnly());
1798 d.autosave_watcher_.setFuture(f);
1799 resetAutosaveTimers();
1803 void GuiView::resetAutosaveTimers()
1806 d.autosave_timeout_.restart();
1810 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1813 Buffer * buf = currentBufferView()
1814 ? ¤tBufferView()->buffer() : 0;
1815 Buffer * doc_buffer = documentBufferView()
1816 ? &(documentBufferView()->buffer()) : 0;
1819 /* In LyX/Mac, when a dialog is open, the menus of the
1820 application can still be accessed without giving focus to
1821 the main window. In this case, we want to disable the menu
1822 entries that are buffer-related.
1823 This code must not be used on Linux and Windows, since it
1824 would disable buffer-related entries when hovering over the
1825 menu (see bug #9574).
1827 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1833 // Check whether we need a buffer
1834 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1835 // no, exit directly
1836 flag.message(from_utf8(N_("Command not allowed with"
1837 "out any document open")));
1838 flag.setEnabled(false);
1842 if (cmd.origin() == FuncRequest::TOC) {
1843 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1844 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1845 flag.setEnabled(false);
1849 switch(cmd.action()) {
1850 case LFUN_BUFFER_IMPORT:
1853 case LFUN_MASTER_BUFFER_UPDATE:
1854 case LFUN_MASTER_BUFFER_VIEW:
1856 && (doc_buffer->parent() != 0
1857 || doc_buffer->hasChildren())
1858 && !d.processing_thread_watcher_.isRunning();
1861 case LFUN_BUFFER_UPDATE:
1862 case LFUN_BUFFER_VIEW: {
1863 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1867 string format = to_utf8(cmd.argument());
1868 if (cmd.argument().empty())
1869 format = doc_buffer->params().getDefaultOutputFormat();
1870 enable = doc_buffer->params().isExportable(format, true);
1874 case LFUN_BUFFER_RELOAD:
1875 enable = doc_buffer && !doc_buffer->isUnnamed()
1876 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1879 case LFUN_BUFFER_CHILD_OPEN:
1880 enable = doc_buffer != 0;
1883 case LFUN_BUFFER_WRITE:
1884 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1887 //FIXME: This LFUN should be moved to GuiApplication.
1888 case LFUN_BUFFER_WRITE_ALL: {
1889 // We enable the command only if there are some modified buffers
1890 Buffer * first = theBufferList().first();
1895 // We cannot use a for loop as the buffer list is a cycle.
1897 if (!b->isClean()) {
1901 b = theBufferList().next(b);
1902 } while (b != first);
1906 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1907 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1910 case LFUN_BUFFER_WRITE_AS:
1911 case LFUN_BUFFER_EXPORT_AS:
1912 enable = doc_buffer != 0;
1915 case LFUN_BUFFER_CLOSE:
1916 case LFUN_VIEW_CLOSE:
1917 enable = doc_buffer != 0;
1920 case LFUN_BUFFER_CLOSE_ALL:
1921 enable = theBufferList().last() != theBufferList().first();
1924 case LFUN_VIEW_SPLIT:
1925 if (cmd.getArg(0) == "vertical")
1926 enable = doc_buffer && (d.splitter_->count() == 1 ||
1927 d.splitter_->orientation() == Qt::Vertical);
1929 enable = doc_buffer && (d.splitter_->count() == 1 ||
1930 d.splitter_->orientation() == Qt::Horizontal);
1933 case LFUN_TAB_GROUP_CLOSE:
1934 enable = d.tabWorkAreaCount() > 1;
1937 case LFUN_DEVEL_MODE_TOGGLE:
1938 flag.setOnOff(devel_mode_);
1941 case LFUN_TOOLBAR_TOGGLE: {
1942 string const name = cmd.getArg(0);
1943 if (GuiToolbar * t = toolbar(name))
1944 flag.setOnOff(t->isVisible());
1947 docstring const msg =
1948 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1954 case LFUN_TOOLBAR_MOVABLE: {
1955 string const name = cmd.getArg(0);
1956 // use negation since locked == !movable
1958 // toolbar name * locks all toolbars
1959 flag.setOnOff(!toolbarsMovable_);
1960 else if (GuiToolbar * t = toolbar(name))
1961 flag.setOnOff(!(t->isMovable()));
1964 docstring const msg =
1965 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1971 case LFUN_ICON_SIZE:
1972 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1975 case LFUN_DROP_LAYOUTS_CHOICE:
1979 case LFUN_UI_TOGGLE:
1980 flag.setOnOff(isFullScreen());
1983 case LFUN_DIALOG_DISCONNECT_INSET:
1986 case LFUN_DIALOG_HIDE:
1987 // FIXME: should we check if the dialog is shown?
1990 case LFUN_DIALOG_TOGGLE:
1991 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1994 case LFUN_DIALOG_SHOW: {
1995 string const name = cmd.getArg(0);
1997 enable = name == "aboutlyx"
1998 || name == "file" //FIXME: should be removed.
2000 || name == "texinfo"
2001 || name == "progress"
2002 || name == "compare";
2003 else if (name == "character" || name == "symbols"
2004 || name == "mathdelimiter" || name == "mathmatrix") {
2005 if (!buf || buf->isReadonly())
2008 Cursor const & cur = currentBufferView()->cursor();
2009 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2012 else if (name == "latexlog")
2013 enable = FileName(doc_buffer->logName()).isReadableFile();
2014 else if (name == "spellchecker")
2015 enable = theSpellChecker()
2016 && !doc_buffer->isReadonly()
2017 && !doc_buffer->text().empty();
2018 else if (name == "vclog")
2019 enable = doc_buffer->lyxvc().inUse();
2023 case LFUN_DIALOG_UPDATE: {
2024 string const name = cmd.getArg(0);
2026 enable = name == "prefs";
2030 case LFUN_COMMAND_EXECUTE:
2032 case LFUN_MENU_OPEN:
2033 // Nothing to check.
2036 case LFUN_COMPLETION_INLINE:
2037 if (!d.current_work_area_
2038 || !d.current_work_area_->completer().inlinePossible(
2039 currentBufferView()->cursor()))
2043 case LFUN_COMPLETION_POPUP:
2044 if (!d.current_work_area_
2045 || !d.current_work_area_->completer().popupPossible(
2046 currentBufferView()->cursor()))
2051 if (!d.current_work_area_
2052 || !d.current_work_area_->completer().inlinePossible(
2053 currentBufferView()->cursor()))
2057 case LFUN_COMPLETION_ACCEPT:
2058 if (!d.current_work_area_
2059 || (!d.current_work_area_->completer().popupVisible()
2060 && !d.current_work_area_->completer().inlineVisible()
2061 && !d.current_work_area_->completer().completionAvailable()))
2065 case LFUN_COMPLETION_CANCEL:
2066 if (!d.current_work_area_
2067 || (!d.current_work_area_->completer().popupVisible()
2068 && !d.current_work_area_->completer().inlineVisible()))
2072 case LFUN_BUFFER_ZOOM_OUT:
2073 case LFUN_BUFFER_ZOOM_IN: {
2074 // only diff between these two is that the default for ZOOM_OUT
2076 bool const neg_zoom =
2077 convert<int>(cmd.argument()) < 0 ||
2078 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2079 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2080 docstring const msg =
2081 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2085 enable = doc_buffer;
2089 case LFUN_BUFFER_ZOOM: {
2090 bool const less_than_min_zoom =
2091 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2092 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2093 docstring const msg =
2094 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2099 enable = doc_buffer;
2103 case LFUN_BUFFER_MOVE_NEXT:
2104 case LFUN_BUFFER_MOVE_PREVIOUS:
2105 // we do not cycle when moving
2106 case LFUN_BUFFER_NEXT:
2107 case LFUN_BUFFER_PREVIOUS:
2108 // because we cycle, it doesn't matter whether on first or last
2109 enable = (d.currentTabWorkArea()->count() > 1);
2111 case LFUN_BUFFER_SWITCH:
2112 // toggle on the current buffer, but do not toggle off
2113 // the other ones (is that a good idea?)
2115 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2116 flag.setOnOff(true);
2119 case LFUN_VC_REGISTER:
2120 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2122 case LFUN_VC_RENAME:
2123 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2126 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2128 case LFUN_VC_CHECK_IN:
2129 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2131 case LFUN_VC_CHECK_OUT:
2132 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2134 case LFUN_VC_LOCKING_TOGGLE:
2135 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2136 && doc_buffer->lyxvc().lockingToggleEnabled();
2137 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2139 case LFUN_VC_REVERT:
2140 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2141 && !doc_buffer->hasReadonlyFlag();
2143 case LFUN_VC_UNDO_LAST:
2144 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2146 case LFUN_VC_REPO_UPDATE:
2147 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2149 case LFUN_VC_COMMAND: {
2150 if (cmd.argument().empty())
2152 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2156 case LFUN_VC_COMPARE:
2157 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2160 case LFUN_SERVER_GOTO_FILE_ROW:
2161 case LFUN_LYX_ACTIVATE:
2163 case LFUN_FORWARD_SEARCH:
2164 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2167 case LFUN_FILE_INSERT_PLAINTEXT:
2168 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2169 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2172 case LFUN_SPELLING_CONTINUOUSLY:
2173 flag.setOnOff(lyxrc.spellcheck_continuously);
2181 flag.setEnabled(false);
2187 static FileName selectTemplateFile()
2189 FileDialog dlg(qt_("Select template file"));
2190 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2191 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2193 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2194 QStringList(qt_("LyX Documents (*.lyx)")));
2196 if (result.first == FileDialog::Later)
2198 if (result.second.isEmpty())
2200 return FileName(fromqstr(result.second));
2204 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2208 Buffer * newBuffer = 0;
2210 newBuffer = checkAndLoadLyXFile(filename);
2211 } catch (ExceptionMessage const & e) {
2218 message(_("Document not loaded."));
2222 setBuffer(newBuffer);
2223 newBuffer->errors("Parse");
2226 theSession().lastFiles().add(filename);
2232 void GuiView::openDocument(string const & fname)
2234 string initpath = lyxrc.document_path;
2236 if (documentBufferView()) {
2237 string const trypath = documentBufferView()->buffer().filePath();
2238 // If directory is writeable, use this as default.
2239 if (FileName(trypath).isDirWritable())
2245 if (fname.empty()) {
2246 FileDialog dlg(qt_("Select document to open"));
2247 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2248 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2250 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2251 FileDialog::Result result =
2252 dlg.open(toqstr(initpath), filter);
2254 if (result.first == FileDialog::Later)
2257 filename = fromqstr(result.second);
2259 // check selected filename
2260 if (filename.empty()) {
2261 message(_("Canceled."));
2267 // get absolute path of file and add ".lyx" to the filename if
2269 FileName const fullname =
2270 fileSearch(string(), filename, "lyx", support::may_not_exist);
2271 if (!fullname.empty())
2272 filename = fullname.absFileName();
2274 if (!fullname.onlyPath().isDirectory()) {
2275 Alert::warning(_("Invalid filename"),
2276 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2277 from_utf8(fullname.absFileName())));
2281 // if the file doesn't exist and isn't already open (bug 6645),
2282 // let the user create one
2283 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2284 !LyXVC::file_not_found_hook(fullname)) {
2285 // the user specifically chose this name. Believe him.
2286 Buffer * const b = newFile(filename, string(), true);
2292 docstring const disp_fn = makeDisplayPath(filename);
2293 message(bformat(_("Opening document %1$s..."), disp_fn));
2296 Buffer * buf = loadDocument(fullname);
2298 str2 = bformat(_("Document %1$s opened."), disp_fn);
2299 if (buf->lyxvc().inUse())
2300 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2301 " " + _("Version control detected.");
2303 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2308 // FIXME: clean that
2309 static bool import(GuiView * lv, FileName const & filename,
2310 string const & format, ErrorList & errorList)
2312 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2314 string loader_format;
2315 vector<string> loaders = theConverters().loaders();
2316 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2317 vector<string>::const_iterator it = loaders.begin();
2318 vector<string>::const_iterator en = loaders.end();
2319 for (; it != en; ++it) {
2320 if (!theConverters().isReachable(format, *it))
2323 string const tofile =
2324 support::changeExtension(filename.absFileName(),
2325 theFormats().extension(*it));
2326 if (!theConverters().convert(0, filename, FileName(tofile),
2327 filename, format, *it, errorList))
2329 loader_format = *it;
2332 if (loader_format.empty()) {
2333 frontend::Alert::error(_("Couldn't import file"),
2334 bformat(_("No information for importing the format %1$s."),
2335 theFormats().prettyName(format)));
2339 loader_format = format;
2341 if (loader_format == "lyx") {
2342 Buffer * buf = lv->loadDocument(lyxfile);
2346 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2350 bool as_paragraphs = loader_format == "textparagraph";
2351 string filename2 = (loader_format == format) ? filename.absFileName()
2352 : support::changeExtension(filename.absFileName(),
2353 theFormats().extension(loader_format));
2354 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2356 guiApp->setCurrentView(lv);
2357 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2364 void GuiView::importDocument(string const & argument)
2367 string filename = split(argument, format, ' ');
2369 LYXERR(Debug::INFO, format << " file: " << filename);
2371 // need user interaction
2372 if (filename.empty()) {
2373 string initpath = lyxrc.document_path;
2374 if (documentBufferView()) {
2375 string const trypath = documentBufferView()->buffer().filePath();
2376 // If directory is writeable, use this as default.
2377 if (FileName(trypath).isDirWritable())
2381 docstring const text = bformat(_("Select %1$s file to import"),
2382 theFormats().prettyName(format));
2384 FileDialog dlg(toqstr(text));
2385 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2386 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2388 docstring filter = theFormats().prettyName(format);
2391 filter += from_utf8(theFormats().extensions(format));
2394 FileDialog::Result result =
2395 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2397 if (result.first == FileDialog::Later)
2400 filename = fromqstr(result.second);
2402 // check selected filename
2403 if (filename.empty())
2404 message(_("Canceled."));
2407 if (filename.empty())
2410 // get absolute path of file
2411 FileName const fullname(support::makeAbsPath(filename));
2413 // Can happen if the user entered a path into the dialog
2415 if (fullname.onlyFileName().empty()) {
2416 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2417 "Aborting import."),
2418 from_utf8(fullname.absFileName()));
2419 frontend::Alert::error(_("File name error"), msg);
2420 message(_("Canceled."));
2425 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2427 // Check if the document already is open
2428 Buffer * buf = theBufferList().getBuffer(lyxfile);
2431 if (!closeBuffer()) {
2432 message(_("Canceled."));
2437 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2439 // if the file exists already, and we didn't do
2440 // -i lyx thefile.lyx, warn
2441 if (lyxfile.exists() && fullname != lyxfile) {
2443 docstring text = bformat(_("The document %1$s already exists.\n\n"
2444 "Do you want to overwrite that document?"), displaypath);
2445 int const ret = Alert::prompt(_("Overwrite document?"),
2446 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2449 message(_("Canceled."));
2454 message(bformat(_("Importing %1$s..."), displaypath));
2455 ErrorList errorList;
2456 if (import(this, fullname, format, errorList))
2457 message(_("imported."));
2459 message(_("file not imported!"));
2461 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2465 void GuiView::newDocument(string const & filename, bool from_template)
2467 FileName initpath(lyxrc.document_path);
2468 if (documentBufferView()) {
2469 FileName const trypath(documentBufferView()->buffer().filePath());
2470 // If directory is writeable, use this as default.
2471 if (trypath.isDirWritable())
2475 string templatefile;
2476 if (from_template) {
2477 templatefile = selectTemplateFile().absFileName();
2478 if (templatefile.empty())
2483 if (filename.empty())
2484 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2486 b = newFile(filename, templatefile, true);
2491 // If no new document could be created, it is unsure
2492 // whether there is a valid BufferView.
2493 if (currentBufferView())
2494 // Ensure the cursor is correctly positioned on screen.
2495 currentBufferView()->showCursor();
2499 void GuiView::insertLyXFile(docstring const & fname)
2501 BufferView * bv = documentBufferView();
2506 FileName filename(to_utf8(fname));
2507 if (filename.empty()) {
2508 // Launch a file browser
2510 string initpath = lyxrc.document_path;
2511 string const trypath = bv->buffer().filePath();
2512 // If directory is writeable, use this as default.
2513 if (FileName(trypath).isDirWritable())
2517 FileDialog dlg(qt_("Select LyX document to insert"));
2518 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2519 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2521 FileDialog::Result result = dlg.open(toqstr(initpath),
2522 QStringList(qt_("LyX Documents (*.lyx)")));
2524 if (result.first == FileDialog::Later)
2528 filename.set(fromqstr(result.second));
2530 // check selected filename
2531 if (filename.empty()) {
2532 // emit message signal.
2533 message(_("Canceled."));
2538 bv->insertLyXFile(filename);
2539 bv->buffer().errors("Parse");
2543 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2545 FileName fname = b.fileName();
2546 FileName const oldname = fname;
2548 if (!newname.empty()) {
2550 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2552 // Switch to this Buffer.
2555 // No argument? Ask user through dialog.
2557 FileDialog dlg(qt_("Choose a filename to save document as"));
2558 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2559 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2561 if (!isLyXFileName(fname.absFileName()))
2562 fname.changeExtension(".lyx");
2564 FileDialog::Result result =
2565 dlg.save(toqstr(fname.onlyPath().absFileName()),
2566 QStringList(qt_("LyX Documents (*.lyx)")),
2567 toqstr(fname.onlyFileName()));
2569 if (result.first == FileDialog::Later)
2572 fname.set(fromqstr(result.second));
2577 if (!isLyXFileName(fname.absFileName()))
2578 fname.changeExtension(".lyx");
2581 // fname is now the new Buffer location.
2583 // if there is already a Buffer open with this name, we do not want
2584 // to have another one. (the second test makes sure we're not just
2585 // trying to overwrite ourselves, which is fine.)
2586 if (theBufferList().exists(fname) && fname != oldname
2587 && theBufferList().getBuffer(fname) != &b) {
2588 docstring const text =
2589 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2590 "Please close it before attempting to overwrite it.\n"
2591 "Do you want to choose a new filename?"),
2592 from_utf8(fname.absFileName()));
2593 int const ret = Alert::prompt(_("Chosen File Already Open"),
2594 text, 0, 1, _("&Rename"), _("&Cancel"));
2596 case 0: return renameBuffer(b, docstring(), kind);
2597 case 1: return false;
2602 bool const existsLocal = fname.exists();
2603 bool const existsInVC = LyXVC::fileInVC(fname);
2604 if (existsLocal || existsInVC) {
2605 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2606 if (kind != LV_WRITE_AS && existsInVC) {
2607 // renaming to a name that is already in VC
2609 docstring text = bformat(_("The document %1$s "
2610 "is already registered.\n\n"
2611 "Do you want to choose a new name?"),
2613 docstring const title = (kind == LV_VC_RENAME) ?
2614 _("Rename document?") : _("Copy document?");
2615 docstring const button = (kind == LV_VC_RENAME) ?
2616 _("&Rename") : _("&Copy");
2617 int const ret = Alert::prompt(title, text, 0, 1,
2618 button, _("&Cancel"));
2620 case 0: return renameBuffer(b, docstring(), kind);
2621 case 1: return false;
2626 docstring text = bformat(_("The document %1$s "
2627 "already exists.\n\n"
2628 "Do you want to overwrite that document?"),
2630 int const ret = Alert::prompt(_("Overwrite document?"),
2631 text, 0, 2, _("&Overwrite"),
2632 _("&Rename"), _("&Cancel"));
2635 case 1: return renameBuffer(b, docstring(), kind);
2636 case 2: return false;
2642 case LV_VC_RENAME: {
2643 string msg = b.lyxvc().rename(fname);
2646 message(from_utf8(msg));
2650 string msg = b.lyxvc().copy(fname);
2653 message(from_utf8(msg));
2659 // LyXVC created the file already in case of LV_VC_RENAME or
2660 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2661 // relative paths of included stuff right if we moved e.g. from
2662 // /a/b.lyx to /a/c/b.lyx.
2664 bool const saved = saveBuffer(b, fname);
2671 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2673 FileName fname = b.fileName();
2675 FileDialog dlg(qt_("Choose a filename to export the document as"));
2676 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2679 QString const anyformat = qt_("Guess from extension (*.*)");
2682 vector<Format const *> export_formats;
2683 for (Format const & f : theFormats())
2684 if (f.documentFormat())
2685 export_formats.push_back(&f);
2686 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2687 map<QString, string> fmap;
2690 for (Format const * f : export_formats) {
2691 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2692 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2694 from_ascii(f->extension())));
2695 types << loc_filter;
2696 fmap[loc_filter] = f->name();
2697 if (from_ascii(f->name()) == iformat) {
2698 filter = loc_filter;
2699 ext = f->extension();
2702 string ofname = fname.onlyFileName();
2704 ofname = support::changeExtension(ofname, ext);
2705 FileDialog::Result result =
2706 dlg.save(toqstr(fname.onlyPath().absFileName()),
2710 if (result.first != FileDialog::Chosen)
2714 fname.set(fromqstr(result.second));
2715 if (filter == anyformat)
2716 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2718 fmt_name = fmap[filter];
2719 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2720 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2722 if (fmt_name.empty() || fname.empty())
2725 // fname is now the new Buffer location.
2726 if (FileName(fname).exists()) {
2727 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2728 docstring text = bformat(_("The document %1$s already "
2729 "exists.\n\nDo you want to "
2730 "overwrite that document?"),
2732 int const ret = Alert::prompt(_("Overwrite document?"),
2733 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2736 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2737 case 2: return false;
2741 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2744 return dr.dispatched();
2748 bool GuiView::saveBuffer(Buffer & b)
2750 return saveBuffer(b, FileName());
2754 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2756 if (workArea(b) && workArea(b)->inDialogMode())
2759 if (fn.empty() && b.isUnnamed())
2760 return renameBuffer(b, docstring());
2762 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2764 theSession().lastFiles().add(b.fileName());
2768 // Switch to this Buffer.
2771 // FIXME: we don't tell the user *WHY* the save failed !!
2772 docstring const file = makeDisplayPath(b.absFileName(), 30);
2773 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2774 "Do you want to rename the document and "
2775 "try again?"), file);
2776 int const ret = Alert::prompt(_("Rename and save?"),
2777 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2780 if (!renameBuffer(b, docstring()))
2789 return saveBuffer(b, fn);
2793 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2795 return closeWorkArea(wa, false);
2799 // We only want to close the buffer if it is not visible in other workareas
2800 // of the same view, nor in other views, and if this is not a child
2801 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2803 Buffer & buf = wa->bufferView().buffer();
2805 bool last_wa = d.countWorkAreasOf(buf) == 1
2806 && !inOtherView(buf) && !buf.parent();
2808 bool close_buffer = last_wa;
2811 if (lyxrc.close_buffer_with_last_view == "yes")
2813 else if (lyxrc.close_buffer_with_last_view == "no")
2814 close_buffer = false;
2817 if (buf.isUnnamed())
2818 file = from_utf8(buf.fileName().onlyFileName());
2820 file = buf.fileName().displayName(30);
2821 docstring const text = bformat(
2822 _("Last view on document %1$s is being closed.\n"
2823 "Would you like to close or hide the document?\n"
2825 "Hidden documents can be displayed back through\n"
2826 "the menu: View->Hidden->...\n"
2828 "To remove this question, set your preference in:\n"
2829 " Tools->Preferences->Look&Feel->UserInterface\n"
2831 int ret = Alert::prompt(_("Close or hide document?"),
2832 text, 0, 1, _("&Close"), _("&Hide"));
2833 close_buffer = (ret == 0);
2837 return closeWorkArea(wa, close_buffer);
2841 bool GuiView::closeBuffer()
2843 GuiWorkArea * wa = currentMainWorkArea();
2844 // coverity complained about this
2845 // it seems unnecessary, but perhaps is worth the check
2846 LASSERT(wa, return false);
2848 setCurrentWorkArea(wa);
2849 Buffer & buf = wa->bufferView().buffer();
2850 return closeWorkArea(wa, !buf.parent());
2854 void GuiView::writeSession() const {
2855 GuiWorkArea const * active_wa = currentMainWorkArea();
2856 for (int i = 0; i < d.splitter_->count(); ++i) {
2857 TabWorkArea * twa = d.tabWorkArea(i);
2858 for (int j = 0; j < twa->count(); ++j) {
2859 GuiWorkArea * wa = twa->workArea(j);
2860 Buffer & buf = wa->bufferView().buffer();
2861 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2867 bool GuiView::closeBufferAll()
2869 // Close the workareas in all other views
2870 QList<int> const ids = guiApp->viewIds();
2871 for (int i = 0; i != ids.size(); ++i) {
2872 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2876 // Close our own workareas
2877 if (!closeWorkAreaAll())
2880 // Now close the hidden buffers. We prevent hidden buffers from being
2881 // dirty, so we can just close them.
2882 theBufferList().closeAll();
2887 bool GuiView::closeWorkAreaAll()
2889 setCurrentWorkArea(currentMainWorkArea());
2891 // We might be in a situation that there is still a tabWorkArea, but
2892 // there are no tabs anymore. This can happen when we get here after a
2893 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2894 // many TabWorkArea's have no documents anymore.
2897 // We have to call count() each time, because it can happen that
2898 // more than one splitter will disappear in one iteration (bug 5998).
2899 while (d.splitter_->count() > empty_twa) {
2900 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2902 if (twa->count() == 0)
2905 setCurrentWorkArea(twa->currentWorkArea());
2906 if (!closeTabWorkArea(twa))
2914 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2919 Buffer & buf = wa->bufferView().buffer();
2921 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2922 Alert::warning(_("Close document"),
2923 _("Document could not be closed because it is being processed by LyX."));
2928 return closeBuffer(buf);
2930 if (!inMultiTabs(wa))
2931 if (!saveBufferIfNeeded(buf, true))
2939 bool GuiView::closeBuffer(Buffer & buf)
2941 // If we are in a close_event all children will be closed in some time,
2942 // so no need to do it here. This will ensure that the children end up
2943 // in the session file in the correct order. If we close the master
2944 // buffer, we can close or release the child buffers here too.
2945 bool success = true;
2947 ListOfBuffers clist = buf.getChildren();
2948 ListOfBuffers::const_iterator it = clist.begin();
2949 ListOfBuffers::const_iterator const bend = clist.end();
2950 for (; it != bend; ++it) {
2951 Buffer * child_buf = *it;
2952 if (theBufferList().isOthersChild(&buf, child_buf)) {
2953 child_buf->setParent(0);
2957 // FIXME: should we look in other tabworkareas?
2958 // ANSWER: I don't think so. I've tested, and if the child is
2959 // open in some other window, it closes without a problem.
2960 GuiWorkArea * child_wa = workArea(*child_buf);
2962 success = closeWorkArea(child_wa, true);
2966 // In this case the child buffer is open but hidden.
2967 // It therefore should not (MUST NOT) be dirty!
2968 LATTEST(child_buf->isClean());
2969 theBufferList().release(child_buf);
2974 // goto bookmark to update bookmark pit.
2975 // FIXME: we should update only the bookmarks related to this buffer!
2976 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2977 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2978 guiApp->gotoBookmark(i+1, false, false);
2980 if (saveBufferIfNeeded(buf, false)) {
2981 buf.removeAutosaveFile();
2982 theBufferList().release(&buf);
2986 // open all children again to avoid a crash because of dangling
2987 // pointers (bug 6603)
2993 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2995 while (twa == d.currentTabWorkArea()) {
2996 twa->setCurrentIndex(twa->count() - 1);
2998 GuiWorkArea * wa = twa->currentWorkArea();
2999 Buffer & b = wa->bufferView().buffer();
3001 // We only want to close the buffer if the same buffer is not visible
3002 // in another view, and if this is not a child and if we are closing
3003 // a view (not a tabgroup).
3004 bool const close_buffer =
3005 !inOtherView(b) && !b.parent() && closing_;
3007 if (!closeWorkArea(wa, close_buffer))
3014 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3016 if (buf.isClean() || buf.paragraphs().empty())
3019 // Switch to this Buffer.
3025 if (buf.isUnnamed()) {
3026 file = from_utf8(buf.fileName().onlyFileName());
3029 FileName filename = buf.fileName();
3031 file = filename.displayName(30);
3032 exists = filename.exists();
3035 // Bring this window to top before asking questions.
3040 if (hiding && buf.isUnnamed()) {
3041 docstring const text = bformat(_("The document %1$s has not been "
3042 "saved yet.\n\nDo you want to save "
3043 "the document?"), file);
3044 ret = Alert::prompt(_("Save new document?"),
3045 text, 0, 1, _("&Save"), _("&Cancel"));
3049 docstring const text = exists ?
3050 bformat(_("The document %1$s has unsaved changes."
3051 "\n\nDo you want to save the document or "
3052 "discard the changes?"), file) :
3053 bformat(_("The document %1$s has not been saved yet."
3054 "\n\nDo you want to save the document or "
3055 "discard it entirely?"), file);
3056 docstring const title = exists ?
3057 _("Save changed document?") : _("Save document?");
3058 ret = Alert::prompt(title, text, 0, 2,
3059 _("&Save"), _("&Discard"), _("&Cancel"));
3064 if (!saveBuffer(buf))
3068 // If we crash after this we could have no autosave file
3069 // but I guess this is really improbable (Jug).
3070 // Sometimes improbable things happen:
3071 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3072 // buf.removeAutosaveFile();
3074 // revert all changes
3085 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3087 Buffer & buf = wa->bufferView().buffer();
3089 for (int i = 0; i != d.splitter_->count(); ++i) {
3090 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3091 if (wa_ && wa_ != wa)
3094 return inOtherView(buf);
3098 bool GuiView::inOtherView(Buffer & buf)
3100 QList<int> const ids = guiApp->viewIds();
3102 for (int i = 0; i != ids.size(); ++i) {
3106 if (guiApp->view(ids[i]).workArea(buf))
3113 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3115 if (!documentBufferView())
3118 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3119 Buffer * const curbuf = &documentBufferView()->buffer();
3120 int nwa = twa->count();
3121 for (int i = 0; i < nwa; ++i) {
3122 if (&workArea(i)->bufferView().buffer() == curbuf) {
3124 if (np == NEXTBUFFER)
3125 next_index = (i == nwa - 1 ? 0 : i + 1);
3127 next_index = (i == 0 ? nwa - 1 : i - 1);
3129 twa->moveTab(i, next_index);
3131 setBuffer(&workArea(next_index)->bufferView().buffer());
3139 /// make sure the document is saved
3140 static bool ensureBufferClean(Buffer * buffer)
3142 LASSERT(buffer, return false);
3143 if (buffer->isClean() && !buffer->isUnnamed())
3146 docstring const file = buffer->fileName().displayName(30);
3149 if (!buffer->isUnnamed()) {
3150 text = bformat(_("The document %1$s has unsaved "
3151 "changes.\n\nDo you want to save "
3152 "the document?"), file);
3153 title = _("Save changed document?");
3156 text = bformat(_("The document %1$s has not been "
3157 "saved yet.\n\nDo you want to save "
3158 "the document?"), file);
3159 title = _("Save new document?");
3161 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3164 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3166 return buffer->isClean() && !buffer->isUnnamed();
3170 bool GuiView::reloadBuffer(Buffer & buf)
3172 Buffer::ReadStatus status = buf.reload();
3173 return status == Buffer::ReadSuccess;
3177 void GuiView::checkExternallyModifiedBuffers()
3179 BufferList::iterator bit = theBufferList().begin();
3180 BufferList::iterator const bend = theBufferList().end();
3181 for (; bit != bend; ++bit) {
3182 Buffer * buf = *bit;
3183 if (buf->fileName().exists() && buf->isChecksumModified()) {
3184 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3185 " Reload now? Any local changes will be lost."),
3186 from_utf8(buf->absFileName()));
3187 int const ret = Alert::prompt(_("Reload externally changed document?"),
3188 text, 0, 1, _("&Reload"), _("&Cancel"));
3196 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3198 Buffer * buffer = documentBufferView()
3199 ? &(documentBufferView()->buffer()) : 0;
3201 switch (cmd.action()) {
3202 case LFUN_VC_REGISTER:
3203 if (!buffer || !ensureBufferClean(buffer))
3205 if (!buffer->lyxvc().inUse()) {
3206 if (buffer->lyxvc().registrer()) {
3207 reloadBuffer(*buffer);
3208 dr.clearMessageUpdate();
3213 case LFUN_VC_RENAME:
3214 case LFUN_VC_COPY: {
3215 if (!buffer || !ensureBufferClean(buffer))
3217 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3218 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3219 // Some changes are not yet committed.
3220 // We test here and not in getStatus(), since
3221 // this test is expensive.
3223 LyXVC::CommandResult ret =
3224 buffer->lyxvc().checkIn(log);
3226 if (ret == LyXVC::ErrorCommand ||
3227 ret == LyXVC::VCSuccess)
3228 reloadBuffer(*buffer);
3229 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3230 frontend::Alert::error(
3231 _("Revision control error."),
3232 _("Document could not be checked in."));
3236 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3237 LV_VC_RENAME : LV_VC_COPY;
3238 renameBuffer(*buffer, cmd.argument(), kind);
3243 case LFUN_VC_CHECK_IN:
3244 if (!buffer || !ensureBufferClean(buffer))
3246 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3248 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3250 // Only skip reloading if the checkin was cancelled or
3251 // an error occurred before the real checkin VCS command
3252 // was executed, since the VCS might have changed the
3253 // file even if it could not checkin successfully.
3254 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3255 reloadBuffer(*buffer);
3259 case LFUN_VC_CHECK_OUT:
3260 if (!buffer || !ensureBufferClean(buffer))
3262 if (buffer->lyxvc().inUse()) {
3263 dr.setMessage(buffer->lyxvc().checkOut());
3264 reloadBuffer(*buffer);
3268 case LFUN_VC_LOCKING_TOGGLE:
3269 LASSERT(buffer, return);
3270 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3272 if (buffer->lyxvc().inUse()) {
3273 string res = buffer->lyxvc().lockingToggle();
3275 frontend::Alert::error(_("Revision control error."),
3276 _("Error when setting the locking property."));
3279 reloadBuffer(*buffer);
3284 case LFUN_VC_REVERT:
3285 LASSERT(buffer, return);
3286 if (buffer->lyxvc().revert()) {
3287 reloadBuffer(*buffer);
3288 dr.clearMessageUpdate();
3292 case LFUN_VC_UNDO_LAST:
3293 LASSERT(buffer, return);
3294 buffer->lyxvc().undoLast();
3295 reloadBuffer(*buffer);
3296 dr.clearMessageUpdate();
3299 case LFUN_VC_REPO_UPDATE:
3300 LASSERT(buffer, return);
3301 if (ensureBufferClean(buffer)) {
3302 dr.setMessage(buffer->lyxvc().repoUpdate());
3303 checkExternallyModifiedBuffers();
3307 case LFUN_VC_COMMAND: {
3308 string flag = cmd.getArg(0);
3309 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3312 if (contains(flag, 'M')) {
3313 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3316 string path = cmd.getArg(1);
3317 if (contains(path, "$$p") && buffer)
3318 path = subst(path, "$$p", buffer->filePath());
3319 LYXERR(Debug::LYXVC, "Directory: " << path);
3321 if (!pp.isReadableDirectory()) {
3322 lyxerr << _("Directory is not accessible.") << endl;
3325 support::PathChanger p(pp);
3327 string command = cmd.getArg(2);
3328 if (command.empty())
3331 command = subst(command, "$$i", buffer->absFileName());
3332 command = subst(command, "$$p", buffer->filePath());
3334 command = subst(command, "$$m", to_utf8(message));
3335 LYXERR(Debug::LYXVC, "Command: " << command);
3337 one.startscript(Systemcall::Wait, command);
3341 if (contains(flag, 'I'))
3342 buffer->markDirty();
3343 if (contains(flag, 'R'))
3344 reloadBuffer(*buffer);
3349 case LFUN_VC_COMPARE: {
3350 if (cmd.argument().empty()) {
3351 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3355 string rev1 = cmd.getArg(0);
3360 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3363 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3364 f2 = buffer->absFileName();
3366 string rev2 = cmd.getArg(1);
3370 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3374 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3375 f1 << "\n" << f2 << "\n" );
3376 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3377 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3387 void GuiView::openChildDocument(string const & fname)
3389 LASSERT(documentBufferView(), return);
3390 Buffer & buffer = documentBufferView()->buffer();
3391 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3392 documentBufferView()->saveBookmark(false);
3394 if (theBufferList().exists(filename)) {
3395 child = theBufferList().getBuffer(filename);
3398 message(bformat(_("Opening child document %1$s..."),
3399 makeDisplayPath(filename.absFileName())));
3400 child = loadDocument(filename, false);
3402 // Set the parent name of the child document.
3403 // This makes insertion of citations and references in the child work,
3404 // when the target is in the parent or another child document.
3406 child->setParent(&buffer);
3410 bool GuiView::goToFileRow(string const & argument)
3414 size_t i = argument.find_last_of(' ');
3415 if (i != string::npos) {
3416 file_name = os::internal_path(trim(argument.substr(0, i)));
3417 istringstream is(argument.substr(i + 1));
3422 if (i == string::npos) {
3423 LYXERR0("Wrong argument: " << argument);
3427 string const abstmp = package().temp_dir().absFileName();
3428 string const realtmp = package().temp_dir().realPath();
3429 // We have to use os::path_prefix_is() here, instead of
3430 // simply prefixIs(), because the file name comes from
3431 // an external application and may need case adjustment.
3432 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3433 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3434 // Needed by inverse dvi search. If it is a file
3435 // in tmpdir, call the apropriated function.
3436 // If tmpdir is a symlink, we may have the real
3437 // path passed back, so we correct for that.
3438 if (!prefixIs(file_name, abstmp))
3439 file_name = subst(file_name, realtmp, abstmp);
3440 buf = theBufferList().getBufferFromTmp(file_name);
3442 // Must replace extension of the file to be .lyx
3443 // and get full path
3444 FileName const s = fileSearch(string(),
3445 support::changeExtension(file_name, ".lyx"), "lyx");
3446 // Either change buffer or load the file
3447 if (theBufferList().exists(s))
3448 buf = theBufferList().getBuffer(s);
3449 else if (s.exists()) {
3450 buf = loadDocument(s);
3455 _("File does not exist: %1$s"),
3456 makeDisplayPath(file_name)));
3462 _("No buffer for file: %1$s."),
3463 makeDisplayPath(file_name))
3468 bool success = documentBufferView()->setCursorFromRow(row);
3470 LYXERR(Debug::LATEX,
3471 "setCursorFromRow: invalid position for row " << row);
3472 frontend::Alert::error(_("Inverse Search Failed"),
3473 _("Invalid position requested by inverse search.\n"
3474 "You may need to update the viewed document."));
3480 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3482 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3483 menu->exec(QCursor::pos());
3488 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3490 Buffer::ExportStatus const status = func(format);
3492 // the cloning operation will have produced a clone of the entire set of
3493 // documents, starting from the master. so we must delete those.
3494 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3496 busyBuffers.remove(orig);
3501 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3503 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3504 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3508 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3510 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3511 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3515 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3517 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3518 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3522 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3523 string const & argument,
3524 Buffer const * used_buffer,
3525 docstring const & msg,
3526 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3527 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3528 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3533 string format = argument;
3535 format = used_buffer->params().getDefaultOutputFormat();
3536 processing_format = format;
3538 progress_->clearMessages();
3541 #if EXPORT_in_THREAD
3542 GuiViewPrivate::busyBuffers.insert(used_buffer);
3543 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3544 if (!cloned_buffer) {
3545 Alert::error(_("Export Error"),
3546 _("Error cloning the Buffer."));
3549 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3554 setPreviewFuture(f);
3555 last_export_format = used_buffer->params().bufferFormat();
3558 // We are asynchronous, so we don't know here anything about the success
3561 Buffer::ExportStatus status;
3563 status = (used_buffer->*syncFunc)(format, true);
3564 } else if (previewFunc) {
3565 status = (used_buffer->*previewFunc)(format);
3568 handleExportStatus(gv_, status, format);
3570 return (status == Buffer::ExportSuccess
3571 || status == Buffer::PreviewSuccess);
3575 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3577 BufferView * bv = currentBufferView();
3578 LASSERT(bv, return);
3580 // Let the current BufferView dispatch its own actions.
3581 bv->dispatch(cmd, dr);
3582 if (dr.dispatched())
3585 // Try with the document BufferView dispatch if any.
3586 BufferView * doc_bv = documentBufferView();
3587 if (doc_bv && doc_bv != bv) {
3588 doc_bv->dispatch(cmd, dr);
3589 if (dr.dispatched())
3593 // Then let the current Cursor dispatch its own actions.
3594 bv->cursor().dispatch(cmd);
3596 // update completion. We do it here and not in
3597 // processKeySym to avoid another redraw just for a
3598 // changed inline completion
3599 if (cmd.origin() == FuncRequest::KEYBOARD) {
3600 if (cmd.action() == LFUN_SELF_INSERT
3601 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3602 updateCompletion(bv->cursor(), true, true);
3603 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3604 updateCompletion(bv->cursor(), false, true);
3606 updateCompletion(bv->cursor(), false, false);
3609 dr = bv->cursor().result();
3613 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3615 BufferView * bv = currentBufferView();
3616 // By default we won't need any update.
3617 dr.screenUpdate(Update::None);
3618 // assume cmd will be dispatched
3619 dr.dispatched(true);
3621 Buffer * doc_buffer = documentBufferView()
3622 ? &(documentBufferView()->buffer()) : 0;
3624 if (cmd.origin() == FuncRequest::TOC) {
3625 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3626 // FIXME: do we need to pass a DispatchResult object here?
3627 toc->doDispatch(bv->cursor(), cmd);
3631 string const argument = to_utf8(cmd.argument());
3633 switch(cmd.action()) {
3634 case LFUN_BUFFER_CHILD_OPEN:
3635 openChildDocument(to_utf8(cmd.argument()));
3638 case LFUN_BUFFER_IMPORT:
3639 importDocument(to_utf8(cmd.argument()));
3642 case LFUN_BUFFER_EXPORT: {
3645 // GCC only sees strfwd.h when building merged
3646 if (::lyx::operator==(cmd.argument(), "custom")) {
3647 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3651 string const dest = cmd.getArg(1);
3652 FileName target_dir;
3653 if (!dest.empty() && FileName::isAbsolute(dest))
3654 target_dir = FileName(support::onlyPath(dest));
3656 target_dir = doc_buffer->fileName().onlyPath();
3658 string const format = (argument.empty() || argument == "default") ?
3659 doc_buffer->params().getDefaultOutputFormat() : argument;
3661 if ((dest.empty() && doc_buffer->isUnnamed())
3662 || !target_dir.isDirWritable()) {
3663 exportBufferAs(*doc_buffer, from_utf8(format));
3666 /* TODO/Review: Is it a problem to also export the children?
3667 See the update_unincluded flag */
3668 d.asyncBufferProcessing(format,
3671 &GuiViewPrivate::exportAndDestroy,
3674 // TODO Inform user about success
3678 case LFUN_BUFFER_EXPORT_AS: {
3679 LASSERT(doc_buffer, break);
3680 docstring f = cmd.argument();
3682 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3683 exportBufferAs(*doc_buffer, f);
3687 case LFUN_BUFFER_UPDATE: {
3688 d.asyncBufferProcessing(argument,
3691 &GuiViewPrivate::compileAndDestroy,
3696 case LFUN_BUFFER_VIEW: {
3697 d.asyncBufferProcessing(argument,
3699 _("Previewing ..."),
3700 &GuiViewPrivate::previewAndDestroy,
3705 case LFUN_MASTER_BUFFER_UPDATE: {
3706 d.asyncBufferProcessing(argument,
3707 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3709 &GuiViewPrivate::compileAndDestroy,
3714 case LFUN_MASTER_BUFFER_VIEW: {
3715 d.asyncBufferProcessing(argument,
3716 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3718 &GuiViewPrivate::previewAndDestroy,
3719 0, &Buffer::preview);
3722 case LFUN_BUFFER_SWITCH: {
3723 string const file_name = to_utf8(cmd.argument());
3724 if (!FileName::isAbsolute(file_name)) {
3726 dr.setMessage(_("Absolute filename expected."));
3730 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3733 dr.setMessage(_("Document not loaded"));
3737 // Do we open or switch to the buffer in this view ?
3738 if (workArea(*buffer)
3739 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3744 // Look for the buffer in other views
3745 QList<int> const ids = guiApp->viewIds();
3747 for (; i != ids.size(); ++i) {
3748 GuiView & gv = guiApp->view(ids[i]);
3749 if (gv.workArea(*buffer)) {
3751 gv.activateWindow();
3753 gv.setBuffer(buffer);
3758 // If necessary, open a new window as a last resort
3759 if (i == ids.size()) {
3760 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3766 case LFUN_BUFFER_NEXT:
3767 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3770 case LFUN_BUFFER_MOVE_NEXT:
3771 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3774 case LFUN_BUFFER_PREVIOUS:
3775 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3778 case LFUN_BUFFER_MOVE_PREVIOUS:
3779 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3782 case LFUN_COMMAND_EXECUTE: {
3783 command_execute_ = true;
3784 minibuffer_focus_ = true;
3787 case LFUN_DROP_LAYOUTS_CHOICE:
3788 d.layout_->showPopup();
3791 case LFUN_MENU_OPEN:
3792 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3793 menu->exec(QCursor::pos());
3796 case LFUN_FILE_INSERT:
3797 insertLyXFile(cmd.argument());
3800 case LFUN_FILE_INSERT_PLAINTEXT:
3801 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3802 string const fname = to_utf8(cmd.argument());
3803 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3804 dr.setMessage(_("Absolute filename expected."));
3808 FileName filename(fname);
3809 if (fname.empty()) {
3810 FileDialog dlg(qt_("Select file to insert"));
3812 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3813 QStringList(qt_("All Files (*)")));
3815 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3816 dr.setMessage(_("Canceled."));
3820 filename.set(fromqstr(result.second));
3824 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3825 bv->dispatch(new_cmd, dr);
3830 case LFUN_BUFFER_RELOAD: {
3831 LASSERT(doc_buffer, break);
3834 if (!doc_buffer->isClean()) {
3835 docstring const file =
3836 makeDisplayPath(doc_buffer->absFileName(), 20);
3837 if (doc_buffer->notifiesExternalModification()) {
3838 docstring text = _("The current version will be lost. "
3839 "Are you sure you want to load the version on disk "
3840 "of the document %1$s?");
3841 ret = Alert::prompt(_("Reload saved document?"),
3842 bformat(text, file), 1, 1,
3843 _("&Reload"), _("&Cancel"));
3845 docstring text = _("Any changes will be lost. "
3846 "Are you sure you want to revert to the saved version "
3847 "of the document %1$s?");
3848 ret = Alert::prompt(_("Revert to saved document?"),
3849 bformat(text, file), 1, 1,
3850 _("&Revert"), _("&Cancel"));
3855 doc_buffer->markClean();
3856 reloadBuffer(*doc_buffer);
3857 dr.forceBufferUpdate();
3862 case LFUN_BUFFER_WRITE:
3863 LASSERT(doc_buffer, break);
3864 saveBuffer(*doc_buffer);
3867 case LFUN_BUFFER_WRITE_AS:
3868 LASSERT(doc_buffer, break);
3869 renameBuffer(*doc_buffer, cmd.argument());
3872 case LFUN_BUFFER_WRITE_ALL: {
3873 Buffer * first = theBufferList().first();
3876 message(_("Saving all documents..."));
3877 // We cannot use a for loop as the buffer list cycles.
3880 if (!b->isClean()) {
3882 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3884 b = theBufferList().next(b);
3885 } while (b != first);
3886 dr.setMessage(_("All documents saved."));
3890 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3891 LASSERT(doc_buffer, break);
3892 doc_buffer->clearExternalModification();
3895 case LFUN_BUFFER_CLOSE:
3899 case LFUN_BUFFER_CLOSE_ALL:
3903 case LFUN_DEVEL_MODE_TOGGLE:
3904 devel_mode_ = !devel_mode_;
3906 dr.setMessage(_("Developer mode is now enabled."));
3908 dr.setMessage(_("Developer mode is now disabled."));
3911 case LFUN_TOOLBAR_TOGGLE: {
3912 string const name = cmd.getArg(0);
3913 if (GuiToolbar * t = toolbar(name))
3918 case LFUN_TOOLBAR_MOVABLE: {
3919 string const name = cmd.getArg(0);
3921 // toggle (all) toolbars movablility
3922 toolbarsMovable_ = !toolbarsMovable_;
3923 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3924 GuiToolbar * tb = toolbar(ti.name);
3925 if (tb && tb->isMovable() != toolbarsMovable_)
3926 // toggle toolbar movablity if it does not fit lock
3927 // (all) toolbars positions state silent = true, since
3928 // status bar notifications are slow
3931 if (toolbarsMovable_)
3932 dr.setMessage(_("Toolbars unlocked."));
3934 dr.setMessage(_("Toolbars locked."));
3935 } else if (GuiToolbar * t = toolbar(name)) {
3936 // toggle current toolbar movablity
3938 // update lock (all) toolbars positions
3939 updateLockToolbars();
3944 case LFUN_ICON_SIZE: {
3945 QSize size = d.iconSize(cmd.argument());
3947 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3948 size.width(), size.height()));
3952 case LFUN_DIALOG_UPDATE: {
3953 string const name = to_utf8(cmd.argument());
3954 if (name == "prefs" || name == "document")
3955 updateDialog(name, string());
3956 else if (name == "paragraph")
3957 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3958 else if (currentBufferView()) {
3959 Inset * inset = currentBufferView()->editedInset(name);
3960 // Can only update a dialog connected to an existing inset
3962 // FIXME: get rid of this indirection; GuiView ask the inset
3963 // if he is kind enough to update itself...
3964 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3965 //FIXME: pass DispatchResult here?
3966 inset->dispatch(currentBufferView()->cursor(), fr);
3972 case LFUN_DIALOG_TOGGLE: {
3973 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3974 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3975 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3979 case LFUN_DIALOG_DISCONNECT_INSET:
3980 disconnectDialog(to_utf8(cmd.argument()));
3983 case LFUN_DIALOG_HIDE: {
3984 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3988 case LFUN_DIALOG_SHOW: {
3989 string const name = cmd.getArg(0);
3990 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3992 if (name == "character") {
3993 data = freefont2string();
3995 showDialog("character", data);
3996 } else if (name == "latexlog") {
3997 // getStatus checks that
3998 LATTEST(doc_buffer);
3999 Buffer::LogType type;
4000 string const logfile = doc_buffer->logName(&type);
4002 case Buffer::latexlog:
4005 case Buffer::buildlog:
4009 data += Lexer::quoteString(logfile);
4010 showDialog("log", data);
4011 } else if (name == "vclog") {
4012 // getStatus checks that
4013 LATTEST(doc_buffer);
4014 string const data = "vc " +
4015 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4016 showDialog("log", data);
4017 } else if (name == "symbols") {
4018 data = bv->cursor().getEncoding()->name();
4020 showDialog("symbols", data);
4022 } else if (name == "prefs" && isFullScreen()) {
4023 lfunUiToggle("fullscreen");
4024 showDialog("prefs", data);
4026 showDialog(name, data);
4031 dr.setMessage(cmd.argument());
4034 case LFUN_UI_TOGGLE: {
4035 string arg = cmd.getArg(0);
4036 if (!lfunUiToggle(arg)) {
4037 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4038 dr.setMessage(bformat(msg, from_utf8(arg)));
4040 // Make sure the keyboard focus stays in the work area.
4045 case LFUN_VIEW_SPLIT: {
4046 LASSERT(doc_buffer, break);
4047 string const orientation = cmd.getArg(0);
4048 d.splitter_->setOrientation(orientation == "vertical"
4049 ? Qt::Vertical : Qt::Horizontal);
4050 TabWorkArea * twa = addTabWorkArea();
4051 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4052 setCurrentWorkArea(wa);
4055 case LFUN_TAB_GROUP_CLOSE:
4056 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4057 closeTabWorkArea(twa);
4058 d.current_work_area_ = 0;
4059 twa = d.currentTabWorkArea();
4060 // Switch to the next GuiWorkArea in the found TabWorkArea.
4062 // Make sure the work area is up to date.
4063 setCurrentWorkArea(twa->currentWorkArea());
4065 setCurrentWorkArea(0);
4070 case LFUN_VIEW_CLOSE:
4071 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4072 closeWorkArea(twa->currentWorkArea());
4073 d.current_work_area_ = 0;
4074 twa = d.currentTabWorkArea();
4075 // Switch to the next GuiWorkArea in the found TabWorkArea.
4077 // Make sure the work area is up to date.
4078 setCurrentWorkArea(twa->currentWorkArea());
4080 setCurrentWorkArea(0);
4085 case LFUN_COMPLETION_INLINE:
4086 if (d.current_work_area_)
4087 d.current_work_area_->completer().showInline();
4090 case LFUN_COMPLETION_POPUP:
4091 if (d.current_work_area_)
4092 d.current_work_area_->completer().showPopup();
4097 if (d.current_work_area_)
4098 d.current_work_area_->completer().tab();
4101 case LFUN_COMPLETION_CANCEL:
4102 if (d.current_work_area_) {
4103 if (d.current_work_area_->completer().popupVisible())
4104 d.current_work_area_->completer().hidePopup();
4106 d.current_work_area_->completer().hideInline();
4110 case LFUN_COMPLETION_ACCEPT:
4111 if (d.current_work_area_)
4112 d.current_work_area_->completer().activate();
4115 case LFUN_BUFFER_ZOOM_IN:
4116 case LFUN_BUFFER_ZOOM_OUT:
4117 case LFUN_BUFFER_ZOOM: {
4118 // use a signed temp to avoid overflow
4119 int zoom = lyxrc.currentZoom;
4120 if (cmd.argument().empty()) {
4121 if (cmd.action() == LFUN_BUFFER_ZOOM)
4123 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4128 if (cmd.action() == LFUN_BUFFER_ZOOM)
4129 zoom = convert<int>(cmd.argument());
4130 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4131 zoom += convert<int>(cmd.argument());
4133 zoom -= convert<int>(cmd.argument());
4136 if (zoom < static_cast<int>(zoom_min_))
4139 lyxrc.currentZoom = zoom;
4141 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4143 // The global QPixmapCache is used in GuiPainter to cache text
4144 // painting so we must reset it.
4145 QPixmapCache::clear();
4146 guiApp->fontLoader().update();
4147 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4151 case LFUN_VC_REGISTER:
4152 case LFUN_VC_RENAME:
4154 case LFUN_VC_CHECK_IN:
4155 case LFUN_VC_CHECK_OUT:
4156 case LFUN_VC_REPO_UPDATE:
4157 case LFUN_VC_LOCKING_TOGGLE:
4158 case LFUN_VC_REVERT:
4159 case LFUN_VC_UNDO_LAST:
4160 case LFUN_VC_COMMAND:
4161 case LFUN_VC_COMPARE:
4162 dispatchVC(cmd, dr);
4165 case LFUN_SERVER_GOTO_FILE_ROW:
4166 if(goToFileRow(to_utf8(cmd.argument())))
4167 dr.screenUpdate(Update::Force | Update::FitCursor);
4170 case LFUN_LYX_ACTIVATE:
4174 case LFUN_FORWARD_SEARCH: {
4175 // it seems safe to assume we have a document buffer, since
4176 // getStatus wants one.
4177 LATTEST(doc_buffer);
4178 Buffer const * doc_master = doc_buffer->masterBuffer();
4179 FileName const path(doc_master->temppath());
4180 string const texname = doc_master->isChild(doc_buffer)
4181 ? DocFileName(changeExtension(
4182 doc_buffer->absFileName(),
4183 "tex")).mangledFileName()
4184 : doc_buffer->latexName();
4185 string const fulltexname =
4186 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4187 string const mastername =
4188 removeExtension(doc_master->latexName());
4189 FileName const dviname(addName(path.absFileName(),
4190 addExtension(mastername, "dvi")));
4191 FileName const pdfname(addName(path.absFileName(),
4192 addExtension(mastername, "pdf")));
4193 bool const have_dvi = dviname.exists();
4194 bool const have_pdf = pdfname.exists();
4195 if (!have_dvi && !have_pdf) {
4196 dr.setMessage(_("Please, preview the document first."));
4199 string outname = dviname.onlyFileName();
4200 string command = lyxrc.forward_search_dvi;
4201 if (!have_dvi || (have_pdf &&
4202 pdfname.lastModified() > dviname.lastModified())) {
4203 outname = pdfname.onlyFileName();
4204 command = lyxrc.forward_search_pdf;
4207 DocIterator cur = bv->cursor();
4208 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4209 LYXERR(Debug::ACTION, "Forward search: row:" << row
4211 if (row == -1 || command.empty()) {
4212 dr.setMessage(_("Couldn't proceed."));
4215 string texrow = convert<string>(row);
4217 command = subst(command, "$$n", texrow);
4218 command = subst(command, "$$f", fulltexname);
4219 command = subst(command, "$$t", texname);
4220 command = subst(command, "$$o", outname);
4222 PathChanger p(path);
4224 one.startscript(Systemcall::DontWait, command);
4228 case LFUN_SPELLING_CONTINUOUSLY:
4229 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4230 dr.screenUpdate(Update::Force);
4234 // The LFUN must be for one of BufferView, Buffer or Cursor;
4236 dispatchToBufferView(cmd, dr);
4240 // Part of automatic menu appearance feature.
4241 if (isFullScreen()) {
4242 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4246 // Need to update bv because many LFUNs here might have destroyed it
4247 bv = currentBufferView();
4249 // Clear non-empty selections
4250 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4252 Cursor & cur = bv->cursor();
4253 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4254 cur.clearSelection();
4260 bool GuiView::lfunUiToggle(string const & ui_component)
4262 if (ui_component == "scrollbar") {
4263 // hide() is of no help
4264 if (d.current_work_area_->verticalScrollBarPolicy() ==
4265 Qt::ScrollBarAlwaysOff)
4267 d.current_work_area_->setVerticalScrollBarPolicy(
4268 Qt::ScrollBarAsNeeded);
4270 d.current_work_area_->setVerticalScrollBarPolicy(
4271 Qt::ScrollBarAlwaysOff);
4272 } else if (ui_component == "statusbar") {
4273 statusBar()->setVisible(!statusBar()->isVisible());
4274 } else if (ui_component == "menubar") {
4275 menuBar()->setVisible(!menuBar()->isVisible());
4277 if (ui_component == "frame") {
4279 getContentsMargins(&l, &t, &r, &b);
4280 //are the frames in default state?
4281 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4283 setContentsMargins(-2, -2, -2, -2);
4285 setContentsMargins(0, 0, 0, 0);
4288 if (ui_component == "fullscreen") {
4296 void GuiView::toggleFullScreen()
4298 if (isFullScreen()) {
4299 for (int i = 0; i != d.splitter_->count(); ++i)
4300 d.tabWorkArea(i)->setFullScreen(false);
4301 setContentsMargins(0, 0, 0, 0);
4302 setWindowState(windowState() ^ Qt::WindowFullScreen);
4305 statusBar()->show();
4308 hideDialogs("prefs", 0);
4309 for (int i = 0; i != d.splitter_->count(); ++i)
4310 d.tabWorkArea(i)->setFullScreen(true);
4311 setContentsMargins(-2, -2, -2, -2);
4313 setWindowState(windowState() ^ Qt::WindowFullScreen);
4314 if (lyxrc.full_screen_statusbar)
4315 statusBar()->hide();
4316 if (lyxrc.full_screen_menubar)
4318 if (lyxrc.full_screen_toolbars) {
4319 ToolbarMap::iterator end = d.toolbars_.end();
4320 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4325 // give dialogs like the TOC a chance to adapt
4330 Buffer const * GuiView::updateInset(Inset const * inset)
4335 Buffer const * inset_buffer = &(inset->buffer());
4337 for (int i = 0; i != d.splitter_->count(); ++i) {
4338 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4341 Buffer const * buffer = &(wa->bufferView().buffer());
4342 if (inset_buffer == buffer)
4343 wa->scheduleRedraw();
4345 return inset_buffer;
4349 void GuiView::restartCursor()
4351 /* When we move around, or type, it's nice to be able to see
4352 * the cursor immediately after the keypress.
4354 if (d.current_work_area_)
4355 d.current_work_area_->startBlinkingCursor();
4357 // Take this occasion to update the other GUI elements.
4363 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4365 if (d.current_work_area_)
4366 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4371 // This list should be kept in sync with the list of insets in
4372 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4373 // dialog should have the same name as the inset.
4374 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4375 // docs in LyXAction.cpp.
4377 char const * const dialognames[] = {
4379 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4380 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4381 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4382 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4383 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4384 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4385 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4386 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4388 char const * const * const end_dialognames =
4389 dialognames + (sizeof(dialognames) / sizeof(char *));
4393 cmpCStr(char const * name) : name_(name) {}
4394 bool operator()(char const * other) {
4395 return strcmp(other, name_) == 0;
4402 bool isValidName(string const & name)
4404 return find_if(dialognames, end_dialognames,
4405 cmpCStr(name.c_str())) != end_dialognames;
4411 void GuiView::resetDialogs()
4413 // Make sure that no LFUN uses any GuiView.
4414 guiApp->setCurrentView(0);
4418 constructToolbars();
4419 guiApp->menus().fillMenuBar(menuBar(), this, false);
4420 d.layout_->updateContents(true);
4421 // Now update controls with current buffer.
4422 guiApp->setCurrentView(this);
4428 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4430 if (!isValidName(name))
4433 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4435 if (it != d.dialogs_.end()) {
4437 it->second->hideView();
4438 return it->second.get();
4441 Dialog * dialog = build(name);
4442 d.dialogs_[name].reset(dialog);
4443 if (lyxrc.allow_geometry_session)
4444 dialog->restoreSession();
4451 void GuiView::showDialog(string const & name, string const & data,
4454 triggerShowDialog(toqstr(name), toqstr(data), inset);
4458 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4464 const string name = fromqstr(qname);
4465 const string data = fromqstr(qdata);
4469 Dialog * dialog = findOrBuild(name, false);
4471 bool const visible = dialog->isVisibleView();
4472 dialog->showData(data);
4473 if (inset && currentBufferView())
4474 currentBufferView()->editInset(name, inset);
4475 // We only set the focus to the new dialog if it was not yet
4476 // visible in order not to change the existing previous behaviour
4478 // activateWindow is needed for floating dockviews
4479 dialog->asQWidget()->raise();
4480 dialog->asQWidget()->activateWindow();
4481 dialog->asQWidget()->setFocus();
4485 catch (ExceptionMessage const & ex) {
4493 bool GuiView::isDialogVisible(string const & name) const
4495 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4496 if (it == d.dialogs_.end())
4498 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4502 void GuiView::hideDialog(string const & name, Inset * inset)
4504 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4505 if (it == d.dialogs_.end())
4509 if (!currentBufferView())
4511 if (inset != currentBufferView()->editedInset(name))
4515 Dialog * const dialog = it->second.get();
4516 if (dialog->isVisibleView())
4518 if (currentBufferView())
4519 currentBufferView()->editInset(name, 0);
4523 void GuiView::disconnectDialog(string const & name)
4525 if (!isValidName(name))
4527 if (currentBufferView())
4528 currentBufferView()->editInset(name, 0);
4532 void GuiView::hideAll() const
4534 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4535 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4537 for(; it != end; ++it)
4538 it->second->hideView();
4542 void GuiView::updateDialogs()
4544 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4545 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4547 for(; it != end; ++it) {
4548 Dialog * dialog = it->second.get();
4550 if (dialog->needBufferOpen() && !documentBufferView())
4551 hideDialog(fromqstr(dialog->name()), 0);
4552 else if (dialog->isVisibleView())
4553 dialog->checkStatus();
4560 Dialog * createDialog(GuiView & lv, string const & name);
4562 // will be replaced by a proper factory...
4563 Dialog * createGuiAbout(GuiView & lv);
4564 Dialog * createGuiBibtex(GuiView & lv);
4565 Dialog * createGuiChanges(GuiView & lv);
4566 Dialog * createGuiCharacter(GuiView & lv);
4567 Dialog * createGuiCitation(GuiView & lv);
4568 Dialog * createGuiCompare(GuiView & lv);
4569 Dialog * createGuiCompareHistory(GuiView & lv);
4570 Dialog * createGuiDelimiter(GuiView & lv);
4571 Dialog * createGuiDocument(GuiView & lv);
4572 Dialog * createGuiErrorList(GuiView & lv);
4573 Dialog * createGuiExternal(GuiView & lv);
4574 Dialog * createGuiGraphics(GuiView & lv);
4575 Dialog * createGuiInclude(GuiView & lv);
4576 Dialog * createGuiIndex(GuiView & lv);
4577 Dialog * createGuiListings(GuiView & lv);
4578 Dialog * createGuiLog(GuiView & lv);
4579 Dialog * createGuiMathMatrix(GuiView & lv);
4580 Dialog * createGuiNote(GuiView & lv);
4581 Dialog * createGuiParagraph(GuiView & lv);
4582 Dialog * createGuiPhantom(GuiView & lv);
4583 Dialog * createGuiPreferences(GuiView & lv);
4584 Dialog * createGuiPrint(GuiView & lv);
4585 Dialog * createGuiPrintindex(GuiView & lv);
4586 Dialog * createGuiRef(GuiView & lv);
4587 Dialog * createGuiSearch(GuiView & lv);
4588 Dialog * createGuiSearchAdv(GuiView & lv);
4589 Dialog * createGuiSendTo(GuiView & lv);
4590 Dialog * createGuiShowFile(GuiView & lv);
4591 Dialog * createGuiSpellchecker(GuiView & lv);
4592 Dialog * createGuiSymbols(GuiView & lv);
4593 Dialog * createGuiTabularCreate(GuiView & lv);
4594 Dialog * createGuiTexInfo(GuiView & lv);
4595 Dialog * createGuiToc(GuiView & lv);
4596 Dialog * createGuiThesaurus(GuiView & lv);
4597 Dialog * createGuiViewSource(GuiView & lv);
4598 Dialog * createGuiWrap(GuiView & lv);
4599 Dialog * createGuiProgressView(GuiView & lv);
4603 Dialog * GuiView::build(string const & name)
4605 LASSERT(isValidName(name), return 0);
4607 Dialog * dialog = createDialog(*this, name);
4611 if (name == "aboutlyx")
4612 return createGuiAbout(*this);
4613 if (name == "bibtex")
4614 return createGuiBibtex(*this);
4615 if (name == "changes")
4616 return createGuiChanges(*this);
4617 if (name == "character")
4618 return createGuiCharacter(*this);
4619 if (name == "citation")
4620 return createGuiCitation(*this);
4621 if (name == "compare")
4622 return createGuiCompare(*this);
4623 if (name == "comparehistory")
4624 return createGuiCompareHistory(*this);
4625 if (name == "document")
4626 return createGuiDocument(*this);
4627 if (name == "errorlist")
4628 return createGuiErrorList(*this);
4629 if (name == "external")
4630 return createGuiExternal(*this);
4632 return createGuiShowFile(*this);
4633 if (name == "findreplace")
4634 return createGuiSearch(*this);
4635 if (name == "findreplaceadv")
4636 return createGuiSearchAdv(*this);
4637 if (name == "graphics")
4638 return createGuiGraphics(*this);
4639 if (name == "include")
4640 return createGuiInclude(*this);
4641 if (name == "index")
4642 return createGuiIndex(*this);
4643 if (name == "index_print")
4644 return createGuiPrintindex(*this);
4645 if (name == "listings")
4646 return createGuiListings(*this);
4648 return createGuiLog(*this);
4649 if (name == "mathdelimiter")
4650 return createGuiDelimiter(*this);
4651 if (name == "mathmatrix")
4652 return createGuiMathMatrix(*this);
4654 return createGuiNote(*this);
4655 if (name == "paragraph")
4656 return createGuiParagraph(*this);
4657 if (name == "phantom")
4658 return createGuiPhantom(*this);
4659 if (name == "prefs")
4660 return createGuiPreferences(*this);
4662 return createGuiRef(*this);
4663 if (name == "sendto")
4664 return createGuiSendTo(*this);
4665 if (name == "spellchecker")
4666 return createGuiSpellchecker(*this);
4667 if (name == "symbols")
4668 return createGuiSymbols(*this);
4669 if (name == "tabularcreate")
4670 return createGuiTabularCreate(*this);
4671 if (name == "texinfo")
4672 return createGuiTexInfo(*this);
4673 if (name == "thesaurus")
4674 return createGuiThesaurus(*this);
4676 return createGuiToc(*this);
4677 if (name == "view-source")
4678 return createGuiViewSource(*this);
4680 return createGuiWrap(*this);
4681 if (name == "progress")
4682 return createGuiProgressView(*this);
4688 SEMenu::SEMenu(QWidget * parent)
4690 QAction * action = addAction(qt_("Disable Shell Escape"));
4691 connect(action, SIGNAL(triggered()),
4692 parent, SLOT(disableShellEscape()));
4696 } // namespace frontend
4699 #include "moc_GuiView.cpp"