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)
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());
1613 setCurrentWorkArea(wa);
1617 void GuiView::connectBuffer(Buffer & buf)
1619 buf.setGuiDelegate(this);
1623 void GuiView::disconnectBuffer()
1625 if (d.current_work_area_)
1626 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1630 void GuiView::connectBufferView(BufferView & bv)
1632 bv.setGuiDelegate(this);
1636 void GuiView::disconnectBufferView()
1638 if (d.current_work_area_)
1639 d.current_work_area_->bufferView().setGuiDelegate(0);
1643 void GuiView::errors(string const & error_type, bool from_master)
1645 BufferView const * const bv = currentBufferView();
1649 #if EXPORT_in_THREAD
1650 // We are called with from_master == false by default, so we
1651 // have to figure out whether that is the case or not.
1652 ErrorList & el = bv->buffer().errorList(error_type);
1654 el = bv->buffer().masterBuffer()->errorList(error_type);
1658 ErrorList const & el = from_master ?
1659 bv->buffer().masterBuffer()->errorList(error_type) :
1660 bv->buffer().errorList(error_type);
1666 string data = error_type;
1668 data = "from_master|" + error_type;
1669 showDialog("errorlist", data);
1673 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1675 d.toc_models_.updateItem(toqstr(type), dit);
1679 void GuiView::structureChanged()
1681 // This is called from the Buffer, which has no way to ensure that cursors
1682 // in BufferView remain valid.
1683 if (documentBufferView())
1684 documentBufferView()->cursor().sanitize();
1685 // FIXME: This is slightly expensive, though less than the tocBackend update
1686 // (#9880). This also resets the view in the Toc Widget (#6675).
1687 d.toc_models_.reset(documentBufferView());
1688 // Navigator needs more than a simple update in this case. It needs to be
1690 updateDialog("toc", "");
1694 void GuiView::updateDialog(string const & name, string const & data)
1696 if (!isDialogVisible(name))
1699 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1700 if (it == d.dialogs_.end())
1703 Dialog * const dialog = it->second.get();
1704 if (dialog->isVisibleView())
1705 dialog->initialiseParams(data);
1709 BufferView * GuiView::documentBufferView()
1711 return currentMainWorkArea()
1712 ? ¤tMainWorkArea()->bufferView()
1717 BufferView const * GuiView::documentBufferView() const
1719 return currentMainWorkArea()
1720 ? ¤tMainWorkArea()->bufferView()
1725 BufferView * GuiView::currentBufferView()
1727 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1731 BufferView const * GuiView::currentBufferView() const
1733 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1737 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1738 Buffer const * orig, Buffer * clone)
1740 bool const success = clone->autoSave();
1742 busyBuffers.remove(orig);
1744 ? _("Automatic save done.")
1745 : _("Automatic save failed!");
1749 void GuiView::autoSave()
1751 LYXERR(Debug::INFO, "Running autoSave()");
1753 Buffer * buffer = documentBufferView()
1754 ? &documentBufferView()->buffer() : 0;
1756 resetAutosaveTimers();
1760 GuiViewPrivate::busyBuffers.insert(buffer);
1761 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1762 buffer, buffer->cloneBufferOnly());
1763 d.autosave_watcher_.setFuture(f);
1764 resetAutosaveTimers();
1768 void GuiView::resetAutosaveTimers()
1771 d.autosave_timeout_.restart();
1775 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1778 Buffer * buf = currentBufferView()
1779 ? ¤tBufferView()->buffer() : 0;
1780 Buffer * doc_buffer = documentBufferView()
1781 ? &(documentBufferView()->buffer()) : 0;
1784 /* In LyX/Mac, when a dialog is open, the menus of the
1785 application can still be accessed without giving focus to
1786 the main window. In this case, we want to disable the menu
1787 entries that are buffer-related.
1788 This code must not be used on Linux and Windows, since it
1789 would disable buffer-related entries when hovering over the
1790 menu (see bug #9574).
1792 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1798 // Check whether we need a buffer
1799 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1800 // no, exit directly
1801 flag.message(from_utf8(N_("Command not allowed with"
1802 "out any document open")));
1803 flag.setEnabled(false);
1807 if (cmd.origin() == FuncRequest::TOC) {
1808 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1809 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1810 flag.setEnabled(false);
1814 switch(cmd.action()) {
1815 case LFUN_BUFFER_IMPORT:
1818 case LFUN_MASTER_BUFFER_UPDATE:
1819 case LFUN_MASTER_BUFFER_VIEW:
1821 && (doc_buffer->parent() != 0
1822 || doc_buffer->hasChildren())
1823 && !d.processing_thread_watcher_.isRunning();
1826 case LFUN_BUFFER_UPDATE:
1827 case LFUN_BUFFER_VIEW: {
1828 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1832 string format = to_utf8(cmd.argument());
1833 if (cmd.argument().empty())
1834 format = doc_buffer->params().getDefaultOutputFormat();
1835 enable = doc_buffer->params().isExportable(format, true);
1839 case LFUN_BUFFER_RELOAD:
1840 enable = doc_buffer && !doc_buffer->isUnnamed()
1841 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1844 case LFUN_BUFFER_CHILD_OPEN:
1845 enable = doc_buffer != 0;
1848 case LFUN_BUFFER_WRITE:
1849 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1852 //FIXME: This LFUN should be moved to GuiApplication.
1853 case LFUN_BUFFER_WRITE_ALL: {
1854 // We enable the command only if there are some modified buffers
1855 Buffer * first = theBufferList().first();
1860 // We cannot use a for loop as the buffer list is a cycle.
1862 if (!b->isClean()) {
1866 b = theBufferList().next(b);
1867 } while (b != first);
1871 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1872 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1875 case LFUN_BUFFER_WRITE_AS:
1876 case LFUN_BUFFER_EXPORT_AS:
1877 enable = doc_buffer != 0;
1880 case LFUN_BUFFER_CLOSE:
1881 case LFUN_VIEW_CLOSE:
1882 enable = doc_buffer != 0;
1885 case LFUN_BUFFER_CLOSE_ALL:
1886 enable = theBufferList().last() != theBufferList().first();
1889 case LFUN_VIEW_SPLIT:
1890 if (cmd.getArg(0) == "vertical")
1891 enable = doc_buffer && (d.splitter_->count() == 1 ||
1892 d.splitter_->orientation() == Qt::Vertical);
1894 enable = doc_buffer && (d.splitter_->count() == 1 ||
1895 d.splitter_->orientation() == Qt::Horizontal);
1898 case LFUN_TAB_GROUP_CLOSE:
1899 enable = d.tabWorkAreaCount() > 1;
1902 case LFUN_TOOLBAR_TOGGLE: {
1903 string const name = cmd.getArg(0);
1904 if (GuiToolbar * t = toolbar(name))
1905 flag.setOnOff(t->isVisible());
1908 docstring const msg =
1909 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1915 case LFUN_TOOLBAR_MOVABLE: {
1916 string const name = cmd.getArg(0);
1917 // use negation since locked == !movable
1919 // toolbar name * locks all toolbars
1920 flag.setOnOff(!toolbarsMovable_);
1921 else if (GuiToolbar * t = toolbar(name))
1922 flag.setOnOff(!(t->isMovable()));
1925 docstring const msg =
1926 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1932 case LFUN_ICON_SIZE:
1933 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1936 case LFUN_DROP_LAYOUTS_CHOICE:
1940 case LFUN_UI_TOGGLE:
1941 flag.setOnOff(isFullScreen());
1944 case LFUN_DIALOG_DISCONNECT_INSET:
1947 case LFUN_DIALOG_HIDE:
1948 // FIXME: should we check if the dialog is shown?
1951 case LFUN_DIALOG_TOGGLE:
1952 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1953 // fall through to set "enable"
1954 case LFUN_DIALOG_SHOW: {
1955 string const name = cmd.getArg(0);
1957 enable = name == "aboutlyx"
1958 || name == "file" //FIXME: should be removed.
1960 || name == "texinfo"
1961 || name == "progress"
1962 || name == "compare";
1963 else if (name == "character" || name == "symbols"
1964 || name == "mathdelimiter" || name == "mathmatrix") {
1965 if (!buf || buf->isReadonly())
1968 Cursor const & cur = currentBufferView()->cursor();
1969 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1972 else if (name == "latexlog")
1973 enable = FileName(doc_buffer->logName()).isReadableFile();
1974 else if (name == "spellchecker")
1975 enable = theSpellChecker()
1976 && !doc_buffer->isReadonly()
1977 && !doc_buffer->text().empty();
1978 else if (name == "vclog")
1979 enable = doc_buffer->lyxvc().inUse();
1983 case LFUN_DIALOG_UPDATE: {
1984 string const name = cmd.getArg(0);
1986 enable = name == "prefs";
1990 case LFUN_COMMAND_EXECUTE:
1992 case LFUN_MENU_OPEN:
1993 // Nothing to check.
1996 case LFUN_COMPLETION_INLINE:
1997 if (!d.current_work_area_
1998 || !d.current_work_area_->completer().inlinePossible(
1999 currentBufferView()->cursor()))
2003 case LFUN_COMPLETION_POPUP:
2004 if (!d.current_work_area_
2005 || !d.current_work_area_->completer().popupPossible(
2006 currentBufferView()->cursor()))
2011 if (!d.current_work_area_
2012 || !d.current_work_area_->completer().inlinePossible(
2013 currentBufferView()->cursor()))
2017 case LFUN_COMPLETION_ACCEPT:
2018 if (!d.current_work_area_
2019 || (!d.current_work_area_->completer().popupVisible()
2020 && !d.current_work_area_->completer().inlineVisible()
2021 && !d.current_work_area_->completer().completionAvailable()))
2025 case LFUN_COMPLETION_CANCEL:
2026 if (!d.current_work_area_
2027 || (!d.current_work_area_->completer().popupVisible()
2028 && !d.current_work_area_->completer().inlineVisible()))
2032 case LFUN_BUFFER_ZOOM_OUT:
2033 case LFUN_BUFFER_ZOOM_IN: {
2034 // only diff between these two is that the default for ZOOM_OUT
2036 bool const neg_zoom =
2037 convert<int>(cmd.argument()) < 0 ||
2038 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2039 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2040 docstring const msg =
2041 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2045 enable = doc_buffer;
2049 case LFUN_BUFFER_ZOOM: {
2050 bool const less_than_min_zoom =
2051 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2052 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2053 docstring const msg =
2054 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2059 enable = doc_buffer;
2063 case LFUN_BUFFER_MOVE_NEXT:
2064 case LFUN_BUFFER_MOVE_PREVIOUS:
2065 // we do not cycle when moving
2066 case LFUN_BUFFER_NEXT:
2067 case LFUN_BUFFER_PREVIOUS:
2068 // because we cycle, it doesn't matter whether on first or last
2069 enable = (d.currentTabWorkArea()->count() > 1);
2071 case LFUN_BUFFER_SWITCH:
2072 // toggle on the current buffer, but do not toggle off
2073 // the other ones (is that a good idea?)
2075 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2076 flag.setOnOff(true);
2079 case LFUN_VC_REGISTER:
2080 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2082 case LFUN_VC_RENAME:
2083 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2086 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2088 case LFUN_VC_CHECK_IN:
2089 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2091 case LFUN_VC_CHECK_OUT:
2092 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2094 case LFUN_VC_LOCKING_TOGGLE:
2095 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2096 && doc_buffer->lyxvc().lockingToggleEnabled();
2097 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2099 case LFUN_VC_REVERT:
2100 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2101 && !doc_buffer->hasReadonlyFlag();
2103 case LFUN_VC_UNDO_LAST:
2104 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2106 case LFUN_VC_REPO_UPDATE:
2107 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2109 case LFUN_VC_COMMAND: {
2110 if (cmd.argument().empty())
2112 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2116 case LFUN_VC_COMPARE:
2117 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2120 case LFUN_SERVER_GOTO_FILE_ROW:
2121 case LFUN_LYX_ACTIVATE:
2123 case LFUN_FORWARD_SEARCH:
2124 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2127 case LFUN_FILE_INSERT_PLAINTEXT:
2128 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2129 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2132 case LFUN_SPELLING_CONTINUOUSLY:
2133 flag.setOnOff(lyxrc.spellcheck_continuously);
2141 flag.setEnabled(false);
2147 static FileName selectTemplateFile()
2149 FileDialog dlg(qt_("Select template file"));
2150 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2151 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2153 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2154 QStringList(qt_("LyX Documents (*.lyx)")));
2156 if (result.first == FileDialog::Later)
2158 if (result.second.isEmpty())
2160 return FileName(fromqstr(result.second));
2164 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2168 Buffer * newBuffer = 0;
2170 newBuffer = checkAndLoadLyXFile(filename);
2171 } catch (ExceptionMessage const & e) {
2178 message(_("Document not loaded."));
2182 setBuffer(newBuffer);
2183 newBuffer->errors("Parse");
2186 theSession().lastFiles().add(filename);
2192 void GuiView::openDocument(string const & fname)
2194 string initpath = lyxrc.document_path;
2196 if (documentBufferView()) {
2197 string const trypath = documentBufferView()->buffer().filePath();
2198 // If directory is writeable, use this as default.
2199 if (FileName(trypath).isDirWritable())
2205 if (fname.empty()) {
2206 FileDialog dlg(qt_("Select document to open"));
2207 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2208 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2210 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2211 FileDialog::Result result =
2212 dlg.open(toqstr(initpath), filter);
2214 if (result.first == FileDialog::Later)
2217 filename = fromqstr(result.second);
2219 // check selected filename
2220 if (filename.empty()) {
2221 message(_("Canceled."));
2227 // get absolute path of file and add ".lyx" to the filename if
2229 FileName const fullname =
2230 fileSearch(string(), filename, "lyx", support::may_not_exist);
2231 if (!fullname.empty())
2232 filename = fullname.absFileName();
2234 if (!fullname.onlyPath().isDirectory()) {
2235 Alert::warning(_("Invalid filename"),
2236 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2237 from_utf8(fullname.absFileName())));
2241 // if the file doesn't exist and isn't already open (bug 6645),
2242 // let the user create one
2243 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2244 !LyXVC::file_not_found_hook(fullname)) {
2245 // the user specifically chose this name. Believe him.
2246 Buffer * const b = newFile(filename, string(), true);
2252 docstring const disp_fn = makeDisplayPath(filename);
2253 message(bformat(_("Opening document %1$s..."), disp_fn));
2256 Buffer * buf = loadDocument(fullname);
2258 str2 = bformat(_("Document %1$s opened."), disp_fn);
2259 if (buf->lyxvc().inUse())
2260 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2261 " " + _("Version control detected.");
2263 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2268 // FIXME: clean that
2269 static bool import(GuiView * lv, FileName const & filename,
2270 string const & format, ErrorList & errorList)
2272 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2274 string loader_format;
2275 vector<string> loaders = theConverters().loaders();
2276 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2277 vector<string>::const_iterator it = loaders.begin();
2278 vector<string>::const_iterator en = loaders.end();
2279 for (; it != en; ++it) {
2280 if (!theConverters().isReachable(format, *it))
2283 string const tofile =
2284 support::changeExtension(filename.absFileName(),
2285 theFormats().extension(*it));
2286 if (!theConverters().convert(0, filename, FileName(tofile),
2287 filename, format, *it, errorList))
2289 loader_format = *it;
2292 if (loader_format.empty()) {
2293 frontend::Alert::error(_("Couldn't import file"),
2294 bformat(_("No information for importing the format %1$s."),
2295 theFormats().prettyName(format)));
2299 loader_format = format;
2301 if (loader_format == "lyx") {
2302 Buffer * buf = lv->loadDocument(lyxfile);
2306 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2310 bool as_paragraphs = loader_format == "textparagraph";
2311 string filename2 = (loader_format == format) ? filename.absFileName()
2312 : support::changeExtension(filename.absFileName(),
2313 theFormats().extension(loader_format));
2314 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2316 guiApp->setCurrentView(lv);
2317 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2324 void GuiView::importDocument(string const & argument)
2327 string filename = split(argument, format, ' ');
2329 LYXERR(Debug::INFO, format << " file: " << filename);
2331 // need user interaction
2332 if (filename.empty()) {
2333 string initpath = lyxrc.document_path;
2334 if (documentBufferView()) {
2335 string const trypath = documentBufferView()->buffer().filePath();
2336 // If directory is writeable, use this as default.
2337 if (FileName(trypath).isDirWritable())
2341 docstring const text = bformat(_("Select %1$s file to import"),
2342 theFormats().prettyName(format));
2344 FileDialog dlg(toqstr(text));
2345 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2346 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2348 docstring filter = theFormats().prettyName(format);
2351 filter += from_utf8(theFormats().extensions(format));
2354 FileDialog::Result result =
2355 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2357 if (result.first == FileDialog::Later)
2360 filename = fromqstr(result.second);
2362 // check selected filename
2363 if (filename.empty())
2364 message(_("Canceled."));
2367 if (filename.empty())
2370 // get absolute path of file
2371 FileName const fullname(support::makeAbsPath(filename));
2373 // Can happen if the user entered a path into the dialog
2375 if (fullname.onlyFileName().empty()) {
2376 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2377 "Aborting import."),
2378 from_utf8(fullname.absFileName()));
2379 frontend::Alert::error(_("File name error"), msg);
2380 message(_("Canceled."));
2385 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2387 // Check if the document already is open
2388 Buffer * buf = theBufferList().getBuffer(lyxfile);
2391 if (!closeBuffer()) {
2392 message(_("Canceled."));
2397 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2399 // if the file exists already, and we didn't do
2400 // -i lyx thefile.lyx, warn
2401 if (lyxfile.exists() && fullname != lyxfile) {
2403 docstring text = bformat(_("The document %1$s already exists.\n\n"
2404 "Do you want to overwrite that document?"), displaypath);
2405 int const ret = Alert::prompt(_("Overwrite document?"),
2406 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2409 message(_("Canceled."));
2414 message(bformat(_("Importing %1$s..."), displaypath));
2415 ErrorList errorList;
2416 if (import(this, fullname, format, errorList))
2417 message(_("imported."));
2419 message(_("file not imported!"));
2421 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2425 void GuiView::newDocument(string const & filename, bool from_template)
2427 FileName initpath(lyxrc.document_path);
2428 if (documentBufferView()) {
2429 FileName const trypath(documentBufferView()->buffer().filePath());
2430 // If directory is writeable, use this as default.
2431 if (trypath.isDirWritable())
2435 string templatefile;
2436 if (from_template) {
2437 templatefile = selectTemplateFile().absFileName();
2438 if (templatefile.empty())
2443 if (filename.empty())
2444 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2446 b = newFile(filename, templatefile, true);
2451 // If no new document could be created, it is unsure
2452 // whether there is a valid BufferView.
2453 if (currentBufferView())
2454 // Ensure the cursor is correctly positioned on screen.
2455 currentBufferView()->showCursor();
2459 void GuiView::insertLyXFile(docstring const & fname)
2461 BufferView * bv = documentBufferView();
2466 FileName filename(to_utf8(fname));
2467 if (filename.empty()) {
2468 // Launch a file browser
2470 string initpath = lyxrc.document_path;
2471 string const trypath = bv->buffer().filePath();
2472 // If directory is writeable, use this as default.
2473 if (FileName(trypath).isDirWritable())
2477 FileDialog dlg(qt_("Select LyX document to insert"));
2478 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2479 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2481 FileDialog::Result result = dlg.open(toqstr(initpath),
2482 QStringList(qt_("LyX Documents (*.lyx)")));
2484 if (result.first == FileDialog::Later)
2488 filename.set(fromqstr(result.second));
2490 // check selected filename
2491 if (filename.empty()) {
2492 // emit message signal.
2493 message(_("Canceled."));
2498 bv->insertLyXFile(filename);
2499 bv->buffer().errors("Parse");
2503 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2505 FileName fname = b.fileName();
2506 FileName const oldname = fname;
2508 if (!newname.empty()) {
2510 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2512 // Switch to this Buffer.
2515 // No argument? Ask user through dialog.
2517 FileDialog dlg(qt_("Choose a filename to save document as"));
2518 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2519 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2521 if (!isLyXFileName(fname.absFileName()))
2522 fname.changeExtension(".lyx");
2524 FileDialog::Result result =
2525 dlg.save(toqstr(fname.onlyPath().absFileName()),
2526 QStringList(qt_("LyX Documents (*.lyx)")),
2527 toqstr(fname.onlyFileName()));
2529 if (result.first == FileDialog::Later)
2532 fname.set(fromqstr(result.second));
2537 if (!isLyXFileName(fname.absFileName()))
2538 fname.changeExtension(".lyx");
2541 // fname is now the new Buffer location.
2543 // if there is already a Buffer open with this name, we do not want
2544 // to have another one. (the second test makes sure we're not just
2545 // trying to overwrite ourselves, which is fine.)
2546 if (theBufferList().exists(fname) && fname != oldname
2547 && theBufferList().getBuffer(fname) != &b) {
2548 docstring const text =
2549 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2550 "Please close it before attempting to overwrite it.\n"
2551 "Do you want to choose a new filename?"),
2552 from_utf8(fname.absFileName()));
2553 int const ret = Alert::prompt(_("Chosen File Already Open"),
2554 text, 0, 1, _("&Rename"), _("&Cancel"));
2556 case 0: return renameBuffer(b, docstring(), kind);
2557 case 1: return false;
2562 bool const existsLocal = fname.exists();
2563 bool const existsInVC = LyXVC::fileInVC(fname);
2564 if (existsLocal || existsInVC) {
2565 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2566 if (kind != LV_WRITE_AS && existsInVC) {
2567 // renaming to a name that is already in VC
2569 docstring text = bformat(_("The document %1$s "
2570 "is already registered.\n\n"
2571 "Do you want to choose a new name?"),
2573 docstring const title = (kind == LV_VC_RENAME) ?
2574 _("Rename document?") : _("Copy document?");
2575 docstring const button = (kind == LV_VC_RENAME) ?
2576 _("&Rename") : _("&Copy");
2577 int const ret = Alert::prompt(title, text, 0, 1,
2578 button, _("&Cancel"));
2580 case 0: return renameBuffer(b, docstring(), kind);
2581 case 1: return false;
2586 docstring text = bformat(_("The document %1$s "
2587 "already exists.\n\n"
2588 "Do you want to overwrite that document?"),
2590 int const ret = Alert::prompt(_("Overwrite document?"),
2591 text, 0, 2, _("&Overwrite"),
2592 _("&Rename"), _("&Cancel"));
2595 case 1: return renameBuffer(b, docstring(), kind);
2596 case 2: return false;
2602 case LV_VC_RENAME: {
2603 string msg = b.lyxvc().rename(fname);
2606 message(from_utf8(msg));
2610 string msg = b.lyxvc().copy(fname);
2613 message(from_utf8(msg));
2619 // LyXVC created the file already in case of LV_VC_RENAME or
2620 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2621 // relative paths of included stuff right if we moved e.g. from
2622 // /a/b.lyx to /a/c/b.lyx.
2624 bool const saved = saveBuffer(b, fname);
2631 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2633 FileName fname = b.fileName();
2635 FileDialog dlg(qt_("Choose a filename to export the document as"));
2636 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2639 QString const anyformat = qt_("Guess from extension (*.*)");
2642 vector<Format const *> export_formats;
2643 for (Format const & f : theFormats())
2644 if (f.documentFormat())
2645 export_formats.push_back(&f);
2646 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2647 map<QString, string> fmap;
2650 for (Format const * f : export_formats) {
2651 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2652 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2654 from_ascii(f->extension())));
2655 types << loc_filter;
2656 fmap[loc_filter] = f->name();
2657 if (from_ascii(f->name()) == iformat) {
2658 filter = loc_filter;
2659 ext = f->extension();
2662 string ofname = fname.onlyFileName();
2664 ofname = support::changeExtension(ofname, ext);
2665 FileDialog::Result result =
2666 dlg.save(toqstr(fname.onlyPath().absFileName()),
2670 if (result.first != FileDialog::Chosen)
2674 fname.set(fromqstr(result.second));
2675 if (filter == anyformat)
2676 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2678 fmt_name = fmap[filter];
2679 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2680 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2682 if (fmt_name.empty() || fname.empty())
2685 // fname is now the new Buffer location.
2686 if (FileName(fname).exists()) {
2687 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2688 docstring text = bformat(_("The document %1$s already "
2689 "exists.\n\nDo you want to "
2690 "overwrite that document?"),
2692 int const ret = Alert::prompt(_("Overwrite document?"),
2693 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2696 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2697 case 2: return false;
2701 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2704 return dr.dispatched();
2708 bool GuiView::saveBuffer(Buffer & b)
2710 return saveBuffer(b, FileName());
2714 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2716 if (workArea(b) && workArea(b)->inDialogMode())
2719 if (fn.empty() && b.isUnnamed())
2720 return renameBuffer(b, docstring());
2722 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2724 theSession().lastFiles().add(b.fileName());
2728 // Switch to this Buffer.
2731 // FIXME: we don't tell the user *WHY* the save failed !!
2732 docstring const file = makeDisplayPath(b.absFileName(), 30);
2733 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2734 "Do you want to rename the document and "
2735 "try again?"), file);
2736 int const ret = Alert::prompt(_("Rename and save?"),
2737 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2740 if (!renameBuffer(b, docstring()))
2749 return saveBuffer(b, fn);
2753 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2755 return closeWorkArea(wa, false);
2759 // We only want to close the buffer if it is not visible in other workareas
2760 // of the same view, nor in other views, and if this is not a child
2761 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2763 Buffer & buf = wa->bufferView().buffer();
2765 bool last_wa = d.countWorkAreasOf(buf) == 1
2766 && !inOtherView(buf) && !buf.parent();
2768 bool close_buffer = last_wa;
2771 if (lyxrc.close_buffer_with_last_view == "yes")
2773 else if (lyxrc.close_buffer_with_last_view == "no")
2774 close_buffer = false;
2777 if (buf.isUnnamed())
2778 file = from_utf8(buf.fileName().onlyFileName());
2780 file = buf.fileName().displayName(30);
2781 docstring const text = bformat(
2782 _("Last view on document %1$s is being closed.\n"
2783 "Would you like to close or hide the document?\n"
2785 "Hidden documents can be displayed back through\n"
2786 "the menu: View->Hidden->...\n"
2788 "To remove this question, set your preference in:\n"
2789 " Tools->Preferences->Look&Feel->UserInterface\n"
2791 int ret = Alert::prompt(_("Close or hide document?"),
2792 text, 0, 1, _("&Close"), _("&Hide"));
2793 close_buffer = (ret == 0);
2797 return closeWorkArea(wa, close_buffer);
2801 bool GuiView::closeBuffer()
2803 GuiWorkArea * wa = currentMainWorkArea();
2804 // coverity complained about this
2805 // it seems unnecessary, but perhaps is worth the check
2806 LASSERT(wa, return false);
2808 setCurrentWorkArea(wa);
2809 Buffer & buf = wa->bufferView().buffer();
2810 return closeWorkArea(wa, !buf.parent());
2814 void GuiView::writeSession() const {
2815 GuiWorkArea const * active_wa = currentMainWorkArea();
2816 for (int i = 0; i < d.splitter_->count(); ++i) {
2817 TabWorkArea * twa = d.tabWorkArea(i);
2818 for (int j = 0; j < twa->count(); ++j) {
2819 GuiWorkArea * wa = twa->workArea(j);
2820 Buffer & buf = wa->bufferView().buffer();
2821 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2827 bool GuiView::closeBufferAll()
2829 // Close the workareas in all other views
2830 QList<int> const ids = guiApp->viewIds();
2831 for (int i = 0; i != ids.size(); ++i) {
2832 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2836 // Close our own workareas
2837 if (!closeWorkAreaAll())
2840 // Now close the hidden buffers. We prevent hidden buffers from being
2841 // dirty, so we can just close them.
2842 theBufferList().closeAll();
2847 bool GuiView::closeWorkAreaAll()
2849 setCurrentWorkArea(currentMainWorkArea());
2851 // We might be in a situation that there is still a tabWorkArea, but
2852 // there are no tabs anymore. This can happen when we get here after a
2853 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2854 // many TabWorkArea's have no documents anymore.
2857 // We have to call count() each time, because it can happen that
2858 // more than one splitter will disappear in one iteration (bug 5998).
2859 while (d.splitter_->count() > empty_twa) {
2860 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2862 if (twa->count() == 0)
2865 setCurrentWorkArea(twa->currentWorkArea());
2866 if (!closeTabWorkArea(twa))
2874 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2879 Buffer & buf = wa->bufferView().buffer();
2881 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2882 Alert::warning(_("Close document"),
2883 _("Document could not be closed because it is being processed by LyX."));
2888 return closeBuffer(buf);
2890 if (!inMultiTabs(wa))
2891 if (!saveBufferIfNeeded(buf, true))
2899 bool GuiView::closeBuffer(Buffer & buf)
2901 // If we are in a close_event all children will be closed in some time,
2902 // so no need to do it here. This will ensure that the children end up
2903 // in the session file in the correct order. If we close the master
2904 // buffer, we can close or release the child buffers here too.
2905 bool success = true;
2907 ListOfBuffers clist = buf.getChildren();
2908 ListOfBuffers::const_iterator it = clist.begin();
2909 ListOfBuffers::const_iterator const bend = clist.end();
2910 for (; it != bend; ++it) {
2911 Buffer * child_buf = *it;
2912 if (theBufferList().isOthersChild(&buf, child_buf)) {
2913 child_buf->setParent(0);
2917 // FIXME: should we look in other tabworkareas?
2918 // ANSWER: I don't think so. I've tested, and if the child is
2919 // open in some other window, it closes without a problem.
2920 GuiWorkArea * child_wa = workArea(*child_buf);
2922 success = closeWorkArea(child_wa, true);
2926 // In this case the child buffer is open but hidden.
2927 // It therefore should not (MUST NOT) be dirty!
2928 LATTEST(child_buf->isClean());
2929 theBufferList().release(child_buf);
2934 // goto bookmark to update bookmark pit.
2935 // FIXME: we should update only the bookmarks related to this buffer!
2936 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2937 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2938 guiApp->gotoBookmark(i+1, false, false);
2940 if (saveBufferIfNeeded(buf, false)) {
2941 buf.removeAutosaveFile();
2942 theBufferList().release(&buf);
2946 // open all children again to avoid a crash because of dangling
2947 // pointers (bug 6603)
2953 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2955 while (twa == d.currentTabWorkArea()) {
2956 twa->setCurrentIndex(twa->count() - 1);
2958 GuiWorkArea * wa = twa->currentWorkArea();
2959 Buffer & b = wa->bufferView().buffer();
2961 // We only want to close the buffer if the same buffer is not visible
2962 // in another view, and if this is not a child and if we are closing
2963 // a view (not a tabgroup).
2964 bool const close_buffer =
2965 !inOtherView(b) && !b.parent() && closing_;
2967 if (!closeWorkArea(wa, close_buffer))
2974 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2976 if (buf.isClean() || buf.paragraphs().empty())
2979 // Switch to this Buffer.
2985 if (buf.isUnnamed()) {
2986 file = from_utf8(buf.fileName().onlyFileName());
2989 FileName filename = buf.fileName();
2991 file = filename.displayName(30);
2992 exists = filename.exists();
2995 // Bring this window to top before asking questions.
3000 if (hiding && buf.isUnnamed()) {
3001 docstring const text = bformat(_("The document %1$s has not been "
3002 "saved yet.\n\nDo you want to save "
3003 "the document?"), file);
3004 ret = Alert::prompt(_("Save new document?"),
3005 text, 0, 1, _("&Save"), _("&Cancel"));
3009 docstring const text = exists ?
3010 bformat(_("The document %1$s has unsaved changes."
3011 "\n\nDo you want to save the document or "
3012 "discard the changes?"), file) :
3013 bformat(_("The document %1$s has not been saved yet."
3014 "\n\nDo you want to save the document or "
3015 "discard it entirely?"), file);
3016 docstring const title = exists ?
3017 _("Save changed document?") : _("Save document?");
3018 ret = Alert::prompt(title, text, 0, 2,
3019 _("&Save"), _("&Discard"), _("&Cancel"));
3024 if (!saveBuffer(buf))
3028 // If we crash after this we could have no autosave file
3029 // but I guess this is really improbable (Jug).
3030 // Sometimes improbable things happen:
3031 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3032 // buf.removeAutosaveFile();
3034 // revert all changes
3045 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3047 Buffer & buf = wa->bufferView().buffer();
3049 for (int i = 0; i != d.splitter_->count(); ++i) {
3050 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3051 if (wa_ && wa_ != wa)
3054 return inOtherView(buf);
3058 bool GuiView::inOtherView(Buffer & buf)
3060 QList<int> const ids = guiApp->viewIds();
3062 for (int i = 0; i != ids.size(); ++i) {
3066 if (guiApp->view(ids[i]).workArea(buf))
3073 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3075 if (!documentBufferView())
3078 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3079 Buffer * const curbuf = &documentBufferView()->buffer();
3080 int nwa = twa->count();
3081 for (int i = 0; i < nwa; ++i) {
3082 if (&workArea(i)->bufferView().buffer() == curbuf) {
3084 if (np == NEXTBUFFER)
3085 next_index = (i == nwa - 1 ? 0 : i + 1);
3087 next_index = (i == 0 ? nwa - 1 : i - 1);
3089 twa->moveTab(i, next_index);
3091 setBuffer(&workArea(next_index)->bufferView().buffer());
3099 /// make sure the document is saved
3100 static bool ensureBufferClean(Buffer * buffer)
3102 LASSERT(buffer, return false);
3103 if (buffer->isClean() && !buffer->isUnnamed())
3106 docstring const file = buffer->fileName().displayName(30);
3109 if (!buffer->isUnnamed()) {
3110 text = bformat(_("The document %1$s has unsaved "
3111 "changes.\n\nDo you want to save "
3112 "the document?"), file);
3113 title = _("Save changed document?");
3116 text = bformat(_("The document %1$s has not been "
3117 "saved yet.\n\nDo you want to save "
3118 "the document?"), file);
3119 title = _("Save new document?");
3121 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3124 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3126 return buffer->isClean() && !buffer->isUnnamed();
3130 bool GuiView::reloadBuffer(Buffer & buf)
3132 Buffer::ReadStatus status = buf.reload();
3133 return status == Buffer::ReadSuccess;
3137 void GuiView::checkExternallyModifiedBuffers()
3139 BufferList::iterator bit = theBufferList().begin();
3140 BufferList::iterator const bend = theBufferList().end();
3141 for (; bit != bend; ++bit) {
3142 Buffer * buf = *bit;
3143 if (buf->fileName().exists() && buf->isChecksumModified()) {
3144 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3145 " Reload now? Any local changes will be lost."),
3146 from_utf8(buf->absFileName()));
3147 int const ret = Alert::prompt(_("Reload externally changed document?"),
3148 text, 0, 1, _("&Reload"), _("&Cancel"));
3156 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3158 Buffer * buffer = documentBufferView()
3159 ? &(documentBufferView()->buffer()) : 0;
3161 switch (cmd.action()) {
3162 case LFUN_VC_REGISTER:
3163 if (!buffer || !ensureBufferClean(buffer))
3165 if (!buffer->lyxvc().inUse()) {
3166 if (buffer->lyxvc().registrer()) {
3167 reloadBuffer(*buffer);
3168 dr.clearMessageUpdate();
3173 case LFUN_VC_RENAME:
3174 case LFUN_VC_COPY: {
3175 if (!buffer || !ensureBufferClean(buffer))
3177 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3178 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3179 // Some changes are not yet committed.
3180 // We test here and not in getStatus(), since
3181 // this test is expensive.
3183 LyXVC::CommandResult ret =
3184 buffer->lyxvc().checkIn(log);
3186 if (ret == LyXVC::ErrorCommand ||
3187 ret == LyXVC::VCSuccess)
3188 reloadBuffer(*buffer);
3189 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3190 frontend::Alert::error(
3191 _("Revision control error."),
3192 _("Document could not be checked in."));
3196 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3197 LV_VC_RENAME : LV_VC_COPY;
3198 renameBuffer(*buffer, cmd.argument(), kind);
3203 case LFUN_VC_CHECK_IN:
3204 if (!buffer || !ensureBufferClean(buffer))
3206 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3208 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3210 // Only skip reloading if the checkin was cancelled or
3211 // an error occurred before the real checkin VCS command
3212 // was executed, since the VCS might have changed the
3213 // file even if it could not checkin successfully.
3214 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3215 reloadBuffer(*buffer);
3219 case LFUN_VC_CHECK_OUT:
3220 if (!buffer || !ensureBufferClean(buffer))
3222 if (buffer->lyxvc().inUse()) {
3223 dr.setMessage(buffer->lyxvc().checkOut());
3224 reloadBuffer(*buffer);
3228 case LFUN_VC_LOCKING_TOGGLE:
3229 LASSERT(buffer, return);
3230 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3232 if (buffer->lyxvc().inUse()) {
3233 string res = buffer->lyxvc().lockingToggle();
3235 frontend::Alert::error(_("Revision control error."),
3236 _("Error when setting the locking property."));
3239 reloadBuffer(*buffer);
3244 case LFUN_VC_REVERT:
3245 LASSERT(buffer, return);
3246 if (buffer->lyxvc().revert()) {
3247 reloadBuffer(*buffer);
3248 dr.clearMessageUpdate();
3252 case LFUN_VC_UNDO_LAST:
3253 LASSERT(buffer, return);
3254 buffer->lyxvc().undoLast();
3255 reloadBuffer(*buffer);
3256 dr.clearMessageUpdate();
3259 case LFUN_VC_REPO_UPDATE:
3260 LASSERT(buffer, return);
3261 if (ensureBufferClean(buffer)) {
3262 dr.setMessage(buffer->lyxvc().repoUpdate());
3263 checkExternallyModifiedBuffers();
3267 case LFUN_VC_COMMAND: {
3268 string flag = cmd.getArg(0);
3269 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3272 if (contains(flag, 'M')) {
3273 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3276 string path = cmd.getArg(1);
3277 if (contains(path, "$$p") && buffer)
3278 path = subst(path, "$$p", buffer->filePath());
3279 LYXERR(Debug::LYXVC, "Directory: " << path);
3281 if (!pp.isReadableDirectory()) {
3282 lyxerr << _("Directory is not accessible.") << endl;
3285 support::PathChanger p(pp);
3287 string command = cmd.getArg(2);
3288 if (command.empty())
3291 command = subst(command, "$$i", buffer->absFileName());
3292 command = subst(command, "$$p", buffer->filePath());
3294 command = subst(command, "$$m", to_utf8(message));
3295 LYXERR(Debug::LYXVC, "Command: " << command);
3297 one.startscript(Systemcall::Wait, command);
3301 if (contains(flag, 'I'))
3302 buffer->markDirty();
3303 if (contains(flag, 'R'))
3304 reloadBuffer(*buffer);
3309 case LFUN_VC_COMPARE: {
3310 if (cmd.argument().empty()) {
3311 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3315 string rev1 = cmd.getArg(0);
3320 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3323 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3324 f2 = buffer->absFileName();
3326 string rev2 = cmd.getArg(1);
3330 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3334 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3335 f1 << "\n" << f2 << "\n" );
3336 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3337 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3347 void GuiView::openChildDocument(string const & fname)
3349 LASSERT(documentBufferView(), return);
3350 Buffer & buffer = documentBufferView()->buffer();
3351 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3352 documentBufferView()->saveBookmark(false);
3354 if (theBufferList().exists(filename)) {
3355 child = theBufferList().getBuffer(filename);
3358 message(bformat(_("Opening child document %1$s..."),
3359 makeDisplayPath(filename.absFileName())));
3360 child = loadDocument(filename, false);
3362 // Set the parent name of the child document.
3363 // This makes insertion of citations and references in the child work,
3364 // when the target is in the parent or another child document.
3366 child->setParent(&buffer);
3370 bool GuiView::goToFileRow(string const & argument)
3374 size_t i = argument.find_last_of(' ');
3375 if (i != string::npos) {
3376 file_name = os::internal_path(trim(argument.substr(0, i)));
3377 istringstream is(argument.substr(i + 1));
3382 if (i == string::npos) {
3383 LYXERR0("Wrong argument: " << argument);
3387 string const abstmp = package().temp_dir().absFileName();
3388 string const realtmp = package().temp_dir().realPath();
3389 // We have to use os::path_prefix_is() here, instead of
3390 // simply prefixIs(), because the file name comes from
3391 // an external application and may need case adjustment.
3392 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3393 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3394 // Needed by inverse dvi search. If it is a file
3395 // in tmpdir, call the apropriated function.
3396 // If tmpdir is a symlink, we may have the real
3397 // path passed back, so we correct for that.
3398 if (!prefixIs(file_name, abstmp))
3399 file_name = subst(file_name, realtmp, abstmp);
3400 buf = theBufferList().getBufferFromTmp(file_name);
3402 // Must replace extension of the file to be .lyx
3403 // and get full path
3404 FileName const s = fileSearch(string(),
3405 support::changeExtension(file_name, ".lyx"), "lyx");
3406 // Either change buffer or load the file
3407 if (theBufferList().exists(s))
3408 buf = theBufferList().getBuffer(s);
3409 else if (s.exists()) {
3410 buf = loadDocument(s);
3415 _("File does not exist: %1$s"),
3416 makeDisplayPath(file_name)));
3422 _("No buffer for file: %1$s."),
3423 makeDisplayPath(file_name))
3428 bool success = documentBufferView()->setCursorFromRow(row);
3430 LYXERR(Debug::LATEX,
3431 "setCursorFromRow: invalid position for row " << row);
3432 frontend::Alert::error(_("Inverse Search Failed"),
3433 _("Invalid position requested by inverse search.\n"
3434 "You may need to update the viewed document."));
3440 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3442 QMenu * menu = new QMenu;
3443 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3444 menu->exec(QCursor::pos());
3449 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3451 Buffer::ExportStatus const status = func(format);
3453 // the cloning operation will have produced a clone of the entire set of
3454 // documents, starting from the master. so we must delete those.
3455 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3457 busyBuffers.remove(orig);
3462 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3464 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3465 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3469 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3471 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3472 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3476 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3478 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3479 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3483 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3484 string const & argument,
3485 Buffer const * used_buffer,
3486 docstring const & msg,
3487 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3488 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3489 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3494 string format = argument;
3496 format = used_buffer->params().getDefaultOutputFormat();
3497 processing_format = format;
3499 progress_->clearMessages();
3502 #if EXPORT_in_THREAD
3503 GuiViewPrivate::busyBuffers.insert(used_buffer);
3504 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3505 if (!cloned_buffer) {
3506 Alert::error(_("Export Error"),
3507 _("Error cloning the Buffer."));
3510 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3515 setPreviewFuture(f);
3516 last_export_format = used_buffer->params().bufferFormat();
3519 // We are asynchronous, so we don't know here anything about the success
3522 Buffer::ExportStatus status;
3524 status = (used_buffer->*syncFunc)(format, true);
3525 } else if (previewFunc) {
3526 status = (used_buffer->*previewFunc)(format);
3529 handleExportStatus(gv_, status, format);
3531 return (status == Buffer::ExportSuccess
3532 || status == Buffer::PreviewSuccess);
3536 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3538 BufferView * bv = currentBufferView();
3539 LASSERT(bv, return);
3541 // Let the current BufferView dispatch its own actions.
3542 bv->dispatch(cmd, dr);
3543 if (dr.dispatched())
3546 // Try with the document BufferView dispatch if any.
3547 BufferView * doc_bv = documentBufferView();
3548 if (doc_bv && doc_bv != bv) {
3549 doc_bv->dispatch(cmd, dr);
3550 if (dr.dispatched())
3554 // Then let the current Cursor dispatch its own actions.
3555 bv->cursor().dispatch(cmd);
3557 // update completion. We do it here and not in
3558 // processKeySym to avoid another redraw just for a
3559 // changed inline completion
3560 if (cmd.origin() == FuncRequest::KEYBOARD) {
3561 if (cmd.action() == LFUN_SELF_INSERT
3562 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3563 updateCompletion(bv->cursor(), true, true);
3564 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3565 updateCompletion(bv->cursor(), false, true);
3567 updateCompletion(bv->cursor(), false, false);
3570 dr = bv->cursor().result();
3574 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3576 BufferView * bv = currentBufferView();
3577 // By default we won't need any update.
3578 dr.screenUpdate(Update::None);
3579 // assume cmd will be dispatched
3580 dr.dispatched(true);
3582 Buffer * doc_buffer = documentBufferView()
3583 ? &(documentBufferView()->buffer()) : 0;
3585 if (cmd.origin() == FuncRequest::TOC) {
3586 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3587 // FIXME: do we need to pass a DispatchResult object here?
3588 toc->doDispatch(bv->cursor(), cmd);
3592 string const argument = to_utf8(cmd.argument());
3594 switch(cmd.action()) {
3595 case LFUN_BUFFER_CHILD_OPEN:
3596 openChildDocument(to_utf8(cmd.argument()));
3599 case LFUN_BUFFER_IMPORT:
3600 importDocument(to_utf8(cmd.argument()));
3603 case LFUN_BUFFER_EXPORT: {
3606 // GCC only sees strfwd.h when building merged
3607 if (::lyx::operator==(cmd.argument(), "custom")) {
3608 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3612 string const dest = cmd.getArg(1);
3613 FileName target_dir;
3614 if (!dest.empty() && FileName::isAbsolute(dest))
3615 target_dir = FileName(support::onlyPath(dest));
3617 target_dir = doc_buffer->fileName().onlyPath();
3619 string const format = (argument.empty() || argument == "default") ?
3620 doc_buffer->params().getDefaultOutputFormat() : argument;
3622 if ((dest.empty() && doc_buffer->isUnnamed())
3623 || !target_dir.isDirWritable()) {
3624 exportBufferAs(*doc_buffer, from_utf8(format));
3627 /* TODO/Review: Is it a problem to also export the children?
3628 See the update_unincluded flag */
3629 d.asyncBufferProcessing(format,
3632 &GuiViewPrivate::exportAndDestroy,
3635 // TODO Inform user about success
3639 case LFUN_BUFFER_EXPORT_AS: {
3640 LASSERT(doc_buffer, break);
3641 docstring f = cmd.argument();
3643 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3644 exportBufferAs(*doc_buffer, f);
3648 case LFUN_BUFFER_UPDATE: {
3649 d.asyncBufferProcessing(argument,
3652 &GuiViewPrivate::compileAndDestroy,
3657 case LFUN_BUFFER_VIEW: {
3658 d.asyncBufferProcessing(argument,
3660 _("Previewing ..."),
3661 &GuiViewPrivate::previewAndDestroy,
3666 case LFUN_MASTER_BUFFER_UPDATE: {
3667 d.asyncBufferProcessing(argument,
3668 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3670 &GuiViewPrivate::compileAndDestroy,
3675 case LFUN_MASTER_BUFFER_VIEW: {
3676 d.asyncBufferProcessing(argument,
3677 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3679 &GuiViewPrivate::previewAndDestroy,
3680 0, &Buffer::preview);
3683 case LFUN_BUFFER_SWITCH: {
3684 string const file_name = to_utf8(cmd.argument());
3685 if (!FileName::isAbsolute(file_name)) {
3687 dr.setMessage(_("Absolute filename expected."));
3691 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3694 dr.setMessage(_("Document not loaded"));
3698 // Do we open or switch to the buffer in this view ?
3699 if (workArea(*buffer)
3700 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3705 // Look for the buffer in other views
3706 QList<int> const ids = guiApp->viewIds();
3708 for (; i != ids.size(); ++i) {
3709 GuiView & gv = guiApp->view(ids[i]);
3710 if (gv.workArea(*buffer)) {
3712 gv.activateWindow();
3714 gv.setBuffer(buffer);
3719 // If necessary, open a new window as a last resort
3720 if (i == ids.size()) {
3721 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3727 case LFUN_BUFFER_NEXT:
3728 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3731 case LFUN_BUFFER_MOVE_NEXT:
3732 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3735 case LFUN_BUFFER_PREVIOUS:
3736 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3739 case LFUN_BUFFER_MOVE_PREVIOUS:
3740 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3743 case LFUN_COMMAND_EXECUTE: {
3744 command_execute_ = true;
3745 minibuffer_focus_ = true;
3748 case LFUN_DROP_LAYOUTS_CHOICE:
3749 d.layout_->showPopup();
3752 case LFUN_MENU_OPEN:
3753 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3754 menu->exec(QCursor::pos());
3757 case LFUN_FILE_INSERT:
3758 insertLyXFile(cmd.argument());
3761 case LFUN_FILE_INSERT_PLAINTEXT:
3762 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3763 string const fname = to_utf8(cmd.argument());
3764 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3765 dr.setMessage(_("Absolute filename expected."));
3769 FileName filename(fname);
3770 if (fname.empty()) {
3771 FileDialog dlg(qt_("Select file to insert"));
3773 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3774 QStringList(qt_("All Files (*)")));
3776 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3777 dr.setMessage(_("Canceled."));
3781 filename.set(fromqstr(result.second));
3785 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3786 bv->dispatch(new_cmd, dr);
3791 case LFUN_BUFFER_RELOAD: {
3792 LASSERT(doc_buffer, break);
3795 if (!doc_buffer->isClean()) {
3796 docstring const file =
3797 makeDisplayPath(doc_buffer->absFileName(), 20);
3798 if (doc_buffer->notifiesExternalModification()) {
3799 docstring text = _("The current version will be lost. "
3800 "Are you sure you want to load the version on disk "
3801 "of the document %1$s?");
3802 ret = Alert::prompt(_("Reload saved document?"),
3803 bformat(text, file), 1, 1,
3804 _("&Reload"), _("&Cancel"));
3806 docstring text = _("Any changes will be lost. "
3807 "Are you sure you want to revert to the saved version "
3808 "of the document %1$s?");
3809 ret = Alert::prompt(_("Revert to saved document?"),
3810 bformat(text, file), 1, 1,
3811 _("&Revert"), _("&Cancel"));
3816 doc_buffer->markClean();
3817 reloadBuffer(*doc_buffer);
3818 dr.forceBufferUpdate();
3823 case LFUN_BUFFER_WRITE:
3824 LASSERT(doc_buffer, break);
3825 saveBuffer(*doc_buffer);
3828 case LFUN_BUFFER_WRITE_AS:
3829 LASSERT(doc_buffer, break);
3830 renameBuffer(*doc_buffer, cmd.argument());
3833 case LFUN_BUFFER_WRITE_ALL: {
3834 Buffer * first = theBufferList().first();
3837 message(_("Saving all documents..."));
3838 // We cannot use a for loop as the buffer list cycles.
3841 if (!b->isClean()) {
3843 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3845 b = theBufferList().next(b);
3846 } while (b != first);
3847 dr.setMessage(_("All documents saved."));
3851 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3852 LASSERT(doc_buffer, break);
3853 doc_buffer->clearExternalModification();
3856 case LFUN_BUFFER_CLOSE:
3860 case LFUN_BUFFER_CLOSE_ALL:
3864 case LFUN_TOOLBAR_TOGGLE: {
3865 string const name = cmd.getArg(0);
3866 if (GuiToolbar * t = toolbar(name))
3871 case LFUN_TOOLBAR_MOVABLE: {
3872 string const name = cmd.getArg(0);
3874 // toggle (all) toolbars movablility
3875 toolbarsMovable_ = !toolbarsMovable_;
3876 for (ToolbarInfo const & ti : guiApp->toolbars()) {
3877 GuiToolbar * tb = toolbar(ti.name);
3878 if (tb && tb->isMovable() != toolbarsMovable_)
3879 // toggle toolbar movablity if it does not fit lock
3880 // (all) toolbars positions state silent = true, since
3881 // status bar notifications are slow
3884 if (toolbarsMovable_)
3885 dr.setMessage(_("Toolbars unlocked."));
3887 dr.setMessage(_("Toolbars locked."));
3888 } else if (GuiToolbar * t = toolbar(name)) {
3889 // toggle current toolbar movablity
3891 // update lock (all) toolbars positions
3892 updateLockToolbars();
3897 case LFUN_ICON_SIZE: {
3898 QSize size = d.iconSize(cmd.argument());
3900 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3901 size.width(), size.height()));
3905 case LFUN_DIALOG_UPDATE: {
3906 string const name = to_utf8(cmd.argument());
3907 if (name == "prefs" || name == "document")
3908 updateDialog(name, string());
3909 else if (name == "paragraph")
3910 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3911 else if (currentBufferView()) {
3912 Inset * inset = currentBufferView()->editedInset(name);
3913 // Can only update a dialog connected to an existing inset
3915 // FIXME: get rid of this indirection; GuiView ask the inset
3916 // if he is kind enough to update itself...
3917 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3918 //FIXME: pass DispatchResult here?
3919 inset->dispatch(currentBufferView()->cursor(), fr);
3925 case LFUN_DIALOG_TOGGLE: {
3926 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3927 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3928 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3932 case LFUN_DIALOG_DISCONNECT_INSET:
3933 disconnectDialog(to_utf8(cmd.argument()));
3936 case LFUN_DIALOG_HIDE: {
3937 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3941 case LFUN_DIALOG_SHOW: {
3942 string const name = cmd.getArg(0);
3943 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3945 if (name == "character") {
3946 data = freefont2string();
3948 showDialog("character", data);
3949 } else if (name == "latexlog") {
3950 // getStatus checks that
3951 LATTEST(doc_buffer);
3952 Buffer::LogType type;
3953 string const logfile = doc_buffer->logName(&type);
3955 case Buffer::latexlog:
3958 case Buffer::buildlog:
3962 data += Lexer::quoteString(logfile);
3963 showDialog("log", data);
3964 } else if (name == "vclog") {
3965 // getStatus checks that
3966 LATTEST(doc_buffer);
3967 string const data = "vc " +
3968 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3969 showDialog("log", data);
3970 } else if (name == "symbols") {
3971 data = bv->cursor().getEncoding()->name();
3973 showDialog("symbols", data);
3975 } else if (name == "prefs" && isFullScreen()) {
3976 lfunUiToggle("fullscreen");
3977 showDialog("prefs", data);
3979 showDialog(name, data);
3984 dr.setMessage(cmd.argument());
3987 case LFUN_UI_TOGGLE: {
3988 string arg = cmd.getArg(0);
3989 if (!lfunUiToggle(arg)) {
3990 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3991 dr.setMessage(bformat(msg, from_utf8(arg)));
3993 // Make sure the keyboard focus stays in the work area.
3998 case LFUN_VIEW_SPLIT: {
3999 LASSERT(doc_buffer, break);
4000 string const orientation = cmd.getArg(0);
4001 d.splitter_->setOrientation(orientation == "vertical"
4002 ? Qt::Vertical : Qt::Horizontal);
4003 TabWorkArea * twa = addTabWorkArea();
4004 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4005 setCurrentWorkArea(wa);
4008 case LFUN_TAB_GROUP_CLOSE:
4009 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4010 closeTabWorkArea(twa);
4011 d.current_work_area_ = 0;
4012 twa = d.currentTabWorkArea();
4013 // Switch to the next GuiWorkArea in the found TabWorkArea.
4015 // Make sure the work area is up to date.
4016 setCurrentWorkArea(twa->currentWorkArea());
4018 setCurrentWorkArea(0);
4023 case LFUN_VIEW_CLOSE:
4024 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4025 closeWorkArea(twa->currentWorkArea());
4026 d.current_work_area_ = 0;
4027 twa = d.currentTabWorkArea();
4028 // Switch to the next GuiWorkArea in the found TabWorkArea.
4030 // Make sure the work area is up to date.
4031 setCurrentWorkArea(twa->currentWorkArea());
4033 setCurrentWorkArea(0);
4038 case LFUN_COMPLETION_INLINE:
4039 if (d.current_work_area_)
4040 d.current_work_area_->completer().showInline();
4043 case LFUN_COMPLETION_POPUP:
4044 if (d.current_work_area_)
4045 d.current_work_area_->completer().showPopup();
4050 if (d.current_work_area_)
4051 d.current_work_area_->completer().tab();
4054 case LFUN_COMPLETION_CANCEL:
4055 if (d.current_work_area_) {
4056 if (d.current_work_area_->completer().popupVisible())
4057 d.current_work_area_->completer().hidePopup();
4059 d.current_work_area_->completer().hideInline();
4063 case LFUN_COMPLETION_ACCEPT:
4064 if (d.current_work_area_)
4065 d.current_work_area_->completer().activate();
4068 case LFUN_BUFFER_ZOOM_IN:
4069 case LFUN_BUFFER_ZOOM_OUT:
4070 case LFUN_BUFFER_ZOOM: {
4071 // use a signed temp to avoid overflow
4072 int zoom = lyxrc.currentZoom;
4073 if (cmd.argument().empty()) {
4074 if (cmd.action() == LFUN_BUFFER_ZOOM)
4076 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4081 if (cmd.action() == LFUN_BUFFER_ZOOM)
4082 zoom = convert<int>(cmd.argument());
4083 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4084 zoom += convert<int>(cmd.argument());
4086 zoom -= convert<int>(cmd.argument());
4089 if (zoom < static_cast<int>(zoom_min_))
4092 lyxrc.currentZoom = zoom;
4094 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4096 // The global QPixmapCache is used in GuiPainter to cache text
4097 // painting so we must reset it.
4098 QPixmapCache::clear();
4099 guiApp->fontLoader().update();
4100 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4104 case LFUN_VC_REGISTER:
4105 case LFUN_VC_RENAME:
4107 case LFUN_VC_CHECK_IN:
4108 case LFUN_VC_CHECK_OUT:
4109 case LFUN_VC_REPO_UPDATE:
4110 case LFUN_VC_LOCKING_TOGGLE:
4111 case LFUN_VC_REVERT:
4112 case LFUN_VC_UNDO_LAST:
4113 case LFUN_VC_COMMAND:
4114 case LFUN_VC_COMPARE:
4115 dispatchVC(cmd, dr);
4118 case LFUN_SERVER_GOTO_FILE_ROW:
4119 if(goToFileRow(to_utf8(cmd.argument())))
4120 dr.screenUpdate(Update::Force | Update::FitCursor);
4123 case LFUN_LYX_ACTIVATE:
4127 case LFUN_FORWARD_SEARCH: {
4128 // it seems safe to assume we have a document buffer, since
4129 // getStatus wants one.
4130 LATTEST(doc_buffer);
4131 Buffer const * doc_master = doc_buffer->masterBuffer();
4132 FileName const path(doc_master->temppath());
4133 string const texname = doc_master->isChild(doc_buffer)
4134 ? DocFileName(changeExtension(
4135 doc_buffer->absFileName(),
4136 "tex")).mangledFileName()
4137 : doc_buffer->latexName();
4138 string const fulltexname =
4139 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4140 string const mastername =
4141 removeExtension(doc_master->latexName());
4142 FileName const dviname(addName(path.absFileName(),
4143 addExtension(mastername, "dvi")));
4144 FileName const pdfname(addName(path.absFileName(),
4145 addExtension(mastername, "pdf")));
4146 bool const have_dvi = dviname.exists();
4147 bool const have_pdf = pdfname.exists();
4148 if (!have_dvi && !have_pdf) {
4149 dr.setMessage(_("Please, preview the document first."));
4152 string outname = dviname.onlyFileName();
4153 string command = lyxrc.forward_search_dvi;
4154 if (!have_dvi || (have_pdf &&
4155 pdfname.lastModified() > dviname.lastModified())) {
4156 outname = pdfname.onlyFileName();
4157 command = lyxrc.forward_search_pdf;
4160 DocIterator cur = bv->cursor();
4161 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4162 LYXERR(Debug::ACTION, "Forward search: row:" << row
4164 if (row == -1 || command.empty()) {
4165 dr.setMessage(_("Couldn't proceed."));
4168 string texrow = convert<string>(row);
4170 command = subst(command, "$$n", texrow);
4171 command = subst(command, "$$f", fulltexname);
4172 command = subst(command, "$$t", texname);
4173 command = subst(command, "$$o", outname);
4175 PathChanger p(path);
4177 one.startscript(Systemcall::DontWait, command);
4181 case LFUN_SPELLING_CONTINUOUSLY:
4182 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4183 dr.screenUpdate(Update::Force);
4187 // The LFUN must be for one of BufferView, Buffer or Cursor;
4189 dispatchToBufferView(cmd, dr);
4193 // Part of automatic menu appearance feature.
4194 if (isFullScreen()) {
4195 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4199 // Need to update bv because many LFUNs here might have destroyed it
4200 bv = currentBufferView();
4202 // Clear non-empty selections
4203 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4205 Cursor & cur = bv->cursor();
4206 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4207 cur.clearSelection();
4213 bool GuiView::lfunUiToggle(string const & ui_component)
4215 if (ui_component == "scrollbar") {
4216 // hide() is of no help
4217 if (d.current_work_area_->verticalScrollBarPolicy() ==
4218 Qt::ScrollBarAlwaysOff)
4220 d.current_work_area_->setVerticalScrollBarPolicy(
4221 Qt::ScrollBarAsNeeded);
4223 d.current_work_area_->setVerticalScrollBarPolicy(
4224 Qt::ScrollBarAlwaysOff);
4225 } else if (ui_component == "statusbar") {
4226 statusBar()->setVisible(!statusBar()->isVisible());
4227 } else if (ui_component == "menubar") {
4228 menuBar()->setVisible(!menuBar()->isVisible());
4230 if (ui_component == "frame") {
4232 getContentsMargins(&l, &t, &r, &b);
4233 //are the frames in default state?
4234 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4236 setContentsMargins(-2, -2, -2, -2);
4238 setContentsMargins(0, 0, 0, 0);
4241 if (ui_component == "fullscreen") {
4249 void GuiView::toggleFullScreen()
4251 if (isFullScreen()) {
4252 for (int i = 0; i != d.splitter_->count(); ++i)
4253 d.tabWorkArea(i)->setFullScreen(false);
4254 setContentsMargins(0, 0, 0, 0);
4255 setWindowState(windowState() ^ Qt::WindowFullScreen);
4258 statusBar()->show();
4261 hideDialogs("prefs", 0);
4262 for (int i = 0; i != d.splitter_->count(); ++i)
4263 d.tabWorkArea(i)->setFullScreen(true);
4264 setContentsMargins(-2, -2, -2, -2);
4266 setWindowState(windowState() ^ Qt::WindowFullScreen);
4267 if (lyxrc.full_screen_statusbar)
4268 statusBar()->hide();
4269 if (lyxrc.full_screen_menubar)
4271 if (lyxrc.full_screen_toolbars) {
4272 ToolbarMap::iterator end = d.toolbars_.end();
4273 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4278 // give dialogs like the TOC a chance to adapt
4283 Buffer const * GuiView::updateInset(Inset const * inset)
4288 Buffer const * inset_buffer = &(inset->buffer());
4290 for (int i = 0; i != d.splitter_->count(); ++i) {
4291 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4294 Buffer const * buffer = &(wa->bufferView().buffer());
4295 if (inset_buffer == buffer)
4296 wa->scheduleRedraw();
4298 return inset_buffer;
4302 void GuiView::restartCursor()
4304 /* When we move around, or type, it's nice to be able to see
4305 * the cursor immediately after the keypress.
4307 if (d.current_work_area_)
4308 d.current_work_area_->startBlinkingCursor();
4310 // Take this occasion to update the other GUI elements.
4316 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4318 if (d.current_work_area_)
4319 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4324 // This list should be kept in sync with the list of insets in
4325 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4326 // dialog should have the same name as the inset.
4327 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4328 // docs in LyXAction.cpp.
4330 char const * const dialognames[] = {
4332 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4333 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4334 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4335 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4336 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4337 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4338 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4339 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4341 char const * const * const end_dialognames =
4342 dialognames + (sizeof(dialognames) / sizeof(char *));
4346 cmpCStr(char const * name) : name_(name) {}
4347 bool operator()(char const * other) {
4348 return strcmp(other, name_) == 0;
4355 bool isValidName(string const & name)
4357 return find_if(dialognames, end_dialognames,
4358 cmpCStr(name.c_str())) != end_dialognames;
4364 void GuiView::resetDialogs()
4366 // Make sure that no LFUN uses any GuiView.
4367 guiApp->setCurrentView(0);
4371 constructToolbars();
4372 guiApp->menus().fillMenuBar(menuBar(), this, false);
4373 d.layout_->updateContents(true);
4374 // Now update controls with current buffer.
4375 guiApp->setCurrentView(this);
4381 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4383 if (!isValidName(name))
4386 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4388 if (it != d.dialogs_.end()) {
4390 it->second->hideView();
4391 return it->second.get();
4394 Dialog * dialog = build(name);
4395 d.dialogs_[name].reset(dialog);
4396 if (lyxrc.allow_geometry_session)
4397 dialog->restoreSession();
4404 void GuiView::showDialog(string const & name, string const & data,
4407 triggerShowDialog(toqstr(name), toqstr(data), inset);
4411 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4417 const string name = fromqstr(qname);
4418 const string data = fromqstr(qdata);
4422 Dialog * dialog = findOrBuild(name, false);
4424 bool const visible = dialog->isVisibleView();
4425 dialog->showData(data);
4426 if (inset && currentBufferView())
4427 currentBufferView()->editInset(name, inset);
4428 // We only set the focus to the new dialog if it was not yet
4429 // visible in order not to change the existing previous behaviour
4431 // activateWindow is needed for floating dockviews
4432 dialog->asQWidget()->raise();
4433 dialog->asQWidget()->activateWindow();
4434 dialog->asQWidget()->setFocus();
4438 catch (ExceptionMessage const & ex) {
4446 bool GuiView::isDialogVisible(string const & name) const
4448 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4449 if (it == d.dialogs_.end())
4451 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4455 void GuiView::hideDialog(string const & name, Inset * inset)
4457 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4458 if (it == d.dialogs_.end())
4462 if (!currentBufferView())
4464 if (inset != currentBufferView()->editedInset(name))
4468 Dialog * const dialog = it->second.get();
4469 if (dialog->isVisibleView())
4471 if (currentBufferView())
4472 currentBufferView()->editInset(name, 0);
4476 void GuiView::disconnectDialog(string const & name)
4478 if (!isValidName(name))
4480 if (currentBufferView())
4481 currentBufferView()->editInset(name, 0);
4485 void GuiView::hideAll() const
4487 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4488 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4490 for(; it != end; ++it)
4491 it->second->hideView();
4495 void GuiView::updateDialogs()
4497 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4498 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4500 for(; it != end; ++it) {
4501 Dialog * dialog = it->second.get();
4503 if (dialog->needBufferOpen() && !documentBufferView())
4504 hideDialog(fromqstr(dialog->name()), 0);
4505 else if (dialog->isVisibleView())
4506 dialog->checkStatus();
4513 Dialog * createDialog(GuiView & lv, string const & name);
4515 // will be replaced by a proper factory...
4516 Dialog * createGuiAbout(GuiView & lv);
4517 Dialog * createGuiBibtex(GuiView & lv);
4518 Dialog * createGuiChanges(GuiView & lv);
4519 Dialog * createGuiCharacter(GuiView & lv);
4520 Dialog * createGuiCitation(GuiView & lv);
4521 Dialog * createGuiCompare(GuiView & lv);
4522 Dialog * createGuiCompareHistory(GuiView & lv);
4523 Dialog * createGuiDelimiter(GuiView & lv);
4524 Dialog * createGuiDocument(GuiView & lv);
4525 Dialog * createGuiErrorList(GuiView & lv);
4526 Dialog * createGuiExternal(GuiView & lv);
4527 Dialog * createGuiGraphics(GuiView & lv);
4528 Dialog * createGuiInclude(GuiView & lv);
4529 Dialog * createGuiIndex(GuiView & lv);
4530 Dialog * createGuiListings(GuiView & lv);
4531 Dialog * createGuiLog(GuiView & lv);
4532 Dialog * createGuiMathMatrix(GuiView & lv);
4533 Dialog * createGuiNote(GuiView & lv);
4534 Dialog * createGuiParagraph(GuiView & lv);
4535 Dialog * createGuiPhantom(GuiView & lv);
4536 Dialog * createGuiPreferences(GuiView & lv);
4537 Dialog * createGuiPrint(GuiView & lv);
4538 Dialog * createGuiPrintindex(GuiView & lv);
4539 Dialog * createGuiRef(GuiView & lv);
4540 Dialog * createGuiSearch(GuiView & lv);
4541 Dialog * createGuiSearchAdv(GuiView & lv);
4542 Dialog * createGuiSendTo(GuiView & lv);
4543 Dialog * createGuiShowFile(GuiView & lv);
4544 Dialog * createGuiSpellchecker(GuiView & lv);
4545 Dialog * createGuiSymbols(GuiView & lv);
4546 Dialog * createGuiTabularCreate(GuiView & lv);
4547 Dialog * createGuiTexInfo(GuiView & lv);
4548 Dialog * createGuiToc(GuiView & lv);
4549 Dialog * createGuiThesaurus(GuiView & lv);
4550 Dialog * createGuiViewSource(GuiView & lv);
4551 Dialog * createGuiWrap(GuiView & lv);
4552 Dialog * createGuiProgressView(GuiView & lv);
4556 Dialog * GuiView::build(string const & name)
4558 LASSERT(isValidName(name), return 0);
4560 Dialog * dialog = createDialog(*this, name);
4564 if (name == "aboutlyx")
4565 return createGuiAbout(*this);
4566 if (name == "bibtex")
4567 return createGuiBibtex(*this);
4568 if (name == "changes")
4569 return createGuiChanges(*this);
4570 if (name == "character")
4571 return createGuiCharacter(*this);
4572 if (name == "citation")
4573 return createGuiCitation(*this);
4574 if (name == "compare")
4575 return createGuiCompare(*this);
4576 if (name == "comparehistory")
4577 return createGuiCompareHistory(*this);
4578 if (name == "document")
4579 return createGuiDocument(*this);
4580 if (name == "errorlist")
4581 return createGuiErrorList(*this);
4582 if (name == "external")
4583 return createGuiExternal(*this);
4585 return createGuiShowFile(*this);
4586 if (name == "findreplace")
4587 return createGuiSearch(*this);
4588 if (name == "findreplaceadv")
4589 return createGuiSearchAdv(*this);
4590 if (name == "graphics")
4591 return createGuiGraphics(*this);
4592 if (name == "include")
4593 return createGuiInclude(*this);
4594 if (name == "index")
4595 return createGuiIndex(*this);
4596 if (name == "index_print")
4597 return createGuiPrintindex(*this);
4598 if (name == "listings")
4599 return createGuiListings(*this);
4601 return createGuiLog(*this);
4602 if (name == "mathdelimiter")
4603 return createGuiDelimiter(*this);
4604 if (name == "mathmatrix")
4605 return createGuiMathMatrix(*this);
4607 return createGuiNote(*this);
4608 if (name == "paragraph")
4609 return createGuiParagraph(*this);
4610 if (name == "phantom")
4611 return createGuiPhantom(*this);
4612 if (name == "prefs")
4613 return createGuiPreferences(*this);
4615 return createGuiRef(*this);
4616 if (name == "sendto")
4617 return createGuiSendTo(*this);
4618 if (name == "spellchecker")
4619 return createGuiSpellchecker(*this);
4620 if (name == "symbols")
4621 return createGuiSymbols(*this);
4622 if (name == "tabularcreate")
4623 return createGuiTabularCreate(*this);
4624 if (name == "texinfo")
4625 return createGuiTexInfo(*this);
4626 if (name == "thesaurus")
4627 return createGuiThesaurus(*this);
4629 return createGuiToc(*this);
4630 if (name == "view-source")
4631 return createGuiViewSource(*this);
4633 return createGuiWrap(*this);
4634 if (name == "progress")
4635 return createGuiProgressView(*this);
4641 } // namespace frontend
4644 #include "moc_GuiView.cpp"