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 // The connection is closed when this is destroyed.
533 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535 d.autosave_timeout_.start();
537 connect(&d.statusbar_timer_, SIGNAL(timeout()),
538 this, SLOT(clearMessage()));
540 // We don't want to keep the window in memory if it is closed.
541 setAttribute(Qt::WA_DeleteOnClose, true);
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544 // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547 // since the icon is provided in the application bundle. We use a themed
548 // version when available and use the bundled one as fallback.
549 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
551 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
557 // use tabbed dock area for multiple docks
558 // (such as "source" and "messages")
559 setDockOptions(QMainWindow::ForceTabbedDocks);
562 setAcceptDrops(true);
564 // add busy indicator to statusbar
565 QLabel * busylabel = new QLabel(statusBar());
566 statusBar()->addPermanentWidget(busylabel);
567 search_mode mode = theGuiApp()->imageSearchMode();
568 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570 busylabel->setMovie(busyanim);
574 connect(&d.processing_thread_watcher_, SIGNAL(started()),
575 busylabel, SLOT(show()));
576 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577 busylabel, SLOT(hide()));
579 QFontMetrics const fm(statusBar()->fontMetrics());
580 int const roheight = max(int(d.normalIconSize), fm.height());
581 QSize const rosize(roheight, roheight);
582 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
583 read_only_ = new QLabel(statusBar());
584 read_only_->setPixmap(readonly);
585 read_only_->setScaledContents(true);
586 read_only_->setAlignment(Qt::AlignCenter);
588 statusBar()->addPermanentWidget(read_only_);
590 version_control_ = new QLabel(statusBar());
591 version_control_->setAlignment(Qt::AlignCenter);
592 version_control_->setFrameStyle(QFrame::StyledPanel);
593 version_control_->hide();
594 statusBar()->addPermanentWidget(version_control_);
596 statusBar()->setSizeGripEnabled(true);
599 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
600 SLOT(autoSaveThreadFinished()));
602 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
603 SLOT(processingThreadStarted()));
604 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
605 SLOT(processingThreadFinished()));
607 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
608 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
610 // set custom application bars context menu, e.g. tool bar and menu bar
611 setContextMenuPolicy(Qt::CustomContextMenu);
612 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
613 SLOT(toolBarPopup(const QPoint &)));
615 // Forbid too small unresizable window because it can happen
616 // with some window manager under X11.
617 setMinimumSize(300, 200);
619 if (lyxrc.allow_geometry_session) {
620 // Now take care of session management.
625 // no session handling, default to a sane size.
626 setGeometry(50, 50, 690, 510);
629 // clear session data if any.
631 settings.remove("views");
641 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
643 QVector<GuiWorkArea*> areas;
644 for (int i = 0; i < tabWorkAreaCount(); i++) {
645 TabWorkArea* ta = tabWorkArea(i);
646 for (int u = 0; u < ta->count(); u++) {
647 areas << ta->workArea(u);
653 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
654 string const & format)
656 docstring const fmt = theFormats().prettyName(format);
659 case Buffer::ExportSuccess:
660 msg = bformat(_("Successful export to format: %1$s"), fmt);
662 case Buffer::ExportCancel:
663 msg = _("Document export cancelled.");
665 case Buffer::ExportError:
666 case Buffer::ExportNoPathToFormat:
667 case Buffer::ExportTexPathHasSpaces:
668 case Buffer::ExportConverterError:
669 msg = bformat(_("Error while exporting format: %1$s"), fmt);
671 case Buffer::PreviewSuccess:
672 msg = bformat(_("Successful preview of format: %1$s"), fmt);
674 case Buffer::PreviewError:
675 msg = bformat(_("Error while previewing format: %1$s"), fmt);
682 void GuiView::processingThreadStarted()
687 void GuiView::processingThreadFinished()
689 QFutureWatcher<Buffer::ExportStatus> const * watcher =
690 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
692 Buffer::ExportStatus const status = watcher->result();
693 handleExportStatus(this, status, d.processing_format);
696 BufferView const * const bv = currentBufferView();
697 if (bv && !bv->buffer().errorList("Export").empty()) {
701 errors(d.last_export_format);
705 void GuiView::autoSaveThreadFinished()
707 QFutureWatcher<docstring> const * watcher =
708 static_cast<QFutureWatcher<docstring> const *>(sender());
709 message(watcher->result());
714 void GuiView::saveLayout() const
717 settings.setValue("zoom", lyxrc.currentZoom);
718 settings.beginGroup("views");
719 settings.beginGroup(QString::number(id_));
720 #if defined(Q_WS_X11) || defined(QPA_XCB)
721 settings.setValue("pos", pos());
722 settings.setValue("size", size());
724 settings.setValue("geometry", saveGeometry());
726 settings.setValue("layout", saveState(0));
727 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
731 void GuiView::saveUISettings() const
733 // Save the toolbar private states
734 ToolbarMap::iterator end = d.toolbars_.end();
735 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
736 it->second->saveSession();
737 // Now take care of all other dialogs
738 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
739 for (; it!= d.dialogs_.end(); ++it)
740 it->second->saveSession();
744 bool GuiView::restoreLayout()
747 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
748 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
749 settings.beginGroup("views");
750 settings.beginGroup(QString::number(id_));
751 QString const icon_key = "icon_size";
752 if (!settings.contains(icon_key))
755 //code below is skipped when when ~/.config/LyX is (re)created
756 setIconSize(d.iconSize(settings.value(icon_key).toString()));
758 #if defined(Q_WS_X11) || defined(QPA_XCB)
759 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
760 QSize size = settings.value("size", QSize(690, 510)).toSize();
764 // Work-around for bug #6034: the window ends up in an undetermined
765 // state when trying to restore a maximized window when it is
766 // already maximized.
767 if (!(windowState() & Qt::WindowMaximized))
768 if (!restoreGeometry(settings.value("geometry").toByteArray()))
769 setGeometry(50, 50, 690, 510);
771 // Make sure layout is correctly oriented.
772 setLayoutDirection(qApp->layoutDirection());
774 // Allow the toc and view-source dock widget to be restored if needed.
776 if ((dialog = findOrBuild("toc", true)))
777 // see bug 5082. At least setup title and enabled state.
778 // Visibility will be adjusted by restoreState below.
779 dialog->prepareView();
780 if ((dialog = findOrBuild("view-source", true)))
781 dialog->prepareView();
782 if ((dialog = findOrBuild("progress", true)))
783 dialog->prepareView();
785 if (!restoreState(settings.value("layout").toByteArray(), 0))
788 // init the toolbars that have not been restored
789 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
790 Toolbars::Infos::iterator end = guiApp->toolbars().end();
791 for (; cit != end; ++cit) {
792 GuiToolbar * tb = toolbar(cit->name);
793 if (tb && !tb->isRestored())
794 initToolbar(cit->name);
797 // update lock (all) toolbars positions
798 updateLockToolbars();
805 GuiToolbar * GuiView::toolbar(string const & name)
807 ToolbarMap::iterator it = d.toolbars_.find(name);
808 if (it != d.toolbars_.end())
811 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
816 void GuiView::updateLockToolbars()
818 toolbarsMovable_ = false;
819 for (ToolbarInfo const & info : guiApp->toolbars()) {
820 GuiToolbar * tb = toolbar(info.name);
821 if (tb && tb->isMovable())
822 toolbarsMovable_ = true;
827 void GuiView::constructToolbars()
829 ToolbarMap::iterator it = d.toolbars_.begin();
830 for (; it != d.toolbars_.end(); ++it)
834 // I don't like doing this here, but the standard toolbar
835 // destroys this object when it's destroyed itself (vfr)
836 d.layout_ = new LayoutBox(*this);
837 d.stack_widget_->addWidget(d.layout_);
838 d.layout_->move(0,0);
840 // extracts the toolbars from the backend
841 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
842 Toolbars::Infos::iterator end = guiApp->toolbars().end();
843 for (; cit != end; ++cit)
844 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
848 void GuiView::initToolbars()
850 // extracts the toolbars from the backend
851 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
852 Toolbars::Infos::iterator end = guiApp->toolbars().end();
853 for (; cit != end; ++cit)
854 initToolbar(cit->name);
858 void GuiView::initToolbar(string const & name)
860 GuiToolbar * tb = toolbar(name);
863 int const visibility = guiApp->toolbars().defaultVisibility(name);
864 bool newline = !(visibility & Toolbars::SAMEROW);
865 tb->setVisible(false);
866 tb->setVisibility(visibility);
868 if (visibility & Toolbars::TOP) {
870 addToolBarBreak(Qt::TopToolBarArea);
871 addToolBar(Qt::TopToolBarArea, tb);
874 if (visibility & Toolbars::BOTTOM) {
876 addToolBarBreak(Qt::BottomToolBarArea);
877 addToolBar(Qt::BottomToolBarArea, tb);
880 if (visibility & Toolbars::LEFT) {
882 addToolBarBreak(Qt::LeftToolBarArea);
883 addToolBar(Qt::LeftToolBarArea, tb);
886 if (visibility & Toolbars::RIGHT) {
888 addToolBarBreak(Qt::RightToolBarArea);
889 addToolBar(Qt::RightToolBarArea, tb);
892 if (visibility & Toolbars::ON)
893 tb->setVisible(true);
895 tb->setMovable(true);
899 TocModels & GuiView::tocModels()
901 return d.toc_models_;
905 void GuiView::setFocus()
907 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
908 QMainWindow::setFocus();
912 bool GuiView::hasFocus() const
914 if (currentWorkArea())
915 return currentWorkArea()->hasFocus();
916 if (currentMainWorkArea())
917 return currentMainWorkArea()->hasFocus();
918 return d.bg_widget_->hasFocus();
922 void GuiView::focusInEvent(QFocusEvent * e)
924 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
925 QMainWindow::focusInEvent(e);
926 // Make sure guiApp points to the correct view.
927 guiApp->setCurrentView(this);
928 if (currentWorkArea())
929 currentWorkArea()->setFocus();
930 else if (currentMainWorkArea())
931 currentMainWorkArea()->setFocus();
933 d.bg_widget_->setFocus();
937 void GuiView::showEvent(QShowEvent * e)
939 LYXERR(Debug::GUI, "Passed Geometry "
940 << size().height() << "x" << size().width()
941 << "+" << pos().x() << "+" << pos().y());
943 if (d.splitter_->count() == 0)
944 // No work area, switch to the background widget.
948 QMainWindow::showEvent(e);
952 bool GuiView::closeScheduled()
959 bool GuiView::prepareAllBuffersForLogout()
961 Buffer * first = theBufferList().first();
965 // First, iterate over all buffers and ask the users if unsaved
966 // changes should be saved.
967 // We cannot use a for loop as the buffer list cycles.
970 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
972 b = theBufferList().next(b);
973 } while (b != first);
975 // Next, save session state
976 // When a view/window was closed before without quitting LyX, there
977 // are already entries in the lastOpened list.
978 theSession().lastOpened().clear();
985 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
986 ** is responsibility of the container (e.g., dialog)
988 void GuiView::closeEvent(QCloseEvent * close_event)
990 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
992 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
993 Alert::warning(_("Exit LyX"),
994 _("LyX could not be closed because documents are being processed by LyX."));
995 close_event->setAccepted(false);
999 // If the user pressed the x (so we didn't call closeView
1000 // programmatically), we want to clear all existing entries.
1002 theSession().lastOpened().clear();
1007 // it can happen that this event arrives without selecting the view,
1008 // e.g. when clicking the close button on a background window.
1010 if (!closeWorkAreaAll()) {
1012 close_event->ignore();
1016 // Make sure that nothing will use this to be closed View.
1017 guiApp->unregisterView(this);
1019 if (isFullScreen()) {
1020 // Switch off fullscreen before closing.
1025 // Make sure the timer time out will not trigger a statusbar update.
1026 d.statusbar_timer_.stop();
1028 // Saving fullscreen requires additional tweaks in the toolbar code.
1029 // It wouldn't also work under linux natively.
1030 if (lyxrc.allow_geometry_session) {
1035 close_event->accept();
1039 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1041 if (event->mimeData()->hasUrls())
1043 /// \todo Ask lyx-devel is this is enough:
1044 /// if (event->mimeData()->hasFormat("text/plain"))
1045 /// event->acceptProposedAction();
1049 void GuiView::dropEvent(QDropEvent * event)
1051 QList<QUrl> files = event->mimeData()->urls();
1052 if (files.isEmpty())
1055 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1056 for (int i = 0; i != files.size(); ++i) {
1057 string const file = os::internal_path(fromqstr(
1058 files.at(i).toLocalFile()));
1062 string const ext = support::getExtension(file);
1063 vector<const Format *> found_formats;
1065 // Find all formats that have the correct extension.
1066 vector<const Format *> const & import_formats
1067 = theConverters().importableFormats();
1068 vector<const Format *>::const_iterator it = import_formats.begin();
1069 for (; it != import_formats.end(); ++it)
1070 if ((*it)->hasExtension(ext))
1071 found_formats.push_back(*it);
1074 if (found_formats.size() >= 1) {
1075 if (found_formats.size() > 1) {
1076 //FIXME: show a dialog to choose the correct importable format
1077 LYXERR(Debug::FILES,
1078 "Multiple importable formats found, selecting first");
1080 string const arg = found_formats[0]->name() + " " + file;
1081 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1084 //FIXME: do we have to explicitly check whether it's a lyx file?
1085 LYXERR(Debug::FILES,
1086 "No formats found, trying to open it as a lyx file");
1087 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1089 // add the functions to the queue
1090 guiApp->addToFuncRequestQueue(cmd);
1093 // now process the collected functions. We perform the events
1094 // asynchronously. This prevents potential problems in case the
1095 // BufferView is closed within an event.
1096 guiApp->processFuncRequestQueueAsync();
1100 void GuiView::message(docstring const & str)
1102 if (ForkedProcess::iAmAChild())
1105 // call is moved to GUI-thread by GuiProgress
1106 d.progress_->appendMessage(toqstr(str));
1110 void GuiView::clearMessageText()
1112 message(docstring());
1116 void GuiView::updateStatusBarMessage(QString const & str)
1118 statusBar()->showMessage(str);
1119 d.statusbar_timer_.stop();
1120 d.statusbar_timer_.start(3000);
1124 void GuiView::clearMessage()
1126 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1127 // the hasFocus function mostly returns false, even if the focus is on
1128 // a workarea in this view.
1132 d.statusbar_timer_.stop();
1136 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1138 if (wa != d.current_work_area_
1139 || wa->bufferView().buffer().isInternal())
1141 Buffer const & buf = wa->bufferView().buffer();
1142 // Set the windows title
1143 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1144 if (buf.notifiesExternalModification()) {
1145 title = bformat(_("%1$s (modified externally)"), title);
1146 // If the external modification status has changed, then maybe the status of
1147 // buffer-save has changed too.
1151 title += from_ascii(" - LyX");
1153 setWindowTitle(toqstr(title));
1154 // Sets the path for the window: this is used by OSX to
1155 // allow a context click on the title bar showing a menu
1156 // with the path up to the file
1157 setWindowFilePath(toqstr(buf.absFileName()));
1158 // Tell Qt whether the current document is changed
1159 setWindowModified(!buf.isClean());
1161 if (buf.hasReadonlyFlag())
1166 if (buf.lyxvc().inUse()) {
1167 version_control_->show();
1168 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1170 version_control_->hide();
1174 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1176 if (d.current_work_area_)
1177 // disconnect the current work area from all slots
1178 QObject::disconnect(d.current_work_area_, 0, this, 0);
1180 disconnectBufferView();
1181 connectBufferView(wa->bufferView());
1182 connectBuffer(wa->bufferView().buffer());
1183 d.current_work_area_ = wa;
1184 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1185 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1186 QObject::connect(wa, SIGNAL(busy(bool)),
1187 this, SLOT(setBusy(bool)));
1188 // connection of a signal to a signal
1189 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1190 this, SIGNAL(bufferViewChanged()));
1191 Q_EMIT updateWindowTitle(wa);
1192 Q_EMIT bufferViewChanged();
1196 void GuiView::onBufferViewChanged()
1199 // Buffer-dependent dialogs must be updated. This is done here because
1200 // some dialogs require buffer()->text.
1205 void GuiView::on_lastWorkAreaRemoved()
1208 // We already are in a close event. Nothing more to do.
1211 if (d.splitter_->count() > 1)
1212 // We have a splitter so don't close anything.
1215 // Reset and updates the dialogs.
1216 Q_EMIT bufferViewChanged();
1221 if (lyxrc.open_buffers_in_tabs)
1222 // Nothing more to do, the window should stay open.
1225 if (guiApp->viewIds().size() > 1) {
1231 // On Mac we also close the last window because the application stay
1232 // resident in memory. On other platforms we don't close the last
1233 // window because this would quit the application.
1239 void GuiView::updateStatusBar()
1241 // let the user see the explicit message
1242 if (d.statusbar_timer_.isActive())
1249 void GuiView::showMessage()
1253 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1254 if (msg.isEmpty()) {
1255 BufferView const * bv = currentBufferView();
1257 msg = toqstr(bv->cursor().currentState());
1259 msg = qt_("Welcome to LyX!");
1261 statusBar()->showMessage(msg);
1265 bool GuiView::event(QEvent * e)
1269 // Useful debug code:
1270 //case QEvent::ActivationChange:
1271 //case QEvent::WindowDeactivate:
1272 //case QEvent::Paint:
1273 //case QEvent::Enter:
1274 //case QEvent::Leave:
1275 //case QEvent::HoverEnter:
1276 //case QEvent::HoverLeave:
1277 //case QEvent::HoverMove:
1278 //case QEvent::StatusTip:
1279 //case QEvent::DragEnter:
1280 //case QEvent::DragLeave:
1281 //case QEvent::Drop:
1284 case QEvent::WindowActivate: {
1285 GuiView * old_view = guiApp->currentView();
1286 if (this == old_view) {
1288 return QMainWindow::event(e);
1290 if (old_view && old_view->currentBufferView()) {
1291 // save current selection to the selection buffer to allow
1292 // middle-button paste in this window.
1293 cap::saveSelection(old_view->currentBufferView()->cursor());
1295 guiApp->setCurrentView(this);
1296 if (d.current_work_area_)
1297 on_currentWorkAreaChanged(d.current_work_area_);
1301 return QMainWindow::event(e);
1304 case QEvent::ShortcutOverride: {
1306 if (isFullScreen() && menuBar()->isHidden()) {
1307 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1308 // FIXME: we should also try to detect special LyX shortcut such as
1309 // Alt-P and Alt-M. Right now there is a hack in
1310 // GuiWorkArea::processKeySym() that hides again the menubar for
1312 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1314 return QMainWindow::event(e);
1317 return QMainWindow::event(e);
1321 return QMainWindow::event(e);
1325 void GuiView::resetWindowTitle()
1327 setWindowTitle(qt_("LyX"));
1330 bool GuiView::focusNextPrevChild(bool /*next*/)
1337 bool GuiView::busy() const
1343 void GuiView::setBusy(bool busy)
1345 bool const busy_before = busy_ > 0;
1346 busy ? ++busy_ : --busy_;
1347 if ((busy_ > 0) == busy_before)
1348 // busy state didn't change
1352 QApplication::setOverrideCursor(Qt::WaitCursor);
1355 QApplication::restoreOverrideCursor();
1360 void GuiView::resetCommandExecute()
1362 command_execute_ = false;
1367 double GuiView::pixelRatio() const
1369 #if QT_VERSION >= 0x050000
1370 return qt_scale_factor * devicePixelRatio();
1377 GuiWorkArea * GuiView::workArea(int index)
1379 if (TabWorkArea * twa = d.currentTabWorkArea())
1380 if (index < twa->count())
1381 return twa->workArea(index);
1386 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1388 if (currentWorkArea()
1389 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1390 return currentWorkArea();
1391 if (TabWorkArea * twa = d.currentTabWorkArea())
1392 return twa->workArea(buffer);
1397 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1399 // Automatically create a TabWorkArea if there are none yet.
1400 TabWorkArea * tab_widget = d.splitter_->count()
1401 ? d.currentTabWorkArea() : addTabWorkArea();
1402 return tab_widget->addWorkArea(buffer, *this);
1406 TabWorkArea * GuiView::addTabWorkArea()
1408 TabWorkArea * twa = new TabWorkArea;
1409 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1410 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1411 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1412 this, SLOT(on_lastWorkAreaRemoved()));
1414 d.splitter_->addWidget(twa);
1415 d.stack_widget_->setCurrentWidget(d.splitter_);
1420 GuiWorkArea const * GuiView::currentWorkArea() const
1422 return d.current_work_area_;
1426 GuiWorkArea * GuiView::currentWorkArea()
1428 return d.current_work_area_;
1432 GuiWorkArea const * GuiView::currentMainWorkArea() const
1434 if (!d.currentTabWorkArea())
1436 return d.currentTabWorkArea()->currentWorkArea();
1440 GuiWorkArea * GuiView::currentMainWorkArea()
1442 if (!d.currentTabWorkArea())
1444 return d.currentTabWorkArea()->currentWorkArea();
1448 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1450 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1452 d.current_work_area_ = 0;
1454 Q_EMIT bufferViewChanged();
1458 // FIXME: I've no clue why this is here and why it accesses
1459 // theGuiApp()->currentView, which might be 0 (bug 6464).
1460 // See also 27525 (vfr).
1461 if (theGuiApp()->currentView() == this
1462 && theGuiApp()->currentView()->currentWorkArea() == wa)
1465 if (currentBufferView())
1466 cap::saveSelection(currentBufferView()->cursor());
1468 theGuiApp()->setCurrentView(this);
1469 d.current_work_area_ = wa;
1471 // We need to reset this now, because it will need to be
1472 // right if the tabWorkArea gets reset in the for loop. We
1473 // will change it back if we aren't in that case.
1474 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1475 d.current_main_work_area_ = wa;
1477 for (int i = 0; i != d.splitter_->count(); ++i) {
1478 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1479 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1480 << ", Current main wa: " << currentMainWorkArea());
1485 d.current_main_work_area_ = old_cmwa;
1487 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1488 on_currentWorkAreaChanged(wa);
1489 BufferView & bv = wa->bufferView();
1490 bv.cursor().fixIfBroken();
1492 wa->setUpdatesEnabled(true);
1493 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1497 void GuiView::removeWorkArea(GuiWorkArea * wa)
1499 LASSERT(wa, return);
1500 if (wa == d.current_work_area_) {
1502 disconnectBufferView();
1503 d.current_work_area_ = 0;
1504 d.current_main_work_area_ = 0;
1507 bool found_twa = false;
1508 for (int i = 0; i != d.splitter_->count(); ++i) {
1509 TabWorkArea * twa = d.tabWorkArea(i);
1510 if (twa->removeWorkArea(wa)) {
1511 // Found in this tab group, and deleted the GuiWorkArea.
1513 if (twa->count() != 0) {
1514 if (d.current_work_area_ == 0)
1515 // This means that we are closing the current GuiWorkArea, so
1516 // switch to the next GuiWorkArea in the found TabWorkArea.
1517 setCurrentWorkArea(twa->currentWorkArea());
1519 // No more WorkAreas in this tab group, so delete it.
1526 // It is not a tabbed work area (i.e., the search work area), so it
1527 // should be deleted by other means.
1528 LASSERT(found_twa, return);
1530 if (d.current_work_area_ == 0) {
1531 if (d.splitter_->count() != 0) {
1532 TabWorkArea * twa = d.currentTabWorkArea();
1533 setCurrentWorkArea(twa->currentWorkArea());
1535 // No more work areas, switch to the background widget.
1536 setCurrentWorkArea(0);
1542 LayoutBox * GuiView::getLayoutDialog() const
1548 void GuiView::updateLayoutList()
1551 d.layout_->updateContents(false);
1555 void GuiView::updateToolbars()
1557 ToolbarMap::iterator end = d.toolbars_.end();
1558 if (d.current_work_area_) {
1560 if (d.current_work_area_->bufferView().cursor().inMathed()
1561 && !d.current_work_area_->bufferView().cursor().inRegexped())
1562 context |= Toolbars::MATH;
1563 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1564 context |= Toolbars::TABLE;
1565 if (currentBufferView()->buffer().areChangesPresent()
1566 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1567 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1568 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1569 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1570 context |= Toolbars::REVIEW;
1571 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1572 context |= Toolbars::MATHMACROTEMPLATE;
1573 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1574 context |= Toolbars::IPA;
1575 if (command_execute_)
1576 context |= Toolbars::MINIBUFFER;
1577 if (minibuffer_focus_) {
1578 context |= Toolbars::MINIBUFFER_FOCUS;
1579 minibuffer_focus_ = false;
1582 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1583 it->second->update(context);
1585 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1586 it->second->update();
1590 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1592 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1593 LASSERT(newBuffer, return);
1595 GuiWorkArea * wa = workArea(*newBuffer);
1598 newBuffer->masterBuffer()->updateBuffer();
1600 wa = addWorkArea(*newBuffer);
1601 // scroll to the position when the BufferView was last closed
1602 if (lyxrc.use_lastfilepos) {
1603 LastFilePosSection::FilePos filepos =
1604 theSession().lastFilePos().load(newBuffer->fileName());
1605 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1608 //Disconnect the old buffer...there's no new one.
1611 connectBuffer(*newBuffer);
1612 connectBufferView(wa->bufferView());
1614 setCurrentWorkArea(wa);
1618 void GuiView::connectBuffer(Buffer & buf)
1620 buf.setGuiDelegate(this);
1624 void GuiView::disconnectBuffer()
1626 if (d.current_work_area_)
1627 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1631 void GuiView::connectBufferView(BufferView & bv)
1633 bv.setGuiDelegate(this);
1637 void GuiView::disconnectBufferView()
1639 if (d.current_work_area_)
1640 d.current_work_area_->bufferView().setGuiDelegate(0);
1644 void GuiView::errors(string const & error_type, bool from_master)
1646 BufferView const * const bv = currentBufferView();
1650 #if EXPORT_in_THREAD
1651 // We are called with from_master == false by default, so we
1652 // have to figure out whether that is the case or not.
1653 ErrorList & el = bv->buffer().errorList(error_type);
1655 el = bv->buffer().masterBuffer()->errorList(error_type);
1659 ErrorList const & el = from_master ?
1660 bv->buffer().masterBuffer()->errorList(error_type) :
1661 bv->buffer().errorList(error_type);
1667 string data = error_type;
1669 data = "from_master|" + error_type;
1670 showDialog("errorlist", data);
1674 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1676 d.toc_models_.updateItem(toqstr(type), dit);
1680 void GuiView::structureChanged()
1682 // This is called from the Buffer, which has no way to ensure that cursors
1683 // in BufferView remain valid.
1684 if (documentBufferView())
1685 documentBufferView()->cursor().sanitize();
1686 // FIXME: This is slightly expensive, though less than the tocBackend update
1687 // (#9880). This also resets the view in the Toc Widget (#6675).
1688 d.toc_models_.reset(documentBufferView());
1689 // Navigator needs more than a simple update in this case. It needs to be
1691 updateDialog("toc", "");
1695 void GuiView::updateDialog(string const & name, string const & data)
1697 if (!isDialogVisible(name))
1700 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1701 if (it == d.dialogs_.end())
1704 Dialog * const dialog = it->second.get();
1705 if (dialog->isVisibleView())
1706 dialog->initialiseParams(data);
1710 BufferView * GuiView::documentBufferView()
1712 return currentMainWorkArea()
1713 ? ¤tMainWorkArea()->bufferView()
1718 BufferView const * GuiView::documentBufferView() const
1720 return currentMainWorkArea()
1721 ? ¤tMainWorkArea()->bufferView()
1726 BufferView * GuiView::currentBufferView()
1728 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1732 BufferView const * GuiView::currentBufferView() const
1734 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1738 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1739 Buffer const * orig, Buffer * clone)
1741 bool const success = clone->autoSave();
1743 busyBuffers.remove(orig);
1745 ? _("Automatic save done.")
1746 : _("Automatic save failed!");
1750 void GuiView::autoSave()
1752 LYXERR(Debug::INFO, "Running autoSave()");
1754 Buffer * buffer = documentBufferView()
1755 ? &documentBufferView()->buffer() : 0;
1757 resetAutosaveTimers();
1761 GuiViewPrivate::busyBuffers.insert(buffer);
1762 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1763 buffer, buffer->cloneBufferOnly());
1764 d.autosave_watcher_.setFuture(f);
1765 resetAutosaveTimers();
1769 void GuiView::resetAutosaveTimers()
1772 d.autosave_timeout_.restart();
1776 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1779 Buffer * buf = currentBufferView()
1780 ? ¤tBufferView()->buffer() : 0;
1781 Buffer * doc_buffer = documentBufferView()
1782 ? &(documentBufferView()->buffer()) : 0;
1785 /* In LyX/Mac, when a dialog is open, the menus of the
1786 application can still be accessed without giving focus to
1787 the main window. In this case, we want to disable the menu
1788 entries that are buffer-related.
1789 This code must not be used on Linux and Windows, since it
1790 would disable buffer-related entries when hovering over the
1791 menu (see bug #9574).
1793 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1799 // Check whether we need a buffer
1800 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1801 // no, exit directly
1802 flag.message(from_utf8(N_("Command not allowed with"
1803 "out any document open")));
1804 flag.setEnabled(false);
1808 if (cmd.origin() == FuncRequest::TOC) {
1809 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1810 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1811 flag.setEnabled(false);
1815 switch(cmd.action()) {
1816 case LFUN_BUFFER_IMPORT:
1819 case LFUN_MASTER_BUFFER_UPDATE:
1820 case LFUN_MASTER_BUFFER_VIEW:
1822 && (doc_buffer->parent() != 0
1823 || doc_buffer->hasChildren())
1824 && !d.processing_thread_watcher_.isRunning();
1827 case LFUN_BUFFER_UPDATE:
1828 case LFUN_BUFFER_VIEW: {
1829 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1833 string format = to_utf8(cmd.argument());
1834 if (cmd.argument().empty())
1835 format = doc_buffer->params().getDefaultOutputFormat();
1836 enable = doc_buffer->params().isExportable(format, true);
1840 case LFUN_BUFFER_RELOAD:
1841 enable = doc_buffer && !doc_buffer->isUnnamed()
1842 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1845 case LFUN_BUFFER_CHILD_OPEN:
1846 enable = doc_buffer != 0;
1849 case LFUN_BUFFER_WRITE:
1850 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1853 //FIXME: This LFUN should be moved to GuiApplication.
1854 case LFUN_BUFFER_WRITE_ALL: {
1855 // We enable the command only if there are some modified buffers
1856 Buffer * first = theBufferList().first();
1861 // We cannot use a for loop as the buffer list is a cycle.
1863 if (!b->isClean()) {
1867 b = theBufferList().next(b);
1868 } while (b != first);
1872 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1873 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1876 case LFUN_BUFFER_WRITE_AS:
1877 case LFUN_BUFFER_EXPORT_AS:
1878 enable = doc_buffer != 0;
1881 case LFUN_BUFFER_CLOSE:
1882 case LFUN_VIEW_CLOSE:
1883 enable = doc_buffer != 0;
1886 case LFUN_BUFFER_CLOSE_ALL:
1887 enable = theBufferList().last() != theBufferList().first();
1890 case LFUN_VIEW_SPLIT:
1891 if (cmd.getArg(0) == "vertical")
1892 enable = doc_buffer && (d.splitter_->count() == 1 ||
1893 d.splitter_->orientation() == Qt::Vertical);
1895 enable = doc_buffer && (d.splitter_->count() == 1 ||
1896 d.splitter_->orientation() == Qt::Horizontal);
1899 case LFUN_TAB_GROUP_CLOSE:
1900 enable = d.tabWorkAreaCount() > 1;
1903 case LFUN_TOOLBAR_TOGGLE: {
1904 string const name = cmd.getArg(0);
1905 if (GuiToolbar * t = toolbar(name))
1906 flag.setOnOff(t->isVisible());
1909 docstring const msg =
1910 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1916 case LFUN_TOOLBAR_MOVABLE: {
1917 string const name = cmd.getArg(0);
1918 // use negation since locked == !movable
1920 // toolbar name * locks all toolbars
1921 flag.setOnOff(!toolbarsMovable_);
1922 else if (GuiToolbar * t = toolbar(name))
1923 flag.setOnOff(!(t->isMovable()));
1926 docstring const msg =
1927 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1933 case LFUN_ICON_SIZE:
1934 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1937 case LFUN_DROP_LAYOUTS_CHOICE:
1941 case LFUN_UI_TOGGLE:
1942 flag.setOnOff(isFullScreen());
1945 case LFUN_DIALOG_DISCONNECT_INSET:
1948 case LFUN_DIALOG_HIDE:
1949 // FIXME: should we check if the dialog is shown?
1952 case LFUN_DIALOG_TOGGLE:
1953 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1954 // fall through to set "enable"
1955 case LFUN_DIALOG_SHOW: {
1956 string const name = cmd.getArg(0);
1958 enable = name == "aboutlyx"
1959 || name == "file" //FIXME: should be removed.
1961 || name == "texinfo"
1962 || name == "progress"
1963 || name == "compare";
1964 else if (name == "character" || name == "symbols"
1965 || name == "mathdelimiter" || name == "mathmatrix") {
1966 if (!buf || buf->isReadonly())
1969 Cursor const & cur = currentBufferView()->cursor();
1970 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1973 else if (name == "latexlog")
1974 enable = FileName(doc_buffer->logName()).isReadableFile();
1975 else if (name == "spellchecker")
1976 enable = theSpellChecker()
1977 && !doc_buffer->isReadonly()
1978 && !doc_buffer->text().empty();
1979 else if (name == "vclog")
1980 enable = doc_buffer->lyxvc().inUse();
1984 case LFUN_DIALOG_UPDATE: {
1985 string const name = cmd.getArg(0);
1987 enable = name == "prefs";
1991 case LFUN_COMMAND_EXECUTE:
1993 case LFUN_MENU_OPEN:
1994 // Nothing to check.
1997 case LFUN_COMPLETION_INLINE:
1998 if (!d.current_work_area_
1999 || !d.current_work_area_->completer().inlinePossible(
2000 currentBufferView()->cursor()))
2004 case LFUN_COMPLETION_POPUP:
2005 if (!d.current_work_area_
2006 || !d.current_work_area_->completer().popupPossible(
2007 currentBufferView()->cursor()))
2012 if (!d.current_work_area_
2013 || !d.current_work_area_->completer().inlinePossible(
2014 currentBufferView()->cursor()))
2018 case LFUN_COMPLETION_ACCEPT:
2019 if (!d.current_work_area_
2020 || (!d.current_work_area_->completer().popupVisible()
2021 && !d.current_work_area_->completer().inlineVisible()
2022 && !d.current_work_area_->completer().completionAvailable()))
2026 case LFUN_COMPLETION_CANCEL:
2027 if (!d.current_work_area_
2028 || (!d.current_work_area_->completer().popupVisible()
2029 && !d.current_work_area_->completer().inlineVisible()))
2033 case LFUN_BUFFER_ZOOM_OUT:
2034 case LFUN_BUFFER_ZOOM_IN: {
2035 // only diff between these two is that the default for ZOOM_OUT
2037 bool const neg_zoom =
2038 convert<int>(cmd.argument()) < 0 ||
2039 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2040 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2041 docstring const msg =
2042 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2046 enable = doc_buffer;
2050 case LFUN_BUFFER_ZOOM: {
2051 bool const less_than_min_zoom =
2052 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2053 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2054 docstring const msg =
2055 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2060 enable = doc_buffer;
2064 case LFUN_BUFFER_MOVE_NEXT:
2065 case LFUN_BUFFER_MOVE_PREVIOUS:
2066 // we do not cycle when moving
2067 case LFUN_BUFFER_NEXT:
2068 case LFUN_BUFFER_PREVIOUS:
2069 // because we cycle, it doesn't matter whether on first or last
2070 enable = (d.currentTabWorkArea()->count() > 1);
2072 case LFUN_BUFFER_SWITCH:
2073 // toggle on the current buffer, but do not toggle off
2074 // the other ones (is that a good idea?)
2076 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2077 flag.setOnOff(true);
2080 case LFUN_VC_REGISTER:
2081 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2083 case LFUN_VC_RENAME:
2084 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2087 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2089 case LFUN_VC_CHECK_IN:
2090 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2092 case LFUN_VC_CHECK_OUT:
2093 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2095 case LFUN_VC_LOCKING_TOGGLE:
2096 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2097 && doc_buffer->lyxvc().lockingToggleEnabled();
2098 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2100 case LFUN_VC_REVERT:
2101 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2102 && !doc_buffer->hasReadonlyFlag();
2104 case LFUN_VC_UNDO_LAST:
2105 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2107 case LFUN_VC_REPO_UPDATE:
2108 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2110 case LFUN_VC_COMMAND: {
2111 if (cmd.argument().empty())
2113 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2117 case LFUN_VC_COMPARE:
2118 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2121 case LFUN_SERVER_GOTO_FILE_ROW:
2122 case LFUN_LYX_ACTIVATE:
2124 case LFUN_FORWARD_SEARCH:
2125 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2128 case LFUN_FILE_INSERT_PLAINTEXT:
2129 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2130 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2133 case LFUN_SPELLING_CONTINUOUSLY:
2134 flag.setOnOff(lyxrc.spellcheck_continuously);
2142 flag.setEnabled(false);
2148 static FileName selectTemplateFile()
2150 FileDialog dlg(qt_("Select template file"));
2151 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2152 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2154 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2155 QStringList(qt_("LyX Documents (*.lyx)")));
2157 if (result.first == FileDialog::Later)
2159 if (result.second.isEmpty())
2161 return FileName(fromqstr(result.second));
2165 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2169 Buffer * newBuffer = 0;
2171 newBuffer = checkAndLoadLyXFile(filename);
2172 } catch (ExceptionMessage const & e) {
2179 message(_("Document not loaded."));
2183 setBuffer(newBuffer);
2184 newBuffer->errors("Parse");
2187 theSession().lastFiles().add(filename);
2193 void GuiView::openDocument(string const & fname)
2195 string initpath = lyxrc.document_path;
2197 if (documentBufferView()) {
2198 string const trypath = documentBufferView()->buffer().filePath();
2199 // If directory is writeable, use this as default.
2200 if (FileName(trypath).isDirWritable())
2206 if (fname.empty()) {
2207 FileDialog dlg(qt_("Select document to open"));
2208 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2209 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2211 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2212 FileDialog::Result result =
2213 dlg.open(toqstr(initpath), filter);
2215 if (result.first == FileDialog::Later)
2218 filename = fromqstr(result.second);
2220 // check selected filename
2221 if (filename.empty()) {
2222 message(_("Canceled."));
2228 // get absolute path of file and add ".lyx" to the filename if
2230 FileName const fullname =
2231 fileSearch(string(), filename, "lyx", support::may_not_exist);
2232 if (!fullname.empty())
2233 filename = fullname.absFileName();
2235 if (!fullname.onlyPath().isDirectory()) {
2236 Alert::warning(_("Invalid filename"),
2237 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2238 from_utf8(fullname.absFileName())));
2242 // if the file doesn't exist and isn't already open (bug 6645),
2243 // let the user create one
2244 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2245 !LyXVC::file_not_found_hook(fullname)) {
2246 // the user specifically chose this name. Believe him.
2247 Buffer * const b = newFile(filename, string(), true);
2253 docstring const disp_fn = makeDisplayPath(filename);
2254 message(bformat(_("Opening document %1$s..."), disp_fn));
2257 Buffer * buf = loadDocument(fullname);
2259 str2 = bformat(_("Document %1$s opened."), disp_fn);
2260 if (buf->lyxvc().inUse())
2261 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2262 " " + _("Version control detected.");
2264 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2269 // FIXME: clean that
2270 static bool import(GuiView * lv, FileName const & filename,
2271 string const & format, ErrorList & errorList)
2273 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2275 string loader_format;
2276 vector<string> loaders = theConverters().loaders();
2277 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2278 vector<string>::const_iterator it = loaders.begin();
2279 vector<string>::const_iterator en = loaders.end();
2280 for (; it != en; ++it) {
2281 if (!theConverters().isReachable(format, *it))
2284 string const tofile =
2285 support::changeExtension(filename.absFileName(),
2286 theFormats().extension(*it));
2287 if (!theConverters().convert(0, filename, FileName(tofile),
2288 filename, format, *it, errorList))
2290 loader_format = *it;
2293 if (loader_format.empty()) {
2294 frontend::Alert::error(_("Couldn't import file"),
2295 bformat(_("No information for importing the format %1$s."),
2296 theFormats().prettyName(format)));
2300 loader_format = format;
2302 if (loader_format == "lyx") {
2303 Buffer * buf = lv->loadDocument(lyxfile);
2307 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2311 bool as_paragraphs = loader_format == "textparagraph";
2312 string filename2 = (loader_format == format) ? filename.absFileName()
2313 : support::changeExtension(filename.absFileName(),
2314 theFormats().extension(loader_format));
2315 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2317 guiApp->setCurrentView(lv);
2318 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2325 void GuiView::importDocument(string const & argument)
2328 string filename = split(argument, format, ' ');
2330 LYXERR(Debug::INFO, format << " file: " << filename);
2332 // need user interaction
2333 if (filename.empty()) {
2334 string initpath = lyxrc.document_path;
2335 if (documentBufferView()) {
2336 string const trypath = documentBufferView()->buffer().filePath();
2337 // If directory is writeable, use this as default.
2338 if (FileName(trypath).isDirWritable())
2342 docstring const text = bformat(_("Select %1$s file to import"),
2343 theFormats().prettyName(format));
2345 FileDialog dlg(toqstr(text));
2346 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2347 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2349 docstring filter = theFormats().prettyName(format);
2352 filter += from_utf8(theFormats().extensions(format));
2355 FileDialog::Result result =
2356 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2358 if (result.first == FileDialog::Later)
2361 filename = fromqstr(result.second);
2363 // check selected filename
2364 if (filename.empty())
2365 message(_("Canceled."));
2368 if (filename.empty())
2371 // get absolute path of file
2372 FileName const fullname(support::makeAbsPath(filename));
2374 // Can happen if the user entered a path into the dialog
2376 if (fullname.onlyFileName().empty()) {
2377 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2378 "Aborting import."),
2379 from_utf8(fullname.absFileName()));
2380 frontend::Alert::error(_("File name error"), msg);
2381 message(_("Canceled."));
2386 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2388 // Check if the document already is open
2389 Buffer * buf = theBufferList().getBuffer(lyxfile);
2392 if (!closeBuffer()) {
2393 message(_("Canceled."));
2398 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2400 // if the file exists already, and we didn't do
2401 // -i lyx thefile.lyx, warn
2402 if (lyxfile.exists() && fullname != lyxfile) {
2404 docstring text = bformat(_("The document %1$s already exists.\n\n"
2405 "Do you want to overwrite that document?"), displaypath);
2406 int const ret = Alert::prompt(_("Overwrite document?"),
2407 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2410 message(_("Canceled."));
2415 message(bformat(_("Importing %1$s..."), displaypath));
2416 ErrorList errorList;
2417 if (import(this, fullname, format, errorList))
2418 message(_("imported."));
2420 message(_("file not imported!"));
2422 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2426 void GuiView::newDocument(string const & filename, bool from_template)
2428 FileName initpath(lyxrc.document_path);
2429 if (documentBufferView()) {
2430 FileName const trypath(documentBufferView()->buffer().filePath());
2431 // If directory is writeable, use this as default.
2432 if (trypath.isDirWritable())
2436 string templatefile;
2437 if (from_template) {
2438 templatefile = selectTemplateFile().absFileName();
2439 if (templatefile.empty())
2444 if (filename.empty())
2445 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2447 b = newFile(filename, templatefile, true);
2452 // If no new document could be created, it is unsure
2453 // whether there is a valid BufferView.
2454 if (currentBufferView())
2455 // Ensure the cursor is correctly positioned on screen.
2456 currentBufferView()->showCursor();
2460 void GuiView::insertLyXFile(docstring const & fname)
2462 BufferView * bv = documentBufferView();
2467 FileName filename(to_utf8(fname));
2468 if (filename.empty()) {
2469 // Launch a file browser
2471 string initpath = lyxrc.document_path;
2472 string const trypath = bv->buffer().filePath();
2473 // If directory is writeable, use this as default.
2474 if (FileName(trypath).isDirWritable())
2478 FileDialog dlg(qt_("Select LyX document to insert"));
2479 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2480 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2482 FileDialog::Result result = dlg.open(toqstr(initpath),
2483 QStringList(qt_("LyX Documents (*.lyx)")));
2485 if (result.first == FileDialog::Later)
2489 filename.set(fromqstr(result.second));
2491 // check selected filename
2492 if (filename.empty()) {
2493 // emit message signal.
2494 message(_("Canceled."));
2499 bv->insertLyXFile(filename);
2500 bv->buffer().errors("Parse");
2504 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2506 FileName fname = b.fileName();
2507 FileName const oldname = fname;
2509 if (!newname.empty()) {
2511 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2513 // Switch to this Buffer.
2516 // No argument? Ask user through dialog.
2518 FileDialog dlg(qt_("Choose a filename to save document as"));
2519 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2520 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2522 if (!isLyXFileName(fname.absFileName()))
2523 fname.changeExtension(".lyx");
2525 FileDialog::Result result =
2526 dlg.save(toqstr(fname.onlyPath().absFileName()),
2527 QStringList(qt_("LyX Documents (*.lyx)")),
2528 toqstr(fname.onlyFileName()));
2530 if (result.first == FileDialog::Later)
2533 fname.set(fromqstr(result.second));
2538 if (!isLyXFileName(fname.absFileName()))
2539 fname.changeExtension(".lyx");
2542 // fname is now the new Buffer location.
2544 // if there is already a Buffer open with this name, we do not want
2545 // to have another one. (the second test makes sure we're not just
2546 // trying to overwrite ourselves, which is fine.)
2547 if (theBufferList().exists(fname) && fname != oldname
2548 && theBufferList().getBuffer(fname) != &b) {
2549 docstring const text =
2550 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2551 "Please close it before attempting to overwrite it.\n"
2552 "Do you want to choose a new filename?"),
2553 from_utf8(fname.absFileName()));
2554 int const ret = Alert::prompt(_("Chosen File Already Open"),
2555 text, 0, 1, _("&Rename"), _("&Cancel"));
2557 case 0: return renameBuffer(b, docstring(), kind);
2558 case 1: return false;
2563 bool const existsLocal = fname.exists();
2564 bool const existsInVC = LyXVC::fileInVC(fname);
2565 if (existsLocal || existsInVC) {
2566 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2567 if (kind != LV_WRITE_AS && existsInVC) {
2568 // renaming to a name that is already in VC
2570 docstring text = bformat(_("The document %1$s "
2571 "is already registered.\n\n"
2572 "Do you want to choose a new name?"),
2574 docstring const title = (kind == LV_VC_RENAME) ?
2575 _("Rename document?") : _("Copy document?");
2576 docstring const button = (kind == LV_VC_RENAME) ?
2577 _("&Rename") : _("&Copy");
2578 int const ret = Alert::prompt(title, text, 0, 1,
2579 button, _("&Cancel"));
2581 case 0: return renameBuffer(b, docstring(), kind);
2582 case 1: return false;
2587 docstring text = bformat(_("The document %1$s "
2588 "already exists.\n\n"
2589 "Do you want to overwrite that document?"),
2591 int const ret = Alert::prompt(_("Overwrite document?"),
2592 text, 0, 2, _("&Overwrite"),
2593 _("&Rename"), _("&Cancel"));
2596 case 1: return renameBuffer(b, docstring(), kind);
2597 case 2: return false;
2603 case LV_VC_RENAME: {
2604 string msg = b.lyxvc().rename(fname);
2607 message(from_utf8(msg));
2611 string msg = b.lyxvc().copy(fname);
2614 message(from_utf8(msg));
2620 // LyXVC created the file already in case of LV_VC_RENAME or
2621 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2622 // relative paths of included stuff right if we moved e.g. from
2623 // /a/b.lyx to /a/c/b.lyx.
2625 bool const saved = saveBuffer(b, fname);
2632 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2634 FileName fname = b.fileName();
2636 FileDialog dlg(qt_("Choose a filename to export the document as"));
2637 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2640 QString const anyformat = qt_("Guess from extension (*.*)");
2643 vector<Format const *> export_formats;
2644 for (Format const & f : theFormats())
2645 if (f.documentFormat())
2646 export_formats.push_back(&f);
2647 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2648 map<QString, string> fmap;
2651 for (Format const * f : export_formats) {
2652 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2653 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2655 from_ascii(f->extension())));
2656 types << loc_filter;
2657 fmap[loc_filter] = f->name();
2658 if (from_ascii(f->name()) == iformat) {
2659 filter = loc_filter;
2660 ext = f->extension();
2663 string ofname = fname.onlyFileName();
2665 ofname = support::changeExtension(ofname, ext);
2666 FileDialog::Result result =
2667 dlg.save(toqstr(fname.onlyPath().absFileName()),
2671 if (result.first != FileDialog::Chosen)
2675 fname.set(fromqstr(result.second));
2676 if (filter == anyformat)
2677 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2679 fmt_name = fmap[filter];
2680 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2681 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2683 if (fmt_name.empty() || fname.empty())
2686 // fname is now the new Buffer location.
2687 if (FileName(fname).exists()) {
2688 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2689 docstring text = bformat(_("The document %1$s already "
2690 "exists.\n\nDo you want to "
2691 "overwrite that document?"),
2693 int const ret = Alert::prompt(_("Overwrite document?"),
2694 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2697 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2698 case 2: return false;
2702 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2705 return dr.dispatched();
2709 bool GuiView::saveBuffer(Buffer & b)
2711 return saveBuffer(b, FileName());
2715 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2717 if (workArea(b) && workArea(b)->inDialogMode())
2720 if (fn.empty() && b.isUnnamed())
2721 return renameBuffer(b, docstring());
2723 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2725 theSession().lastFiles().add(b.fileName());
2729 // Switch to this Buffer.
2732 // FIXME: we don't tell the user *WHY* the save failed !!
2733 docstring const file = makeDisplayPath(b.absFileName(), 30);
2734 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2735 "Do you want to rename the document and "
2736 "try again?"), file);
2737 int const ret = Alert::prompt(_("Rename and save?"),
2738 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2741 if (!renameBuffer(b, docstring()))
2750 return saveBuffer(b, fn);
2754 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2756 return closeWorkArea(wa, false);
2760 // We only want to close the buffer if it is not visible in other workareas
2761 // of the same view, nor in other views, and if this is not a child
2762 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2764 Buffer & buf = wa->bufferView().buffer();
2766 bool last_wa = d.countWorkAreasOf(buf) == 1
2767 && !inOtherView(buf) && !buf.parent();
2769 bool close_buffer = last_wa;
2772 if (lyxrc.close_buffer_with_last_view == "yes")
2774 else if (lyxrc.close_buffer_with_last_view == "no")
2775 close_buffer = false;
2778 if (buf.isUnnamed())
2779 file = from_utf8(buf.fileName().onlyFileName());
2781 file = buf.fileName().displayName(30);
2782 docstring const text = bformat(
2783 _("Last view on document %1$s is being closed.\n"
2784 "Would you like to close or hide the document?\n"
2786 "Hidden documents can be displayed back through\n"
2787 "the menu: View->Hidden->...\n"
2789 "To remove this question, set your preference in:\n"
2790 " Tools->Preferences->Look&Feel->UserInterface\n"
2792 int ret = Alert::prompt(_("Close or hide document?"),
2793 text, 0, 1, _("&Close"), _("&Hide"));
2794 close_buffer = (ret == 0);
2798 return closeWorkArea(wa, close_buffer);
2802 bool GuiView::closeBuffer()
2804 GuiWorkArea * wa = currentMainWorkArea();
2805 // coverity complained about this
2806 // it seems unnecessary, but perhaps is worth the check
2807 LASSERT(wa, return false);
2809 setCurrentWorkArea(wa);
2810 Buffer & buf = wa->bufferView().buffer();
2811 return closeWorkArea(wa, !buf.parent());
2815 void GuiView::writeSession() const {
2816 GuiWorkArea const * active_wa = currentMainWorkArea();
2817 for (int i = 0; i < d.splitter_->count(); ++i) {
2818 TabWorkArea * twa = d.tabWorkArea(i);
2819 for (int j = 0; j < twa->count(); ++j) {
2820 GuiWorkArea * wa = twa->workArea(j);
2821 Buffer & buf = wa->bufferView().buffer();
2822 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2828 bool GuiView::closeBufferAll()
2830 // Close the workareas in all other views
2831 QList<int> const ids = guiApp->viewIds();
2832 for (int i = 0; i != ids.size(); ++i) {
2833 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2837 // Close our own workareas
2838 if (!closeWorkAreaAll())
2841 // Now close the hidden buffers. We prevent hidden buffers from being
2842 // dirty, so we can just close them.
2843 theBufferList().closeAll();
2848 bool GuiView::closeWorkAreaAll()
2850 setCurrentWorkArea(currentMainWorkArea());
2852 // We might be in a situation that there is still a tabWorkArea, but
2853 // there are no tabs anymore. This can happen when we get here after a
2854 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2855 // many TabWorkArea's have no documents anymore.
2858 // We have to call count() each time, because it can happen that
2859 // more than one splitter will disappear in one iteration (bug 5998).
2860 while (d.splitter_->count() > empty_twa) {
2861 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2863 if (twa->count() == 0)
2866 setCurrentWorkArea(twa->currentWorkArea());
2867 if (!closeTabWorkArea(twa))
2875 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2880 Buffer & buf = wa->bufferView().buffer();
2882 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2883 Alert::warning(_("Close document"),
2884 _("Document could not be closed because it is being processed by LyX."));
2889 return closeBuffer(buf);
2891 if (!inMultiTabs(wa))
2892 if (!saveBufferIfNeeded(buf, true))
2900 bool GuiView::closeBuffer(Buffer & buf)
2902 // If we are in a close_event all children will be closed in some time,
2903 // so no need to do it here. This will ensure that the children end up
2904 // in the session file in the correct order. If we close the master
2905 // buffer, we can close or release the child buffers here too.
2906 bool success = true;
2908 ListOfBuffers clist = buf.getChildren();
2909 ListOfBuffers::const_iterator it = clist.begin();
2910 ListOfBuffers::const_iterator const bend = clist.end();
2911 for (; it != bend; ++it) {
2912 Buffer * child_buf = *it;
2913 if (theBufferList().isOthersChild(&buf, child_buf)) {
2914 child_buf->setParent(0);
2918 // FIXME: should we look in other tabworkareas?
2919 // ANSWER: I don't think so. I've tested, and if the child is
2920 // open in some other window, it closes without a problem.
2921 GuiWorkArea * child_wa = workArea(*child_buf);
2923 success = closeWorkArea(child_wa, true);
2927 // In this case the child buffer is open but hidden.
2928 // It therefore should not (MUST NOT) be dirty!
2929 LATTEST(child_buf->isClean());
2930 theBufferList().release(child_buf);
2935 // goto bookmark to update bookmark pit.
2936 // FIXME: we should update only the bookmarks related to this buffer!
2937 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2938 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2939 guiApp->gotoBookmark(i+1, false, false);
2941 if (saveBufferIfNeeded(buf, false)) {
2942 buf.removeAutosaveFile();
2943 theBufferList().release(&buf);
2947 // open all children again to avoid a crash because of dangling
2948 // pointers (bug 6603)
2954 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2956 while (twa == d.currentTabWorkArea()) {
2957 twa->setCurrentIndex(twa->count() - 1);
2959 GuiWorkArea * wa = twa->currentWorkArea();
2960 Buffer & b = wa->bufferView().buffer();
2962 // We only want to close the buffer if the same buffer is not visible
2963 // in another view, and if this is not a child and if we are closing
2964 // a view (not a tabgroup).
2965 bool const close_buffer =
2966 !inOtherView(b) && !b.parent() && closing_;
2968 if (!closeWorkArea(wa, close_buffer))
2975 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2977 if (buf.isClean() || buf.paragraphs().empty())
2980 // Switch to this Buffer.
2986 if (buf.isUnnamed()) {
2987 file = from_utf8(buf.fileName().onlyFileName());
2990 FileName filename = buf.fileName();
2992 file = filename.displayName(30);
2993 exists = filename.exists();
2996 // Bring this window to top before asking questions.
3001 if (hiding && buf.isUnnamed()) {
3002 docstring const text = bformat(_("The document %1$s has not been "
3003 "saved yet.\n\nDo you want to save "
3004 "the document?"), file);
3005 ret = Alert::prompt(_("Save new document?"),
3006 text, 0, 1, _("&Save"), _("&Cancel"));
3010 docstring const text = exists ?
3011 bformat(_("The document %1$s has unsaved changes."
3012 "\n\nDo you want to save the document or "
3013 "discard the changes?"), file) :
3014 bformat(_("The document %1$s has not been saved yet."
3015 "\n\nDo you want to save the document or "
3016 "discard it entirely?"), file);
3017 docstring const title = exists ?
3018 _("Save changed document?") : _("Save document?");
3019 ret = Alert::prompt(title, text, 0, 2,
3020 _("&Save"), _("&Discard"), _("&Cancel"));
3025 if (!saveBuffer(buf))
3029 // If we crash after this we could have no autosave file
3030 // but I guess this is really improbable (Jug).
3031 // Sometimes improbable things happen:
3032 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3033 // buf.removeAutosaveFile();
3035 // revert all changes
3046 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3048 Buffer & buf = wa->bufferView().buffer();
3050 for (int i = 0; i != d.splitter_->count(); ++i) {
3051 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3052 if (wa_ && wa_ != wa)
3055 return inOtherView(buf);
3059 bool GuiView::inOtherView(Buffer & buf)
3061 QList<int> const ids = guiApp->viewIds();
3063 for (int i = 0; i != ids.size(); ++i) {
3067 if (guiApp->view(ids[i]).workArea(buf))
3074 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3076 if (!documentBufferView())
3079 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3080 Buffer * const curbuf = &documentBufferView()->buffer();
3081 int nwa = twa->count();
3082 for (int i = 0; i < nwa; ++i) {
3083 if (&workArea(i)->bufferView().buffer() == curbuf) {
3085 if (np == NEXTBUFFER)
3086 next_index = (i == nwa - 1 ? 0 : i + 1);
3088 next_index = (i == 0 ? nwa - 1 : i - 1);
3090 twa->moveTab(i, next_index);
3092 setBuffer(&workArea(next_index)->bufferView().buffer());
3100 /// make sure the document is saved
3101 static bool ensureBufferClean(Buffer * buffer)
3103 LASSERT(buffer, return false);
3104 if (buffer->isClean() && !buffer->isUnnamed())
3107 docstring const file = buffer->fileName().displayName(30);
3110 if (!buffer->isUnnamed()) {
3111 text = bformat(_("The document %1$s has unsaved "
3112 "changes.\n\nDo you want to save "
3113 "the document?"), file);
3114 title = _("Save changed document?");
3117 text = bformat(_("The document %1$s has not been "
3118 "saved yet.\n\nDo you want to save "
3119 "the document?"), file);
3120 title = _("Save new document?");
3122 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3125 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3127 return buffer->isClean() && !buffer->isUnnamed();
3131 bool GuiView::reloadBuffer(Buffer & buf)
3133 Buffer::ReadStatus status = buf.reload();
3134 return status == Buffer::ReadSuccess;
3138 void GuiView::checkExternallyModifiedBuffers()
3140 BufferList::iterator bit = theBufferList().begin();
3141 BufferList::iterator const bend = theBufferList().end();
3142 for (; bit != bend; ++bit) {
3143 Buffer * buf = *bit;
3144 if (buf->fileName().exists() && buf->isChecksumModified()) {
3145 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3146 " Reload now? Any local changes will be lost."),
3147 from_utf8(buf->absFileName()));
3148 int const ret = Alert::prompt(_("Reload externally changed document?"),
3149 text, 0, 1, _("&Reload"), _("&Cancel"));
3157 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3159 Buffer * buffer = documentBufferView()
3160 ? &(documentBufferView()->buffer()) : 0;
3162 switch (cmd.action()) {
3163 case LFUN_VC_REGISTER:
3164 if (!buffer || !ensureBufferClean(buffer))
3166 if (!buffer->lyxvc().inUse()) {
3167 if (buffer->lyxvc().registrer()) {
3168 reloadBuffer(*buffer);
3169 dr.clearMessageUpdate();
3174 case LFUN_VC_RENAME:
3175 case LFUN_VC_COPY: {
3176 if (!buffer || !ensureBufferClean(buffer))
3178 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3179 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3180 // Some changes are not yet committed.
3181 // We test here and not in getStatus(), since
3182 // this test is expensive.
3184 LyXVC::CommandResult ret =
3185 buffer->lyxvc().checkIn(log);
3187 if (ret == LyXVC::ErrorCommand ||
3188 ret == LyXVC::VCSuccess)
3189 reloadBuffer(*buffer);
3190 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3191 frontend::Alert::error(
3192 _("Revision control error."),
3193 _("Document could not be checked in."));
3197 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3198 LV_VC_RENAME : LV_VC_COPY;
3199 renameBuffer(*buffer, cmd.argument(), kind);
3204 case LFUN_VC_CHECK_IN:
3205 if (!buffer || !ensureBufferClean(buffer))
3207 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3209 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3211 // Only skip reloading if the checkin was cancelled or
3212 // an error occurred before the real checkin VCS command
3213 // was executed, since the VCS might have changed the
3214 // file even if it could not checkin successfully.
3215 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3216 reloadBuffer(*buffer);
3220 case LFUN_VC_CHECK_OUT:
3221 if (!buffer || !ensureBufferClean(buffer))
3223 if (buffer->lyxvc().inUse()) {
3224 dr.setMessage(buffer->lyxvc().checkOut());
3225 reloadBuffer(*buffer);
3229 case LFUN_VC_LOCKING_TOGGLE:
3230 LASSERT(buffer, return);
3231 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3233 if (buffer->lyxvc().inUse()) {
3234 string res = buffer->lyxvc().lockingToggle();
3236 frontend::Alert::error(_("Revision control error."),
3237 _("Error when setting the locking property."));
3240 reloadBuffer(*buffer);
3245 case LFUN_VC_REVERT:
3246 LASSERT(buffer, return);
3247 if (buffer->lyxvc().revert()) {
3248 reloadBuffer(*buffer);
3249 dr.clearMessageUpdate();
3253 case LFUN_VC_UNDO_LAST:
3254 LASSERT(buffer, return);
3255 buffer->lyxvc().undoLast();
3256 reloadBuffer(*buffer);
3257 dr.clearMessageUpdate();
3260 case LFUN_VC_REPO_UPDATE:
3261 LASSERT(buffer, return);
3262 if (ensureBufferClean(buffer)) {
3263 dr.setMessage(buffer->lyxvc().repoUpdate());
3264 checkExternallyModifiedBuffers();
3268 case LFUN_VC_COMMAND: {
3269 string flag = cmd.getArg(0);
3270 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3273 if (contains(flag, 'M')) {
3274 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3277 string path = cmd.getArg(1);
3278 if (contains(path, "$$p") && buffer)
3279 path = subst(path, "$$p", buffer->filePath());
3280 LYXERR(Debug::LYXVC, "Directory: " << path);
3282 if (!pp.isReadableDirectory()) {
3283 lyxerr << _("Directory is not accessible.") << endl;
3286 support::PathChanger p(pp);
3288 string command = cmd.getArg(2);
3289 if (command.empty())
3292 command = subst(command, "$$i", buffer->absFileName());
3293 command = subst(command, "$$p", buffer->filePath());
3295 command = subst(command, "$$m", to_utf8(message));
3296 LYXERR(Debug::LYXVC, "Command: " << command);
3298 one.startscript(Systemcall::Wait, command);
3302 if (contains(flag, 'I'))
3303 buffer->markDirty();
3304 if (contains(flag, 'R'))
3305 reloadBuffer(*buffer);
3310 case LFUN_VC_COMPARE: {
3311 if (cmd.argument().empty()) {
3312 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3316 string rev1 = cmd.getArg(0);
3321 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3324 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3325 f2 = buffer->absFileName();
3327 string rev2 = cmd.getArg(1);
3331 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3335 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3336 f1 << "\n" << f2 << "\n" );
3337 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3338 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3348 void GuiView::openChildDocument(string const & fname)
3350 LASSERT(documentBufferView(), return);
3351 Buffer & buffer = documentBufferView()->buffer();
3352 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3353 documentBufferView()->saveBookmark(false);
3355 if (theBufferList().exists(filename)) {
3356 child = theBufferList().getBuffer(filename);
3359 message(bformat(_("Opening child document %1$s..."),
3360 makeDisplayPath(filename.absFileName())));
3361 child = loadDocument(filename, false);
3363 // Set the parent name of the child document.
3364 // This makes insertion of citations and references in the child work,
3365 // when the target is in the parent or another child document.
3367 child->setParent(&buffer);
3371 bool GuiView::goToFileRow(string const & argument)
3375 size_t i = argument.find_last_of(' ');
3376 if (i != string::npos) {
3377 file_name = os::internal_path(trim(argument.substr(0, i)));
3378 istringstream is(argument.substr(i + 1));
3383 if (i == string::npos) {
3384 LYXERR0("Wrong argument: " << argument);
3388 string const abstmp = package().temp_dir().absFileName();
3389 string const realtmp = package().temp_dir().realPath();
3390 // We have to use os::path_prefix_is() here, instead of
3391 // simply prefixIs(), because the file name comes from
3392 // an external application and may need case adjustment.
3393 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3394 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3395 // Needed by inverse dvi search. If it is a file
3396 // in tmpdir, call the apropriated function.
3397 // If tmpdir is a symlink, we may have the real
3398 // path passed back, so we correct for that.
3399 if (!prefixIs(file_name, abstmp))
3400 file_name = subst(file_name, realtmp, abstmp);
3401 buf = theBufferList().getBufferFromTmp(file_name);
3403 // Must replace extension of the file to be .lyx
3404 // and get full path
3405 FileName const s = fileSearch(string(),
3406 support::changeExtension(file_name, ".lyx"), "lyx");
3407 // Either change buffer or load the file
3408 if (theBufferList().exists(s))
3409 buf = theBufferList().getBuffer(s);
3410 else if (s.exists()) {
3411 buf = loadDocument(s);
3416 _("File does not exist: %1$s"),
3417 makeDisplayPath(file_name)));
3423 _("No buffer for file: %1$s."),
3424 makeDisplayPath(file_name))
3429 bool success = documentBufferView()->setCursorFromRow(row);
3431 LYXERR(Debug::LATEX,
3432 "setCursorFromRow: invalid position for row " << row);
3433 frontend::Alert::error(_("Inverse Search Failed"),
3434 _("Invalid position requested by inverse search.\n"
3435 "You may need to update the viewed document."));
3441 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3443 QMenu * menu = new QMenu;
3444 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3445 menu->exec(QCursor::pos());
3450 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3452 Buffer::ExportStatus const status = func(format);
3454 // the cloning operation will have produced a clone of the entire set of
3455 // documents, starting from the master. so we must delete those.
3456 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3458 busyBuffers.remove(orig);
3463 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3465 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3466 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3470 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3472 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3473 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3477 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3479 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3480 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3484 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3485 string const & argument,
3486 Buffer const * used_buffer,
3487 docstring const & msg,
3488 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3489 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3490 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3495 string format = argument;
3497 format = used_buffer->params().getDefaultOutputFormat();
3498 processing_format = format;
3500 progress_->clearMessages();
3503 #if EXPORT_in_THREAD
3504 GuiViewPrivate::busyBuffers.insert(used_buffer);
3505 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3506 if (!cloned_buffer) {
3507 Alert::error(_("Export Error"),
3508 _("Error cloning the Buffer."));
3511 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3516 setPreviewFuture(f);
3517 last_export_format = used_buffer->params().bufferFormat();
3520 // We are asynchronous, so we don't know here anything about the success
3523 Buffer::ExportStatus status;
3525 status = (used_buffer->*syncFunc)(format, true);
3526 } else if (previewFunc) {
3527 status = (used_buffer->*previewFunc)(format);
3530 handleExportStatus(gv_, status, format);
3532 return (status == Buffer::ExportSuccess
3533 || status == Buffer::PreviewSuccess);
3537 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3539 BufferView * bv = currentBufferView();
3540 LASSERT(bv, return);
3542 // Let the current BufferView dispatch its own actions.
3543 bv->dispatch(cmd, dr);
3544 if (dr.dispatched())
3547 // Try with the document BufferView dispatch if any.
3548 BufferView * doc_bv = documentBufferView();
3549 if (doc_bv && doc_bv != bv) {
3550 doc_bv->dispatch(cmd, dr);
3551 if (dr.dispatched())
3555 // Then let the current Cursor dispatch its own actions.
3556 bv->cursor().dispatch(cmd);
3558 // update completion. We do it here and not in
3559 // processKeySym to avoid another redraw just for a
3560 // changed inline completion
3561 if (cmd.origin() == FuncRequest::KEYBOARD) {
3562 if (cmd.action() == LFUN_SELF_INSERT
3563 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3564 updateCompletion(bv->cursor(), true, true);
3565 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3566 updateCompletion(bv->cursor(), false, true);
3568 updateCompletion(bv->cursor(), false, false);
3571 dr = bv->cursor().result();
3575 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3577 BufferView * bv = currentBufferView();
3578 // By default we won't need any update.
3579 dr.screenUpdate(Update::None);
3580 // assume cmd will be dispatched
3581 dr.dispatched(true);
3583 Buffer * doc_buffer = documentBufferView()
3584 ? &(documentBufferView()->buffer()) : 0;
3586 if (cmd.origin() == FuncRequest::TOC) {
3587 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3588 // FIXME: do we need to pass a DispatchResult object here?
3589 toc->doDispatch(bv->cursor(), cmd);
3593 string const argument = to_utf8(cmd.argument());
3595 switch(cmd.action()) {
3596 case LFUN_BUFFER_CHILD_OPEN:
3597 openChildDocument(to_utf8(cmd.argument()));
3600 case LFUN_BUFFER_IMPORT:
3601 importDocument(to_utf8(cmd.argument()));
3604 case LFUN_BUFFER_EXPORT: {
3607 // GCC only sees strfwd.h when building merged
3608 if (::lyx::operator==(cmd.argument(), "custom")) {
3609 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3613 string const dest = cmd.getArg(1);
3614 FileName target_dir;
3615 if (!dest.empty() && FileName::isAbsolute(dest))
3616 target_dir = FileName(support::onlyPath(dest));
3618 target_dir = doc_buffer->fileName().onlyPath();
3620 string const format = (argument.empty() || argument == "default") ?
3621 doc_buffer->params().getDefaultOutputFormat() : argument;
3623 if ((dest.empty() && doc_buffer->isUnnamed())
3624 || !target_dir.isDirWritable()) {
3625 exportBufferAs(*doc_buffer, from_utf8(format));
3628 /* TODO/Review: Is it a problem to also export the children?
3629 See the update_unincluded flag */
3630 d.asyncBufferProcessing(format,
3633 &GuiViewPrivate::exportAndDestroy,
3636 // TODO Inform user about success
3640 case LFUN_BUFFER_EXPORT_AS: {
3641 LASSERT(doc_buffer, break);
3642 docstring f = cmd.argument();
3644 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3645 exportBufferAs(*doc_buffer, f);
3649 case LFUN_BUFFER_UPDATE: {
3650 d.asyncBufferProcessing(argument,
3653 &GuiViewPrivate::compileAndDestroy,
3658 case LFUN_BUFFER_VIEW: {
3659 d.asyncBufferProcessing(argument,
3661 _("Previewing ..."),
3662 &GuiViewPrivate::previewAndDestroy,
3667 case LFUN_MASTER_BUFFER_UPDATE: {
3668 d.asyncBufferProcessing(argument,
3669 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3671 &GuiViewPrivate::compileAndDestroy,
3676 case LFUN_MASTER_BUFFER_VIEW: {
3677 d.asyncBufferProcessing(argument,
3678 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3680 &GuiViewPrivate::previewAndDestroy,
3681 0, &Buffer::preview);
3684 case LFUN_BUFFER_SWITCH: {
3685 string const file_name = to_utf8(cmd.argument());
3686 if (!FileName::isAbsolute(file_name)) {
3688 dr.setMessage(_("Absolute filename expected."));
3692 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3695 dr.setMessage(_("Document not loaded"));
3699 // Do we open or switch to the buffer in this view ?
3700 if (workArea(*buffer)
3701 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3706 // Look for the buffer in other views
3707 QList<int> const ids = guiApp->viewIds();
3709 for (; i != ids.size(); ++i) {
3710 GuiView & gv = guiApp->view(ids[i]);
3711 if (gv.workArea(*buffer)) {
3713 gv.activateWindow();
3715 gv.setBuffer(buffer);
3720 // If necessary, open a new window as a last resort
3721 if (i == ids.size()) {
3722 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3728 case LFUN_BUFFER_NEXT:
3729 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3732 case LFUN_BUFFER_MOVE_NEXT:
3733 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3736 case LFUN_BUFFER_PREVIOUS:
3737 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3740 case LFUN_BUFFER_MOVE_PREVIOUS:
3741 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3744 case LFUN_COMMAND_EXECUTE: {
3745 command_execute_ = true;
3746 minibuffer_focus_ = true;
3749 case LFUN_DROP_LAYOUTS_CHOICE:
3750 d.layout_->showPopup();
3753 case LFUN_MENU_OPEN:
3754 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3755 menu->exec(QCursor::pos());
3758 case LFUN_FILE_INSERT:
3759 insertLyXFile(cmd.argument());
3762 case LFUN_FILE_INSERT_PLAINTEXT:
3763 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3764 string const fname = to_utf8(cmd.argument());
3765 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3766 dr.setMessage(_("Absolute filename expected."));
3770 FileName filename(fname);
3771 if (fname.empty()) {
3772 FileDialog dlg(qt_("Select file to insert"));
3774 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3775 QStringList(qt_("All Files (*)")));
3777 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3778 dr.setMessage(_("Canceled."));
3782 filename.set(fromqstr(result.second));
3786 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3787 bv->dispatch(new_cmd, dr);
3792 case LFUN_BUFFER_RELOAD: {
3793 LASSERT(doc_buffer, break);
3796 if (!doc_buffer->isClean()) {
3797 docstring const file =
3798 makeDisplayPath(doc_buffer->absFileName(), 20);
3799 if (doc_buffer->notifiesExternalModification()) {
3800 docstring text = _("The current version will be lost. "
3801 "Are you sure you want to load the version on disk "
3802 "of the document %1$s?");
3803 ret = Alert::prompt(_("Reload saved document?"),
3804 bformat(text, file), 1, 1,
3805 _("&Reload"), _("&Cancel"));
3807 docstring text = _("Any changes will be lost. "
3808 "Are you sure you want to revert to the saved version "
3809 "of the document %1$s?");
3810 ret = Alert::prompt(_("Revert to saved document?"),
3811 bformat(text, file), 1, 1,
3812 _("&Revert"), _("&Cancel"));
3817 doc_buffer->markClean();
3818 reloadBuffer(*doc_buffer);
3819 dr.forceBufferUpdate();
3824 case LFUN_BUFFER_WRITE:
3825 LASSERT(doc_buffer, break);
3826 saveBuffer(*doc_buffer);
3829 case LFUN_BUFFER_WRITE_AS:
3830 LASSERT(doc_buffer, break);
3831 renameBuffer(*doc_buffer, cmd.argument());
3834 case LFUN_BUFFER_WRITE_ALL: {
3835 Buffer * first = theBufferList().first();
3838 message(_("Saving all documents..."));
3839 // We cannot use a for loop as the buffer list cycles.
3842 if (!b->isClean()) {
3844 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3846 b = theBufferList().next(b);
3847 } while (b != first);
3848 dr.setMessage(_("All documents saved."));
3852 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3853 LASSERT(doc_buffer, break);
3854 doc_buffer->clearExternalModification();
3857 case LFUN_BUFFER_CLOSE:
3861 case LFUN_BUFFER_CLOSE_ALL:
3865 case LFUN_TOOLBAR_TOGGLE: {
3866 string const name = cmd.getArg(0);
3867 if (GuiToolbar * t = toolbar(name))
3872 case LFUN_TOOLBAR_MOVABLE: {
3873 string const name = cmd.getArg(0);
3875 // toggle (all) toolbars movablility
3876 toolbarsMovable_ = !toolbarsMovable_;
3877 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3878 GuiToolbar * tb = toolbar(ti.name);
3879 if (tb && tb->isMovable() != toolbarsMovable_)
3880 // toggle toolbar movablity if it does not fit lock
3881 // (all) toolbars positions state silent = true, since
3882 // status bar notifications are slow
3885 if (toolbarsMovable_)
3886 dr.setMessage(_("Toolbars unlocked."));
3888 dr.setMessage(_("Toolbars locked."));
3889 } else if (GuiToolbar * t = toolbar(name)) {
3890 // toggle current toolbar movablity
3892 // update lock (all) toolbars positions
3893 updateLockToolbars();
3898 case LFUN_ICON_SIZE: {
3899 QSize size = d.iconSize(cmd.argument());
3901 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3902 size.width(), size.height()));
3906 case LFUN_DIALOG_UPDATE: {
3907 string const name = to_utf8(cmd.argument());
3908 if (name == "prefs" || name == "document")
3909 updateDialog(name, string());
3910 else if (name == "paragraph")
3911 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3912 else if (currentBufferView()) {
3913 Inset * inset = currentBufferView()->editedInset(name);
3914 // Can only update a dialog connected to an existing inset
3916 // FIXME: get rid of this indirection; GuiView ask the inset
3917 // if he is kind enough to update itself...
3918 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3919 //FIXME: pass DispatchResult here?
3920 inset->dispatch(currentBufferView()->cursor(), fr);
3926 case LFUN_DIALOG_TOGGLE: {
3927 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3928 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3929 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3933 case LFUN_DIALOG_DISCONNECT_INSET:
3934 disconnectDialog(to_utf8(cmd.argument()));
3937 case LFUN_DIALOG_HIDE: {
3938 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3942 case LFUN_DIALOG_SHOW: {
3943 string const name = cmd.getArg(0);
3944 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3946 if (name == "character") {
3947 data = freefont2string();
3949 showDialog("character", data);
3950 } else if (name == "latexlog") {
3951 // getStatus checks that
3952 LATTEST(doc_buffer);
3953 Buffer::LogType type;
3954 string const logfile = doc_buffer->logName(&type);
3956 case Buffer::latexlog:
3959 case Buffer::buildlog:
3963 data += Lexer::quoteString(logfile);
3964 showDialog("log", data);
3965 } else if (name == "vclog") {
3966 // getStatus checks that
3967 LATTEST(doc_buffer);
3968 string const data = "vc " +
3969 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3970 showDialog("log", data);
3971 } else if (name == "symbols") {
3972 data = bv->cursor().getEncoding()->name();
3974 showDialog("symbols", data);
3976 } else if (name == "prefs" && isFullScreen()) {
3977 lfunUiToggle("fullscreen");
3978 showDialog("prefs", data);
3980 showDialog(name, data);
3985 dr.setMessage(cmd.argument());
3988 case LFUN_UI_TOGGLE: {
3989 string arg = cmd.getArg(0);
3990 if (!lfunUiToggle(arg)) {
3991 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3992 dr.setMessage(bformat(msg, from_utf8(arg)));
3994 // Make sure the keyboard focus stays in the work area.
3999 case LFUN_VIEW_SPLIT: {
4000 LASSERT(doc_buffer, break);
4001 string const orientation = cmd.getArg(0);
4002 d.splitter_->setOrientation(orientation == "vertical"
4003 ? Qt::Vertical : Qt::Horizontal);
4004 TabWorkArea * twa = addTabWorkArea();
4005 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4006 setCurrentWorkArea(wa);
4009 case LFUN_TAB_GROUP_CLOSE:
4010 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4011 closeTabWorkArea(twa);
4012 d.current_work_area_ = 0;
4013 twa = d.currentTabWorkArea();
4014 // Switch to the next GuiWorkArea in the found TabWorkArea.
4016 // Make sure the work area is up to date.
4017 setCurrentWorkArea(twa->currentWorkArea());
4019 setCurrentWorkArea(0);
4024 case LFUN_VIEW_CLOSE:
4025 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4026 closeWorkArea(twa->currentWorkArea());
4027 d.current_work_area_ = 0;
4028 twa = d.currentTabWorkArea();
4029 // Switch to the next GuiWorkArea in the found TabWorkArea.
4031 // Make sure the work area is up to date.
4032 setCurrentWorkArea(twa->currentWorkArea());
4034 setCurrentWorkArea(0);
4039 case LFUN_COMPLETION_INLINE:
4040 if (d.current_work_area_)
4041 d.current_work_area_->completer().showInline();
4044 case LFUN_COMPLETION_POPUP:
4045 if (d.current_work_area_)
4046 d.current_work_area_->completer().showPopup();
4051 if (d.current_work_area_)
4052 d.current_work_area_->completer().tab();
4055 case LFUN_COMPLETION_CANCEL:
4056 if (d.current_work_area_) {
4057 if (d.current_work_area_->completer().popupVisible())
4058 d.current_work_area_->completer().hidePopup();
4060 d.current_work_area_->completer().hideInline();
4064 case LFUN_COMPLETION_ACCEPT:
4065 if (d.current_work_area_)
4066 d.current_work_area_->completer().activate();
4069 case LFUN_BUFFER_ZOOM_IN:
4070 case LFUN_BUFFER_ZOOM_OUT:
4071 case LFUN_BUFFER_ZOOM: {
4072 // use a signed temp to avoid overflow
4073 int zoom = lyxrc.currentZoom;
4074 if (cmd.argument().empty()) {
4075 if (cmd.action() == LFUN_BUFFER_ZOOM)
4077 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4082 if (cmd.action() == LFUN_BUFFER_ZOOM)
4083 zoom = convert<int>(cmd.argument());
4084 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4085 zoom += convert<int>(cmd.argument());
4087 zoom -= convert<int>(cmd.argument());
4090 if (zoom < static_cast<int>(zoom_min_))
4093 lyxrc.currentZoom = zoom;
4095 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4097 // The global QPixmapCache is used in GuiPainter to cache text
4098 // painting so we must reset it.
4099 QPixmapCache::clear();
4100 guiApp->fontLoader().update();
4101 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4105 case LFUN_VC_REGISTER:
4106 case LFUN_VC_RENAME:
4108 case LFUN_VC_CHECK_IN:
4109 case LFUN_VC_CHECK_OUT:
4110 case LFUN_VC_REPO_UPDATE:
4111 case LFUN_VC_LOCKING_TOGGLE:
4112 case LFUN_VC_REVERT:
4113 case LFUN_VC_UNDO_LAST:
4114 case LFUN_VC_COMMAND:
4115 case LFUN_VC_COMPARE:
4116 dispatchVC(cmd, dr);
4119 case LFUN_SERVER_GOTO_FILE_ROW:
4120 if(goToFileRow(to_utf8(cmd.argument())))
4121 dr.screenUpdate(Update::Force | Update::FitCursor);
4124 case LFUN_LYX_ACTIVATE:
4128 case LFUN_FORWARD_SEARCH: {
4129 // it seems safe to assume we have a document buffer, since
4130 // getStatus wants one.
4131 LATTEST(doc_buffer);
4132 Buffer const * doc_master = doc_buffer->masterBuffer();
4133 FileName const path(doc_master->temppath());
4134 string const texname = doc_master->isChild(doc_buffer)
4135 ? DocFileName(changeExtension(
4136 doc_buffer->absFileName(),
4137 "tex")).mangledFileName()
4138 : doc_buffer->latexName();
4139 string const fulltexname =
4140 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4141 string const mastername =
4142 removeExtension(doc_master->latexName());
4143 FileName const dviname(addName(path.absFileName(),
4144 addExtension(mastername, "dvi")));
4145 FileName const pdfname(addName(path.absFileName(),
4146 addExtension(mastername, "pdf")));
4147 bool const have_dvi = dviname.exists();
4148 bool const have_pdf = pdfname.exists();
4149 if (!have_dvi && !have_pdf) {
4150 dr.setMessage(_("Please, preview the document first."));
4153 string outname = dviname.onlyFileName();
4154 string command = lyxrc.forward_search_dvi;
4155 if (!have_dvi || (have_pdf &&
4156 pdfname.lastModified() > dviname.lastModified())) {
4157 outname = pdfname.onlyFileName();
4158 command = lyxrc.forward_search_pdf;
4161 DocIterator cur = bv->cursor();
4162 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4163 LYXERR(Debug::ACTION, "Forward search: row:" << row
4165 if (row == -1 || command.empty()) {
4166 dr.setMessage(_("Couldn't proceed."));
4169 string texrow = convert<string>(row);
4171 command = subst(command, "$$n", texrow);
4172 command = subst(command, "$$f", fulltexname);
4173 command = subst(command, "$$t", texname);
4174 command = subst(command, "$$o", outname);
4176 PathChanger p(path);
4178 one.startscript(Systemcall::DontWait, command);
4182 case LFUN_SPELLING_CONTINUOUSLY:
4183 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4184 dr.screenUpdate(Update::Force);
4188 // The LFUN must be for one of BufferView, Buffer or Cursor;
4190 dispatchToBufferView(cmd, dr);
4194 // Part of automatic menu appearance feature.
4195 if (isFullScreen()) {
4196 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4200 // Need to update bv because many LFUNs here might have destroyed it
4201 bv = currentBufferView();
4203 // Clear non-empty selections
4204 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4206 Cursor & cur = bv->cursor();
4207 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4208 cur.clearSelection();
4214 bool GuiView::lfunUiToggle(string const & ui_component)
4216 if (ui_component == "scrollbar") {
4217 // hide() is of no help
4218 if (d.current_work_area_->verticalScrollBarPolicy() ==
4219 Qt::ScrollBarAlwaysOff)
4221 d.current_work_area_->setVerticalScrollBarPolicy(
4222 Qt::ScrollBarAsNeeded);
4224 d.current_work_area_->setVerticalScrollBarPolicy(
4225 Qt::ScrollBarAlwaysOff);
4226 } else if (ui_component == "statusbar") {
4227 statusBar()->setVisible(!statusBar()->isVisible());
4228 } else if (ui_component == "menubar") {
4229 menuBar()->setVisible(!menuBar()->isVisible());
4231 if (ui_component == "frame") {
4233 getContentsMargins(&l, &t, &r, &b);
4234 //are the frames in default state?
4235 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4237 setContentsMargins(-2, -2, -2, -2);
4239 setContentsMargins(0, 0, 0, 0);
4242 if (ui_component == "fullscreen") {
4250 void GuiView::toggleFullScreen()
4252 if (isFullScreen()) {
4253 for (int i = 0; i != d.splitter_->count(); ++i)
4254 d.tabWorkArea(i)->setFullScreen(false);
4255 setContentsMargins(0, 0, 0, 0);
4256 setWindowState(windowState() ^ Qt::WindowFullScreen);
4259 statusBar()->show();
4262 hideDialogs("prefs", 0);
4263 for (int i = 0; i != d.splitter_->count(); ++i)
4264 d.tabWorkArea(i)->setFullScreen(true);
4265 setContentsMargins(-2, -2, -2, -2);
4267 setWindowState(windowState() ^ Qt::WindowFullScreen);
4268 if (lyxrc.full_screen_statusbar)
4269 statusBar()->hide();
4270 if (lyxrc.full_screen_menubar)
4272 if (lyxrc.full_screen_toolbars) {
4273 ToolbarMap::iterator end = d.toolbars_.end();
4274 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4279 // give dialogs like the TOC a chance to adapt
4284 Buffer const * GuiView::updateInset(Inset const * inset)
4289 Buffer const * inset_buffer = &(inset->buffer());
4291 for (int i = 0; i != d.splitter_->count(); ++i) {
4292 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4295 Buffer const * buffer = &(wa->bufferView().buffer());
4296 if (inset_buffer == buffer)
4297 wa->scheduleRedraw();
4299 return inset_buffer;
4303 void GuiView::restartCursor()
4305 /* When we move around, or type, it's nice to be able to see
4306 * the cursor immediately after the keypress.
4308 if (d.current_work_area_)
4309 d.current_work_area_->startBlinkingCursor();
4311 // Take this occasion to update the other GUI elements.
4317 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4319 if (d.current_work_area_)
4320 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4325 // This list should be kept in sync with the list of insets in
4326 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4327 // dialog should have the same name as the inset.
4328 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4329 // docs in LyXAction.cpp.
4331 char const * const dialognames[] = {
4333 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4334 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4335 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4336 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4337 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4338 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4339 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4340 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4342 char const * const * const end_dialognames =
4343 dialognames + (sizeof(dialognames) / sizeof(char *));
4347 cmpCStr(char const * name) : name_(name) {}
4348 bool operator()(char const * other) {
4349 return strcmp(other, name_) == 0;
4356 bool isValidName(string const & name)
4358 return find_if(dialognames, end_dialognames,
4359 cmpCStr(name.c_str())) != end_dialognames;
4365 void GuiView::resetDialogs()
4367 // Make sure that no LFUN uses any GuiView.
4368 guiApp->setCurrentView(0);
4372 constructToolbars();
4373 guiApp->menus().fillMenuBar(menuBar(), this, false);
4374 d.layout_->updateContents(true);
4375 // Now update controls with current buffer.
4376 guiApp->setCurrentView(this);
4382 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4384 if (!isValidName(name))
4387 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4389 if (it != d.dialogs_.end()) {
4391 it->second->hideView();
4392 return it->second.get();
4395 Dialog * dialog = build(name);
4396 d.dialogs_[name].reset(dialog);
4397 if (lyxrc.allow_geometry_session)
4398 dialog->restoreSession();
4405 void GuiView::showDialog(string const & name, string const & data,
4408 triggerShowDialog(toqstr(name), toqstr(data), inset);
4412 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4418 const string name = fromqstr(qname);
4419 const string data = fromqstr(qdata);
4423 Dialog * dialog = findOrBuild(name, false);
4425 bool const visible = dialog->isVisibleView();
4426 dialog->showData(data);
4427 if (inset && currentBufferView())
4428 currentBufferView()->editInset(name, inset);
4429 // We only set the focus to the new dialog if it was not yet
4430 // visible in order not to change the existing previous behaviour
4432 // activateWindow is needed for floating dockviews
4433 dialog->asQWidget()->raise();
4434 dialog->asQWidget()->activateWindow();
4435 dialog->asQWidget()->setFocus();
4439 catch (ExceptionMessage const & ex) {
4447 bool GuiView::isDialogVisible(string const & name) const
4449 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4450 if (it == d.dialogs_.end())
4452 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4456 void GuiView::hideDialog(string const & name, Inset * inset)
4458 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4459 if (it == d.dialogs_.end())
4463 if (!currentBufferView())
4465 if (inset != currentBufferView()->editedInset(name))
4469 Dialog * const dialog = it->second.get();
4470 if (dialog->isVisibleView())
4472 if (currentBufferView())
4473 currentBufferView()->editInset(name, 0);
4477 void GuiView::disconnectDialog(string const & name)
4479 if (!isValidName(name))
4481 if (currentBufferView())
4482 currentBufferView()->editInset(name, 0);
4486 void GuiView::hideAll() const
4488 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4489 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4491 for(; it != end; ++it)
4492 it->second->hideView();
4496 void GuiView::updateDialogs()
4498 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4499 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4501 for(; it != end; ++it) {
4502 Dialog * dialog = it->second.get();
4504 if (dialog->needBufferOpen() && !documentBufferView())
4505 hideDialog(fromqstr(dialog->name()), 0);
4506 else if (dialog->isVisibleView())
4507 dialog->checkStatus();
4514 Dialog * createDialog(GuiView & lv, string const & name);
4516 // will be replaced by a proper factory...
4517 Dialog * createGuiAbout(GuiView & lv);
4518 Dialog * createGuiBibtex(GuiView & lv);
4519 Dialog * createGuiChanges(GuiView & lv);
4520 Dialog * createGuiCharacter(GuiView & lv);
4521 Dialog * createGuiCitation(GuiView & lv);
4522 Dialog * createGuiCompare(GuiView & lv);
4523 Dialog * createGuiCompareHistory(GuiView & lv);
4524 Dialog * createGuiDelimiter(GuiView & lv);
4525 Dialog * createGuiDocument(GuiView & lv);
4526 Dialog * createGuiErrorList(GuiView & lv);
4527 Dialog * createGuiExternal(GuiView & lv);
4528 Dialog * createGuiGraphics(GuiView & lv);
4529 Dialog * createGuiInclude(GuiView & lv);
4530 Dialog * createGuiIndex(GuiView & lv);
4531 Dialog * createGuiListings(GuiView & lv);
4532 Dialog * createGuiLog(GuiView & lv);
4533 Dialog * createGuiMathMatrix(GuiView & lv);
4534 Dialog * createGuiNote(GuiView & lv);
4535 Dialog * createGuiParagraph(GuiView & lv);
4536 Dialog * createGuiPhantom(GuiView & lv);
4537 Dialog * createGuiPreferences(GuiView & lv);
4538 Dialog * createGuiPrint(GuiView & lv);
4539 Dialog * createGuiPrintindex(GuiView & lv);
4540 Dialog * createGuiRef(GuiView & lv);
4541 Dialog * createGuiSearch(GuiView & lv);
4542 Dialog * createGuiSearchAdv(GuiView & lv);
4543 Dialog * createGuiSendTo(GuiView & lv);
4544 Dialog * createGuiShowFile(GuiView & lv);
4545 Dialog * createGuiSpellchecker(GuiView & lv);
4546 Dialog * createGuiSymbols(GuiView & lv);
4547 Dialog * createGuiTabularCreate(GuiView & lv);
4548 Dialog * createGuiTexInfo(GuiView & lv);
4549 Dialog * createGuiToc(GuiView & lv);
4550 Dialog * createGuiThesaurus(GuiView & lv);
4551 Dialog * createGuiViewSource(GuiView & lv);
4552 Dialog * createGuiWrap(GuiView & lv);
4553 Dialog * createGuiProgressView(GuiView & lv);
4557 Dialog * GuiView::build(string const & name)
4559 LASSERT(isValidName(name), return 0);
4561 Dialog * dialog = createDialog(*this, name);
4565 if (name == "aboutlyx")
4566 return createGuiAbout(*this);
4567 if (name == "bibtex")
4568 return createGuiBibtex(*this);
4569 if (name == "changes")
4570 return createGuiChanges(*this);
4571 if (name == "character")
4572 return createGuiCharacter(*this);
4573 if (name == "citation")
4574 return createGuiCitation(*this);
4575 if (name == "compare")
4576 return createGuiCompare(*this);
4577 if (name == "comparehistory")
4578 return createGuiCompareHistory(*this);
4579 if (name == "document")
4580 return createGuiDocument(*this);
4581 if (name == "errorlist")
4582 return createGuiErrorList(*this);
4583 if (name == "external")
4584 return createGuiExternal(*this);
4586 return createGuiShowFile(*this);
4587 if (name == "findreplace")
4588 return createGuiSearch(*this);
4589 if (name == "findreplaceadv")
4590 return createGuiSearchAdv(*this);
4591 if (name == "graphics")
4592 return createGuiGraphics(*this);
4593 if (name == "include")
4594 return createGuiInclude(*this);
4595 if (name == "index")
4596 return createGuiIndex(*this);
4597 if (name == "index_print")
4598 return createGuiPrintindex(*this);
4599 if (name == "listings")
4600 return createGuiListings(*this);
4602 return createGuiLog(*this);
4603 if (name == "mathdelimiter")
4604 return createGuiDelimiter(*this);
4605 if (name == "mathmatrix")
4606 return createGuiMathMatrix(*this);
4608 return createGuiNote(*this);
4609 if (name == "paragraph")
4610 return createGuiParagraph(*this);
4611 if (name == "phantom")
4612 return createGuiPhantom(*this);
4613 if (name == "prefs")
4614 return createGuiPreferences(*this);
4616 return createGuiRef(*this);
4617 if (name == "sendto")
4618 return createGuiSendTo(*this);
4619 if (name == "spellchecker")
4620 return createGuiSpellchecker(*this);
4621 if (name == "symbols")
4622 return createGuiSymbols(*this);
4623 if (name == "tabularcreate")
4624 return createGuiTabularCreate(*this);
4625 if (name == "texinfo")
4626 return createGuiTexInfo(*this);
4627 if (name == "thesaurus")
4628 return createGuiThesaurus(*this);
4630 return createGuiToc(*this);
4631 if (name == "view-source")
4632 return createGuiViewSource(*this);
4634 return createGuiWrap(*this);
4635 if (name == "progress")
4636 return createGuiProgressView(*this);
4642 } // namespace frontend
4645 #include "moc_GuiView.cpp"