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)
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 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
533 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
534 d.autosave_timeout_.start();
536 connect(&d.statusbar_timer_, SIGNAL(timeout()),
537 this, SLOT(clearMessage()));
539 // We don't want to keep the window in memory if it is closed.
540 setAttribute(Qt::WA_DeleteOnClose, true);
542 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
543 // QIcon::fromTheme was introduced in Qt 4.6
544 #if (QT_VERSION >= 0x040600)
545 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
546 // since the icon is provided in the application bundle. We use a themed
547 // version when available and use the bundled one as fallback.
548 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
556 // use tabbed dock area for multiple docks
557 // (such as "source" and "messages")
558 setDockOptions(QMainWindow::ForceTabbedDocks);
561 setAcceptDrops(true);
563 // add busy indicator to statusbar
564 QLabel * busylabel = new QLabel(statusBar());
565 statusBar()->addPermanentWidget(busylabel);
566 search_mode mode = theGuiApp()->imageSearchMode();
567 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
568 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
569 busylabel->setMovie(busyanim);
573 connect(&d.processing_thread_watcher_, SIGNAL(started()),
574 busylabel, SLOT(show()));
575 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
576 busylabel, SLOT(hide()));
578 QFontMetrics const fm(statusBar()->fontMetrics());
579 int const roheight = max(int(d.normalIconSize), fm.height());
580 QSize const rosize(roheight, roheight);
581 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
582 read_only_ = new QLabel(statusBar());
583 read_only_->setPixmap(readonly);
584 read_only_->setScaledContents(true);
585 read_only_->setAlignment(Qt::AlignCenter);
587 statusBar()->addPermanentWidget(read_only_);
589 version_control_ = new QLabel(statusBar());
590 version_control_->setAlignment(Qt::AlignCenter);
591 version_control_->setFrameStyle(QFrame::StyledPanel);
592 version_control_->hide();
593 statusBar()->addPermanentWidget(version_control_);
595 statusBar()->setSizeGripEnabled(true);
598 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
599 SLOT(autoSaveThreadFinished()));
601 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
602 SLOT(processingThreadStarted()));
603 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
604 SLOT(processingThreadFinished()));
606 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
607 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
609 // set custom application bars context menu, e.g. tool bar and menu bar
610 setContextMenuPolicy(Qt::CustomContextMenu);
611 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
612 SLOT(toolBarPopup(const QPoint &)));
614 // Forbid too small unresizable window because it can happen
615 // with some window manager under X11.
616 setMinimumSize(300, 200);
618 if (lyxrc.allow_geometry_session) {
619 // Now take care of session management.
624 // no session handling, default to a sane size.
625 setGeometry(50, 50, 690, 510);
628 // clear session data if any.
630 settings.remove("views");
640 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
642 QVector<GuiWorkArea*> areas;
643 for (int i = 0; i < tabWorkAreaCount(); i++) {
644 TabWorkArea* ta = tabWorkArea(i);
645 for (int u = 0; u < ta->count(); u++) {
646 areas << ta->workArea(u);
652 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
653 string const & format)
655 docstring const fmt = theFormats().prettyName(format);
658 case Buffer::ExportSuccess:
659 msg = bformat(_("Successful export to format: %1$s"), fmt);
661 case Buffer::ExportCancel:
662 msg = _("Document export cancelled.");
664 case Buffer::ExportError:
665 case Buffer::ExportNoPathToFormat:
666 case Buffer::ExportTexPathHasSpaces:
667 case Buffer::ExportConverterError:
668 msg = bformat(_("Error while exporting format: %1$s"), fmt);
670 case Buffer::PreviewSuccess:
671 msg = bformat(_("Successful preview of format: %1$s"), fmt);
673 case Buffer::PreviewError:
674 msg = bformat(_("Error while previewing format: %1$s"), fmt);
681 void GuiView::processingThreadStarted()
686 void GuiView::processingThreadFinished()
688 QFutureWatcher<Buffer::ExportStatus> const * watcher =
689 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
691 Buffer::ExportStatus const status = watcher->result();
692 handleExportStatus(this, status, d.processing_format);
695 BufferView const * const bv = currentBufferView();
696 if (bv && !bv->buffer().errorList("Export").empty()) {
700 errors(d.last_export_format);
704 void GuiView::autoSaveThreadFinished()
706 QFutureWatcher<docstring> const * watcher =
707 static_cast<QFutureWatcher<docstring> const *>(sender());
708 message(watcher->result());
713 void GuiView::saveLayout() const
716 settings.setValue("zoom", lyxrc.currentZoom);
717 settings.beginGroup("views");
718 settings.beginGroup(QString::number(id_));
719 #if defined(Q_WS_X11) || defined(QPA_XCB)
720 settings.setValue("pos", pos());
721 settings.setValue("size", size());
723 settings.setValue("geometry", saveGeometry());
725 settings.setValue("layout", saveState(0));
726 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
730 void GuiView::saveUISettings() const
732 // Save the toolbar private states
733 ToolbarMap::iterator end = d.toolbars_.end();
734 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
735 it->second->saveSession();
736 // Now take care of all other dialogs
737 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
738 for (; it!= d.dialogs_.end(); ++it)
739 it->second->saveSession();
743 bool GuiView::restoreLayout()
746 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
747 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
748 settings.beginGroup("views");
749 settings.beginGroup(QString::number(id_));
750 QString const icon_key = "icon_size";
751 if (!settings.contains(icon_key))
754 //code below is skipped when when ~/.config/LyX is (re)created
755 setIconSize(d.iconSize(settings.value(icon_key).toString()));
757 #if defined(Q_WS_X11) || defined(QPA_XCB)
758 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
759 QSize size = settings.value("size", QSize(690, 510)).toSize();
763 // Work-around for bug #6034: the window ends up in an undetermined
764 // state when trying to restore a maximized window when it is
765 // already maximized.
766 if (!(windowState() & Qt::WindowMaximized))
767 if (!restoreGeometry(settings.value("geometry").toByteArray()))
768 setGeometry(50, 50, 690, 510);
770 // Make sure layout is correctly oriented.
771 setLayoutDirection(qApp->layoutDirection());
773 // Allow the toc and view-source dock widget to be restored if needed.
775 if ((dialog = findOrBuild("toc", true)))
776 // see bug 5082. At least setup title and enabled state.
777 // Visibility will be adjusted by restoreState below.
778 dialog->prepareView();
779 if ((dialog = findOrBuild("view-source", true)))
780 dialog->prepareView();
781 if ((dialog = findOrBuild("progress", true)))
782 dialog->prepareView();
784 if (!restoreState(settings.value("layout").toByteArray(), 0))
787 // init the toolbars that have not been restored
788 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
789 Toolbars::Infos::iterator end = guiApp->toolbars().end();
790 for (; cit != end; ++cit) {
791 GuiToolbar * tb = toolbar(cit->name);
792 if (tb && !tb->isRestored())
793 initToolbar(cit->name);
796 // update lock (all) toolbars positions
797 updateLockToolbars();
804 GuiToolbar * GuiView::toolbar(string const & name)
806 ToolbarMap::iterator it = d.toolbars_.find(name);
807 if (it != d.toolbars_.end())
810 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
815 void GuiView::updateLockToolbars()
817 toolbarsMovable_ = false;
818 for (ToolbarInfo const & info : guiApp->toolbars()) {
819 GuiToolbar * tb = toolbar(info.name);
820 if (tb && tb->isMovable())
821 toolbarsMovable_ = true;
826 void GuiView::constructToolbars()
828 ToolbarMap::iterator it = d.toolbars_.begin();
829 for (; it != d.toolbars_.end(); ++it)
833 // I don't like doing this here, but the standard toolbar
834 // destroys this object when it's destroyed itself (vfr)
835 d.layout_ = new LayoutBox(*this);
836 d.stack_widget_->addWidget(d.layout_);
837 d.layout_->move(0,0);
839 // extracts the toolbars from the backend
840 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
841 Toolbars::Infos::iterator end = guiApp->toolbars().end();
842 for (; cit != end; ++cit)
843 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
847 void GuiView::initToolbars()
849 // extracts the toolbars from the backend
850 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
851 Toolbars::Infos::iterator end = guiApp->toolbars().end();
852 for (; cit != end; ++cit)
853 initToolbar(cit->name);
857 void GuiView::initToolbar(string const & name)
859 GuiToolbar * tb = toolbar(name);
862 int const visibility = guiApp->toolbars().defaultVisibility(name);
863 bool newline = !(visibility & Toolbars::SAMEROW);
864 tb->setVisible(false);
865 tb->setVisibility(visibility);
867 if (visibility & Toolbars::TOP) {
869 addToolBarBreak(Qt::TopToolBarArea);
870 addToolBar(Qt::TopToolBarArea, tb);
873 if (visibility & Toolbars::BOTTOM) {
875 addToolBarBreak(Qt::BottomToolBarArea);
876 addToolBar(Qt::BottomToolBarArea, tb);
879 if (visibility & Toolbars::LEFT) {
881 addToolBarBreak(Qt::LeftToolBarArea);
882 addToolBar(Qt::LeftToolBarArea, tb);
885 if (visibility & Toolbars::RIGHT) {
887 addToolBarBreak(Qt::RightToolBarArea);
888 addToolBar(Qt::RightToolBarArea, tb);
891 if (visibility & Toolbars::ON)
892 tb->setVisible(true);
894 tb->setMovable(true);
898 TocModels & GuiView::tocModels()
900 return d.toc_models_;
904 void GuiView::setFocus()
906 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
907 QMainWindow::setFocus();
911 bool GuiView::hasFocus() const
913 if (currentWorkArea())
914 return currentWorkArea()->hasFocus();
915 if (currentMainWorkArea())
916 return currentMainWorkArea()->hasFocus();
917 return d.bg_widget_->hasFocus();
921 void GuiView::focusInEvent(QFocusEvent * e)
923 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
924 QMainWindow::focusInEvent(e);
925 // Make sure guiApp points to the correct view.
926 guiApp->setCurrentView(this);
927 if (currentWorkArea())
928 currentWorkArea()->setFocus();
929 else if (currentMainWorkArea())
930 currentMainWorkArea()->setFocus();
932 d.bg_widget_->setFocus();
936 void GuiView::showEvent(QShowEvent * e)
938 LYXERR(Debug::GUI, "Passed Geometry "
939 << size().height() << "x" << size().width()
940 << "+" << pos().x() << "+" << pos().y());
942 if (d.splitter_->count() == 0)
943 // No work area, switch to the background widget.
947 QMainWindow::showEvent(e);
951 bool GuiView::closeScheduled()
958 bool GuiView::prepareAllBuffersForLogout()
960 Buffer * first = theBufferList().first();
964 // First, iterate over all buffers and ask the users if unsaved
965 // changes should be saved.
966 // We cannot use a for loop as the buffer list cycles.
969 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
971 b = theBufferList().next(b);
972 } while (b != first);
974 // Next, save session state
975 // When a view/window was closed before without quitting LyX, there
976 // are already entries in the lastOpened list.
977 theSession().lastOpened().clear();
984 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
985 ** is responsibility of the container (e.g., dialog)
987 void GuiView::closeEvent(QCloseEvent * close_event)
989 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
991 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
992 Alert::warning(_("Exit LyX"),
993 _("LyX could not be closed because documents are being processed by LyX."));
994 close_event->setAccepted(false);
998 // If the user pressed the x (so we didn't call closeView
999 // programmatically), we want to clear all existing entries.
1001 theSession().lastOpened().clear();
1006 // it can happen that this event arrives without selecting the view,
1007 // e.g. when clicking the close button on a background window.
1009 if (!closeWorkAreaAll()) {
1011 close_event->ignore();
1015 // Make sure that nothing will use this to be closed View.
1016 guiApp->unregisterView(this);
1018 if (isFullScreen()) {
1019 // Switch off fullscreen before closing.
1024 // Make sure the timer time out will not trigger a statusbar update.
1025 d.statusbar_timer_.stop();
1027 // Saving fullscreen requires additional tweaks in the toolbar code.
1028 // It wouldn't also work under linux natively.
1029 if (lyxrc.allow_geometry_session) {
1034 close_event->accept();
1038 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1040 if (event->mimeData()->hasUrls())
1042 /// \todo Ask lyx-devel is this is enough:
1043 /// if (event->mimeData()->hasFormat("text/plain"))
1044 /// event->acceptProposedAction();
1048 void GuiView::dropEvent(QDropEvent * event)
1050 QList<QUrl> files = event->mimeData()->urls();
1051 if (files.isEmpty())
1054 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1055 for (int i = 0; i != files.size(); ++i) {
1056 string const file = os::internal_path(fromqstr(
1057 files.at(i).toLocalFile()));
1061 string const ext = support::getExtension(file);
1062 vector<const Format *> found_formats;
1064 // Find all formats that have the correct extension.
1065 vector<const Format *> const & import_formats
1066 = theConverters().importableFormats();
1067 vector<const Format *>::const_iterator it = import_formats.begin();
1068 for (; it != import_formats.end(); ++it)
1069 if ((*it)->hasExtension(ext))
1070 found_formats.push_back(*it);
1073 if (found_formats.size() >= 1) {
1074 if (found_formats.size() > 1) {
1075 //FIXME: show a dialog to choose the correct importable format
1076 LYXERR(Debug::FILES,
1077 "Multiple importable formats found, selecting first");
1079 string const arg = found_formats[0]->name() + " " + file;
1080 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1083 //FIXME: do we have to explicitly check whether it's a lyx file?
1084 LYXERR(Debug::FILES,
1085 "No formats found, trying to open it as a lyx file");
1086 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1088 // add the functions to the queue
1089 guiApp->addToFuncRequestQueue(cmd);
1092 // now process the collected functions. We perform the events
1093 // asynchronously. This prevents potential problems in case the
1094 // BufferView is closed within an event.
1095 guiApp->processFuncRequestQueueAsync();
1099 void GuiView::message(docstring const & str)
1101 if (ForkedProcess::iAmAChild())
1104 // call is moved to GUI-thread by GuiProgress
1105 d.progress_->appendMessage(toqstr(str));
1109 void GuiView::clearMessageText()
1111 message(docstring());
1115 void GuiView::updateStatusBarMessage(QString const & str)
1117 statusBar()->showMessage(str);
1118 d.statusbar_timer_.stop();
1119 d.statusbar_timer_.start(3000);
1123 void GuiView::clearMessage()
1125 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1126 // the hasFocus function mostly returns false, even if the focus is on
1127 // a workarea in this view.
1131 d.statusbar_timer_.stop();
1135 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1137 if (wa != d.current_work_area_
1138 || wa->bufferView().buffer().isInternal())
1140 Buffer const & buf = wa->bufferView().buffer();
1141 // Set the windows title
1142 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1143 if (buf.notifiesExternalModification()) {
1144 title = bformat(_("%1$s (modified externally)"), title);
1145 // If the external modification status has changed, then maybe the status of
1146 // buffer-save has changed too.
1150 title += from_ascii(" - LyX");
1152 setWindowTitle(toqstr(title));
1153 // Sets the path for the window: this is used by OSX to
1154 // allow a context click on the title bar showing a menu
1155 // with the path up to the file
1156 setWindowFilePath(toqstr(buf.absFileName()));
1157 // Tell Qt whether the current document is changed
1158 setWindowModified(!buf.isClean());
1160 if (buf.hasReadonlyFlag())
1165 if (buf.lyxvc().inUse()) {
1166 version_control_->show();
1167 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1169 version_control_->hide();
1173 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1175 if (d.current_work_area_)
1176 // disconnect the current work area from all slots
1177 QObject::disconnect(d.current_work_area_, 0, this, 0);
1179 disconnectBufferView();
1180 connectBufferView(wa->bufferView());
1181 connectBuffer(wa->bufferView().buffer());
1182 d.current_work_area_ = wa;
1183 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1184 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1185 QObject::connect(wa, SIGNAL(busy(bool)),
1186 this, SLOT(setBusy(bool)));
1187 // connection of a signal to a signal
1188 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1189 this, SIGNAL(bufferViewChanged()));
1190 Q_EMIT updateWindowTitle(wa);
1191 Q_EMIT bufferViewChanged();
1195 void GuiView::onBufferViewChanged()
1198 // Buffer-dependent dialogs must be updated. This is done here because
1199 // some dialogs require buffer()->text.
1204 void GuiView::on_lastWorkAreaRemoved()
1207 // We already are in a close event. Nothing more to do.
1210 if (d.splitter_->count() > 1)
1211 // We have a splitter so don't close anything.
1214 // Reset and updates the dialogs.
1215 Q_EMIT bufferViewChanged();
1220 if (lyxrc.open_buffers_in_tabs)
1221 // Nothing more to do, the window should stay open.
1224 if (guiApp->viewIds().size() > 1) {
1230 // On Mac we also close the last window because the application stay
1231 // resident in memory. On other platforms we don't close the last
1232 // window because this would quit the application.
1238 void GuiView::updateStatusBar()
1240 // let the user see the explicit message
1241 if (d.statusbar_timer_.isActive())
1248 void GuiView::showMessage()
1252 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1253 if (msg.isEmpty()) {
1254 BufferView const * bv = currentBufferView();
1256 msg = toqstr(bv->cursor().currentState());
1258 msg = qt_("Welcome to LyX!");
1260 statusBar()->showMessage(msg);
1264 bool GuiView::event(QEvent * e)
1268 // Useful debug code:
1269 //case QEvent::ActivationChange:
1270 //case QEvent::WindowDeactivate:
1271 //case QEvent::Paint:
1272 //case QEvent::Enter:
1273 //case QEvent::Leave:
1274 //case QEvent::HoverEnter:
1275 //case QEvent::HoverLeave:
1276 //case QEvent::HoverMove:
1277 //case QEvent::StatusTip:
1278 //case QEvent::DragEnter:
1279 //case QEvent::DragLeave:
1280 //case QEvent::Drop:
1283 case QEvent::WindowActivate: {
1284 GuiView * old_view = guiApp->currentView();
1285 if (this == old_view) {
1287 return QMainWindow::event(e);
1289 if (old_view && old_view->currentBufferView()) {
1290 // save current selection to the selection buffer to allow
1291 // middle-button paste in this window.
1292 cap::saveSelection(old_view->currentBufferView()->cursor());
1294 guiApp->setCurrentView(this);
1295 if (d.current_work_area_)
1296 on_currentWorkAreaChanged(d.current_work_area_);
1300 return QMainWindow::event(e);
1303 case QEvent::ShortcutOverride: {
1305 if (isFullScreen() && menuBar()->isHidden()) {
1306 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1307 // FIXME: we should also try to detect special LyX shortcut such as
1308 // Alt-P and Alt-M. Right now there is a hack in
1309 // GuiWorkArea::processKeySym() that hides again the menubar for
1311 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1313 return QMainWindow::event(e);
1316 return QMainWindow::event(e);
1320 return QMainWindow::event(e);
1324 void GuiView::resetWindowTitle()
1326 setWindowTitle(qt_("LyX"));
1329 bool GuiView::focusNextPrevChild(bool /*next*/)
1336 bool GuiView::busy() const
1342 void GuiView::setBusy(bool busy)
1344 bool const busy_before = busy_ > 0;
1345 busy ? ++busy_ : --busy_;
1346 if ((busy_ > 0) == busy_before)
1347 // busy state didn't change
1351 QApplication::setOverrideCursor(Qt::WaitCursor);
1354 QApplication::restoreOverrideCursor();
1359 void GuiView::resetCommandExecute()
1361 command_execute_ = false;
1366 double GuiView::pixelRatio() const
1368 #if QT_VERSION >= 0x050000
1369 return qt_scale_factor * devicePixelRatio();
1376 GuiWorkArea * GuiView::workArea(int index)
1378 if (TabWorkArea * twa = d.currentTabWorkArea())
1379 if (index < twa->count())
1380 return twa->workArea(index);
1385 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1387 if (currentWorkArea()
1388 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1389 return currentWorkArea();
1390 if (TabWorkArea * twa = d.currentTabWorkArea())
1391 return twa->workArea(buffer);
1396 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1398 // Automatically create a TabWorkArea if there are none yet.
1399 TabWorkArea * tab_widget = d.splitter_->count()
1400 ? d.currentTabWorkArea() : addTabWorkArea();
1401 return tab_widget->addWorkArea(buffer, *this);
1405 TabWorkArea * GuiView::addTabWorkArea()
1407 TabWorkArea * twa = new TabWorkArea;
1408 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1409 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1410 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1411 this, SLOT(on_lastWorkAreaRemoved()));
1413 d.splitter_->addWidget(twa);
1414 d.stack_widget_->setCurrentWidget(d.splitter_);
1419 GuiWorkArea const * GuiView::currentWorkArea() const
1421 return d.current_work_area_;
1425 GuiWorkArea * GuiView::currentWorkArea()
1427 return d.current_work_area_;
1431 GuiWorkArea const * GuiView::currentMainWorkArea() const
1433 if (!d.currentTabWorkArea())
1435 return d.currentTabWorkArea()->currentWorkArea();
1439 GuiWorkArea * GuiView::currentMainWorkArea()
1441 if (!d.currentTabWorkArea())
1443 return d.currentTabWorkArea()->currentWorkArea();
1447 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1449 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1451 d.current_work_area_ = 0;
1453 Q_EMIT bufferViewChanged();
1457 // FIXME: I've no clue why this is here and why it accesses
1458 // theGuiApp()->currentView, which might be 0 (bug 6464).
1459 // See also 27525 (vfr).
1460 if (theGuiApp()->currentView() == this
1461 && theGuiApp()->currentView()->currentWorkArea() == wa)
1464 if (currentBufferView())
1465 cap::saveSelection(currentBufferView()->cursor());
1467 theGuiApp()->setCurrentView(this);
1468 d.current_work_area_ = wa;
1470 // We need to reset this now, because it will need to be
1471 // right if the tabWorkArea gets reset in the for loop. We
1472 // will change it back if we aren't in that case.
1473 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1474 d.current_main_work_area_ = wa;
1476 for (int i = 0; i != d.splitter_->count(); ++i) {
1477 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1478 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1479 << ", Current main wa: " << currentMainWorkArea());
1484 d.current_main_work_area_ = old_cmwa;
1486 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1487 on_currentWorkAreaChanged(wa);
1488 BufferView & bv = wa->bufferView();
1489 bv.cursor().fixIfBroken();
1491 wa->setUpdatesEnabled(true);
1492 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1496 void GuiView::removeWorkArea(GuiWorkArea * wa)
1498 LASSERT(wa, return);
1499 if (wa == d.current_work_area_) {
1501 disconnectBufferView();
1502 d.current_work_area_ = 0;
1503 d.current_main_work_area_ = 0;
1506 bool found_twa = false;
1507 for (int i = 0; i != d.splitter_->count(); ++i) {
1508 TabWorkArea * twa = d.tabWorkArea(i);
1509 if (twa->removeWorkArea(wa)) {
1510 // Found in this tab group, and deleted the GuiWorkArea.
1512 if (twa->count() != 0) {
1513 if (d.current_work_area_ == 0)
1514 // This means that we are closing the current GuiWorkArea, so
1515 // switch to the next GuiWorkArea in the found TabWorkArea.
1516 setCurrentWorkArea(twa->currentWorkArea());
1518 // No more WorkAreas in this tab group, so delete it.
1525 // It is not a tabbed work area (i.e., the search work area), so it
1526 // should be deleted by other means.
1527 LASSERT(found_twa, return);
1529 if (d.current_work_area_ == 0) {
1530 if (d.splitter_->count() != 0) {
1531 TabWorkArea * twa = d.currentTabWorkArea();
1532 setCurrentWorkArea(twa->currentWorkArea());
1534 // No more work areas, switch to the background widget.
1535 setCurrentWorkArea(0);
1541 LayoutBox * GuiView::getLayoutDialog() const
1547 void GuiView::updateLayoutList()
1550 d.layout_->updateContents(false);
1554 void GuiView::updateToolbars()
1556 ToolbarMap::iterator end = d.toolbars_.end();
1557 if (d.current_work_area_) {
1559 if (d.current_work_area_->bufferView().cursor().inMathed()
1560 && !d.current_work_area_->bufferView().cursor().inRegexped())
1561 context |= Toolbars::MATH;
1562 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1563 context |= Toolbars::TABLE;
1564 if (currentBufferView()->buffer().areChangesPresent()
1565 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1566 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1567 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1568 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1569 context |= Toolbars::REVIEW;
1570 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1571 context |= Toolbars::MATHMACROTEMPLATE;
1572 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1573 context |= Toolbars::IPA;
1574 if (command_execute_)
1575 context |= Toolbars::MINIBUFFER;
1576 if (minibuffer_focus_) {
1577 context |= Toolbars::MINIBUFFER_FOCUS;
1578 minibuffer_focus_ = false;
1581 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1582 it->second->update(context);
1584 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1585 it->second->update();
1589 void GuiView::setBuffer(Buffer * newBuffer)
1591 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1592 LASSERT(newBuffer, return);
1594 GuiWorkArea * wa = workArea(*newBuffer);
1597 newBuffer->masterBuffer()->updateBuffer();
1599 wa = addWorkArea(*newBuffer);
1600 // scroll to the position when the BufferView was last closed
1601 if (lyxrc.use_lastfilepos) {
1602 LastFilePosSection::FilePos filepos =
1603 theSession().lastFilePos().load(newBuffer->fileName());
1604 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1607 //Disconnect the old buffer...there's no new one.
1610 connectBuffer(*newBuffer);
1611 connectBufferView(wa->bufferView());
1612 setCurrentWorkArea(wa);
1616 void GuiView::connectBuffer(Buffer & buf)
1618 buf.setGuiDelegate(this);
1622 void GuiView::disconnectBuffer()
1624 if (d.current_work_area_)
1625 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1629 void GuiView::connectBufferView(BufferView & bv)
1631 bv.setGuiDelegate(this);
1635 void GuiView::disconnectBufferView()
1637 if (d.current_work_area_)
1638 d.current_work_area_->bufferView().setGuiDelegate(0);
1642 void GuiView::errors(string const & error_type, bool from_master)
1644 BufferView const * const bv = currentBufferView();
1648 #if EXPORT_in_THREAD
1649 // We are called with from_master == false by default, so we
1650 // have to figure out whether that is the case or not.
1651 ErrorList & el = bv->buffer().errorList(error_type);
1653 el = bv->buffer().masterBuffer()->errorList(error_type);
1657 ErrorList const & el = from_master ?
1658 bv->buffer().masterBuffer()->errorList(error_type) :
1659 bv->buffer().errorList(error_type);
1665 string data = error_type;
1667 data = "from_master|" + error_type;
1668 showDialog("errorlist", data);
1672 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1674 d.toc_models_.updateItem(toqstr(type), dit);
1678 void GuiView::structureChanged()
1680 // This is called from the Buffer, which has no way to ensure that cursors
1681 // in BufferView remain valid.
1682 if (documentBufferView())
1683 documentBufferView()->cursor().sanitize();
1684 // FIXME: This is slightly expensive, though less than the tocBackend update
1685 // (#9880). This also resets the view in the Toc Widget (#6675).
1686 d.toc_models_.reset(documentBufferView());
1687 // Navigator needs more than a simple update in this case. It needs to be
1689 updateDialog("toc", "");
1693 void GuiView::updateDialog(string const & name, string const & data)
1695 if (!isDialogVisible(name))
1698 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1699 if (it == d.dialogs_.end())
1702 Dialog * const dialog = it->second.get();
1703 if (dialog->isVisibleView())
1704 dialog->initialiseParams(data);
1708 BufferView * GuiView::documentBufferView()
1710 return currentMainWorkArea()
1711 ? ¤tMainWorkArea()->bufferView()
1716 BufferView const * GuiView::documentBufferView() const
1718 return currentMainWorkArea()
1719 ? ¤tMainWorkArea()->bufferView()
1724 BufferView * GuiView::currentBufferView()
1726 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1730 BufferView const * GuiView::currentBufferView() const
1732 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1736 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1737 Buffer const * orig, Buffer * clone)
1739 bool const success = clone->autoSave();
1741 busyBuffers.remove(orig);
1743 ? _("Automatic save done.")
1744 : _("Automatic save failed!");
1748 void GuiView::autoSave()
1750 LYXERR(Debug::INFO, "Running autoSave()");
1752 Buffer * buffer = documentBufferView()
1753 ? &documentBufferView()->buffer() : 0;
1755 resetAutosaveTimers();
1759 GuiViewPrivate::busyBuffers.insert(buffer);
1760 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1761 buffer, buffer->cloneBufferOnly());
1762 d.autosave_watcher_.setFuture(f);
1763 resetAutosaveTimers();
1767 void GuiView::resetAutosaveTimers()
1770 d.autosave_timeout_.restart();
1774 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1777 Buffer * buf = currentBufferView()
1778 ? ¤tBufferView()->buffer() : 0;
1779 Buffer * doc_buffer = documentBufferView()
1780 ? &(documentBufferView()->buffer()) : 0;
1783 /* In LyX/Mac, when a dialog is open, the menus of the
1784 application can still be accessed without giving focus to
1785 the main window. In this case, we want to disable the menu
1786 entries that are buffer-related.
1787 This code must not be used on Linux and Windows, since it
1788 would disable buffer-related entries when hovering over the
1789 menu (see bug #9574).
1791 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1797 // Check whether we need a buffer
1798 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1799 // no, exit directly
1800 flag.message(from_utf8(N_("Command not allowed with"
1801 "out any document open")));
1802 flag.setEnabled(false);
1806 if (cmd.origin() == FuncRequest::TOC) {
1807 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1808 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1809 flag.setEnabled(false);
1813 switch(cmd.action()) {
1814 case LFUN_BUFFER_IMPORT:
1817 case LFUN_MASTER_BUFFER_UPDATE:
1818 case LFUN_MASTER_BUFFER_VIEW:
1820 && (doc_buffer->parent() != 0
1821 || doc_buffer->hasChildren())
1822 && !d.processing_thread_watcher_.isRunning();
1825 case LFUN_BUFFER_UPDATE:
1826 case LFUN_BUFFER_VIEW: {
1827 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1831 string format = to_utf8(cmd.argument());
1832 if (cmd.argument().empty())
1833 format = doc_buffer->params().getDefaultOutputFormat();
1834 enable = doc_buffer->params().isExportable(format, true);
1838 case LFUN_BUFFER_RELOAD:
1839 enable = doc_buffer && !doc_buffer->isUnnamed()
1840 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1843 case LFUN_BUFFER_CHILD_OPEN:
1844 enable = doc_buffer != 0;
1847 case LFUN_BUFFER_WRITE:
1848 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1851 //FIXME: This LFUN should be moved to GuiApplication.
1852 case LFUN_BUFFER_WRITE_ALL: {
1853 // We enable the command only if there are some modified buffers
1854 Buffer * first = theBufferList().first();
1859 // We cannot use a for loop as the buffer list is a cycle.
1861 if (!b->isClean()) {
1865 b = theBufferList().next(b);
1866 } while (b != first);
1870 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1871 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1874 case LFUN_BUFFER_WRITE_AS:
1875 case LFUN_BUFFER_EXPORT_AS:
1876 enable = doc_buffer != 0;
1879 case LFUN_BUFFER_CLOSE:
1880 case LFUN_VIEW_CLOSE:
1881 enable = doc_buffer != 0;
1884 case LFUN_BUFFER_CLOSE_ALL:
1885 enable = theBufferList().last() != theBufferList().first();
1888 case LFUN_VIEW_SPLIT:
1889 if (cmd.getArg(0) == "vertical")
1890 enable = doc_buffer && (d.splitter_->count() == 1 ||
1891 d.splitter_->orientation() == Qt::Vertical);
1893 enable = doc_buffer && (d.splitter_->count() == 1 ||
1894 d.splitter_->orientation() == Qt::Horizontal);
1897 case LFUN_TAB_GROUP_CLOSE:
1898 enable = d.tabWorkAreaCount() > 1;
1901 case LFUN_TOOLBAR_TOGGLE: {
1902 string const name = cmd.getArg(0);
1903 if (GuiToolbar * t = toolbar(name))
1904 flag.setOnOff(t->isVisible());
1907 docstring const msg =
1908 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1914 case LFUN_TOOLBAR_MOVABLE: {
1915 string const name = cmd.getArg(0);
1916 // use negation since locked == !movable
1918 // toolbar name * locks all toolbars
1919 flag.setOnOff(!toolbarsMovable_);
1920 else if (GuiToolbar * t = toolbar(name))
1921 flag.setOnOff(!(t->isMovable()));
1924 docstring const msg =
1925 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1931 case LFUN_ICON_SIZE:
1932 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1935 case LFUN_DROP_LAYOUTS_CHOICE:
1939 case LFUN_UI_TOGGLE:
1940 flag.setOnOff(isFullScreen());
1943 case LFUN_DIALOG_DISCONNECT_INSET:
1946 case LFUN_DIALOG_HIDE:
1947 // FIXME: should we check if the dialog is shown?
1950 case LFUN_DIALOG_TOGGLE:
1951 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1952 // fall through to set "enable"
1953 case LFUN_DIALOG_SHOW: {
1954 string const name = cmd.getArg(0);
1956 enable = name == "aboutlyx"
1957 || name == "file" //FIXME: should be removed.
1959 || name == "texinfo"
1960 || name == "progress"
1961 || name == "compare";
1962 else if (name == "character" || name == "symbols"
1963 || name == "mathdelimiter" || name == "mathmatrix") {
1964 if (!buf || buf->isReadonly())
1967 Cursor const & cur = currentBufferView()->cursor();
1968 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1971 else if (name == "latexlog")
1972 enable = FileName(doc_buffer->logName()).isReadableFile();
1973 else if (name == "spellchecker")
1974 enable = theSpellChecker()
1975 && !doc_buffer->isReadonly()
1976 && !doc_buffer->text().empty();
1977 else if (name == "vclog")
1978 enable = doc_buffer->lyxvc().inUse();
1982 case LFUN_DIALOG_UPDATE: {
1983 string const name = cmd.getArg(0);
1985 enable = name == "prefs";
1989 case LFUN_COMMAND_EXECUTE:
1991 case LFUN_MENU_OPEN:
1992 // Nothing to check.
1995 case LFUN_COMPLETION_INLINE:
1996 if (!d.current_work_area_
1997 || !d.current_work_area_->completer().inlinePossible(
1998 currentBufferView()->cursor()))
2002 case LFUN_COMPLETION_POPUP:
2003 if (!d.current_work_area_
2004 || !d.current_work_area_->completer().popupPossible(
2005 currentBufferView()->cursor()))
2010 if (!d.current_work_area_
2011 || !d.current_work_area_->completer().inlinePossible(
2012 currentBufferView()->cursor()))
2016 case LFUN_COMPLETION_ACCEPT:
2017 if (!d.current_work_area_
2018 || (!d.current_work_area_->completer().popupVisible()
2019 && !d.current_work_area_->completer().inlineVisible()
2020 && !d.current_work_area_->completer().completionAvailable()))
2024 case LFUN_COMPLETION_CANCEL:
2025 if (!d.current_work_area_
2026 || (!d.current_work_area_->completer().popupVisible()
2027 && !d.current_work_area_->completer().inlineVisible()))
2031 case LFUN_BUFFER_ZOOM_OUT:
2032 case LFUN_BUFFER_ZOOM_IN: {
2033 // only diff between these two is that the default for ZOOM_OUT
2035 bool const neg_zoom =
2036 convert<int>(cmd.argument()) < 0 ||
2037 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2038 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2039 docstring const msg =
2040 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2044 enable = doc_buffer;
2048 case LFUN_BUFFER_ZOOM: {
2049 bool const less_than_min_zoom =
2050 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2051 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2052 docstring const msg =
2053 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2058 enable = doc_buffer;
2062 case LFUN_BUFFER_MOVE_NEXT:
2063 case LFUN_BUFFER_MOVE_PREVIOUS:
2064 // we do not cycle when moving
2065 case LFUN_BUFFER_NEXT:
2066 case LFUN_BUFFER_PREVIOUS:
2067 // because we cycle, it doesn't matter whether on first or last
2068 enable = (d.currentTabWorkArea()->count() > 1);
2070 case LFUN_BUFFER_SWITCH:
2071 // toggle on the current buffer, but do not toggle off
2072 // the other ones (is that a good idea?)
2074 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2075 flag.setOnOff(true);
2078 case LFUN_VC_REGISTER:
2079 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2081 case LFUN_VC_RENAME:
2082 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2085 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2087 case LFUN_VC_CHECK_IN:
2088 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2090 case LFUN_VC_CHECK_OUT:
2091 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2093 case LFUN_VC_LOCKING_TOGGLE:
2094 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2095 && doc_buffer->lyxvc().lockingToggleEnabled();
2096 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2098 case LFUN_VC_REVERT:
2099 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2100 && !doc_buffer->hasReadonlyFlag();
2102 case LFUN_VC_UNDO_LAST:
2103 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2105 case LFUN_VC_REPO_UPDATE:
2106 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2108 case LFUN_VC_COMMAND: {
2109 if (cmd.argument().empty())
2111 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2115 case LFUN_VC_COMPARE:
2116 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2119 case LFUN_SERVER_GOTO_FILE_ROW:
2120 case LFUN_LYX_ACTIVATE:
2122 case LFUN_FORWARD_SEARCH:
2123 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2126 case LFUN_FILE_INSERT_PLAINTEXT:
2127 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2128 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2131 case LFUN_SPELLING_CONTINUOUSLY:
2132 flag.setOnOff(lyxrc.spellcheck_continuously);
2140 flag.setEnabled(false);
2146 static FileName selectTemplateFile()
2148 FileDialog dlg(qt_("Select template file"));
2149 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2150 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2152 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2153 QStringList(qt_("LyX Documents (*.lyx)")));
2155 if (result.first == FileDialog::Later)
2157 if (result.second.isEmpty())
2159 return FileName(fromqstr(result.second));
2163 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2167 Buffer * newBuffer = 0;
2169 newBuffer = checkAndLoadLyXFile(filename);
2170 } catch (ExceptionMessage const & e) {
2177 message(_("Document not loaded."));
2181 setBuffer(newBuffer);
2182 newBuffer->errors("Parse");
2185 theSession().lastFiles().add(filename);
2191 void GuiView::openDocument(string const & fname)
2193 string initpath = lyxrc.document_path;
2195 if (documentBufferView()) {
2196 string const trypath = documentBufferView()->buffer().filePath();
2197 // If directory is writeable, use this as default.
2198 if (FileName(trypath).isDirWritable())
2204 if (fname.empty()) {
2205 FileDialog dlg(qt_("Select document to open"));
2206 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2207 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2209 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2210 FileDialog::Result result =
2211 dlg.open(toqstr(initpath), filter);
2213 if (result.first == FileDialog::Later)
2216 filename = fromqstr(result.second);
2218 // check selected filename
2219 if (filename.empty()) {
2220 message(_("Canceled."));
2226 // get absolute path of file and add ".lyx" to the filename if
2228 FileName const fullname =
2229 fileSearch(string(), filename, "lyx", support::may_not_exist);
2230 if (!fullname.empty())
2231 filename = fullname.absFileName();
2233 if (!fullname.onlyPath().isDirectory()) {
2234 Alert::warning(_("Invalid filename"),
2235 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2236 from_utf8(fullname.absFileName())));
2240 // if the file doesn't exist and isn't already open (bug 6645),
2241 // let the user create one
2242 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2243 !LyXVC::file_not_found_hook(fullname)) {
2244 // the user specifically chose this name. Believe him.
2245 Buffer * const b = newFile(filename, string(), true);
2251 docstring const disp_fn = makeDisplayPath(filename);
2252 message(bformat(_("Opening document %1$s..."), disp_fn));
2255 Buffer * buf = loadDocument(fullname);
2257 str2 = bformat(_("Document %1$s opened."), disp_fn);
2258 if (buf->lyxvc().inUse())
2259 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2260 " " + _("Version control detected.");
2262 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2267 // FIXME: clean that
2268 static bool import(GuiView * lv, FileName const & filename,
2269 string const & format, ErrorList & errorList)
2271 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2273 string loader_format;
2274 vector<string> loaders = theConverters().loaders();
2275 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2276 vector<string>::const_iterator it = loaders.begin();
2277 vector<string>::const_iterator en = loaders.end();
2278 for (; it != en; ++it) {
2279 if (!theConverters().isReachable(format, *it))
2282 string const tofile =
2283 support::changeExtension(filename.absFileName(),
2284 theFormats().extension(*it));
2285 if (!theConverters().convert(0, filename, FileName(tofile),
2286 filename, format, *it, errorList))
2288 loader_format = *it;
2291 if (loader_format.empty()) {
2292 frontend::Alert::error(_("Couldn't import file"),
2293 bformat(_("No information for importing the format %1$s."),
2294 theFormats().prettyName(format)));
2298 loader_format = format;
2300 if (loader_format == "lyx") {
2301 Buffer * buf = lv->loadDocument(lyxfile);
2305 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2309 bool as_paragraphs = loader_format == "textparagraph";
2310 string filename2 = (loader_format == format) ? filename.absFileName()
2311 : support::changeExtension(filename.absFileName(),
2312 theFormats().extension(loader_format));
2313 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2315 guiApp->setCurrentView(lv);
2316 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2323 void GuiView::importDocument(string const & argument)
2326 string filename = split(argument, format, ' ');
2328 LYXERR(Debug::INFO, format << " file: " << filename);
2330 // need user interaction
2331 if (filename.empty()) {
2332 string initpath = lyxrc.document_path;
2333 if (documentBufferView()) {
2334 string const trypath = documentBufferView()->buffer().filePath();
2335 // If directory is writeable, use this as default.
2336 if (FileName(trypath).isDirWritable())
2340 docstring const text = bformat(_("Select %1$s file to import"),
2341 theFormats().prettyName(format));
2343 FileDialog dlg(toqstr(text));
2344 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2345 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2347 docstring filter = theFormats().prettyName(format);
2350 filter += from_utf8(theFormats().extensions(format));
2353 FileDialog::Result result =
2354 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2356 if (result.first == FileDialog::Later)
2359 filename = fromqstr(result.second);
2361 // check selected filename
2362 if (filename.empty())
2363 message(_("Canceled."));
2366 if (filename.empty())
2369 // get absolute path of file
2370 FileName const fullname(support::makeAbsPath(filename));
2372 // Can happen if the user entered a path into the dialog
2374 if (fullname.onlyFileName().empty()) {
2375 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2376 "Aborting import."),
2377 from_utf8(fullname.absFileName()));
2378 frontend::Alert::error(_("File name error"), msg);
2379 message(_("Canceled."));
2384 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2386 // Check if the document already is open
2387 Buffer * buf = theBufferList().getBuffer(lyxfile);
2390 if (!closeBuffer()) {
2391 message(_("Canceled."));
2396 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2398 // if the file exists already, and we didn't do
2399 // -i lyx thefile.lyx, warn
2400 if (lyxfile.exists() && fullname != lyxfile) {
2402 docstring text = bformat(_("The document %1$s already exists.\n\n"
2403 "Do you want to overwrite that document?"), displaypath);
2404 int const ret = Alert::prompt(_("Overwrite document?"),
2405 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2408 message(_("Canceled."));
2413 message(bformat(_("Importing %1$s..."), displaypath));
2414 ErrorList errorList;
2415 if (import(this, fullname, format, errorList))
2416 message(_("imported."));
2418 message(_("file not imported!"));
2420 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2424 void GuiView::newDocument(string const & filename, bool from_template)
2426 FileName initpath(lyxrc.document_path);
2427 if (documentBufferView()) {
2428 FileName const trypath(documentBufferView()->buffer().filePath());
2429 // If directory is writeable, use this as default.
2430 if (trypath.isDirWritable())
2434 string templatefile;
2435 if (from_template) {
2436 templatefile = selectTemplateFile().absFileName();
2437 if (templatefile.empty())
2442 if (filename.empty())
2443 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2445 b = newFile(filename, templatefile, true);
2450 // If no new document could be created, it is unsure
2451 // whether there is a valid BufferView.
2452 if (currentBufferView())
2453 // Ensure the cursor is correctly positioned on screen.
2454 currentBufferView()->showCursor();
2458 void GuiView::insertLyXFile(docstring const & fname)
2460 BufferView * bv = documentBufferView();
2465 FileName filename(to_utf8(fname));
2466 if (filename.empty()) {
2467 // Launch a file browser
2469 string initpath = lyxrc.document_path;
2470 string const trypath = bv->buffer().filePath();
2471 // If directory is writeable, use this as default.
2472 if (FileName(trypath).isDirWritable())
2476 FileDialog dlg(qt_("Select LyX document to insert"));
2477 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2478 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2480 FileDialog::Result result = dlg.open(toqstr(initpath),
2481 QStringList(qt_("LyX Documents (*.lyx)")));
2483 if (result.first == FileDialog::Later)
2487 filename.set(fromqstr(result.second));
2489 // check selected filename
2490 if (filename.empty()) {
2491 // emit message signal.
2492 message(_("Canceled."));
2497 bv->insertLyXFile(filename);
2498 bv->buffer().errors("Parse");
2502 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2504 FileName fname = b.fileName();
2505 FileName const oldname = fname;
2507 if (!newname.empty()) {
2509 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2511 // Switch to this Buffer.
2514 // No argument? Ask user through dialog.
2516 FileDialog dlg(qt_("Choose a filename to save document as"));
2517 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2518 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2520 if (!isLyXFileName(fname.absFileName()))
2521 fname.changeExtension(".lyx");
2523 FileDialog::Result result =
2524 dlg.save(toqstr(fname.onlyPath().absFileName()),
2525 QStringList(qt_("LyX Documents (*.lyx)")),
2526 toqstr(fname.onlyFileName()));
2528 if (result.first == FileDialog::Later)
2531 fname.set(fromqstr(result.second));
2536 if (!isLyXFileName(fname.absFileName()))
2537 fname.changeExtension(".lyx");
2540 // fname is now the new Buffer location.
2542 // if there is already a Buffer open with this name, we do not want
2543 // to have another one. (the second test makes sure we're not just
2544 // trying to overwrite ourselves, which is fine.)
2545 if (theBufferList().exists(fname) && fname != oldname
2546 && theBufferList().getBuffer(fname) != &b) {
2547 docstring const text =
2548 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2549 "Please close it before attempting to overwrite it.\n"
2550 "Do you want to choose a new filename?"),
2551 from_utf8(fname.absFileName()));
2552 int const ret = Alert::prompt(_("Chosen File Already Open"),
2553 text, 0, 1, _("&Rename"), _("&Cancel"));
2555 case 0: return renameBuffer(b, docstring(), kind);
2556 case 1: return false;
2561 bool const existsLocal = fname.exists();
2562 bool const existsInVC = LyXVC::fileInVC(fname);
2563 if (existsLocal || existsInVC) {
2564 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2565 if (kind != LV_WRITE_AS && existsInVC) {
2566 // renaming to a name that is already in VC
2568 docstring text = bformat(_("The document %1$s "
2569 "is already registered.\n\n"
2570 "Do you want to choose a new name?"),
2572 docstring const title = (kind == LV_VC_RENAME) ?
2573 _("Rename document?") : _("Copy document?");
2574 docstring const button = (kind == LV_VC_RENAME) ?
2575 _("&Rename") : _("&Copy");
2576 int const ret = Alert::prompt(title, text, 0, 1,
2577 button, _("&Cancel"));
2579 case 0: return renameBuffer(b, docstring(), kind);
2580 case 1: return false;
2585 docstring text = bformat(_("The document %1$s "
2586 "already exists.\n\n"
2587 "Do you want to overwrite that document?"),
2589 int const ret = Alert::prompt(_("Overwrite document?"),
2590 text, 0, 2, _("&Overwrite"),
2591 _("&Rename"), _("&Cancel"));
2594 case 1: return renameBuffer(b, docstring(), kind);
2595 case 2: return false;
2601 case LV_VC_RENAME: {
2602 string msg = b.lyxvc().rename(fname);
2605 message(from_utf8(msg));
2609 string msg = b.lyxvc().copy(fname);
2612 message(from_utf8(msg));
2618 // LyXVC created the file already in case of LV_VC_RENAME or
2619 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2620 // relative paths of included stuff right if we moved e.g. from
2621 // /a/b.lyx to /a/c/b.lyx.
2623 bool const saved = saveBuffer(b, fname);
2630 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2632 FileName fname = b.fileName();
2634 FileDialog dlg(qt_("Choose a filename to export the document as"));
2635 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2638 QString const anyformat = qt_("Guess from extension (*.*)");
2641 vector<Format const *> export_formats;
2642 for (Format const & f : theFormats())
2643 if (f.documentFormat())
2644 export_formats.push_back(&f);
2645 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2646 map<QString, string> fmap;
2649 for (Format const * f : export_formats) {
2650 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2651 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2653 from_ascii(f->extension())));
2654 types << loc_filter;
2655 fmap[loc_filter] = f->name();
2656 if (from_ascii(f->name()) == iformat) {
2657 filter = loc_filter;
2658 ext = f->extension();
2661 string ofname = fname.onlyFileName();
2663 ofname = support::changeExtension(ofname, ext);
2664 FileDialog::Result result =
2665 dlg.save(toqstr(fname.onlyPath().absFileName()),
2669 if (result.first != FileDialog::Chosen)
2673 fname.set(fromqstr(result.second));
2674 if (filter == anyformat)
2675 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2677 fmt_name = fmap[filter];
2678 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2679 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2681 if (fmt_name.empty() || fname.empty())
2684 // fname is now the new Buffer location.
2685 if (FileName(fname).exists()) {
2686 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2687 docstring text = bformat(_("The document %1$s already "
2688 "exists.\n\nDo you want to "
2689 "overwrite that document?"),
2691 int const ret = Alert::prompt(_("Overwrite document?"),
2692 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2695 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2696 case 2: return false;
2700 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2703 return dr.dispatched();
2707 bool GuiView::saveBuffer(Buffer & b)
2709 return saveBuffer(b, FileName());
2713 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2715 if (workArea(b) && workArea(b)->inDialogMode())
2718 if (fn.empty() && b.isUnnamed())
2719 return renameBuffer(b, docstring());
2721 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2723 theSession().lastFiles().add(b.fileName());
2727 // Switch to this Buffer.
2730 // FIXME: we don't tell the user *WHY* the save failed !!
2731 docstring const file = makeDisplayPath(b.absFileName(), 30);
2732 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2733 "Do you want to rename the document and "
2734 "try again?"), file);
2735 int const ret = Alert::prompt(_("Rename and save?"),
2736 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2739 if (!renameBuffer(b, docstring()))
2748 return saveBuffer(b, fn);
2752 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2754 return closeWorkArea(wa, false);
2758 // We only want to close the buffer if it is not visible in other workareas
2759 // of the same view, nor in other views, and if this is not a child
2760 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2762 Buffer & buf = wa->bufferView().buffer();
2764 bool last_wa = d.countWorkAreasOf(buf) == 1
2765 && !inOtherView(buf) && !buf.parent();
2767 bool close_buffer = last_wa;
2770 if (lyxrc.close_buffer_with_last_view == "yes")
2772 else if (lyxrc.close_buffer_with_last_view == "no")
2773 close_buffer = false;
2776 if (buf.isUnnamed())
2777 file = from_utf8(buf.fileName().onlyFileName());
2779 file = buf.fileName().displayName(30);
2780 docstring const text = bformat(
2781 _("Last view on document %1$s is being closed.\n"
2782 "Would you like to close or hide the document?\n"
2784 "Hidden documents can be displayed back through\n"
2785 "the menu: View->Hidden->...\n"
2787 "To remove this question, set your preference in:\n"
2788 " Tools->Preferences->Look&Feel->UserInterface\n"
2790 int ret = Alert::prompt(_("Close or hide document?"),
2791 text, 0, 1, _("&Close"), _("&Hide"));
2792 close_buffer = (ret == 0);
2796 return closeWorkArea(wa, close_buffer);
2800 bool GuiView::closeBuffer()
2802 GuiWorkArea * wa = currentMainWorkArea();
2803 // coverity complained about this
2804 // it seems unnecessary, but perhaps is worth the check
2805 LASSERT(wa, return false);
2807 setCurrentWorkArea(wa);
2808 Buffer & buf = wa->bufferView().buffer();
2809 return closeWorkArea(wa, !buf.parent());
2813 void GuiView::writeSession() const {
2814 GuiWorkArea const * active_wa = currentMainWorkArea();
2815 for (int i = 0; i < d.splitter_->count(); ++i) {
2816 TabWorkArea * twa = d.tabWorkArea(i);
2817 for (int j = 0; j < twa->count(); ++j) {
2818 GuiWorkArea * wa = twa->workArea(j);
2819 Buffer & buf = wa->bufferView().buffer();
2820 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2826 bool GuiView::closeBufferAll()
2828 // Close the workareas in all other views
2829 QList<int> const ids = guiApp->viewIds();
2830 for (int i = 0; i != ids.size(); ++i) {
2831 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2835 // Close our own workareas
2836 if (!closeWorkAreaAll())
2839 // Now close the hidden buffers. We prevent hidden buffers from being
2840 // dirty, so we can just close them.
2841 theBufferList().closeAll();
2846 bool GuiView::closeWorkAreaAll()
2848 setCurrentWorkArea(currentMainWorkArea());
2850 // We might be in a situation that there is still a tabWorkArea, but
2851 // there are no tabs anymore. This can happen when we get here after a
2852 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2853 // many TabWorkArea's have no documents anymore.
2856 // We have to call count() each time, because it can happen that
2857 // more than one splitter will disappear in one iteration (bug 5998).
2858 while (d.splitter_->count() > empty_twa) {
2859 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2861 if (twa->count() == 0)
2864 setCurrentWorkArea(twa->currentWorkArea());
2865 if (!closeTabWorkArea(twa))
2873 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2878 Buffer & buf = wa->bufferView().buffer();
2880 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2881 Alert::warning(_("Close document"),
2882 _("Document could not be closed because it is being processed by LyX."));
2887 return closeBuffer(buf);
2889 if (!inMultiTabs(wa))
2890 if (!saveBufferIfNeeded(buf, true))
2898 bool GuiView::closeBuffer(Buffer & buf)
2900 // If we are in a close_event all children will be closed in some time,
2901 // so no need to do it here. This will ensure that the children end up
2902 // in the session file in the correct order. If we close the master
2903 // buffer, we can close or release the child buffers here too.
2904 bool success = true;
2906 ListOfBuffers clist = buf.getChildren();
2907 ListOfBuffers::const_iterator it = clist.begin();
2908 ListOfBuffers::const_iterator const bend = clist.end();
2909 for (; it != bend; ++it) {
2910 Buffer * child_buf = *it;
2911 if (theBufferList().isOthersChild(&buf, child_buf)) {
2912 child_buf->setParent(0);
2916 // FIXME: should we look in other tabworkareas?
2917 // ANSWER: I don't think so. I've tested, and if the child is
2918 // open in some other window, it closes without a problem.
2919 GuiWorkArea * child_wa = workArea(*child_buf);
2921 success = closeWorkArea(child_wa, true);
2925 // In this case the child buffer is open but hidden.
2926 // It therefore should not (MUST NOT) be dirty!
2927 LATTEST(child_buf->isClean());
2928 theBufferList().release(child_buf);
2933 // goto bookmark to update bookmark pit.
2934 // FIXME: we should update only the bookmarks related to this buffer!
2935 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2936 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2937 guiApp->gotoBookmark(i+1, false, false);
2939 if (saveBufferIfNeeded(buf, false)) {
2940 buf.removeAutosaveFile();
2941 theBufferList().release(&buf);
2945 // open all children again to avoid a crash because of dangling
2946 // pointers (bug 6603)
2952 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2954 while (twa == d.currentTabWorkArea()) {
2955 twa->setCurrentIndex(twa->count() - 1);
2957 GuiWorkArea * wa = twa->currentWorkArea();
2958 Buffer & b = wa->bufferView().buffer();
2960 // We only want to close the buffer if the same buffer is not visible
2961 // in another view, and if this is not a child and if we are closing
2962 // a view (not a tabgroup).
2963 bool const close_buffer =
2964 !inOtherView(b) && !b.parent() && closing_;
2966 if (!closeWorkArea(wa, close_buffer))
2973 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2975 if (buf.isClean() || buf.paragraphs().empty())
2978 // Switch to this Buffer.
2984 if (buf.isUnnamed()) {
2985 file = from_utf8(buf.fileName().onlyFileName());
2988 FileName filename = buf.fileName();
2990 file = filename.displayName(30);
2991 exists = filename.exists();
2994 // Bring this window to top before asking questions.
2999 if (hiding && buf.isUnnamed()) {
3000 docstring const text = bformat(_("The document %1$s has not been "
3001 "saved yet.\n\nDo you want to save "
3002 "the document?"), file);
3003 ret = Alert::prompt(_("Save new document?"),
3004 text, 0, 1, _("&Save"), _("&Cancel"));
3008 docstring const text = exists ?
3009 bformat(_("The document %1$s has unsaved changes."
3010 "\n\nDo you want to save the document or "
3011 "discard the changes?"), file) :
3012 bformat(_("The document %1$s has not been saved yet."
3013 "\n\nDo you want to save the document or "
3014 "discard it entirely?"), file);
3015 docstring const title = exists ?
3016 _("Save changed document?") : _("Save document?");
3017 ret = Alert::prompt(title, text, 0, 2,
3018 _("&Save"), _("&Discard"), _("&Cancel"));
3023 if (!saveBuffer(buf))
3027 // If we crash after this we could have no autosave file
3028 // but I guess this is really improbable (Jug).
3029 // Sometimes improbable things happen:
3030 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3031 // buf.removeAutosaveFile();
3033 // revert all changes
3044 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3046 Buffer & buf = wa->bufferView().buffer();
3048 for (int i = 0; i != d.splitter_->count(); ++i) {
3049 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3050 if (wa_ && wa_ != wa)
3053 return inOtherView(buf);
3057 bool GuiView::inOtherView(Buffer & buf)
3059 QList<int> const ids = guiApp->viewIds();
3061 for (int i = 0; i != ids.size(); ++i) {
3065 if (guiApp->view(ids[i]).workArea(buf))
3072 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3074 if (!documentBufferView())
3077 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3078 Buffer * const curbuf = &documentBufferView()->buffer();
3079 int nwa = twa->count();
3080 for (int i = 0; i < nwa; ++i) {
3081 if (&workArea(i)->bufferView().buffer() == curbuf) {
3083 if (np == NEXTBUFFER)
3084 next_index = (i == nwa - 1 ? 0 : i + 1);
3086 next_index = (i == 0 ? nwa - 1 : i - 1);
3088 twa->moveTab(i, next_index);
3090 setBuffer(&workArea(next_index)->bufferView().buffer());
3098 /// make sure the document is saved
3099 static bool ensureBufferClean(Buffer * buffer)
3101 LASSERT(buffer, return false);
3102 if (buffer->isClean() && !buffer->isUnnamed())
3105 docstring const file = buffer->fileName().displayName(30);
3108 if (!buffer->isUnnamed()) {
3109 text = bformat(_("The document %1$s has unsaved "
3110 "changes.\n\nDo you want to save "
3111 "the document?"), file);
3112 title = _("Save changed document?");
3115 text = bformat(_("The document %1$s has not been "
3116 "saved yet.\n\nDo you want to save "
3117 "the document?"), file);
3118 title = _("Save new document?");
3120 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3123 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3125 return buffer->isClean() && !buffer->isUnnamed();
3129 bool GuiView::reloadBuffer(Buffer & buf)
3131 Buffer::ReadStatus status = buf.reload();
3132 return status == Buffer::ReadSuccess;
3136 void GuiView::checkExternallyModifiedBuffers()
3138 BufferList::iterator bit = theBufferList().begin();
3139 BufferList::iterator const bend = theBufferList().end();
3140 for (; bit != bend; ++bit) {
3141 Buffer * buf = *bit;
3142 if (buf->fileName().exists() && buf->isChecksumModified()) {
3143 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3144 " Reload now? Any local changes will be lost."),
3145 from_utf8(buf->absFileName()));
3146 int const ret = Alert::prompt(_("Reload externally changed document?"),
3147 text, 0, 1, _("&Reload"), _("&Cancel"));
3155 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3157 Buffer * buffer = documentBufferView()
3158 ? &(documentBufferView()->buffer()) : 0;
3160 switch (cmd.action()) {
3161 case LFUN_VC_REGISTER:
3162 if (!buffer || !ensureBufferClean(buffer))
3164 if (!buffer->lyxvc().inUse()) {
3165 if (buffer->lyxvc().registrer()) {
3166 reloadBuffer(*buffer);
3167 dr.clearMessageUpdate();
3172 case LFUN_VC_RENAME:
3173 case LFUN_VC_COPY: {
3174 if (!buffer || !ensureBufferClean(buffer))
3176 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3177 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3178 // Some changes are not yet committed.
3179 // We test here and not in getStatus(), since
3180 // this test is expensive.
3182 LyXVC::CommandResult ret =
3183 buffer->lyxvc().checkIn(log);
3185 if (ret == LyXVC::ErrorCommand ||
3186 ret == LyXVC::VCSuccess)
3187 reloadBuffer(*buffer);
3188 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3189 frontend::Alert::error(
3190 _("Revision control error."),
3191 _("Document could not be checked in."));
3195 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3196 LV_VC_RENAME : LV_VC_COPY;
3197 renameBuffer(*buffer, cmd.argument(), kind);
3202 case LFUN_VC_CHECK_IN:
3203 if (!buffer || !ensureBufferClean(buffer))
3205 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3207 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3209 // Only skip reloading if the checkin was cancelled or
3210 // an error occurred before the real checkin VCS command
3211 // was executed, since the VCS might have changed the
3212 // file even if it could not checkin successfully.
3213 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3214 reloadBuffer(*buffer);
3218 case LFUN_VC_CHECK_OUT:
3219 if (!buffer || !ensureBufferClean(buffer))
3221 if (buffer->lyxvc().inUse()) {
3222 dr.setMessage(buffer->lyxvc().checkOut());
3223 reloadBuffer(*buffer);
3227 case LFUN_VC_LOCKING_TOGGLE:
3228 LASSERT(buffer, return);
3229 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3231 if (buffer->lyxvc().inUse()) {
3232 string res = buffer->lyxvc().lockingToggle();
3234 frontend::Alert::error(_("Revision control error."),
3235 _("Error when setting the locking property."));
3238 reloadBuffer(*buffer);
3243 case LFUN_VC_REVERT:
3244 LASSERT(buffer, return);
3245 if (buffer->lyxvc().revert()) {
3246 reloadBuffer(*buffer);
3247 dr.clearMessageUpdate();
3251 case LFUN_VC_UNDO_LAST:
3252 LASSERT(buffer, return);
3253 buffer->lyxvc().undoLast();
3254 reloadBuffer(*buffer);
3255 dr.clearMessageUpdate();
3258 case LFUN_VC_REPO_UPDATE:
3259 LASSERT(buffer, return);
3260 if (ensureBufferClean(buffer)) {
3261 dr.setMessage(buffer->lyxvc().repoUpdate());
3262 checkExternallyModifiedBuffers();
3266 case LFUN_VC_COMMAND: {
3267 string flag = cmd.getArg(0);
3268 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3271 if (contains(flag, 'M')) {
3272 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3275 string path = cmd.getArg(1);
3276 if (contains(path, "$$p") && buffer)
3277 path = subst(path, "$$p", buffer->filePath());
3278 LYXERR(Debug::LYXVC, "Directory: " << path);
3280 if (!pp.isReadableDirectory()) {
3281 lyxerr << _("Directory is not accessible.") << endl;
3284 support::PathChanger p(pp);
3286 string command = cmd.getArg(2);
3287 if (command.empty())
3290 command = subst(command, "$$i", buffer->absFileName());
3291 command = subst(command, "$$p", buffer->filePath());
3293 command = subst(command, "$$m", to_utf8(message));
3294 LYXERR(Debug::LYXVC, "Command: " << command);
3296 one.startscript(Systemcall::Wait, command);
3300 if (contains(flag, 'I'))
3301 buffer->markDirty();
3302 if (contains(flag, 'R'))
3303 reloadBuffer(*buffer);
3308 case LFUN_VC_COMPARE: {
3309 if (cmd.argument().empty()) {
3310 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3314 string rev1 = cmd.getArg(0);
3319 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3322 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3323 f2 = buffer->absFileName();
3325 string rev2 = cmd.getArg(1);
3329 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3333 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3334 f1 << "\n" << f2 << "\n" );
3335 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3336 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3346 void GuiView::openChildDocument(string const & fname)
3348 LASSERT(documentBufferView(), return);
3349 Buffer & buffer = documentBufferView()->buffer();
3350 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3351 documentBufferView()->saveBookmark(false);
3353 if (theBufferList().exists(filename)) {
3354 child = theBufferList().getBuffer(filename);
3357 message(bformat(_("Opening child document %1$s..."),
3358 makeDisplayPath(filename.absFileName())));
3359 child = loadDocument(filename, false);
3361 // Set the parent name of the child document.
3362 // This makes insertion of citations and references in the child work,
3363 // when the target is in the parent or another child document.
3365 child->setParent(&buffer);
3369 bool GuiView::goToFileRow(string const & argument)
3373 size_t i = argument.find_last_of(' ');
3374 if (i != string::npos) {
3375 file_name = os::internal_path(trim(argument.substr(0, i)));
3376 istringstream is(argument.substr(i + 1));
3381 if (i == string::npos) {
3382 LYXERR0("Wrong argument: " << argument);
3386 string const abstmp = package().temp_dir().absFileName();
3387 string const realtmp = package().temp_dir().realPath();
3388 // We have to use os::path_prefix_is() here, instead of
3389 // simply prefixIs(), because the file name comes from
3390 // an external application and may need case adjustment.
3391 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3392 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3393 // Needed by inverse dvi search. If it is a file
3394 // in tmpdir, call the apropriated function.
3395 // If tmpdir is a symlink, we may have the real
3396 // path passed back, so we correct for that.
3397 if (!prefixIs(file_name, abstmp))
3398 file_name = subst(file_name, realtmp, abstmp);
3399 buf = theBufferList().getBufferFromTmp(file_name);
3401 // Must replace extension of the file to be .lyx
3402 // and get full path
3403 FileName const s = fileSearch(string(),
3404 support::changeExtension(file_name, ".lyx"), "lyx");
3405 // Either change buffer or load the file
3406 if (theBufferList().exists(s))
3407 buf = theBufferList().getBuffer(s);
3408 else if (s.exists()) {
3409 buf = loadDocument(s);
3414 _("File does not exist: %1$s"),
3415 makeDisplayPath(file_name)));
3421 _("No buffer for file: %1$s."),
3422 makeDisplayPath(file_name))
3427 bool success = documentBufferView()->setCursorFromRow(row);
3429 LYXERR(Debug::LATEX,
3430 "setCursorFromRow: invalid position for row " << row);
3431 frontend::Alert::error(_("Inverse Search Failed"),
3432 _("Invalid position requested by inverse search.\n"
3433 "You may need to update the viewed document."));
3439 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3441 QMenu * menu = new QMenu;
3442 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3443 menu->exec(QCursor::pos());
3448 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3450 Buffer::ExportStatus const status = func(format);
3452 // the cloning operation will have produced a clone of the entire set of
3453 // documents, starting from the master. so we must delete those.
3454 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3456 busyBuffers.remove(orig);
3461 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3463 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3464 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3468 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3470 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3471 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3475 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3477 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3478 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3482 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3483 string const & argument,
3484 Buffer const * used_buffer,
3485 docstring const & msg,
3486 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3487 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3488 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3493 string format = argument;
3495 format = used_buffer->params().getDefaultOutputFormat();
3496 processing_format = format;
3498 progress_->clearMessages();
3501 #if EXPORT_in_THREAD
3502 GuiViewPrivate::busyBuffers.insert(used_buffer);
3503 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3504 if (!cloned_buffer) {
3505 Alert::error(_("Export Error"),
3506 _("Error cloning the Buffer."));
3509 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3514 setPreviewFuture(f);
3515 last_export_format = used_buffer->params().bufferFormat();
3518 // We are asynchronous, so we don't know here anything about the success
3521 Buffer::ExportStatus status;
3523 status = (used_buffer->*syncFunc)(format, true);
3524 } else if (previewFunc) {
3525 status = (used_buffer->*previewFunc)(format);
3528 handleExportStatus(gv_, status, format);
3530 return (status == Buffer::ExportSuccess
3531 || status == Buffer::PreviewSuccess);
3535 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3537 BufferView * bv = currentBufferView();
3538 LASSERT(bv, return);
3540 // Let the current BufferView dispatch its own actions.
3541 bv->dispatch(cmd, dr);
3542 if (dr.dispatched())
3545 // Try with the document BufferView dispatch if any.
3546 BufferView * doc_bv = documentBufferView();
3547 if (doc_bv && doc_bv != bv) {
3548 doc_bv->dispatch(cmd, dr);
3549 if (dr.dispatched())
3553 // Then let the current Cursor dispatch its own actions.
3554 bv->cursor().dispatch(cmd);
3556 // update completion. We do it here and not in
3557 // processKeySym to avoid another redraw just for a
3558 // changed inline completion
3559 if (cmd.origin() == FuncRequest::KEYBOARD) {
3560 if (cmd.action() == LFUN_SELF_INSERT
3561 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3562 updateCompletion(bv->cursor(), true, true);
3563 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3564 updateCompletion(bv->cursor(), false, true);
3566 updateCompletion(bv->cursor(), false, false);
3569 dr = bv->cursor().result();
3573 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3575 BufferView * bv = currentBufferView();
3576 // By default we won't need any update.
3577 dr.screenUpdate(Update::None);
3578 // assume cmd will be dispatched
3579 dr.dispatched(true);
3581 Buffer * doc_buffer = documentBufferView()
3582 ? &(documentBufferView()->buffer()) : 0;
3584 if (cmd.origin() == FuncRequest::TOC) {
3585 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3586 // FIXME: do we need to pass a DispatchResult object here?
3587 toc->doDispatch(bv->cursor(), cmd);
3591 string const argument = to_utf8(cmd.argument());
3593 switch(cmd.action()) {
3594 case LFUN_BUFFER_CHILD_OPEN:
3595 openChildDocument(to_utf8(cmd.argument()));
3598 case LFUN_BUFFER_IMPORT:
3599 importDocument(to_utf8(cmd.argument()));
3602 case LFUN_BUFFER_EXPORT: {
3605 // GCC only sees strfwd.h when building merged
3606 if (::lyx::operator==(cmd.argument(), "custom")) {
3607 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3611 string const dest = cmd.getArg(1);
3612 FileName target_dir;
3613 if (!dest.empty() && FileName::isAbsolute(dest))
3614 target_dir = FileName(support::onlyPath(dest));
3616 target_dir = doc_buffer->fileName().onlyPath();
3618 string const format = (argument.empty() || argument == "default") ?
3619 doc_buffer->params().getDefaultOutputFormat() : argument;
3621 if ((dest.empty() && doc_buffer->isUnnamed())
3622 || !target_dir.isDirWritable()) {
3623 exportBufferAs(*doc_buffer, from_utf8(format));
3626 /* TODO/Review: Is it a problem to also export the children?
3627 See the update_unincluded flag */
3628 d.asyncBufferProcessing(format,
3631 &GuiViewPrivate::exportAndDestroy,
3634 // TODO Inform user about success
3638 case LFUN_BUFFER_EXPORT_AS: {
3639 LASSERT(doc_buffer, break);
3640 docstring f = cmd.argument();
3642 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3643 exportBufferAs(*doc_buffer, f);
3647 case LFUN_BUFFER_UPDATE: {
3648 d.asyncBufferProcessing(argument,
3651 &GuiViewPrivate::compileAndDestroy,
3656 case LFUN_BUFFER_VIEW: {
3657 d.asyncBufferProcessing(argument,
3659 _("Previewing ..."),
3660 &GuiViewPrivate::previewAndDestroy,
3665 case LFUN_MASTER_BUFFER_UPDATE: {
3666 d.asyncBufferProcessing(argument,
3667 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3669 &GuiViewPrivate::compileAndDestroy,
3674 case LFUN_MASTER_BUFFER_VIEW: {
3675 d.asyncBufferProcessing(argument,
3676 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3678 &GuiViewPrivate::previewAndDestroy,
3679 0, &Buffer::preview);
3682 case LFUN_BUFFER_SWITCH: {
3683 string const file_name = to_utf8(cmd.argument());
3684 if (!FileName::isAbsolute(file_name)) {
3686 dr.setMessage(_("Absolute filename expected."));
3690 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3693 dr.setMessage(_("Document not loaded"));
3697 // Do we open or switch to the buffer in this view ?
3698 if (workArea(*buffer)
3699 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3704 // Look for the buffer in other views
3705 QList<int> const ids = guiApp->viewIds();
3707 for (; i != ids.size(); ++i) {
3708 GuiView & gv = guiApp->view(ids[i]);
3709 if (gv.workArea(*buffer)) {
3711 gv.activateWindow();
3713 gv.setBuffer(buffer);
3718 // If necessary, open a new window as a last resort
3719 if (i == ids.size()) {
3720 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3726 case LFUN_BUFFER_NEXT:
3727 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3730 case LFUN_BUFFER_MOVE_NEXT:
3731 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3734 case LFUN_BUFFER_PREVIOUS:
3735 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3738 case LFUN_BUFFER_MOVE_PREVIOUS:
3739 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3742 case LFUN_COMMAND_EXECUTE: {
3743 command_execute_ = true;
3744 minibuffer_focus_ = true;
3747 case LFUN_DROP_LAYOUTS_CHOICE:
3748 d.layout_->showPopup();
3751 case LFUN_MENU_OPEN:
3752 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3753 menu->exec(QCursor::pos());
3756 case LFUN_FILE_INSERT:
3757 insertLyXFile(cmd.argument());
3760 case LFUN_FILE_INSERT_PLAINTEXT:
3761 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3762 string const fname = to_utf8(cmd.argument());
3763 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3764 dr.setMessage(_("Absolute filename expected."));
3768 FileName filename(fname);
3769 if (fname.empty()) {
3770 FileDialog dlg(qt_("Select file to insert"));
3772 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3773 QStringList(qt_("All Files (*)")));
3775 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3776 dr.setMessage(_("Canceled."));
3780 filename.set(fromqstr(result.second));
3784 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3785 bv->dispatch(new_cmd, dr);
3790 case LFUN_BUFFER_RELOAD: {
3791 LASSERT(doc_buffer, break);
3794 if (!doc_buffer->isClean()) {
3795 docstring const file =
3796 makeDisplayPath(doc_buffer->absFileName(), 20);
3797 if (doc_buffer->notifiesExternalModification()) {
3798 docstring text = _("The current version will be lost. "
3799 "Are you sure you want to load the version on disk "
3800 "of the document %1$s?");
3801 ret = Alert::prompt(_("Reload saved document?"),
3802 bformat(text, file), 1, 1,
3803 _("&Reload"), _("&Cancel"));
3805 docstring text = _("Any changes will be lost. "
3806 "Are you sure you want to revert to the saved version "
3807 "of the document %1$s?");
3808 ret = Alert::prompt(_("Revert to saved document?"),
3809 bformat(text, file), 1, 1,
3810 _("&Revert"), _("&Cancel"));
3815 doc_buffer->markClean();
3816 reloadBuffer(*doc_buffer);
3817 dr.forceBufferUpdate();
3822 case LFUN_BUFFER_WRITE:
3823 LASSERT(doc_buffer, break);
3824 saveBuffer(*doc_buffer);
3827 case LFUN_BUFFER_WRITE_AS:
3828 LASSERT(doc_buffer, break);
3829 renameBuffer(*doc_buffer, cmd.argument());
3832 case LFUN_BUFFER_WRITE_ALL: {
3833 Buffer * first = theBufferList().first();
3836 message(_("Saving all documents..."));
3837 // We cannot use a for loop as the buffer list cycles.
3840 if (!b->isClean()) {
3842 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3844 b = theBufferList().next(b);
3845 } while (b != first);
3846 dr.setMessage(_("All documents saved."));
3850 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3851 LASSERT(doc_buffer, break);
3852 doc_buffer->clearExternalModification();
3855 case LFUN_BUFFER_CLOSE:
3859 case LFUN_BUFFER_CLOSE_ALL:
3863 case LFUN_TOOLBAR_TOGGLE: {
3864 string const name = cmd.getArg(0);
3865 if (GuiToolbar * t = toolbar(name))
3870 case LFUN_TOOLBAR_MOVABLE: {
3871 string const name = cmd.getArg(0);
3873 // toggle (all) toolbars movablility
3874 toolbarsMovable_ = !toolbarsMovable_;
3875 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3876 GuiToolbar * tb = toolbar(ti.name);
3877 if (tb && tb->isMovable() != toolbarsMovable_)
3878 // toggle toolbar movablity if it does not fit lock
3879 // (all) toolbars positions state silent = true, since
3880 // status bar notifications are slow
3883 if (toolbarsMovable_)
3884 dr.setMessage(_("Toolbars unlocked."));
3886 dr.setMessage(_("Toolbars locked."));
3887 } else if (GuiToolbar * t = toolbar(name)) {
3888 // toggle current toolbar movablity
3890 // update lock (all) toolbars positions
3891 updateLockToolbars();
3896 case LFUN_ICON_SIZE: {
3897 QSize size = d.iconSize(cmd.argument());
3899 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3900 size.width(), size.height()));
3904 case LFUN_DIALOG_UPDATE: {
3905 string const name = to_utf8(cmd.argument());
3906 if (name == "prefs" || name == "document")
3907 updateDialog(name, string());
3908 else if (name == "paragraph")
3909 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3910 else if (currentBufferView()) {
3911 Inset * inset = currentBufferView()->editedInset(name);
3912 // Can only update a dialog connected to an existing inset
3914 // FIXME: get rid of this indirection; GuiView ask the inset
3915 // if he is kind enough to update itself...
3916 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3917 //FIXME: pass DispatchResult here?
3918 inset->dispatch(currentBufferView()->cursor(), fr);
3924 case LFUN_DIALOG_TOGGLE: {
3925 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3926 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3927 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3931 case LFUN_DIALOG_DISCONNECT_INSET:
3932 disconnectDialog(to_utf8(cmd.argument()));
3935 case LFUN_DIALOG_HIDE: {
3936 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3940 case LFUN_DIALOG_SHOW: {
3941 string const name = cmd.getArg(0);
3942 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3944 if (name == "character") {
3945 data = freefont2string();
3947 showDialog("character", data);
3948 } else if (name == "latexlog") {
3949 // getStatus checks that
3950 LATTEST(doc_buffer);
3951 Buffer::LogType type;
3952 string const logfile = doc_buffer->logName(&type);
3954 case Buffer::latexlog:
3957 case Buffer::buildlog:
3961 data += Lexer::quoteString(logfile);
3962 showDialog("log", data);
3963 } else if (name == "vclog") {
3964 // getStatus checks that
3965 LATTEST(doc_buffer);
3966 string const data = "vc " +
3967 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3968 showDialog("log", data);
3969 } else if (name == "symbols") {
3970 data = bv->cursor().getEncoding()->name();
3972 showDialog("symbols", data);
3974 } else if (name == "prefs" && isFullScreen()) {
3975 lfunUiToggle("fullscreen");
3976 showDialog("prefs", data);
3978 showDialog(name, data);
3983 dr.setMessage(cmd.argument());
3986 case LFUN_UI_TOGGLE: {
3987 string arg = cmd.getArg(0);
3988 if (!lfunUiToggle(arg)) {
3989 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3990 dr.setMessage(bformat(msg, from_utf8(arg)));
3992 // Make sure the keyboard focus stays in the work area.
3997 case LFUN_VIEW_SPLIT: {
3998 LASSERT(doc_buffer, break);
3999 string const orientation = cmd.getArg(0);
4000 d.splitter_->setOrientation(orientation == "vertical"
4001 ? Qt::Vertical : Qt::Horizontal);
4002 TabWorkArea * twa = addTabWorkArea();
4003 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4004 setCurrentWorkArea(wa);
4007 case LFUN_TAB_GROUP_CLOSE:
4008 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4009 closeTabWorkArea(twa);
4010 d.current_work_area_ = 0;
4011 twa = d.currentTabWorkArea();
4012 // Switch to the next GuiWorkArea in the found TabWorkArea.
4014 // Make sure the work area is up to date.
4015 setCurrentWorkArea(twa->currentWorkArea());
4017 setCurrentWorkArea(0);
4022 case LFUN_VIEW_CLOSE:
4023 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4024 closeWorkArea(twa->currentWorkArea());
4025 d.current_work_area_ = 0;
4026 twa = d.currentTabWorkArea();
4027 // Switch to the next GuiWorkArea in the found TabWorkArea.
4029 // Make sure the work area is up to date.
4030 setCurrentWorkArea(twa->currentWorkArea());
4032 setCurrentWorkArea(0);
4037 case LFUN_COMPLETION_INLINE:
4038 if (d.current_work_area_)
4039 d.current_work_area_->completer().showInline();
4042 case LFUN_COMPLETION_POPUP:
4043 if (d.current_work_area_)
4044 d.current_work_area_->completer().showPopup();
4049 if (d.current_work_area_)
4050 d.current_work_area_->completer().tab();
4053 case LFUN_COMPLETION_CANCEL:
4054 if (d.current_work_area_) {
4055 if (d.current_work_area_->completer().popupVisible())
4056 d.current_work_area_->completer().hidePopup();
4058 d.current_work_area_->completer().hideInline();
4062 case LFUN_COMPLETION_ACCEPT:
4063 if (d.current_work_area_)
4064 d.current_work_area_->completer().activate();
4067 case LFUN_BUFFER_ZOOM_IN:
4068 case LFUN_BUFFER_ZOOM_OUT:
4069 case LFUN_BUFFER_ZOOM: {
4070 // use a signed temp to avoid overflow
4071 int zoom = lyxrc.currentZoom;
4072 if (cmd.argument().empty()) {
4073 if (cmd.action() == LFUN_BUFFER_ZOOM)
4075 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4080 if (cmd.action() == LFUN_BUFFER_ZOOM)
4081 zoom = convert<int>(cmd.argument());
4082 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4083 zoom += convert<int>(cmd.argument());
4085 zoom -= convert<int>(cmd.argument());
4088 if (zoom < static_cast<int>(zoom_min_))
4091 lyxrc.currentZoom = zoom;
4093 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4095 // The global QPixmapCache is used in GuiPainter to cache text
4096 // painting so we must reset it.
4097 QPixmapCache::clear();
4098 guiApp->fontLoader().update();
4099 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4103 case LFUN_VC_REGISTER:
4104 case LFUN_VC_RENAME:
4106 case LFUN_VC_CHECK_IN:
4107 case LFUN_VC_CHECK_OUT:
4108 case LFUN_VC_REPO_UPDATE:
4109 case LFUN_VC_LOCKING_TOGGLE:
4110 case LFUN_VC_REVERT:
4111 case LFUN_VC_UNDO_LAST:
4112 case LFUN_VC_COMMAND:
4113 case LFUN_VC_COMPARE:
4114 dispatchVC(cmd, dr);
4117 case LFUN_SERVER_GOTO_FILE_ROW:
4118 if(goToFileRow(to_utf8(cmd.argument())))
4119 dr.screenUpdate(Update::Force | Update::FitCursor);
4122 case LFUN_LYX_ACTIVATE:
4126 case LFUN_FORWARD_SEARCH: {
4127 // it seems safe to assume we have a document buffer, since
4128 // getStatus wants one.
4129 LATTEST(doc_buffer);
4130 Buffer const * doc_master = doc_buffer->masterBuffer();
4131 FileName const path(doc_master->temppath());
4132 string const texname = doc_master->isChild(doc_buffer)
4133 ? DocFileName(changeExtension(
4134 doc_buffer->absFileName(),
4135 "tex")).mangledFileName()
4136 : doc_buffer->latexName();
4137 string const fulltexname =
4138 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4139 string const mastername =
4140 removeExtension(doc_master->latexName());
4141 FileName const dviname(addName(path.absFileName(),
4142 addExtension(mastername, "dvi")));
4143 FileName const pdfname(addName(path.absFileName(),
4144 addExtension(mastername, "pdf")));
4145 bool const have_dvi = dviname.exists();
4146 bool const have_pdf = pdfname.exists();
4147 if (!have_dvi && !have_pdf) {
4148 dr.setMessage(_("Please, preview the document first."));
4151 string outname = dviname.onlyFileName();
4152 string command = lyxrc.forward_search_dvi;
4153 if (!have_dvi || (have_pdf &&
4154 pdfname.lastModified() > dviname.lastModified())) {
4155 outname = pdfname.onlyFileName();
4156 command = lyxrc.forward_search_pdf;
4159 DocIterator cur = bv->cursor();
4160 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4161 LYXERR(Debug::ACTION, "Forward search: row:" << row
4163 if (row == -1 || command.empty()) {
4164 dr.setMessage(_("Couldn't proceed."));
4167 string texrow = convert<string>(row);
4169 command = subst(command, "$$n", texrow);
4170 command = subst(command, "$$f", fulltexname);
4171 command = subst(command, "$$t", texname);
4172 command = subst(command, "$$o", outname);
4174 PathChanger p(path);
4176 one.startscript(Systemcall::DontWait, command);
4180 case LFUN_SPELLING_CONTINUOUSLY:
4181 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4182 dr.screenUpdate(Update::Force);
4186 // The LFUN must be for one of BufferView, Buffer or Cursor;
4188 dispatchToBufferView(cmd, dr);
4192 // Part of automatic menu appearance feature.
4193 if (isFullScreen()) {
4194 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4198 // Need to update bv because many LFUNs here might have destroyed it
4199 bv = currentBufferView();
4201 // Clear non-empty selections
4202 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4204 Cursor & cur = bv->cursor();
4205 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4206 cur.clearSelection();
4212 bool GuiView::lfunUiToggle(string const & ui_component)
4214 if (ui_component == "scrollbar") {
4215 // hide() is of no help
4216 if (d.current_work_area_->verticalScrollBarPolicy() ==
4217 Qt::ScrollBarAlwaysOff)
4219 d.current_work_area_->setVerticalScrollBarPolicy(
4220 Qt::ScrollBarAsNeeded);
4222 d.current_work_area_->setVerticalScrollBarPolicy(
4223 Qt::ScrollBarAlwaysOff);
4224 } else if (ui_component == "statusbar") {
4225 statusBar()->setVisible(!statusBar()->isVisible());
4226 } else if (ui_component == "menubar") {
4227 menuBar()->setVisible(!menuBar()->isVisible());
4229 if (ui_component == "frame") {
4231 getContentsMargins(&l, &t, &r, &b);
4232 //are the frames in default state?
4233 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4235 setContentsMargins(-2, -2, -2, -2);
4237 setContentsMargins(0, 0, 0, 0);
4240 if (ui_component == "fullscreen") {
4248 void GuiView::toggleFullScreen()
4250 if (isFullScreen()) {
4251 for (int i = 0; i != d.splitter_->count(); ++i)
4252 d.tabWorkArea(i)->setFullScreen(false);
4253 setContentsMargins(0, 0, 0, 0);
4254 setWindowState(windowState() ^ Qt::WindowFullScreen);
4257 statusBar()->show();
4260 hideDialogs("prefs", 0);
4261 for (int i = 0; i != d.splitter_->count(); ++i)
4262 d.tabWorkArea(i)->setFullScreen(true);
4263 setContentsMargins(-2, -2, -2, -2);
4265 setWindowState(windowState() ^ Qt::WindowFullScreen);
4266 if (lyxrc.full_screen_statusbar)
4267 statusBar()->hide();
4268 if (lyxrc.full_screen_menubar)
4270 if (lyxrc.full_screen_toolbars) {
4271 ToolbarMap::iterator end = d.toolbars_.end();
4272 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4277 // give dialogs like the TOC a chance to adapt
4282 Buffer const * GuiView::updateInset(Inset const * inset)
4287 Buffer const * inset_buffer = &(inset->buffer());
4289 for (int i = 0; i != d.splitter_->count(); ++i) {
4290 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4293 Buffer const * buffer = &(wa->bufferView().buffer());
4294 if (inset_buffer == buffer)
4295 wa->scheduleRedraw();
4297 return inset_buffer;
4301 void GuiView::restartCursor()
4303 /* When we move around, or type, it's nice to be able to see
4304 * the cursor immediately after the keypress.
4306 if (d.current_work_area_)
4307 d.current_work_area_->startBlinkingCursor();
4309 // Take this occasion to update the other GUI elements.
4315 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4317 if (d.current_work_area_)
4318 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4323 // This list should be kept in sync with the list of insets in
4324 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4325 // dialog should have the same name as the inset.
4326 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4327 // docs in LyXAction.cpp.
4329 char const * const dialognames[] = {
4331 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4332 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4333 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4334 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4335 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4336 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4337 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4338 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4340 char const * const * const end_dialognames =
4341 dialognames + (sizeof(dialognames) / sizeof(char *));
4345 cmpCStr(char const * name) : name_(name) {}
4346 bool operator()(char const * other) {
4347 return strcmp(other, name_) == 0;
4354 bool isValidName(string const & name)
4356 return find_if(dialognames, end_dialognames,
4357 cmpCStr(name.c_str())) != end_dialognames;
4363 void GuiView::resetDialogs()
4365 // Make sure that no LFUN uses any GuiView.
4366 guiApp->setCurrentView(0);
4370 constructToolbars();
4371 guiApp->menus().fillMenuBar(menuBar(), this, false);
4372 d.layout_->updateContents(true);
4373 // Now update controls with current buffer.
4374 guiApp->setCurrentView(this);
4380 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4382 if (!isValidName(name))
4385 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4387 if (it != d.dialogs_.end()) {
4389 it->second->hideView();
4390 return it->second.get();
4393 Dialog * dialog = build(name);
4394 d.dialogs_[name].reset(dialog);
4395 if (lyxrc.allow_geometry_session)
4396 dialog->restoreSession();
4403 void GuiView::showDialog(string const & name, string const & data,
4406 triggerShowDialog(toqstr(name), toqstr(data), inset);
4410 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4416 const string name = fromqstr(qname);
4417 const string data = fromqstr(qdata);
4421 Dialog * dialog = findOrBuild(name, false);
4423 bool const visible = dialog->isVisibleView();
4424 dialog->showData(data);
4425 if (inset && currentBufferView())
4426 currentBufferView()->editInset(name, inset);
4427 // We only set the focus to the new dialog if it was not yet
4428 // visible in order not to change the existing previous behaviour
4430 // activateWindow is needed for floating dockviews
4431 dialog->asQWidget()->raise();
4432 dialog->asQWidget()->activateWindow();
4433 dialog->asQWidget()->setFocus();
4437 catch (ExceptionMessage const & ex) {
4445 bool GuiView::isDialogVisible(string const & name) const
4447 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4448 if (it == d.dialogs_.end())
4450 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4454 void GuiView::hideDialog(string const & name, Inset * inset)
4456 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4457 if (it == d.dialogs_.end())
4461 if (!currentBufferView())
4463 if (inset != currentBufferView()->editedInset(name))
4467 Dialog * const dialog = it->second.get();
4468 if (dialog->isVisibleView())
4470 if (currentBufferView())
4471 currentBufferView()->editInset(name, 0);
4475 void GuiView::disconnectDialog(string const & name)
4477 if (!isValidName(name))
4479 if (currentBufferView())
4480 currentBufferView()->editInset(name, 0);
4484 void GuiView::hideAll() const
4486 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4487 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4489 for(; it != end; ++it)
4490 it->second->hideView();
4494 void GuiView::updateDialogs()
4496 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4497 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4499 for(; it != end; ++it) {
4500 Dialog * dialog = it->second.get();
4502 if (dialog->needBufferOpen() && !documentBufferView())
4503 hideDialog(fromqstr(dialog->name()), 0);
4504 else if (dialog->isVisibleView())
4505 dialog->checkStatus();
4512 Dialog * createDialog(GuiView & lv, string const & name);
4514 // will be replaced by a proper factory...
4515 Dialog * createGuiAbout(GuiView & lv);
4516 Dialog * createGuiBibtex(GuiView & lv);
4517 Dialog * createGuiChanges(GuiView & lv);
4518 Dialog * createGuiCharacter(GuiView & lv);
4519 Dialog * createGuiCitation(GuiView & lv);
4520 Dialog * createGuiCompare(GuiView & lv);
4521 Dialog * createGuiCompareHistory(GuiView & lv);
4522 Dialog * createGuiDelimiter(GuiView & lv);
4523 Dialog * createGuiDocument(GuiView & lv);
4524 Dialog * createGuiErrorList(GuiView & lv);
4525 Dialog * createGuiExternal(GuiView & lv);
4526 Dialog * createGuiGraphics(GuiView & lv);
4527 Dialog * createGuiInclude(GuiView & lv);
4528 Dialog * createGuiIndex(GuiView & lv);
4529 Dialog * createGuiListings(GuiView & lv);
4530 Dialog * createGuiLog(GuiView & lv);
4531 Dialog * createGuiMathMatrix(GuiView & lv);
4532 Dialog * createGuiNote(GuiView & lv);
4533 Dialog * createGuiParagraph(GuiView & lv);
4534 Dialog * createGuiPhantom(GuiView & lv);
4535 Dialog * createGuiPreferences(GuiView & lv);
4536 Dialog * createGuiPrint(GuiView & lv);
4537 Dialog * createGuiPrintindex(GuiView & lv);
4538 Dialog * createGuiRef(GuiView & lv);
4539 Dialog * createGuiSearch(GuiView & lv);
4540 Dialog * createGuiSearchAdv(GuiView & lv);
4541 Dialog * createGuiSendTo(GuiView & lv);
4542 Dialog * createGuiShowFile(GuiView & lv);
4543 Dialog * createGuiSpellchecker(GuiView & lv);
4544 Dialog * createGuiSymbols(GuiView & lv);
4545 Dialog * createGuiTabularCreate(GuiView & lv);
4546 Dialog * createGuiTexInfo(GuiView & lv);
4547 Dialog * createGuiToc(GuiView & lv);
4548 Dialog * createGuiThesaurus(GuiView & lv);
4549 Dialog * createGuiViewSource(GuiView & lv);
4550 Dialog * createGuiWrap(GuiView & lv);
4551 Dialog * createGuiProgressView(GuiView & lv);
4555 Dialog * GuiView::build(string const & name)
4557 LASSERT(isValidName(name), return 0);
4559 Dialog * dialog = createDialog(*this, name);
4563 if (name == "aboutlyx")
4564 return createGuiAbout(*this);
4565 if (name == "bibtex")
4566 return createGuiBibtex(*this);
4567 if (name == "changes")
4568 return createGuiChanges(*this);
4569 if (name == "character")
4570 return createGuiCharacter(*this);
4571 if (name == "citation")
4572 return createGuiCitation(*this);
4573 if (name == "compare")
4574 return createGuiCompare(*this);
4575 if (name == "comparehistory")
4576 return createGuiCompareHistory(*this);
4577 if (name == "document")
4578 return createGuiDocument(*this);
4579 if (name == "errorlist")
4580 return createGuiErrorList(*this);
4581 if (name == "external")
4582 return createGuiExternal(*this);
4584 return createGuiShowFile(*this);
4585 if (name == "findreplace")
4586 return createGuiSearch(*this);
4587 if (name == "findreplaceadv")
4588 return createGuiSearchAdv(*this);
4589 if (name == "graphics")
4590 return createGuiGraphics(*this);
4591 if (name == "include")
4592 return createGuiInclude(*this);
4593 if (name == "index")
4594 return createGuiIndex(*this);
4595 if (name == "index_print")
4596 return createGuiPrintindex(*this);
4597 if (name == "listings")
4598 return createGuiListings(*this);
4600 return createGuiLog(*this);
4601 if (name == "mathdelimiter")
4602 return createGuiDelimiter(*this);
4603 if (name == "mathmatrix")
4604 return createGuiMathMatrix(*this);
4606 return createGuiNote(*this);
4607 if (name == "paragraph")
4608 return createGuiParagraph(*this);
4609 if (name == "phantom")
4610 return createGuiPhantom(*this);
4611 if (name == "prefs")
4612 return createGuiPreferences(*this);
4614 return createGuiRef(*this);
4615 if (name == "sendto")
4616 return createGuiSendTo(*this);
4617 if (name == "spellchecker")
4618 return createGuiSpellchecker(*this);
4619 if (name == "symbols")
4620 return createGuiSymbols(*this);
4621 if (name == "tabularcreate")
4622 return createGuiTabularCreate(*this);
4623 if (name == "texinfo")
4624 return createGuiTexInfo(*this);
4625 if (name == "thesaurus")
4626 return createGuiThesaurus(*this);
4628 return createGuiToc(*this);
4629 if (name == "view-source")
4630 return createGuiViewSource(*this);
4632 return createGuiWrap(*this);
4633 if (name == "progress")
4634 return createGuiProgressView(*this);
4640 } // namespace frontend
4643 #include "moc_GuiView.cpp"