3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
386 QSize iconSize(docstring const & icon_size)
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
395 else if (icon_size == "huge")
397 else if (icon_size == "giant")
398 size = giantIconSize;
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
402 if (size < smallIconSize)
403 size = smallIconSize;
405 return QSize(size, size);
408 QSize iconSize(QString const & icon_size)
410 return iconSize(qstring_to_ucs4(icon_size));
413 string & iconSize(QSize const & qsize)
415 LATTEST(qsize.width() == qsize.height());
417 static string icon_size;
419 unsigned int size = qsize.width();
421 if (size < smallIconSize)
422 size = smallIconSize;
424 if (size == smallIconSize)
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
430 else if (size == hugeIconSize)
432 else if (size == giantIconSize)
435 icon_size = convert<string>(size);
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
452 * \warning Don't Delete! The layout box is actually owned by
453 * whichever toolbar contains it. All the GuiView class needs is a
454 * means of accessing it.
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
462 map<string, DialogPtr> dialogs_;
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
470 QTimer statusbar_timer_;
471 /// auto-saving of buffers
472 Timeout autosave_timeout_;
473 /// flag against a race condition due to multiclicks, see bug #1119
477 TocModels toc_models_;
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
483 string last_export_format;
484 string processing_format;
486 static QSet<Buffer const *> busyBuffers;
487 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
495 // TODO syncFunc/previewFunc: use bind
496 bool asyncBufferProcessing(string const & argument,
497 Buffer const * used_buffer,
498 docstring const & msg,
499 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
503 QVector<GuiWorkArea*> guiWorkAreas();
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
509 GuiView::GuiView(int id)
510 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511 command_execute_(false), minibuffer_focus_(false)
513 connect(this, SIGNAL(bufferViewChanged()),
514 this, SLOT(onBufferViewChanged()));
516 // GuiToolbars *must* be initialised before the menu bar.
517 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 // The connection is closed when this is destroyed.
533 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
534 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
535 d.autosave_timeout_.start();
537 connect(&d.statusbar_timer_, SIGNAL(timeout()),
538 this, SLOT(clearMessage()));
540 // We don't want to keep the window in memory if it is closed.
541 setAttribute(Qt::WA_DeleteOnClose, true);
543 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
544 // QIcon::fromTheme was introduced in Qt 4.6
545 #if (QT_VERSION >= 0x040600)
546 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
547 // since the icon is provided in the application bundle. We use a themed
548 // version when available and use the bundled one as fallback.
549 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
551 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
557 // use tabbed dock area for multiple docks
558 // (such as "source" and "messages")
559 setDockOptions(QMainWindow::ForceTabbedDocks);
562 setAcceptDrops(true);
564 // add busy indicator to statusbar
565 QLabel * busylabel = new QLabel(statusBar());
566 statusBar()->addPermanentWidget(busylabel);
567 search_mode mode = theGuiApp()->imageSearchMode();
568 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
569 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
570 busylabel->setMovie(busyanim);
574 connect(&d.processing_thread_watcher_, SIGNAL(started()),
575 busylabel, SLOT(show()));
576 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
577 busylabel, SLOT(hide()));
579 QFontMetrics const fm(statusBar()->fontMetrics());
580 int const roheight = max(int(d.normalIconSize), fm.height());
581 QSize const rosize(roheight, roheight);
582 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
583 read_only_ = new QLabel(statusBar());
584 read_only_->setPixmap(readonly);
585 read_only_->setScaledContents(true);
586 read_only_->setAlignment(Qt::AlignCenter);
588 statusBar()->addPermanentWidget(read_only_);
590 version_control_ = new QLabel(statusBar());
591 version_control_->setAlignment(Qt::AlignCenter);
592 version_control_->setFrameStyle(QFrame::StyledPanel);
593 version_control_->hide();
594 statusBar()->addPermanentWidget(version_control_);
596 statusBar()->setSizeGripEnabled(true);
599 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
600 SLOT(autoSaveThreadFinished()));
602 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
603 SLOT(processingThreadStarted()));
604 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
605 SLOT(processingThreadFinished()));
607 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
608 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
610 // set custom application bars context menu, e.g. tool bar and menu bar
611 setContextMenuPolicy(Qt::CustomContextMenu);
612 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
613 SLOT(toolBarPopup(const QPoint &)));
615 // Forbid too small unresizable window because it can happen
616 // with some window manager under X11.
617 setMinimumSize(300, 200);
619 if (lyxrc.allow_geometry_session) {
620 // Now take care of session management.
625 // no session handling, default to a sane size.
626 setGeometry(50, 50, 690, 510);
629 // clear session data if any.
631 settings.remove("views");
641 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
643 QVector<GuiWorkArea*> areas;
644 for (int i = 0; i < tabWorkAreaCount(); i++) {
645 TabWorkArea* ta = tabWorkArea(i);
646 for (int u = 0; u < ta->count(); u++) {
647 areas << ta->workArea(u);
653 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
654 string const & format)
656 docstring const fmt = theFormats().prettyName(format);
659 case Buffer::ExportSuccess:
660 msg = bformat(_("Successful export to format: %1$s"), fmt);
662 case Buffer::ExportCancel:
663 msg = _("Document export cancelled.");
665 case Buffer::ExportError:
666 case Buffer::ExportNoPathToFormat:
667 case Buffer::ExportTexPathHasSpaces:
668 case Buffer::ExportConverterError:
669 msg = bformat(_("Error while exporting format: %1$s"), fmt);
671 case Buffer::PreviewSuccess:
672 msg = bformat(_("Successful preview of format: %1$s"), fmt);
674 case Buffer::PreviewError:
675 msg = bformat(_("Error while previewing format: %1$s"), fmt);
682 void GuiView::processingThreadStarted()
687 void GuiView::processingThreadFinished()
689 QFutureWatcher<Buffer::ExportStatus> const * watcher =
690 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
692 Buffer::ExportStatus const status = watcher->result();
693 handleExportStatus(this, status, d.processing_format);
696 BufferView const * const bv = currentBufferView();
697 if (bv && !bv->buffer().errorList("Export").empty()) {
701 errors(d.last_export_format);
705 void GuiView::autoSaveThreadFinished()
707 QFutureWatcher<docstring> const * watcher =
708 static_cast<QFutureWatcher<docstring> const *>(sender());
709 message(watcher->result());
714 void GuiView::saveLayout() const
717 settings.setValue("zoom", lyxrc.currentZoom);
718 settings.beginGroup("views");
719 settings.beginGroup(QString::number(id_));
720 #if defined(Q_WS_X11) || defined(QPA_XCB)
721 settings.setValue("pos", pos());
722 settings.setValue("size", size());
724 settings.setValue("geometry", saveGeometry());
726 settings.setValue("layout", saveState(0));
727 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
731 void GuiView::saveUISettings() const
733 // Save the toolbar private states
734 ToolbarMap::iterator end = d.toolbars_.end();
735 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
736 it->second->saveSession();
737 // Now take care of all other dialogs
738 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
739 for (; it!= d.dialogs_.end(); ++it)
740 it->second->saveSession();
744 bool GuiView::restoreLayout()
747 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
748 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
749 settings.beginGroup("views");
750 settings.beginGroup(QString::number(id_));
751 QString const icon_key = "icon_size";
752 if (!settings.contains(icon_key))
755 //code below is skipped when when ~/.config/LyX is (re)created
756 setIconSize(d.iconSize(settings.value(icon_key).toString()));
758 #if defined(Q_WS_X11) || defined(QPA_XCB)
759 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
760 QSize size = settings.value("size", QSize(690, 510)).toSize();
764 // Work-around for bug #6034: the window ends up in an undetermined
765 // state when trying to restore a maximized window when it is
766 // already maximized.
767 if (!(windowState() & Qt::WindowMaximized))
768 if (!restoreGeometry(settings.value("geometry").toByteArray()))
769 setGeometry(50, 50, 690, 510);
771 // Make sure layout is correctly oriented.
772 setLayoutDirection(qApp->layoutDirection());
774 // Allow the toc and view-source dock widget to be restored if needed.
776 if ((dialog = findOrBuild("toc", true)))
777 // see bug 5082. At least setup title and enabled state.
778 // Visibility will be adjusted by restoreState below.
779 dialog->prepareView();
780 if ((dialog = findOrBuild("view-source", true)))
781 dialog->prepareView();
782 if ((dialog = findOrBuild("progress", true)))
783 dialog->prepareView();
785 if (!restoreState(settings.value("layout").toByteArray(), 0))
788 // init the toolbars that have not been restored
789 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
790 Toolbars::Infos::iterator end = guiApp->toolbars().end();
791 for (; cit != end; ++cit) {
792 GuiToolbar * tb = toolbar(cit->name);
793 if (tb && !tb->isRestored())
794 initToolbar(cit->name);
797 // update lock (all) toolbars positions
798 updateLockToolbars();
805 GuiToolbar * GuiView::toolbar(string const & name)
807 ToolbarMap::iterator it = d.toolbars_.find(name);
808 if (it != d.toolbars_.end())
811 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
816 void GuiView::updateLockToolbars()
818 toolbarsMovable_ = false;
819 for (ToolbarInfo const & info : guiApp->toolbars()) {
820 GuiToolbar * tb = toolbar(info.name);
821 if (tb && tb->isMovable())
822 toolbarsMovable_ = true;
827 void GuiView::constructToolbars()
829 ToolbarMap::iterator it = d.toolbars_.begin();
830 for (; it != d.toolbars_.end(); ++it)
834 // I don't like doing this here, but the standard toolbar
835 // destroys this object when it's destroyed itself (vfr)
836 d.layout_ = new LayoutBox(*this);
837 d.stack_widget_->addWidget(d.layout_);
838 d.layout_->move(0,0);
840 // extracts the toolbars from the backend
841 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
842 Toolbars::Infos::iterator end = guiApp->toolbars().end();
843 for (; cit != end; ++cit)
844 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
848 void GuiView::initToolbars()
850 // extracts the toolbars from the backend
851 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
852 Toolbars::Infos::iterator end = guiApp->toolbars().end();
853 for (; cit != end; ++cit)
854 initToolbar(cit->name);
858 void GuiView::initToolbar(string const & name)
860 GuiToolbar * tb = toolbar(name);
863 int const visibility = guiApp->toolbars().defaultVisibility(name);
864 bool newline = !(visibility & Toolbars::SAMEROW);
865 tb->setVisible(false);
866 tb->setVisibility(visibility);
868 if (visibility & Toolbars::TOP) {
870 addToolBarBreak(Qt::TopToolBarArea);
871 addToolBar(Qt::TopToolBarArea, tb);
874 if (visibility & Toolbars::BOTTOM) {
876 addToolBarBreak(Qt::BottomToolBarArea);
877 addToolBar(Qt::BottomToolBarArea, tb);
880 if (visibility & Toolbars::LEFT) {
882 addToolBarBreak(Qt::LeftToolBarArea);
883 addToolBar(Qt::LeftToolBarArea, tb);
886 if (visibility & Toolbars::RIGHT) {
888 addToolBarBreak(Qt::RightToolBarArea);
889 addToolBar(Qt::RightToolBarArea, tb);
892 if (visibility & Toolbars::ON)
893 tb->setVisible(true);
895 tb->setMovable(true);
899 TocModels & GuiView::tocModels()
901 return d.toc_models_;
905 void GuiView::setFocus()
907 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
908 QMainWindow::setFocus();
912 bool GuiView::hasFocus() const
914 if (currentWorkArea())
915 return currentWorkArea()->hasFocus();
916 if (currentMainWorkArea())
917 return currentMainWorkArea()->hasFocus();
918 return d.bg_widget_->hasFocus();
922 void GuiView::focusInEvent(QFocusEvent * e)
924 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
925 QMainWindow::focusInEvent(e);
926 // Make sure guiApp points to the correct view.
927 guiApp->setCurrentView(this);
928 if (currentWorkArea())
929 currentWorkArea()->setFocus();
930 else if (currentMainWorkArea())
931 currentMainWorkArea()->setFocus();
933 d.bg_widget_->setFocus();
937 void GuiView::showEvent(QShowEvent * e)
939 LYXERR(Debug::GUI, "Passed Geometry "
940 << size().height() << "x" << size().width()
941 << "+" << pos().x() << "+" << pos().y());
943 if (d.splitter_->count() == 0)
944 // No work area, switch to the background widget.
948 QMainWindow::showEvent(e);
952 bool GuiView::closeScheduled()
959 bool GuiView::prepareAllBuffersForLogout()
961 Buffer * first = theBufferList().first();
965 // First, iterate over all buffers and ask the users if unsaved
966 // changes should be saved.
967 // We cannot use a for loop as the buffer list cycles.
970 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
972 b = theBufferList().next(b);
973 } while (b != first);
975 // Next, save session state
976 // When a view/window was closed before without quitting LyX, there
977 // are already entries in the lastOpened list.
978 theSession().lastOpened().clear();
985 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
986 ** is responsibility of the container (e.g., dialog)
988 void GuiView::closeEvent(QCloseEvent * close_event)
990 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
992 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
993 Alert::warning(_("Exit LyX"),
994 _("LyX could not be closed because documents are being processed by LyX."));
995 close_event->setAccepted(false);
999 // If the user pressed the x (so we didn't call closeView
1000 // programmatically), we want to clear all existing entries.
1002 theSession().lastOpened().clear();
1007 // it can happen that this event arrives without selecting the view,
1008 // e.g. when clicking the close button on a background window.
1010 if (!closeWorkAreaAll()) {
1012 close_event->ignore();
1016 // Make sure that nothing will use this to be closed View.
1017 guiApp->unregisterView(this);
1019 if (isFullScreen()) {
1020 // Switch off fullscreen before closing.
1025 // Make sure the timer time out will not trigger a statusbar update.
1026 d.statusbar_timer_.stop();
1028 // Saving fullscreen requires additional tweaks in the toolbar code.
1029 // It wouldn't also work under linux natively.
1030 if (lyxrc.allow_geometry_session) {
1035 close_event->accept();
1039 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1041 if (event->mimeData()->hasUrls())
1043 /// \todo Ask lyx-devel is this is enough:
1044 /// if (event->mimeData()->hasFormat("text/plain"))
1045 /// event->acceptProposedAction();
1049 void GuiView::dropEvent(QDropEvent * event)
1051 QList<QUrl> files = event->mimeData()->urls();
1052 if (files.isEmpty())
1055 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1056 for (int i = 0; i != files.size(); ++i) {
1057 string const file = os::internal_path(fromqstr(
1058 files.at(i).toLocalFile()));
1062 string const ext = support::getExtension(file);
1063 vector<const Format *> found_formats;
1065 // Find all formats that have the correct extension.
1066 vector<const Format *> const & import_formats
1067 = theConverters().importableFormats();
1068 vector<const Format *>::const_iterator it = import_formats.begin();
1069 for (; it != import_formats.end(); ++it)
1070 if ((*it)->hasExtension(ext))
1071 found_formats.push_back(*it);
1074 if (found_formats.size() >= 1) {
1075 if (found_formats.size() > 1) {
1076 //FIXME: show a dialog to choose the correct importable format
1077 LYXERR(Debug::FILES,
1078 "Multiple importable formats found, selecting first");
1080 string const arg = found_formats[0]->name() + " " + file;
1081 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1084 //FIXME: do we have to explicitly check whether it's a lyx file?
1085 LYXERR(Debug::FILES,
1086 "No formats found, trying to open it as a lyx file");
1087 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1089 // add the functions to the queue
1090 guiApp->addToFuncRequestQueue(cmd);
1093 // now process the collected functions. We perform the events
1094 // asynchronously. This prevents potential problems in case the
1095 // BufferView is closed within an event.
1096 guiApp->processFuncRequestQueueAsync();
1100 void GuiView::message(docstring const & str)
1102 if (ForkedProcess::iAmAChild())
1105 // call is moved to GUI-thread by GuiProgress
1106 d.progress_->appendMessage(toqstr(str));
1110 void GuiView::clearMessageText()
1112 message(docstring());
1116 void GuiView::updateStatusBarMessage(QString const & str)
1118 statusBar()->showMessage(str);
1119 d.statusbar_timer_.stop();
1120 d.statusbar_timer_.start(3000);
1124 void GuiView::clearMessage()
1126 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1127 // the hasFocus function mostly returns false, even if the focus is on
1128 // a workarea in this view.
1132 d.statusbar_timer_.stop();
1136 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1138 if (wa != d.current_work_area_
1139 || wa->bufferView().buffer().isInternal())
1141 Buffer const & buf = wa->bufferView().buffer();
1142 // Set the windows title
1143 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1144 if (buf.notifiesExternalModification()) {
1145 title = bformat(_("%1$s (modified externally)"), title);
1146 // If the external modification status has changed, then maybe the status of
1147 // buffer-save has changed too.
1151 title += from_ascii(" - LyX");
1153 setWindowTitle(toqstr(title));
1154 // Sets the path for the window: this is used by OSX to
1155 // allow a context click on the title bar showing a menu
1156 // with the path up to the file
1157 setWindowFilePath(toqstr(buf.absFileName()));
1158 // Tell Qt whether the current document is changed
1159 setWindowModified(!buf.isClean());
1161 if (buf.hasReadonlyFlag())
1166 if (buf.lyxvc().inUse()) {
1167 version_control_->show();
1168 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1170 version_control_->hide();
1174 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1176 if (d.current_work_area_)
1177 // disconnect the current work area from all slots
1178 QObject::disconnect(d.current_work_area_, 0, this, 0);
1180 disconnectBufferView();
1181 connectBufferView(wa->bufferView());
1182 connectBuffer(wa->bufferView().buffer());
1183 d.current_work_area_ = wa;
1184 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1185 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1186 QObject::connect(wa, SIGNAL(busy(bool)),
1187 this, SLOT(setBusy(bool)));
1188 // connection of a signal to a signal
1189 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1190 this, SIGNAL(bufferViewChanged()));
1191 Q_EMIT updateWindowTitle(wa);
1192 Q_EMIT bufferViewChanged();
1196 void GuiView::onBufferViewChanged()
1199 // Buffer-dependent dialogs must be updated. This is done here because
1200 // some dialogs require buffer()->text.
1205 void GuiView::on_lastWorkAreaRemoved()
1208 // We already are in a close event. Nothing more to do.
1211 if (d.splitter_->count() > 1)
1212 // We have a splitter so don't close anything.
1215 // Reset and updates the dialogs.
1216 Q_EMIT bufferViewChanged();
1221 if (lyxrc.open_buffers_in_tabs)
1222 // Nothing more to do, the window should stay open.
1225 if (guiApp->viewIds().size() > 1) {
1231 // On Mac we also close the last window because the application stay
1232 // resident in memory. On other platforms we don't close the last
1233 // window because this would quit the application.
1239 void GuiView::updateStatusBar()
1241 // let the user see the explicit message
1242 if (d.statusbar_timer_.isActive())
1249 void GuiView::showMessage()
1253 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1254 if (msg.isEmpty()) {
1255 BufferView const * bv = currentBufferView();
1257 msg = toqstr(bv->cursor().currentState());
1259 msg = qt_("Welcome to LyX!");
1261 statusBar()->showMessage(msg);
1265 bool GuiView::event(QEvent * e)
1269 // Useful debug code:
1270 //case QEvent::ActivationChange:
1271 //case QEvent::WindowDeactivate:
1272 //case QEvent::Paint:
1273 //case QEvent::Enter:
1274 //case QEvent::Leave:
1275 //case QEvent::HoverEnter:
1276 //case QEvent::HoverLeave:
1277 //case QEvent::HoverMove:
1278 //case QEvent::StatusTip:
1279 //case QEvent::DragEnter:
1280 //case QEvent::DragLeave:
1281 //case QEvent::Drop:
1284 case QEvent::WindowActivate: {
1285 GuiView * old_view = guiApp->currentView();
1286 if (this == old_view) {
1288 return QMainWindow::event(e);
1290 if (old_view && old_view->currentBufferView()) {
1291 // save current selection to the selection buffer to allow
1292 // middle-button paste in this window.
1293 cap::saveSelection(old_view->currentBufferView()->cursor());
1295 guiApp->setCurrentView(this);
1296 if (d.current_work_area_)
1297 on_currentWorkAreaChanged(d.current_work_area_);
1301 return QMainWindow::event(e);
1304 case QEvent::ShortcutOverride: {
1306 if (isFullScreen() && menuBar()->isHidden()) {
1307 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1308 // FIXME: we should also try to detect special LyX shortcut such as
1309 // Alt-P and Alt-M. Right now there is a hack in
1310 // GuiWorkArea::processKeySym() that hides again the menubar for
1312 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1314 return QMainWindow::event(e);
1317 return QMainWindow::event(e);
1321 return QMainWindow::event(e);
1325 void GuiView::resetWindowTitle()
1327 setWindowTitle(qt_("LyX"));
1330 bool GuiView::focusNextPrevChild(bool /*next*/)
1337 bool GuiView::busy() const
1343 void GuiView::setBusy(bool busy)
1345 bool const busy_before = busy_ > 0;
1346 busy ? ++busy_ : --busy_;
1347 if ((busy_ > 0) == busy_before)
1348 // busy state didn't change
1352 QApplication::setOverrideCursor(Qt::WaitCursor);
1355 QApplication::restoreOverrideCursor();
1360 void GuiView::resetCommandExecute()
1362 command_execute_ = false;
1367 double GuiView::pixelRatio() const
1369 #if QT_VERSION >= 0x050000
1370 return qt_scale_factor * devicePixelRatio();
1377 GuiWorkArea * GuiView::workArea(int index)
1379 if (TabWorkArea * twa = d.currentTabWorkArea())
1380 if (index < twa->count())
1381 return twa->workArea(index);
1386 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1388 if (currentWorkArea()
1389 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1390 return currentWorkArea();
1391 if (TabWorkArea * twa = d.currentTabWorkArea())
1392 return twa->workArea(buffer);
1397 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1399 // Automatically create a TabWorkArea if there are none yet.
1400 TabWorkArea * tab_widget = d.splitter_->count()
1401 ? d.currentTabWorkArea() : addTabWorkArea();
1402 return tab_widget->addWorkArea(buffer, *this);
1406 TabWorkArea * GuiView::addTabWorkArea()
1408 TabWorkArea * twa = new TabWorkArea;
1409 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1410 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1411 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1412 this, SLOT(on_lastWorkAreaRemoved()));
1414 d.splitter_->addWidget(twa);
1415 d.stack_widget_->setCurrentWidget(d.splitter_);
1420 GuiWorkArea const * GuiView::currentWorkArea() const
1422 return d.current_work_area_;
1426 GuiWorkArea * GuiView::currentWorkArea()
1428 return d.current_work_area_;
1432 GuiWorkArea const * GuiView::currentMainWorkArea() const
1434 if (!d.currentTabWorkArea())
1436 return d.currentTabWorkArea()->currentWorkArea();
1440 GuiWorkArea * GuiView::currentMainWorkArea()
1442 if (!d.currentTabWorkArea())
1444 return d.currentTabWorkArea()->currentWorkArea();
1448 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1450 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1452 d.current_work_area_ = 0;
1454 Q_EMIT bufferViewChanged();
1458 // FIXME: I've no clue why this is here and why it accesses
1459 // theGuiApp()->currentView, which might be 0 (bug 6464).
1460 // See also 27525 (vfr).
1461 if (theGuiApp()->currentView() == this
1462 && theGuiApp()->currentView()->currentWorkArea() == wa)
1465 if (currentBufferView())
1466 cap::saveSelection(currentBufferView()->cursor());
1468 theGuiApp()->setCurrentView(this);
1469 d.current_work_area_ = wa;
1471 // We need to reset this now, because it will need to be
1472 // right if the tabWorkArea gets reset in the for loop. We
1473 // will change it back if we aren't in that case.
1474 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1475 d.current_main_work_area_ = wa;
1477 for (int i = 0; i != d.splitter_->count(); ++i) {
1478 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1479 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1480 << ", Current main wa: " << currentMainWorkArea());
1485 d.current_main_work_area_ = old_cmwa;
1487 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1488 on_currentWorkAreaChanged(wa);
1489 BufferView & bv = wa->bufferView();
1490 bv.cursor().fixIfBroken();
1492 wa->setUpdatesEnabled(true);
1493 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1497 void GuiView::removeWorkArea(GuiWorkArea * wa)
1499 LASSERT(wa, return);
1500 if (wa == d.current_work_area_) {
1502 disconnectBufferView();
1503 d.current_work_area_ = 0;
1504 d.current_main_work_area_ = 0;
1507 bool found_twa = false;
1508 for (int i = 0; i != d.splitter_->count(); ++i) {
1509 TabWorkArea * twa = d.tabWorkArea(i);
1510 if (twa->removeWorkArea(wa)) {
1511 // Found in this tab group, and deleted the GuiWorkArea.
1513 if (twa->count() != 0) {
1514 if (d.current_work_area_ == 0)
1515 // This means that we are closing the current GuiWorkArea, so
1516 // switch to the next GuiWorkArea in the found TabWorkArea.
1517 setCurrentWorkArea(twa->currentWorkArea());
1519 // No more WorkAreas in this tab group, so delete it.
1526 // It is not a tabbed work area (i.e., the search work area), so it
1527 // should be deleted by other means.
1528 LASSERT(found_twa, return);
1530 if (d.current_work_area_ == 0) {
1531 if (d.splitter_->count() != 0) {
1532 TabWorkArea * twa = d.currentTabWorkArea();
1533 setCurrentWorkArea(twa->currentWorkArea());
1535 // No more work areas, switch to the background widget.
1536 setCurrentWorkArea(0);
1542 LayoutBox * GuiView::getLayoutDialog() const
1548 void GuiView::updateLayoutList()
1551 d.layout_->updateContents(false);
1555 void GuiView::updateToolbars()
1557 ToolbarMap::iterator end = d.toolbars_.end();
1558 if (d.current_work_area_) {
1560 if (d.current_work_area_->bufferView().cursor().inMathed()
1561 && !d.current_work_area_->bufferView().cursor().inRegexped())
1562 context |= Toolbars::MATH;
1563 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1564 context |= Toolbars::TABLE;
1565 if (currentBufferView()->buffer().areChangesPresent()
1566 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1567 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1568 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1569 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1570 context |= Toolbars::REVIEW;
1571 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1572 context |= Toolbars::MATHMACROTEMPLATE;
1573 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1574 context |= Toolbars::IPA;
1575 if (command_execute_)
1576 context |= Toolbars::MINIBUFFER;
1577 if (minibuffer_focus_) {
1578 context |= Toolbars::MINIBUFFER_FOCUS;
1579 minibuffer_focus_ = false;
1582 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1583 it->second->update(context);
1585 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1586 it->second->update();
1590 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1592 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1593 LASSERT(newBuffer, return);
1595 GuiWorkArea * wa = workArea(*newBuffer);
1598 newBuffer->masterBuffer()->updateBuffer();
1600 wa = addWorkArea(*newBuffer);
1601 // scroll to the position when the BufferView was last closed
1602 if (lyxrc.use_lastfilepos) {
1603 LastFilePosSection::FilePos filepos =
1604 theSession().lastFilePos().load(newBuffer->fileName());
1605 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1608 //Disconnect the old buffer...there's no new one.
1611 connectBuffer(*newBuffer);
1612 connectBufferView(wa->bufferView());
1614 setCurrentWorkArea(wa);
1618 void GuiView::connectBuffer(Buffer & buf)
1620 buf.setGuiDelegate(this);
1624 void GuiView::disconnectBuffer()
1626 if (d.current_work_area_)
1627 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1631 void GuiView::connectBufferView(BufferView & bv)
1633 bv.setGuiDelegate(this);
1637 void GuiView::disconnectBufferView()
1639 if (d.current_work_area_)
1640 d.current_work_area_->bufferView().setGuiDelegate(0);
1644 void GuiView::errors(string const & error_type, bool from_master)
1646 BufferView const * const bv = currentBufferView();
1650 #if EXPORT_in_THREAD
1651 // We are called with from_master == false by default, so we
1652 // have to figure out whether that is the case or not.
1653 ErrorList & el = bv->buffer().errorList(error_type);
1655 el = bv->buffer().masterBuffer()->errorList(error_type);
1659 ErrorList const & el = from_master ?
1660 bv->buffer().masterBuffer()->errorList(error_type) :
1661 bv->buffer().errorList(error_type);
1667 string data = error_type;
1669 data = "from_master|" + error_type;
1670 showDialog("errorlist", data);
1674 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1676 d.toc_models_.updateItem(toqstr(type), dit);
1680 void GuiView::structureChanged()
1682 // This is called from the Buffer, which has no way to ensure that cursors
1683 // in BufferView remain valid.
1684 if (documentBufferView())
1685 documentBufferView()->cursor().sanitize();
1686 // FIXME: This is slightly expensive, though less than the tocBackend update
1687 // (#9880). This also resets the view in the Toc Widget (#6675).
1688 d.toc_models_.reset(documentBufferView());
1689 // Navigator needs more than a simple update in this case. It needs to be
1691 updateDialog("toc", "");
1695 void GuiView::updateDialog(string const & name, string const & data)
1697 if (!isDialogVisible(name))
1700 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1701 if (it == d.dialogs_.end())
1704 Dialog * const dialog = it->second.get();
1705 if (dialog->isVisibleView())
1706 dialog->initialiseParams(data);
1710 BufferView * GuiView::documentBufferView()
1712 return currentMainWorkArea()
1713 ? ¤tMainWorkArea()->bufferView()
1718 BufferView const * GuiView::documentBufferView() const
1720 return currentMainWorkArea()
1721 ? ¤tMainWorkArea()->bufferView()
1726 BufferView * GuiView::currentBufferView()
1728 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1732 BufferView const * GuiView::currentBufferView() const
1734 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1738 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1739 Buffer const * orig, Buffer * clone)
1741 bool const success = clone->autoSave();
1743 busyBuffers.remove(orig);
1745 ? _("Automatic save done.")
1746 : _("Automatic save failed!");
1750 void GuiView::autoSave()
1752 LYXERR(Debug::INFO, "Running autoSave()");
1754 Buffer * buffer = documentBufferView()
1755 ? &documentBufferView()->buffer() : 0;
1757 resetAutosaveTimers();
1761 GuiViewPrivate::busyBuffers.insert(buffer);
1762 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1763 buffer, buffer->cloneBufferOnly());
1764 d.autosave_watcher_.setFuture(f);
1765 resetAutosaveTimers();
1769 void GuiView::resetAutosaveTimers()
1772 d.autosave_timeout_.restart();
1776 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1779 Buffer * buf = currentBufferView()
1780 ? ¤tBufferView()->buffer() : 0;
1781 Buffer * doc_buffer = documentBufferView()
1782 ? &(documentBufferView()->buffer()) : 0;
1785 /* In LyX/Mac, when a dialog is open, the menus of the
1786 application can still be accessed without giving focus to
1787 the main window. In this case, we want to disable the menu
1788 entries that are buffer-related.
1789 This code must not be used on Linux and Windows, since it
1790 would disable buffer-related entries when hovering over the
1791 menu (see bug #9574).
1793 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1799 // Check whether we need a buffer
1800 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1801 // no, exit directly
1802 flag.message(from_utf8(N_("Command not allowed with"
1803 "out any document open")));
1804 flag.setEnabled(false);
1808 if (cmd.origin() == FuncRequest::TOC) {
1809 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1810 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1811 flag.setEnabled(false);
1815 switch(cmd.action()) {
1816 case LFUN_BUFFER_IMPORT:
1819 case LFUN_MASTER_BUFFER_UPDATE:
1820 case LFUN_MASTER_BUFFER_VIEW:
1822 && (doc_buffer->parent() != 0
1823 || doc_buffer->hasChildren())
1824 && !d.processing_thread_watcher_.isRunning();
1827 case LFUN_BUFFER_UPDATE:
1828 case LFUN_BUFFER_VIEW: {
1829 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1833 string format = to_utf8(cmd.argument());
1834 if (cmd.argument().empty())
1835 format = doc_buffer->params().getDefaultOutputFormat();
1836 enable = doc_buffer->params().isExportable(format, true);
1840 case LFUN_BUFFER_RELOAD:
1841 enable = doc_buffer && !doc_buffer->isUnnamed()
1842 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1845 case LFUN_BUFFER_CHILD_OPEN:
1846 enable = doc_buffer != 0;
1849 case LFUN_BUFFER_WRITE:
1850 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1853 //FIXME: This LFUN should be moved to GuiApplication.
1854 case LFUN_BUFFER_WRITE_ALL: {
1855 // We enable the command only if there are some modified buffers
1856 Buffer * first = theBufferList().first();
1861 // We cannot use a for loop as the buffer list is a cycle.
1863 if (!b->isClean()) {
1867 b = theBufferList().next(b);
1868 } while (b != first);
1872 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1873 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1876 case LFUN_BUFFER_WRITE_AS:
1877 case LFUN_BUFFER_EXPORT_AS:
1878 enable = doc_buffer != 0;
1881 case LFUN_BUFFER_CLOSE:
1882 case LFUN_VIEW_CLOSE:
1883 enable = doc_buffer != 0;
1886 case LFUN_BUFFER_CLOSE_ALL:
1887 enable = theBufferList().last() != theBufferList().first();
1890 case LFUN_VIEW_SPLIT:
1891 if (cmd.getArg(0) == "vertical")
1892 enable = doc_buffer && (d.splitter_->count() == 1 ||
1893 d.splitter_->orientation() == Qt::Vertical);
1895 enable = doc_buffer && (d.splitter_->count() == 1 ||
1896 d.splitter_->orientation() == Qt::Horizontal);
1899 case LFUN_TAB_GROUP_CLOSE:
1900 enable = d.tabWorkAreaCount() > 1;
1903 case LFUN_TOOLBAR_TOGGLE: {
1904 string const name = cmd.getArg(0);
1905 if (GuiToolbar * t = toolbar(name))
1906 flag.setOnOff(t->isVisible());
1909 docstring const msg =
1910 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1916 case LFUN_TOOLBAR_MOVABLE: {
1917 string const name = cmd.getArg(0);
1918 // use negation since locked == !movable
1920 // toolbar name * locks all toolbars
1921 flag.setOnOff(!toolbarsMovable_);
1922 else if (GuiToolbar * t = toolbar(name))
1923 flag.setOnOff(!(t->isMovable()));
1926 docstring const msg =
1927 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1933 case LFUN_ICON_SIZE:
1934 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1937 case LFUN_DROP_LAYOUTS_CHOICE:
1941 case LFUN_UI_TOGGLE:
1942 flag.setOnOff(isFullScreen());
1945 case LFUN_DIALOG_DISCONNECT_INSET:
1948 case LFUN_DIALOG_HIDE:
1949 // FIXME: should we check if the dialog is shown?
1952 case LFUN_DIALOG_TOGGLE:
1953 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1954 // fall through to set "enable"
1955 case LFUN_DIALOG_SHOW: {
1956 string const name = cmd.getArg(0);
1958 enable = name == "aboutlyx"
1959 || name == "file" //FIXME: should be removed.
1961 || name == "texinfo"
1962 || name == "progress"
1963 || name == "compare";
1964 else if (name == "character" || name == "symbols"
1965 || name == "mathdelimiter" || name == "mathmatrix") {
1966 if (!buf || buf->isReadonly())
1969 Cursor const & cur = currentBufferView()->cursor();
1970 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1973 else if (name == "latexlog")
1974 enable = FileName(doc_buffer->logName()).isReadableFile();
1975 else if (name == "spellchecker")
1976 enable = theSpellChecker()
1977 && !doc_buffer->isReadonly()
1978 && !doc_buffer->text().empty();
1979 else if (name == "vclog")
1980 enable = doc_buffer->lyxvc().inUse();
1984 case LFUN_DIALOG_UPDATE: {
1985 string const name = cmd.getArg(0);
1987 enable = name == "prefs";
1991 case LFUN_COMMAND_EXECUTE:
1993 case LFUN_MENU_OPEN:
1994 // Nothing to check.
1997 case LFUN_COMPLETION_INLINE:
1998 if (!d.current_work_area_
1999 || !d.current_work_area_->completer().inlinePossible(
2000 currentBufferView()->cursor()))
2004 case LFUN_COMPLETION_POPUP:
2005 if (!d.current_work_area_
2006 || !d.current_work_area_->completer().popupPossible(
2007 currentBufferView()->cursor()))
2012 if (!d.current_work_area_
2013 || !d.current_work_area_->completer().inlinePossible(
2014 currentBufferView()->cursor()))
2018 case LFUN_COMPLETION_ACCEPT:
2019 if (!d.current_work_area_
2020 || (!d.current_work_area_->completer().popupVisible()
2021 && !d.current_work_area_->completer().inlineVisible()
2022 && !d.current_work_area_->completer().completionAvailable()))
2026 case LFUN_COMPLETION_CANCEL:
2027 if (!d.current_work_area_
2028 || (!d.current_work_area_->completer().popupVisible()
2029 && !d.current_work_area_->completer().inlineVisible()))
2033 case LFUN_BUFFER_ZOOM_OUT:
2034 case LFUN_BUFFER_ZOOM_IN: {
2035 // only diff between these two is that the default for ZOOM_OUT
2037 bool const neg_zoom =
2038 convert<int>(cmd.argument()) < 0 ||
2039 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2040 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2041 docstring const msg =
2042 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2046 enable = doc_buffer;
2050 case LFUN_BUFFER_ZOOM: {
2051 bool const less_than_min_zoom =
2052 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2053 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2054 docstring const msg =
2055 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2060 enable = doc_buffer;
2064 case LFUN_BUFFER_MOVE_NEXT:
2065 case LFUN_BUFFER_MOVE_PREVIOUS:
2066 // we do not cycle when moving
2067 case LFUN_BUFFER_NEXT:
2068 case LFUN_BUFFER_PREVIOUS:
2069 // because we cycle, it doesn't matter whether on first or last
2070 enable = (d.currentTabWorkArea()->count() > 1);
2072 case LFUN_BUFFER_SWITCH:
2073 // toggle on the current buffer, but do not toggle off
2074 // the other ones (is that a good idea?)
2076 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2077 flag.setOnOff(true);
2080 case LFUN_VC_REGISTER:
2081 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2083 case LFUN_VC_RENAME:
2084 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2087 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2089 case LFUN_VC_CHECK_IN:
2090 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2092 case LFUN_VC_CHECK_OUT:
2093 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2095 case LFUN_VC_LOCKING_TOGGLE:
2096 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2097 && doc_buffer->lyxvc().lockingToggleEnabled();
2098 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2100 case LFUN_VC_REVERT:
2101 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2102 && !doc_buffer->hasReadonlyFlag();
2104 case LFUN_VC_UNDO_LAST:
2105 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2107 case LFUN_VC_REPO_UPDATE:
2108 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2110 case LFUN_VC_COMMAND: {
2111 if (cmd.argument().empty())
2113 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2117 case LFUN_VC_COMPARE:
2118 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2121 case LFUN_SERVER_GOTO_FILE_ROW:
2122 case LFUN_LYX_ACTIVATE:
2124 case LFUN_FORWARD_SEARCH:
2125 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2128 case LFUN_FILE_INSERT_PLAINTEXT:
2129 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2130 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2133 case LFUN_SPELLING_CONTINUOUSLY:
2134 flag.setOnOff(lyxrc.spellcheck_continuously);
2142 flag.setEnabled(false);
2148 static FileName selectTemplateFile()
2150 FileDialog dlg(qt_("Select template file"));
2151 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2152 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2154 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2155 QStringList(qt_("LyX Documents (*.lyx)")));
2157 if (result.first == FileDialog::Later)
2159 if (result.second.isEmpty())
2161 return FileName(fromqstr(result.second));
2165 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2169 Buffer * newBuffer = 0;
2171 newBuffer = checkAndLoadLyXFile(filename);
2172 } catch (ExceptionMessage const & e) {
2179 message(_("Document not loaded."));
2183 setBuffer(newBuffer);
2184 newBuffer->errors("Parse");
2187 theSession().lastFiles().add(filename);
2193 void GuiView::openDocument(string const & fname)
2195 string initpath = lyxrc.document_path;
2197 if (documentBufferView()) {
2198 string const trypath = documentBufferView()->buffer().filePath();
2199 // If directory is writeable, use this as default.
2200 if (FileName(trypath).isDirWritable())
2206 if (fname.empty()) {
2207 FileDialog dlg(qt_("Select document to open"));
2208 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2209 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2211 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2212 FileDialog::Result result =
2213 dlg.open(toqstr(initpath), filter);
2215 if (result.first == FileDialog::Later)
2218 filename = fromqstr(result.second);
2220 // check selected filename
2221 if (filename.empty()) {
2222 message(_("Canceled."));
2228 // get absolute path of file and add ".lyx" to the filename if
2230 FileName const fullname =
2231 fileSearch(string(), filename, "lyx", support::may_not_exist);
2232 if (!fullname.empty())
2233 filename = fullname.absFileName();
2235 if (!fullname.onlyPath().isDirectory()) {
2236 Alert::warning(_("Invalid filename"),
2237 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2238 from_utf8(fullname.absFileName())));
2242 // if the file doesn't exist and isn't already open (bug 6645),
2243 // let the user create one
2244 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2245 !LyXVC::file_not_found_hook(fullname)) {
2246 // the user specifically chose this name. Believe him.
2247 Buffer * const b = newFile(filename, string(), true);
2253 docstring const disp_fn = makeDisplayPath(filename);
2254 message(bformat(_("Opening document %1$s..."), disp_fn));
2257 Buffer * buf = loadDocument(fullname);
2259 str2 = bformat(_("Document %1$s opened."), disp_fn);
2260 if (buf->lyxvc().inUse())
2261 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2262 " " + _("Version control detected.");
2264 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2269 // FIXME: clean that
2270 static bool import(GuiView * lv, FileName const & filename,
2271 string const & format, ErrorList & errorList)
2273 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2275 string loader_format;
2276 vector<string> loaders = theConverters().loaders();
2277 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2278 vector<string>::const_iterator it = loaders.begin();
2279 vector<string>::const_iterator en = loaders.end();
2280 for (; it != en; ++it) {
2281 if (!theConverters().isReachable(format, *it))
2284 string const tofile =
2285 support::changeExtension(filename.absFileName(),
2286 theFormats().extension(*it));
2287 if (!theConverters().convert(0, filename, FileName(tofile),
2288 filename, format, *it, errorList))
2290 loader_format = *it;
2293 if (loader_format.empty()) {
2294 frontend::Alert::error(_("Couldn't import file"),
2295 bformat(_("No information for importing the format %1$s."),
2296 theFormats().prettyName(format)));
2300 loader_format = format;
2302 if (loader_format == "lyx") {
2303 Buffer * buf = lv->loadDocument(lyxfile);
2307 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2311 bool as_paragraphs = loader_format == "textparagraph";
2312 string filename2 = (loader_format == format) ? filename.absFileName()
2313 : support::changeExtension(filename.absFileName(),
2314 theFormats().extension(loader_format));
2315 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2317 guiApp->setCurrentView(lv);
2318 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2325 void GuiView::importDocument(string const & argument)
2328 string filename = split(argument, format, ' ');
2330 LYXERR(Debug::INFO, format << " file: " << filename);
2332 // need user interaction
2333 if (filename.empty()) {
2334 string initpath = lyxrc.document_path;
2335 if (documentBufferView()) {
2336 string const trypath = documentBufferView()->buffer().filePath();
2337 // If directory is writeable, use this as default.
2338 if (FileName(trypath).isDirWritable())
2342 docstring const text = bformat(_("Select %1$s file to import"),
2343 theFormats().prettyName(format));
2345 FileDialog dlg(toqstr(text));
2346 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2347 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2349 docstring filter = theFormats().prettyName(format);
2352 filter += from_utf8(theFormats().extensions(format));
2355 FileDialog::Result result =
2356 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2358 if (result.first == FileDialog::Later)
2361 filename = fromqstr(result.second);
2363 // check selected filename
2364 if (filename.empty())
2365 message(_("Canceled."));
2368 if (filename.empty())
2371 // get absolute path of file
2372 FileName const fullname(support::makeAbsPath(filename));
2374 // Can happen if the user entered a path into the dialog
2376 if (fullname.onlyFileName().empty()) {
2377 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2378 "Aborting import."),
2379 from_utf8(fullname.absFileName()));
2380 frontend::Alert::error(_("File name error"), msg);
2381 message(_("Canceled."));
2386 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2388 // Check if the document already is open
2389 Buffer * buf = theBufferList().getBuffer(lyxfile);
2392 if (!closeBuffer()) {
2393 message(_("Canceled."));
2398 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2400 // if the file exists already, and we didn't do
2401 // -i lyx thefile.lyx, warn
2402 if (lyxfile.exists() && fullname != lyxfile) {
2404 docstring text = bformat(_("The document %1$s already exists.\n\n"
2405 "Do you want to overwrite that document?"), displaypath);
2406 int const ret = Alert::prompt(_("Overwrite document?"),
2407 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2410 message(_("Canceled."));
2415 message(bformat(_("Importing %1$s..."), displaypath));
2416 ErrorList errorList;
2417 if (import(this, fullname, format, errorList))
2418 message(_("imported."));
2420 message(_("file not imported!"));
2422 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2426 void GuiView::newDocument(string const & filename, bool from_template)
2428 FileName initpath(lyxrc.document_path);
2429 if (documentBufferView()) {
2430 FileName const trypath(documentBufferView()->buffer().filePath());
2431 // If directory is writeable, use this as default.
2432 if (trypath.isDirWritable())
2436 string templatefile;
2437 if (from_template) {
2438 templatefile = selectTemplateFile().absFileName();
2439 if (templatefile.empty())
2444 if (filename.empty())
2445 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2447 b = newFile(filename, templatefile, true);
2452 // If no new document could be created, it is unsure
2453 // whether there is a valid BufferView.
2454 if (currentBufferView())
2455 // Ensure the cursor is correctly positioned on screen.
2456 currentBufferView()->showCursor();
2460 void GuiView::insertLyXFile(docstring const & fname)
2462 BufferView * bv = documentBufferView();
2467 FileName filename(to_utf8(fname));
2468 if (filename.empty()) {
2469 // Launch a file browser
2471 string initpath = lyxrc.document_path;
2472 string const trypath = bv->buffer().filePath();
2473 // If directory is writeable, use this as default.
2474 if (FileName(trypath).isDirWritable())
2478 FileDialog dlg(qt_("Select LyX document to insert"));
2479 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2480 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2482 FileDialog::Result result = dlg.open(toqstr(initpath),
2483 QStringList(qt_("LyX Documents (*.lyx)")));
2485 if (result.first == FileDialog::Later)
2489 filename.set(fromqstr(result.second));
2491 // check selected filename
2492 if (filename.empty()) {
2493 // emit message signal.
2494 message(_("Canceled."));
2499 bv->insertLyXFile(filename);
2500 bv->buffer().errors("Parse");
2504 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2506 FileName fname = b.fileName();
2507 FileName const oldname = fname;
2509 if (!newname.empty()) {
2511 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2513 // Switch to this Buffer.
2516 // No argument? Ask user through dialog.
2518 FileDialog dlg(qt_("Choose a filename to save document as"));
2519 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2520 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2522 if (!isLyXFileName(fname.absFileName()))
2523 fname.changeExtension(".lyx");
2525 FileDialog::Result result =
2526 dlg.save(toqstr(fname.onlyPath().absFileName()),
2527 QStringList(qt_("LyX Documents (*.lyx)")),
2528 toqstr(fname.onlyFileName()));
2530 if (result.first == FileDialog::Later)
2533 fname.set(fromqstr(result.second));
2538 if (!isLyXFileName(fname.absFileName()))
2539 fname.changeExtension(".lyx");
2542 // fname is now the new Buffer location.
2544 // if there is already a Buffer open with this name, we do not want
2545 // to have another one. (the second test makes sure we're not just
2546 // trying to overwrite ourselves, which is fine.)
2547 if (theBufferList().exists(fname) && fname != oldname
2548 && theBufferList().getBuffer(fname) != &b) {
2549 docstring const text =
2550 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2551 "Please close it before attempting to overwrite it.\n"
2552 "Do you want to choose a new filename?"),
2553 from_utf8(fname.absFileName()));
2554 int const ret = Alert::prompt(_("Chosen File Already Open"),
2555 text, 0, 1, _("&Rename"), _("&Cancel"));
2557 case 0: return renameBuffer(b, docstring(), kind);
2558 case 1: return false;
2563 bool const existsLocal = fname.exists();
2564 bool const existsInVC = LyXVC::fileInVC(fname);
2565 if (existsLocal || existsInVC) {
2566 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2567 if (kind != LV_WRITE_AS && existsInVC) {
2568 // renaming to a name that is already in VC
2570 docstring text = bformat(_("The document %1$s "
2571 "is already registered.\n\n"
2572 "Do you want to choose a new name?"),
2574 docstring const title = (kind == LV_VC_RENAME) ?
2575 _("Rename document?") : _("Copy document?");
2576 docstring const button = (kind == LV_VC_RENAME) ?
2577 _("&Rename") : _("&Copy");
2578 int const ret = Alert::prompt(title, text, 0, 1,
2579 button, _("&Cancel"));
2581 case 0: return renameBuffer(b, docstring(), kind);
2582 case 1: return false;
2587 docstring text = bformat(_("The document %1$s "
2588 "already exists.\n\n"
2589 "Do you want to overwrite that document?"),
2591 int const ret = Alert::prompt(_("Overwrite document?"),
2592 text, 0, 2, _("&Overwrite"),
2593 _("&Rename"), _("&Cancel"));
2596 case 1: return renameBuffer(b, docstring(), kind);
2597 case 2: return false;
2603 case LV_VC_RENAME: {
2604 string msg = b.lyxvc().rename(fname);
2607 message(from_utf8(msg));
2611 string msg = b.lyxvc().copy(fname);
2614 message(from_utf8(msg));
2620 // LyXVC created the file already in case of LV_VC_RENAME or
2621 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2622 // relative paths of included stuff right if we moved e.g. from
2623 // /a/b.lyx to /a/c/b.lyx.
2625 bool const saved = saveBuffer(b, fname);
2632 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2634 FileName fname = b.fileName();
2636 FileDialog dlg(qt_("Choose a filename to export the document as"));
2637 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2640 QString const anyformat = qt_("Guess from extension (*.*)");
2643 vector<Format const *> export_formats;
2644 for (Format const & f : theFormats())
2645 if (f.documentFormat())
2646 export_formats.push_back(&f);
2647 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2648 map<QString, string> fmap;
2651 for (Format const * f : export_formats) {
2652 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2653 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2655 from_ascii(f->extension())));
2656 types << loc_filter;
2657 fmap[loc_filter] = f->name();
2658 if (from_ascii(f->name()) == iformat) {
2659 filter = loc_filter;
2660 ext = f->extension();
2663 string ofname = fname.onlyFileName();
2665 ofname = support::changeExtension(ofname, ext);
2666 FileDialog::Result result =
2667 dlg.save(toqstr(fname.onlyPath().absFileName()),
2671 if (result.first != FileDialog::Chosen)
2675 fname.set(fromqstr(result.second));
2676 if (filter == anyformat)
2677 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2679 fmt_name = fmap[filter];
2680 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2681 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2683 if (fmt_name.empty() || fname.empty())
2686 // fname is now the new Buffer location.
2687 if (FileName(fname).exists()) {
2688 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2689 docstring text = bformat(_("The document %1$s already "
2690 "exists.\n\nDo you want to "
2691 "overwrite that document?"),
2693 int const ret = Alert::prompt(_("Overwrite document?"),
2694 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2697 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2698 case 2: return false;
2702 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2705 return dr.dispatched();
2709 bool GuiView::saveBuffer(Buffer & b)
2711 return saveBuffer(b, FileName());
2715 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2717 if (workArea(b) && workArea(b)->inDialogMode())
2720 if (fn.empty() && b.isUnnamed())
2721 return renameBuffer(b, docstring());
2723 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2725 theSession().lastFiles().add(b.fileName());
2729 // Switch to this Buffer.
2732 // FIXME: we don't tell the user *WHY* the save failed !!
2733 docstring const file = makeDisplayPath(b.absFileName(), 30);
2734 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2735 "Do you want to rename the document and "
2736 "try again?"), file);
2737 int const ret = Alert::prompt(_("Rename and save?"),
2738 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2741 if (!renameBuffer(b, docstring()))
2750 return saveBuffer(b, fn);
2754 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2756 return closeWorkArea(wa, false);
2760 // We only want to close the buffer if it is not visible in other workareas
2761 // of the same view, nor in other views, and if this is not a child
2762 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2764 Buffer & buf = wa->bufferView().buffer();
2766 bool last_wa = d.countWorkAreasOf(buf) == 1
2767 && !inOtherView(buf) && !buf.parent();
2769 bool close_buffer = last_wa;
2772 if (lyxrc.close_buffer_with_last_view == "yes")
2774 else if (lyxrc.close_buffer_with_last_view == "no")
2775 close_buffer = false;
2778 if (buf.isUnnamed())
2779 file = from_utf8(buf.fileName().onlyFileName());
2781 file = buf.fileName().displayName(30);
2782 docstring const text = bformat(
2783 _("Last view on document %1$s is being closed.\n"
2784 "Would you like to close or hide the document?\n"
2786 "Hidden documents can be displayed back through\n"
2787 "the menu: View->Hidden->...\n"
2789 "To remove this question, set your preference in:\n"
2790 " Tools->Preferences->Look&Feel->UserInterface\n"
2792 int ret = Alert::prompt(_("Close or hide document?"),
2793 text, 0, 1, _("&Close"), _("&Hide"));
2794 close_buffer = (ret == 0);
2798 return closeWorkArea(wa, close_buffer);
2802 bool GuiView::closeBuffer()
2804 GuiWorkArea * wa = currentMainWorkArea();
2805 // coverity complained about this
2806 // it seems unnecessary, but perhaps is worth the check
2807 LASSERT(wa, return false);
2809 setCurrentWorkArea(wa);
2810 Buffer & buf = wa->bufferView().buffer();
2811 return closeWorkArea(wa, !buf.parent());
2815 void GuiView::writeSession() const {
2816 GuiWorkArea const * active_wa = currentMainWorkArea();
2817 for (int i = 0; i < d.splitter_->count(); ++i) {
2818 TabWorkArea * twa = d.tabWorkArea(i);
2819 for (int j = 0; j < twa->count(); ++j) {
2820 GuiWorkArea * wa = twa->workArea(j);
2821 Buffer & buf = wa->bufferView().buffer();
2822 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2828 bool GuiView::closeBufferAll()
2830 // Close the workareas in all other views
2831 QList<int> const ids = guiApp->viewIds();
2832 for (int i = 0; i != ids.size(); ++i) {
2833 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2837 // Close our own workareas
2838 if (!closeWorkAreaAll())
2841 // Now close the hidden buffers. We prevent hidden buffers from being
2842 // dirty, so we can just close them.
2843 theBufferList().closeAll();
2848 bool GuiView::closeWorkAreaAll()
2850 setCurrentWorkArea(currentMainWorkArea());
2852 // We might be in a situation that there is still a tabWorkArea, but
2853 // there are no tabs anymore. This can happen when we get here after a
2854 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2855 // many TabWorkArea's have no documents anymore.
2858 // We have to call count() each time, because it can happen that
2859 // more than one splitter will disappear in one iteration (bug 5998).
2860 while (d.splitter_->count() > empty_twa) {
2861 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2863 if (twa->count() == 0)
2866 setCurrentWorkArea(twa->currentWorkArea());
2867 if (!closeTabWorkArea(twa))
2875 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2880 Buffer & buf = wa->bufferView().buffer();
2882 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2883 Alert::warning(_("Close document"),
2884 _("Document could not be closed because it is being processed by LyX."));
2889 return closeBuffer(buf);
2891 if (!inMultiTabs(wa))
2892 if (!saveBufferIfNeeded(buf, true))
2900 bool GuiView::closeBuffer(Buffer & buf)
2902 // If we are in a close_event all children will be closed in some time,
2903 // so no need to do it here. This will ensure that the children end up
2904 // in the session file in the correct order. If we close the master
2905 // buffer, we can close or release the child buffers here too.
2906 bool success = true;
2908 ListOfBuffers clist = buf.getChildren();
2909 ListOfBuffers::const_iterator it = clist.begin();
2910 ListOfBuffers::const_iterator const bend = clist.end();
2911 for (; it != bend; ++it) {
2912 Buffer * child_buf = *it;
2913 if (theBufferList().isOthersChild(&buf, child_buf)) {
2914 child_buf->setParent(0);
2918 // FIXME: should we look in other tabworkareas?
2919 // ANSWER: I don't think so. I've tested, and if the child is
2920 // open in some other window, it closes without a problem.
2921 GuiWorkArea * child_wa = workArea(*child_buf);
2923 success = closeWorkArea(child_wa, true);
2927 // In this case the child buffer is open but hidden.
2928 // It therefore should not (MUST NOT) be dirty!
2929 LATTEST(child_buf->isClean());
2930 theBufferList().release(child_buf);
2935 // goto bookmark to update bookmark pit.
2936 // FIXME: we should update only the bookmarks related to this buffer!
2937 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2938 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2939 guiApp->gotoBookmark(i+1, false, false);
2941 if (saveBufferIfNeeded(buf, false)) {
2942 buf.removeAutosaveFile();
2943 theBufferList().release(&buf);
2947 // open all children again to avoid a crash because of dangling
2948 // pointers (bug 6603)
2954 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2956 while (twa == d.currentTabWorkArea()) {
2957 twa->setCurrentIndex(twa->count() - 1);
2959 GuiWorkArea * wa = twa->currentWorkArea();
2960 Buffer & b = wa->bufferView().buffer();
2962 // We only want to close the buffer if the same buffer is not visible
2963 // in another view, and if this is not a child and if we are closing
2964 // a view (not a tabgroup).
2965 bool const close_buffer =
2966 !inOtherView(b) && !b.parent() && closing_;
2968 if (!closeWorkArea(wa, close_buffer))
2975 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2977 if (buf.isClean() || buf.paragraphs().empty())
2980 // Switch to this Buffer.
2986 if (buf.isUnnamed()) {
2987 file = from_utf8(buf.fileName().onlyFileName());
2990 FileName filename = buf.fileName();
2992 file = filename.displayName(30);
2993 exists = filename.exists();
2996 // Bring this window to top before asking questions.
3001 if (hiding && buf.isUnnamed()) {
3002 docstring const text = bformat(_("The document %1$s has not been "
3003 "saved yet.\n\nDo you want to save "
3004 "the document?"), file);
3005 ret = Alert::prompt(_("Save new document?"),
3006 text, 0, 1, _("&Save"), _("&Cancel"));
3010 docstring const text = exists ?
3011 bformat(_("The document %1$s has unsaved changes."
3012 "\n\nDo you want to save the document or "
3013 "discard the changes?"), file) :
3014 bformat(_("The document %1$s has not been saved yet."
3015 "\n\nDo you want to save the document or "
3016 "discard it entirely?"), file);
3017 docstring const title = exists ?
3018 _("Save changed document?") : _("Save document?");
3019 ret = Alert::prompt(title, text, 0, 2,
3020 _("&Save"), _("&Discard"), _("&Cancel"));
3025 if (!saveBuffer(buf))
3029 // If we crash after this we could have no autosave file
3030 // but I guess this is really improbable (Jug).
3031 // Sometimes improbable things happen:
3032 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3033 // buf.removeAutosaveFile();
3035 // revert all changes
3046 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3048 Buffer & buf = wa->bufferView().buffer();
3050 for (int i = 0; i != d.splitter_->count(); ++i) {
3051 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3052 if (wa_ && wa_ != wa)
3055 return inOtherView(buf);
3059 bool GuiView::inOtherView(Buffer & buf)
3061 QList<int> const ids = guiApp->viewIds();
3063 for (int i = 0; i != ids.size(); ++i) {
3067 if (guiApp->view(ids[i]).workArea(buf))
3074 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3076 if (!documentBufferView())
3079 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3080 Buffer * const curbuf = &documentBufferView()->buffer();
3081 int nwa = twa->count();
3082 for (int i = 0; i < nwa; ++i) {
3083 if (&workArea(i)->bufferView().buffer() == curbuf) {
3085 if (np == NEXTBUFFER)
3086 next_index = (i == nwa - 1 ? 0 : i + 1);
3088 next_index = (i == 0 ? nwa - 1 : i - 1);
3090 twa->moveTab(i, next_index);
3092 setBuffer(&workArea(next_index)->bufferView().buffer());
3100 /// make sure the document is saved
3101 static bool ensureBufferClean(Buffer * buffer)
3103 LASSERT(buffer, return false);
3104 if (buffer->isClean() && !buffer->isUnnamed())
3107 docstring const file = buffer->fileName().displayName(30);
3110 if (!buffer->isUnnamed()) {
3111 text = bformat(_("The document %1$s has unsaved "
3112 "changes.\n\nDo you want to save "
3113 "the document?"), file);
3114 title = _("Save changed document?");
3117 text = bformat(_("The document %1$s has not been "
3118 "saved yet.\n\nDo you want to save "
3119 "the document?"), file);
3120 title = _("Save new document?");
3122 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3125 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3127 return buffer->isClean() && !buffer->isUnnamed();
3131 bool GuiView::reloadBuffer(Buffer & buf)
3133 Buffer::ReadStatus status = buf.reload();
3134 return status == Buffer::ReadSuccess;
3138 void GuiView::checkExternallyModifiedBuffers()
3140 BufferList::iterator bit = theBufferList().begin();
3141 BufferList::iterator const bend = theBufferList().end();
3142 for (; bit != bend; ++bit) {
3143 Buffer * buf = *bit;
3144 if (buf->fileName().exists() && buf->isChecksumModified()) {
3145 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3146 " Reload now? Any local changes will be lost."),
3147 from_utf8(buf->absFileName()));
3148 int const ret = Alert::prompt(_("Reload externally changed document?"),
3149 text, 0, 1, _("&Reload"), _("&Cancel"));
3157 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3159 Buffer * buffer = documentBufferView()
3160 ? &(documentBufferView()->buffer()) : 0;
3162 switch (cmd.action()) {
3163 case LFUN_VC_REGISTER:
3164 if (!buffer || !ensureBufferClean(buffer))
3166 if (!buffer->lyxvc().inUse()) {
3167 if (buffer->lyxvc().registrer()) {
3168 reloadBuffer(*buffer);
3169 dr.clearMessageUpdate();
3174 case LFUN_VC_RENAME:
3175 case LFUN_VC_COPY: {
3176 if (!buffer || !ensureBufferClean(buffer))
3178 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3179 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3180 // Some changes are not yet committed.
3181 // We test here and not in getStatus(), since
3182 // this test is expensive.
3184 LyXVC::CommandResult ret =
3185 buffer->lyxvc().checkIn(log);
3187 if (ret == LyXVC::ErrorCommand ||
3188 ret == LyXVC::VCSuccess)
3189 reloadBuffer(*buffer);
3190 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3191 frontend::Alert::error(
3192 _("Revision control error."),
3193 _("Document could not be checked in."));
3197 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3198 LV_VC_RENAME : LV_VC_COPY;
3199 renameBuffer(*buffer, cmd.argument(), kind);
3204 case LFUN_VC_CHECK_IN:
3205 if (!buffer || !ensureBufferClean(buffer))
3207 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3209 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3211 // Only skip reloading if the checkin was cancelled or
3212 // an error occurred before the real checkin VCS command
3213 // was executed, since the VCS might have changed the
3214 // file even if it could not checkin successfully.
3215 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3216 reloadBuffer(*buffer);
3220 case LFUN_VC_CHECK_OUT:
3221 if (!buffer || !ensureBufferClean(buffer))
3223 if (buffer->lyxvc().inUse()) {
3224 dr.setMessage(buffer->lyxvc().checkOut());
3225 reloadBuffer(*buffer);
3229 case LFUN_VC_LOCKING_TOGGLE:
3230 LASSERT(buffer, return);
3231 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3233 if (buffer->lyxvc().inUse()) {
3234 string res = buffer->lyxvc().lockingToggle();
3236 frontend::Alert::error(_("Revision control error."),
3237 _("Error when setting the locking property."));
3240 reloadBuffer(*buffer);
3245 case LFUN_VC_REVERT:
3246 LASSERT(buffer, return);
3247 if (buffer->lyxvc().revert()) {
3248 reloadBuffer(*buffer);
3249 dr.clearMessageUpdate();
3253 case LFUN_VC_UNDO_LAST:
3254 LASSERT(buffer, return);
3255 buffer->lyxvc().undoLast();
3256 reloadBuffer(*buffer);
3257 dr.clearMessageUpdate();
3260 case LFUN_VC_REPO_UPDATE:
3261 LASSERT(buffer, return);
3262 if (ensureBufferClean(buffer)) {
3263 dr.setMessage(buffer->lyxvc().repoUpdate());
3264 checkExternallyModifiedBuffers();
3268 case LFUN_VC_COMMAND: {
3269 string flag = cmd.getArg(0);
3270 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3273 if (contains(flag, 'M')) {
3274 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3277 string path = cmd.getArg(1);
3278 if (contains(path, "$$p") && buffer)
3279 path = subst(path, "$$p", buffer->filePath());
3280 LYXERR(Debug::LYXVC, "Directory: " << path);
3282 if (!pp.isReadableDirectory()) {
3283 lyxerr << _("Directory is not accessible.") << endl;
3286 support::PathChanger p(pp);
3288 string command = cmd.getArg(2);
3289 if (command.empty())
3292 command = subst(command, "$$i", buffer->absFileName());
3293 command = subst(command, "$$p", buffer->filePath());
3295 command = subst(command, "$$m", to_utf8(message));
3296 LYXERR(Debug::LYXVC, "Command: " << command);
3298 one.startscript(Systemcall::Wait, command);
3302 if (contains(flag, 'I'))
3303 buffer->markDirty();
3304 if (contains(flag, 'R'))
3305 reloadBuffer(*buffer);
3310 case LFUN_VC_COMPARE: {
3311 if (cmd.argument().empty()) {
3312 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3316 string rev1 = cmd.getArg(0);
3321 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3324 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3325 f2 = buffer->absFileName();
3327 string rev2 = cmd.getArg(1);
3331 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3335 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3336 f1 << "\n" << f2 << "\n" );
3337 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3338 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3348 void GuiView::openChildDocument(string const & fname)
3350 LASSERT(documentBufferView(), return);
3351 Buffer & buffer = documentBufferView()->buffer();
3352 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3353 documentBufferView()->saveBookmark(false);
3355 if (theBufferList().exists(filename)) {
3356 child = theBufferList().getBuffer(filename);
3359 message(bformat(_("Opening child document %1$s..."),
3360 makeDisplayPath(filename.absFileName())));
3361 child = loadDocument(filename, false);
3363 // Set the parent name of the child document.
3364 // This makes insertion of citations and references in the child work,
3365 // when the target is in the parent or another child document.
3367 child->setParent(&buffer);
3371 bool GuiView::goToFileRow(string const & argument)
3375 size_t i = argument.find_last_of(' ');
3376 if (i != string::npos) {
3377 file_name = os::internal_path(trim(argument.substr(0, i)));
3378 istringstream is(argument.substr(i + 1));
3383 if (i == string::npos) {
3384 LYXERR0("Wrong argument: " << argument);
3388 string const abstmp = package().temp_dir().absFileName();
3389 string const realtmp = package().temp_dir().realPath();
3390 // We have to use os::path_prefix_is() here, instead of
3391 // simply prefixIs(), because the file name comes from
3392 // an external application and may need case adjustment.
3393 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3394 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3395 // Needed by inverse dvi search. If it is a file
3396 // in tmpdir, call the apropriated function.
3397 // If tmpdir is a symlink, we may have the real
3398 // path passed back, so we correct for that.
3399 if (!prefixIs(file_name, abstmp))
3400 file_name = subst(file_name, realtmp, abstmp);
3401 buf = theBufferList().getBufferFromTmp(file_name);
3403 // Must replace extension of the file to be .lyx
3404 // and get full path
3405 FileName const s = fileSearch(string(),
3406 support::changeExtension(file_name, ".lyx"), "lyx");
3407 // Either change buffer or load the file
3408 if (theBufferList().exists(s))
3409 buf = theBufferList().getBuffer(s);
3410 else if (s.exists()) {
3411 buf = loadDocument(s);
3416 _("File does not exist: %1$s"),
3417 makeDisplayPath(file_name)));
3423 _("No buffer for file: %1$s."),
3424 makeDisplayPath(file_name))
3429 bool success = documentBufferView()->setCursorFromRow(row);
3431 LYXERR(Debug::LATEX,
3432 "setCursorFromRow: invalid position for row " << row);
3433 frontend::Alert::error(_("Inverse Search Failed"),
3434 _("Invalid position requested by inverse search.\n"
3435 "You may need to update the viewed document."));
3441 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3443 QMenu * menu = 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"