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.
2983 if (buf.isUnnamed())
2984 file = from_utf8(buf.fileName().onlyFileName());
2986 file = buf.fileName().displayName(30);
2988 // Bring this window to top before asking questions.
2993 if (hiding && buf.isUnnamed()) {
2994 docstring const text = bformat(_("The document %1$s has not been "
2995 "saved yet.\n\nDo you want to save "
2996 "the document?"), file);
2997 ret = Alert::prompt(_("Save new document?"),
2998 text, 0, 1, _("&Save"), _("&Cancel"));
3002 docstring const text = bformat(_("The document %1$s has unsaved changes."
3003 "\n\nDo you want to save the document or discard the changes?"), file);
3004 ret = Alert::prompt(_("Save changed document?"),
3005 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
3010 if (!saveBuffer(buf))
3014 // If we crash after this we could have no autosave file
3015 // but I guess this is really improbable (Jug).
3016 // Sometimes improbable things happen:
3017 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3018 // buf.removeAutosaveFile();
3020 // revert all changes
3031 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3033 Buffer & buf = wa->bufferView().buffer();
3035 for (int i = 0; i != d.splitter_->count(); ++i) {
3036 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3037 if (wa_ && wa_ != wa)
3040 return inOtherView(buf);
3044 bool GuiView::inOtherView(Buffer & buf)
3046 QList<int> const ids = guiApp->viewIds();
3048 for (int i = 0; i != ids.size(); ++i) {
3052 if (guiApp->view(ids[i]).workArea(buf))
3059 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3061 if (!documentBufferView())
3064 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3065 Buffer * const curbuf = &documentBufferView()->buffer();
3066 int nwa = twa->count();
3067 for (int i = 0; i < nwa; ++i) {
3068 if (&workArea(i)->bufferView().buffer() == curbuf) {
3070 if (np == NEXTBUFFER)
3071 next_index = (i == nwa - 1 ? 0 : i + 1);
3073 next_index = (i == 0 ? nwa - 1 : i - 1);
3075 twa->moveTab(i, next_index);
3077 setBuffer(&workArea(next_index)->bufferView().buffer());
3085 /// make sure the document is saved
3086 static bool ensureBufferClean(Buffer * buffer)
3088 LASSERT(buffer, return false);
3089 if (buffer->isClean() && !buffer->isUnnamed())
3092 docstring const file = buffer->fileName().displayName(30);
3095 if (!buffer->isUnnamed()) {
3096 text = bformat(_("The document %1$s has unsaved "
3097 "changes.\n\nDo you want to save "
3098 "the document?"), file);
3099 title = _("Save changed document?");
3102 text = bformat(_("The document %1$s has not been "
3103 "saved yet.\n\nDo you want to save "
3104 "the document?"), file);
3105 title = _("Save new document?");
3107 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3110 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3112 return buffer->isClean() && !buffer->isUnnamed();
3116 bool GuiView::reloadBuffer(Buffer & buf)
3118 Buffer::ReadStatus status = buf.reload();
3119 return status == Buffer::ReadSuccess;
3123 void GuiView::checkExternallyModifiedBuffers()
3125 BufferList::iterator bit = theBufferList().begin();
3126 BufferList::iterator const bend = theBufferList().end();
3127 for (; bit != bend; ++bit) {
3128 Buffer * buf = *bit;
3129 if (buf->fileName().exists() && buf->isChecksumModified()) {
3130 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3131 " Reload now? Any local changes will be lost."),
3132 from_utf8(buf->absFileName()));
3133 int const ret = Alert::prompt(_("Reload externally changed document?"),
3134 text, 0, 1, _("&Reload"), _("&Cancel"));
3142 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3144 Buffer * buffer = documentBufferView()
3145 ? &(documentBufferView()->buffer()) : 0;
3147 switch (cmd.action()) {
3148 case LFUN_VC_REGISTER:
3149 if (!buffer || !ensureBufferClean(buffer))
3151 if (!buffer->lyxvc().inUse()) {
3152 if (buffer->lyxvc().registrer()) {
3153 reloadBuffer(*buffer);
3154 dr.clearMessageUpdate();
3159 case LFUN_VC_RENAME:
3160 case LFUN_VC_COPY: {
3161 if (!buffer || !ensureBufferClean(buffer))
3163 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3164 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3165 // Some changes are not yet committed.
3166 // We test here and not in getStatus(), since
3167 // this test is expensive.
3169 LyXVC::CommandResult ret =
3170 buffer->lyxvc().checkIn(log);
3172 if (ret == LyXVC::ErrorCommand ||
3173 ret == LyXVC::VCSuccess)
3174 reloadBuffer(*buffer);
3175 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3176 frontend::Alert::error(
3177 _("Revision control error."),
3178 _("Document could not be checked in."));
3182 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3183 LV_VC_RENAME : LV_VC_COPY;
3184 renameBuffer(*buffer, cmd.argument(), kind);
3189 case LFUN_VC_CHECK_IN:
3190 if (!buffer || !ensureBufferClean(buffer))
3192 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3194 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3196 // Only skip reloading if the checkin was cancelled or
3197 // an error occurred before the real checkin VCS command
3198 // was executed, since the VCS might have changed the
3199 // file even if it could not checkin successfully.
3200 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3201 reloadBuffer(*buffer);
3205 case LFUN_VC_CHECK_OUT:
3206 if (!buffer || !ensureBufferClean(buffer))
3208 if (buffer->lyxvc().inUse()) {
3209 dr.setMessage(buffer->lyxvc().checkOut());
3210 reloadBuffer(*buffer);
3214 case LFUN_VC_LOCKING_TOGGLE:
3215 LASSERT(buffer, return);
3216 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3218 if (buffer->lyxvc().inUse()) {
3219 string res = buffer->lyxvc().lockingToggle();
3221 frontend::Alert::error(_("Revision control error."),
3222 _("Error when setting the locking property."));
3225 reloadBuffer(*buffer);
3230 case LFUN_VC_REVERT:
3231 LASSERT(buffer, return);
3232 if (buffer->lyxvc().revert()) {
3233 reloadBuffer(*buffer);
3234 dr.clearMessageUpdate();
3238 case LFUN_VC_UNDO_LAST:
3239 LASSERT(buffer, return);
3240 buffer->lyxvc().undoLast();
3241 reloadBuffer(*buffer);
3242 dr.clearMessageUpdate();
3245 case LFUN_VC_REPO_UPDATE:
3246 LASSERT(buffer, return);
3247 if (ensureBufferClean(buffer)) {
3248 dr.setMessage(buffer->lyxvc().repoUpdate());
3249 checkExternallyModifiedBuffers();
3253 case LFUN_VC_COMMAND: {
3254 string flag = cmd.getArg(0);
3255 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3258 if (contains(flag, 'M')) {
3259 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3262 string path = cmd.getArg(1);
3263 if (contains(path, "$$p") && buffer)
3264 path = subst(path, "$$p", buffer->filePath());
3265 LYXERR(Debug::LYXVC, "Directory: " << path);
3267 if (!pp.isReadableDirectory()) {
3268 lyxerr << _("Directory is not accessible.") << endl;
3271 support::PathChanger p(pp);
3273 string command = cmd.getArg(2);
3274 if (command.empty())
3277 command = subst(command, "$$i", buffer->absFileName());
3278 command = subst(command, "$$p", buffer->filePath());
3280 command = subst(command, "$$m", to_utf8(message));
3281 LYXERR(Debug::LYXVC, "Command: " << command);
3283 one.startscript(Systemcall::Wait, command);
3287 if (contains(flag, 'I'))
3288 buffer->markDirty();
3289 if (contains(flag, 'R'))
3290 reloadBuffer(*buffer);
3295 case LFUN_VC_COMPARE: {
3296 if (cmd.argument().empty()) {
3297 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3301 string rev1 = cmd.getArg(0);
3306 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3309 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3310 f2 = buffer->absFileName();
3312 string rev2 = cmd.getArg(1);
3316 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3320 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3321 f1 << "\n" << f2 << "\n" );
3322 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3323 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3333 void GuiView::openChildDocument(string const & fname)
3335 LASSERT(documentBufferView(), return);
3336 Buffer & buffer = documentBufferView()->buffer();
3337 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3338 documentBufferView()->saveBookmark(false);
3340 if (theBufferList().exists(filename)) {
3341 child = theBufferList().getBuffer(filename);
3344 message(bformat(_("Opening child document %1$s..."),
3345 makeDisplayPath(filename.absFileName())));
3346 child = loadDocument(filename, false);
3348 // Set the parent name of the child document.
3349 // This makes insertion of citations and references in the child work,
3350 // when the target is in the parent or another child document.
3352 child->setParent(&buffer);
3356 bool GuiView::goToFileRow(string const & argument)
3360 size_t i = argument.find_last_of(' ');
3361 if (i != string::npos) {
3362 file_name = os::internal_path(trim(argument.substr(0, i)));
3363 istringstream is(argument.substr(i + 1));
3368 if (i == string::npos) {
3369 LYXERR0("Wrong argument: " << argument);
3373 string const abstmp = package().temp_dir().absFileName();
3374 string const realtmp = package().temp_dir().realPath();
3375 // We have to use os::path_prefix_is() here, instead of
3376 // simply prefixIs(), because the file name comes from
3377 // an external application and may need case adjustment.
3378 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3379 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3380 // Needed by inverse dvi search. If it is a file
3381 // in tmpdir, call the apropriated function.
3382 // If tmpdir is a symlink, we may have the real
3383 // path passed back, so we correct for that.
3384 if (!prefixIs(file_name, abstmp))
3385 file_name = subst(file_name, realtmp, abstmp);
3386 buf = theBufferList().getBufferFromTmp(file_name);
3388 // Must replace extension of the file to be .lyx
3389 // and get full path
3390 FileName const s = fileSearch(string(),
3391 support::changeExtension(file_name, ".lyx"), "lyx");
3392 // Either change buffer or load the file
3393 if (theBufferList().exists(s))
3394 buf = theBufferList().getBuffer(s);
3395 else if (s.exists()) {
3396 buf = loadDocument(s);
3401 _("File does not exist: %1$s"),
3402 makeDisplayPath(file_name)));
3408 _("No buffer for file: %1$s."),
3409 makeDisplayPath(file_name))
3414 bool success = documentBufferView()->setCursorFromRow(row);
3416 LYXERR(Debug::LATEX,
3417 "setCursorFromRow: invalid position for row " << row);
3418 frontend::Alert::error(_("Inverse Search Failed"),
3419 _("Invalid position requested by inverse search.\n"
3420 "You may need to update the viewed document."));
3426 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3428 QMenu * menu = new QMenu;
3429 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3430 menu->exec(QCursor::pos());
3435 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3437 Buffer::ExportStatus const status = func(format);
3439 // the cloning operation will have produced a clone of the entire set of
3440 // documents, starting from the master. so we must delete those.
3441 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3443 busyBuffers.remove(orig);
3448 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3450 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3451 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3455 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3457 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3458 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3462 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3464 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3465 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3469 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3470 string const & argument,
3471 Buffer const * used_buffer,
3472 docstring const & msg,
3473 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3474 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3475 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3480 string format = argument;
3482 format = used_buffer->params().getDefaultOutputFormat();
3483 processing_format = format;
3485 progress_->clearMessages();
3488 #if EXPORT_in_THREAD
3489 GuiViewPrivate::busyBuffers.insert(used_buffer);
3490 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3491 if (!cloned_buffer) {
3492 Alert::error(_("Export Error"),
3493 _("Error cloning the Buffer."));
3496 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3501 setPreviewFuture(f);
3502 last_export_format = used_buffer->params().bufferFormat();
3505 // We are asynchronous, so we don't know here anything about the success
3508 Buffer::ExportStatus status;
3510 status = (used_buffer->*syncFunc)(format, true);
3511 } else if (previewFunc) {
3512 status = (used_buffer->*previewFunc)(format);
3515 handleExportStatus(gv_, status, format);
3517 return (status == Buffer::ExportSuccess
3518 || status == Buffer::PreviewSuccess);
3522 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3524 BufferView * bv = currentBufferView();
3525 LASSERT(bv, return);
3527 // Let the current BufferView dispatch its own actions.
3528 bv->dispatch(cmd, dr);
3529 if (dr.dispatched())
3532 // Try with the document BufferView dispatch if any.
3533 BufferView * doc_bv = documentBufferView();
3534 if (doc_bv && doc_bv != bv) {
3535 doc_bv->dispatch(cmd, dr);
3536 if (dr.dispatched())
3540 // Then let the current Cursor dispatch its own actions.
3541 bv->cursor().dispatch(cmd);
3543 // update completion. We do it here and not in
3544 // processKeySym to avoid another redraw just for a
3545 // changed inline completion
3546 if (cmd.origin() == FuncRequest::KEYBOARD) {
3547 if (cmd.action() == LFUN_SELF_INSERT
3548 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3549 updateCompletion(bv->cursor(), true, true);
3550 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3551 updateCompletion(bv->cursor(), false, true);
3553 updateCompletion(bv->cursor(), false, false);
3556 dr = bv->cursor().result();
3560 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3562 BufferView * bv = currentBufferView();
3563 // By default we won't need any update.
3564 dr.screenUpdate(Update::None);
3565 // assume cmd will be dispatched
3566 dr.dispatched(true);
3568 Buffer * doc_buffer = documentBufferView()
3569 ? &(documentBufferView()->buffer()) : 0;
3571 if (cmd.origin() == FuncRequest::TOC) {
3572 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3573 // FIXME: do we need to pass a DispatchResult object here?
3574 toc->doDispatch(bv->cursor(), cmd);
3578 string const argument = to_utf8(cmd.argument());
3580 switch(cmd.action()) {
3581 case LFUN_BUFFER_CHILD_OPEN:
3582 openChildDocument(to_utf8(cmd.argument()));
3585 case LFUN_BUFFER_IMPORT:
3586 importDocument(to_utf8(cmd.argument()));
3589 case LFUN_BUFFER_EXPORT: {
3592 // GCC only sees strfwd.h when building merged
3593 if (::lyx::operator==(cmd.argument(), "custom")) {
3594 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3598 string const dest = cmd.getArg(1);
3599 FileName target_dir;
3600 if (!dest.empty() && FileName::isAbsolute(dest))
3601 target_dir = FileName(support::onlyPath(dest));
3603 target_dir = doc_buffer->fileName().onlyPath();
3605 string const format = (argument.empty() || argument == "default") ?
3606 doc_buffer->params().getDefaultOutputFormat() : argument;
3608 if ((dest.empty() && doc_buffer->isUnnamed())
3609 || !target_dir.isDirWritable()) {
3610 exportBufferAs(*doc_buffer, from_utf8(format));
3613 /* TODO/Review: Is it a problem to also export the children?
3614 See the update_unincluded flag */
3615 d.asyncBufferProcessing(format,
3618 &GuiViewPrivate::exportAndDestroy,
3621 // TODO Inform user about success
3625 case LFUN_BUFFER_EXPORT_AS: {
3626 LASSERT(doc_buffer, break);
3627 docstring f = cmd.argument();
3629 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3630 exportBufferAs(*doc_buffer, f);
3634 case LFUN_BUFFER_UPDATE: {
3635 d.asyncBufferProcessing(argument,
3638 &GuiViewPrivate::compileAndDestroy,
3643 case LFUN_BUFFER_VIEW: {
3644 d.asyncBufferProcessing(argument,
3646 _("Previewing ..."),
3647 &GuiViewPrivate::previewAndDestroy,
3652 case LFUN_MASTER_BUFFER_UPDATE: {
3653 d.asyncBufferProcessing(argument,
3654 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3656 &GuiViewPrivate::compileAndDestroy,
3661 case LFUN_MASTER_BUFFER_VIEW: {
3662 d.asyncBufferProcessing(argument,
3663 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3665 &GuiViewPrivate::previewAndDestroy,
3666 0, &Buffer::preview);
3669 case LFUN_BUFFER_SWITCH: {
3670 string const file_name = to_utf8(cmd.argument());
3671 if (!FileName::isAbsolute(file_name)) {
3673 dr.setMessage(_("Absolute filename expected."));
3677 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3680 dr.setMessage(_("Document not loaded"));
3684 // Do we open or switch to the buffer in this view ?
3685 if (workArea(*buffer)
3686 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3691 // Look for the buffer in other views
3692 QList<int> const ids = guiApp->viewIds();
3694 for (; i != ids.size(); ++i) {
3695 GuiView & gv = guiApp->view(ids[i]);
3696 if (gv.workArea(*buffer)) {
3698 gv.activateWindow();
3700 gv.setBuffer(buffer);
3705 // If necessary, open a new window as a last resort
3706 if (i == ids.size()) {
3707 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3713 case LFUN_BUFFER_NEXT:
3714 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3717 case LFUN_BUFFER_MOVE_NEXT:
3718 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3721 case LFUN_BUFFER_PREVIOUS:
3722 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3725 case LFUN_BUFFER_MOVE_PREVIOUS:
3726 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3729 case LFUN_COMMAND_EXECUTE: {
3730 command_execute_ = true;
3731 minibuffer_focus_ = true;
3734 case LFUN_DROP_LAYOUTS_CHOICE:
3735 d.layout_->showPopup();
3738 case LFUN_MENU_OPEN:
3739 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3740 menu->exec(QCursor::pos());
3743 case LFUN_FILE_INSERT:
3744 insertLyXFile(cmd.argument());
3747 case LFUN_FILE_INSERT_PLAINTEXT:
3748 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3749 string const fname = to_utf8(cmd.argument());
3750 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3751 dr.setMessage(_("Absolute filename expected."));
3755 FileName filename(fname);
3756 if (fname.empty()) {
3757 FileDialog dlg(qt_("Select file to insert"));
3759 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3760 QStringList(qt_("All Files (*)")));
3762 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3763 dr.setMessage(_("Canceled."));
3767 filename.set(fromqstr(result.second));
3771 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3772 bv->dispatch(new_cmd, dr);
3777 case LFUN_BUFFER_RELOAD: {
3778 LASSERT(doc_buffer, break);
3781 if (!doc_buffer->isClean()) {
3782 docstring const file =
3783 makeDisplayPath(doc_buffer->absFileName(), 20);
3784 docstring text = doc_buffer->notifiesExternalModification() ?
3785 _("Any changes will be lost. "
3786 "Are you sure you want to load the version on disk "
3787 "of the document %1$s?")
3788 : _("Any changes will be lost. "
3789 "Are you sure you want to revert to the saved version "
3790 "of the document %1$s?");
3791 ret = Alert::prompt(_("Revert to file on disk?"),
3792 bformat(text, file), 1, 1, _("&Revert"), _("&Cancel"));
3796 doc_buffer->markClean();
3797 reloadBuffer(*doc_buffer);
3798 dr.forceBufferUpdate();
3803 case LFUN_BUFFER_WRITE:
3804 LASSERT(doc_buffer, break);
3805 saveBuffer(*doc_buffer);
3808 case LFUN_BUFFER_WRITE_AS:
3809 LASSERT(doc_buffer, break);
3810 renameBuffer(*doc_buffer, cmd.argument());
3813 case LFUN_BUFFER_WRITE_ALL: {
3814 Buffer * first = theBufferList().first();
3817 message(_("Saving all documents..."));
3818 // We cannot use a for loop as the buffer list cycles.
3821 if (!b->isClean()) {
3823 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3825 b = theBufferList().next(b);
3826 } while (b != first);
3827 dr.setMessage(_("All documents saved."));
3831 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3832 LASSERT(doc_buffer, break);
3833 doc_buffer->clearExternalModification();
3836 case LFUN_BUFFER_CLOSE:
3840 case LFUN_BUFFER_CLOSE_ALL:
3844 case LFUN_TOOLBAR_TOGGLE: {
3845 string const name = cmd.getArg(0);
3846 if (GuiToolbar * t = toolbar(name))
3851 case LFUN_TOOLBAR_MOVABLE: {
3852 string const name = cmd.getArg(0);
3854 // toggle (all) toolbars movablility
3855 toolbarsMovable_ = !toolbarsMovable_;
3856 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3857 GuiToolbar * tb = toolbar(ti.name);
3858 if (tb && tb->isMovable() != toolbarsMovable_)
3859 // toggle toolbar movablity if it does not fit lock
3860 // (all) toolbars positions state silent = true, since
3861 // status bar notifications are slow
3864 if (toolbarsMovable_)
3865 dr.setMessage(_("Toolbars unlocked."));
3867 dr.setMessage(_("Toolbars locked."));
3868 } else if (GuiToolbar * t = toolbar(name)) {
3869 // toggle current toolbar movablity
3871 // update lock (all) toolbars positions
3872 updateLockToolbars();
3877 case LFUN_ICON_SIZE: {
3878 QSize size = d.iconSize(cmd.argument());
3880 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3881 size.width(), size.height()));
3885 case LFUN_DIALOG_UPDATE: {
3886 string const name = to_utf8(cmd.argument());
3887 if (name == "prefs" || name == "document")
3888 updateDialog(name, string());
3889 else if (name == "paragraph")
3890 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3891 else if (currentBufferView()) {
3892 Inset * inset = currentBufferView()->editedInset(name);
3893 // Can only update a dialog connected to an existing inset
3895 // FIXME: get rid of this indirection; GuiView ask the inset
3896 // if he is kind enough to update itself...
3897 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3898 //FIXME: pass DispatchResult here?
3899 inset->dispatch(currentBufferView()->cursor(), fr);
3905 case LFUN_DIALOG_TOGGLE: {
3906 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3907 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3908 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3912 case LFUN_DIALOG_DISCONNECT_INSET:
3913 disconnectDialog(to_utf8(cmd.argument()));
3916 case LFUN_DIALOG_HIDE: {
3917 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3921 case LFUN_DIALOG_SHOW: {
3922 string const name = cmd.getArg(0);
3923 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3925 if (name == "character") {
3926 data = freefont2string();
3928 showDialog("character", data);
3929 } else if (name == "latexlog") {
3930 // getStatus checks that
3931 LATTEST(doc_buffer);
3932 Buffer::LogType type;
3933 string const logfile = doc_buffer->logName(&type);
3935 case Buffer::latexlog:
3938 case Buffer::buildlog:
3942 data += Lexer::quoteString(logfile);
3943 showDialog("log", data);
3944 } else if (name == "vclog") {
3945 // getStatus checks that
3946 LATTEST(doc_buffer);
3947 string const data = "vc " +
3948 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3949 showDialog("log", data);
3950 } else if (name == "symbols") {
3951 data = bv->cursor().getEncoding()->name();
3953 showDialog("symbols", data);
3955 } else if (name == "prefs" && isFullScreen()) {
3956 lfunUiToggle("fullscreen");
3957 showDialog("prefs", data);
3959 showDialog(name, data);
3964 dr.setMessage(cmd.argument());
3967 case LFUN_UI_TOGGLE: {
3968 string arg = cmd.getArg(0);
3969 if (!lfunUiToggle(arg)) {
3970 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3971 dr.setMessage(bformat(msg, from_utf8(arg)));
3973 // Make sure the keyboard focus stays in the work area.
3978 case LFUN_VIEW_SPLIT: {
3979 LASSERT(doc_buffer, break);
3980 string const orientation = cmd.getArg(0);
3981 d.splitter_->setOrientation(orientation == "vertical"
3982 ? Qt::Vertical : Qt::Horizontal);
3983 TabWorkArea * twa = addTabWorkArea();
3984 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3985 setCurrentWorkArea(wa);
3988 case LFUN_TAB_GROUP_CLOSE:
3989 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3990 closeTabWorkArea(twa);
3991 d.current_work_area_ = 0;
3992 twa = d.currentTabWorkArea();
3993 // Switch to the next GuiWorkArea in the found TabWorkArea.
3995 // Make sure the work area is up to date.
3996 setCurrentWorkArea(twa->currentWorkArea());
3998 setCurrentWorkArea(0);
4003 case LFUN_VIEW_CLOSE:
4004 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4005 closeWorkArea(twa->currentWorkArea());
4006 d.current_work_area_ = 0;
4007 twa = d.currentTabWorkArea();
4008 // Switch to the next GuiWorkArea in the found TabWorkArea.
4010 // Make sure the work area is up to date.
4011 setCurrentWorkArea(twa->currentWorkArea());
4013 setCurrentWorkArea(0);
4018 case LFUN_COMPLETION_INLINE:
4019 if (d.current_work_area_)
4020 d.current_work_area_->completer().showInline();
4023 case LFUN_COMPLETION_POPUP:
4024 if (d.current_work_area_)
4025 d.current_work_area_->completer().showPopup();
4030 if (d.current_work_area_)
4031 d.current_work_area_->completer().tab();
4034 case LFUN_COMPLETION_CANCEL:
4035 if (d.current_work_area_) {
4036 if (d.current_work_area_->completer().popupVisible())
4037 d.current_work_area_->completer().hidePopup();
4039 d.current_work_area_->completer().hideInline();
4043 case LFUN_COMPLETION_ACCEPT:
4044 if (d.current_work_area_)
4045 d.current_work_area_->completer().activate();
4048 case LFUN_BUFFER_ZOOM_IN:
4049 case LFUN_BUFFER_ZOOM_OUT:
4050 case LFUN_BUFFER_ZOOM: {
4051 // use a signed temp to avoid overflow
4052 int zoom = lyxrc.currentZoom;
4053 if (cmd.argument().empty()) {
4054 if (cmd.action() == LFUN_BUFFER_ZOOM)
4056 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4061 if (cmd.action() == LFUN_BUFFER_ZOOM)
4062 zoom = convert<int>(cmd.argument());
4063 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4064 zoom += convert<int>(cmd.argument());
4066 zoom -= convert<int>(cmd.argument());
4069 if (zoom < static_cast<int>(zoom_min_))
4072 lyxrc.currentZoom = zoom;
4074 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4076 // The global QPixmapCache is used in GuiPainter to cache text
4077 // painting so we must reset it.
4078 QPixmapCache::clear();
4079 guiApp->fontLoader().update();
4080 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4084 case LFUN_VC_REGISTER:
4085 case LFUN_VC_RENAME:
4087 case LFUN_VC_CHECK_IN:
4088 case LFUN_VC_CHECK_OUT:
4089 case LFUN_VC_REPO_UPDATE:
4090 case LFUN_VC_LOCKING_TOGGLE:
4091 case LFUN_VC_REVERT:
4092 case LFUN_VC_UNDO_LAST:
4093 case LFUN_VC_COMMAND:
4094 case LFUN_VC_COMPARE:
4095 dispatchVC(cmd, dr);
4098 case LFUN_SERVER_GOTO_FILE_ROW:
4099 if(goToFileRow(to_utf8(cmd.argument())))
4100 dr.screenUpdate(Update::Force | Update::FitCursor);
4103 case LFUN_LYX_ACTIVATE:
4107 case LFUN_FORWARD_SEARCH: {
4108 // it seems safe to assume we have a document buffer, since
4109 // getStatus wants one.
4110 LATTEST(doc_buffer);
4111 Buffer const * doc_master = doc_buffer->masterBuffer();
4112 FileName const path(doc_master->temppath());
4113 string const texname = doc_master->isChild(doc_buffer)
4114 ? DocFileName(changeExtension(
4115 doc_buffer->absFileName(),
4116 "tex")).mangledFileName()
4117 : doc_buffer->latexName();
4118 string const fulltexname =
4119 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4120 string const mastername =
4121 removeExtension(doc_master->latexName());
4122 FileName const dviname(addName(path.absFileName(),
4123 addExtension(mastername, "dvi")));
4124 FileName const pdfname(addName(path.absFileName(),
4125 addExtension(mastername, "pdf")));
4126 bool const have_dvi = dviname.exists();
4127 bool const have_pdf = pdfname.exists();
4128 if (!have_dvi && !have_pdf) {
4129 dr.setMessage(_("Please, preview the document first."));
4132 string outname = dviname.onlyFileName();
4133 string command = lyxrc.forward_search_dvi;
4134 if (!have_dvi || (have_pdf &&
4135 pdfname.lastModified() > dviname.lastModified())) {
4136 outname = pdfname.onlyFileName();
4137 command = lyxrc.forward_search_pdf;
4140 DocIterator cur = bv->cursor();
4141 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4142 LYXERR(Debug::ACTION, "Forward search: row:" << row
4144 if (row == -1 || command.empty()) {
4145 dr.setMessage(_("Couldn't proceed."));
4148 string texrow = convert<string>(row);
4150 command = subst(command, "$$n", texrow);
4151 command = subst(command, "$$f", fulltexname);
4152 command = subst(command, "$$t", texname);
4153 command = subst(command, "$$o", outname);
4155 PathChanger p(path);
4157 one.startscript(Systemcall::DontWait, command);
4161 case LFUN_SPELLING_CONTINUOUSLY:
4162 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4163 dr.screenUpdate(Update::Force);
4167 // The LFUN must be for one of BufferView, Buffer or Cursor;
4169 dispatchToBufferView(cmd, dr);
4173 // Part of automatic menu appearance feature.
4174 if (isFullScreen()) {
4175 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4179 // Need to update bv because many LFUNs here might have destroyed it
4180 bv = currentBufferView();
4182 // Clear non-empty selections
4183 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4185 Cursor & cur = bv->cursor();
4186 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4187 cur.clearSelection();
4193 bool GuiView::lfunUiToggle(string const & ui_component)
4195 if (ui_component == "scrollbar") {
4196 // hide() is of no help
4197 if (d.current_work_area_->verticalScrollBarPolicy() ==
4198 Qt::ScrollBarAlwaysOff)
4200 d.current_work_area_->setVerticalScrollBarPolicy(
4201 Qt::ScrollBarAsNeeded);
4203 d.current_work_area_->setVerticalScrollBarPolicy(
4204 Qt::ScrollBarAlwaysOff);
4205 } else if (ui_component == "statusbar") {
4206 statusBar()->setVisible(!statusBar()->isVisible());
4207 } else if (ui_component == "menubar") {
4208 menuBar()->setVisible(!menuBar()->isVisible());
4210 if (ui_component == "frame") {
4212 getContentsMargins(&l, &t, &r, &b);
4213 //are the frames in default state?
4214 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4216 setContentsMargins(-2, -2, -2, -2);
4218 setContentsMargins(0, 0, 0, 0);
4221 if (ui_component == "fullscreen") {
4229 void GuiView::toggleFullScreen()
4231 if (isFullScreen()) {
4232 for (int i = 0; i != d.splitter_->count(); ++i)
4233 d.tabWorkArea(i)->setFullScreen(false);
4234 setContentsMargins(0, 0, 0, 0);
4235 setWindowState(windowState() ^ Qt::WindowFullScreen);
4238 statusBar()->show();
4241 hideDialogs("prefs", 0);
4242 for (int i = 0; i != d.splitter_->count(); ++i)
4243 d.tabWorkArea(i)->setFullScreen(true);
4244 setContentsMargins(-2, -2, -2, -2);
4246 setWindowState(windowState() ^ Qt::WindowFullScreen);
4247 if (lyxrc.full_screen_statusbar)
4248 statusBar()->hide();
4249 if (lyxrc.full_screen_menubar)
4251 if (lyxrc.full_screen_toolbars) {
4252 ToolbarMap::iterator end = d.toolbars_.end();
4253 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4258 // give dialogs like the TOC a chance to adapt
4263 Buffer const * GuiView::updateInset(Inset const * inset)
4268 Buffer const * inset_buffer = &(inset->buffer());
4270 for (int i = 0; i != d.splitter_->count(); ++i) {
4271 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4274 Buffer const * buffer = &(wa->bufferView().buffer());
4275 if (inset_buffer == buffer)
4276 wa->scheduleRedraw();
4278 return inset_buffer;
4282 void GuiView::restartCursor()
4284 /* When we move around, or type, it's nice to be able to see
4285 * the cursor immediately after the keypress.
4287 if (d.current_work_area_)
4288 d.current_work_area_->startBlinkingCursor();
4290 // Take this occasion to update the other GUI elements.
4296 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4298 if (d.current_work_area_)
4299 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4304 // This list should be kept in sync with the list of insets in
4305 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4306 // dialog should have the same name as the inset.
4307 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4308 // docs in LyXAction.cpp.
4310 char const * const dialognames[] = {
4312 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4313 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4314 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4315 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4316 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4317 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4318 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4319 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4321 char const * const * const end_dialognames =
4322 dialognames + (sizeof(dialognames) / sizeof(char *));
4326 cmpCStr(char const * name) : name_(name) {}
4327 bool operator()(char const * other) {
4328 return strcmp(other, name_) == 0;
4335 bool isValidName(string const & name)
4337 return find_if(dialognames, end_dialognames,
4338 cmpCStr(name.c_str())) != end_dialognames;
4344 void GuiView::resetDialogs()
4346 // Make sure that no LFUN uses any GuiView.
4347 guiApp->setCurrentView(0);
4351 constructToolbars();
4352 guiApp->menus().fillMenuBar(menuBar(), this, false);
4353 d.layout_->updateContents(true);
4354 // Now update controls with current buffer.
4355 guiApp->setCurrentView(this);
4361 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4363 if (!isValidName(name))
4366 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4368 if (it != d.dialogs_.end()) {
4370 it->second->hideView();
4371 return it->second.get();
4374 Dialog * dialog = build(name);
4375 d.dialogs_[name].reset(dialog);
4376 if (lyxrc.allow_geometry_session)
4377 dialog->restoreSession();
4384 void GuiView::showDialog(string const & name, string const & data,
4387 triggerShowDialog(toqstr(name), toqstr(data), inset);
4391 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4397 const string name = fromqstr(qname);
4398 const string data = fromqstr(qdata);
4402 Dialog * dialog = findOrBuild(name, false);
4404 bool const visible = dialog->isVisibleView();
4405 dialog->showData(data);
4406 if (inset && currentBufferView())
4407 currentBufferView()->editInset(name, inset);
4408 // We only set the focus to the new dialog if it was not yet
4409 // visible in order not to change the existing previous behaviour
4411 // activateWindow is needed for floating dockviews
4412 dialog->asQWidget()->raise();
4413 dialog->asQWidget()->activateWindow();
4414 dialog->asQWidget()->setFocus();
4418 catch (ExceptionMessage const & ex) {
4426 bool GuiView::isDialogVisible(string const & name) const
4428 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4429 if (it == d.dialogs_.end())
4431 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4435 void GuiView::hideDialog(string const & name, Inset * inset)
4437 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4438 if (it == d.dialogs_.end())
4442 if (!currentBufferView())
4444 if (inset != currentBufferView()->editedInset(name))
4448 Dialog * const dialog = it->second.get();
4449 if (dialog->isVisibleView())
4451 if (currentBufferView())
4452 currentBufferView()->editInset(name, 0);
4456 void GuiView::disconnectDialog(string const & name)
4458 if (!isValidName(name))
4460 if (currentBufferView())
4461 currentBufferView()->editInset(name, 0);
4465 void GuiView::hideAll() const
4467 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4468 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4470 for(; it != end; ++it)
4471 it->second->hideView();
4475 void GuiView::updateDialogs()
4477 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4478 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4480 for(; it != end; ++it) {
4481 Dialog * dialog = it->second.get();
4483 if (dialog->needBufferOpen() && !documentBufferView())
4484 hideDialog(fromqstr(dialog->name()), 0);
4485 else if (dialog->isVisibleView())
4486 dialog->checkStatus();
4493 Dialog * createDialog(GuiView & lv, string const & name);
4495 // will be replaced by a proper factory...
4496 Dialog * createGuiAbout(GuiView & lv);
4497 Dialog * createGuiBibtex(GuiView & lv);
4498 Dialog * createGuiChanges(GuiView & lv);
4499 Dialog * createGuiCharacter(GuiView & lv);
4500 Dialog * createGuiCitation(GuiView & lv);
4501 Dialog * createGuiCompare(GuiView & lv);
4502 Dialog * createGuiCompareHistory(GuiView & lv);
4503 Dialog * createGuiDelimiter(GuiView & lv);
4504 Dialog * createGuiDocument(GuiView & lv);
4505 Dialog * createGuiErrorList(GuiView & lv);
4506 Dialog * createGuiExternal(GuiView & lv);
4507 Dialog * createGuiGraphics(GuiView & lv);
4508 Dialog * createGuiInclude(GuiView & lv);
4509 Dialog * createGuiIndex(GuiView & lv);
4510 Dialog * createGuiListings(GuiView & lv);
4511 Dialog * createGuiLog(GuiView & lv);
4512 Dialog * createGuiMathMatrix(GuiView & lv);
4513 Dialog * createGuiNote(GuiView & lv);
4514 Dialog * createGuiParagraph(GuiView & lv);
4515 Dialog * createGuiPhantom(GuiView & lv);
4516 Dialog * createGuiPreferences(GuiView & lv);
4517 Dialog * createGuiPrint(GuiView & lv);
4518 Dialog * createGuiPrintindex(GuiView & lv);
4519 Dialog * createGuiRef(GuiView & lv);
4520 Dialog * createGuiSearch(GuiView & lv);
4521 Dialog * createGuiSearchAdv(GuiView & lv);
4522 Dialog * createGuiSendTo(GuiView & lv);
4523 Dialog * createGuiShowFile(GuiView & lv);
4524 Dialog * createGuiSpellchecker(GuiView & lv);
4525 Dialog * createGuiSymbols(GuiView & lv);
4526 Dialog * createGuiTabularCreate(GuiView & lv);
4527 Dialog * createGuiTexInfo(GuiView & lv);
4528 Dialog * createGuiToc(GuiView & lv);
4529 Dialog * createGuiThesaurus(GuiView & lv);
4530 Dialog * createGuiViewSource(GuiView & lv);
4531 Dialog * createGuiWrap(GuiView & lv);
4532 Dialog * createGuiProgressView(GuiView & lv);
4536 Dialog * GuiView::build(string const & name)
4538 LASSERT(isValidName(name), return 0);
4540 Dialog * dialog = createDialog(*this, name);
4544 if (name == "aboutlyx")
4545 return createGuiAbout(*this);
4546 if (name == "bibtex")
4547 return createGuiBibtex(*this);
4548 if (name == "changes")
4549 return createGuiChanges(*this);
4550 if (name == "character")
4551 return createGuiCharacter(*this);
4552 if (name == "citation")
4553 return createGuiCitation(*this);
4554 if (name == "compare")
4555 return createGuiCompare(*this);
4556 if (name == "comparehistory")
4557 return createGuiCompareHistory(*this);
4558 if (name == "document")
4559 return createGuiDocument(*this);
4560 if (name == "errorlist")
4561 return createGuiErrorList(*this);
4562 if (name == "external")
4563 return createGuiExternal(*this);
4565 return createGuiShowFile(*this);
4566 if (name == "findreplace")
4567 return createGuiSearch(*this);
4568 if (name == "findreplaceadv")
4569 return createGuiSearchAdv(*this);
4570 if (name == "graphics")
4571 return createGuiGraphics(*this);
4572 if (name == "include")
4573 return createGuiInclude(*this);
4574 if (name == "index")
4575 return createGuiIndex(*this);
4576 if (name == "index_print")
4577 return createGuiPrintindex(*this);
4578 if (name == "listings")
4579 return createGuiListings(*this);
4581 return createGuiLog(*this);
4582 if (name == "mathdelimiter")
4583 return createGuiDelimiter(*this);
4584 if (name == "mathmatrix")
4585 return createGuiMathMatrix(*this);
4587 return createGuiNote(*this);
4588 if (name == "paragraph")
4589 return createGuiParagraph(*this);
4590 if (name == "phantom")
4591 return createGuiPhantom(*this);
4592 if (name == "prefs")
4593 return createGuiPreferences(*this);
4595 return createGuiRef(*this);
4596 if (name == "sendto")
4597 return createGuiSendTo(*this);
4598 if (name == "spellchecker")
4599 return createGuiSpellchecker(*this);
4600 if (name == "symbols")
4601 return createGuiSymbols(*this);
4602 if (name == "tabularcreate")
4603 return createGuiTabularCreate(*this);
4604 if (name == "texinfo")
4605 return createGuiTexInfo(*this);
4606 if (name == "thesaurus")
4607 return createGuiThesaurus(*this);
4609 return createGuiToc(*this);
4610 if (name == "view-source")
4611 return createGuiViewSource(*this);
4613 return createGuiWrap(*this);
4614 if (name == "progress")
4615 return createGuiProgressView(*this);
4621 } // namespace frontend
4624 #include "moc_GuiView.cpp"