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 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_;
330 QMenu * toolBarPopup(GuiView * parent)
332 // FIXME: translation
333 QMenu * menu = new QMenu(parent);
334 QActionGroup * iconSizeGroup = new QActionGroup(parent);
336 QAction * smallIcons = new QAction(iconSizeGroup);
337 smallIcons->setText(qt_("Small-sized icons"));
338 smallIcons->setCheckable(true);
339 QObject::connect(smallIcons, SIGNAL(triggered()),
340 parent, SLOT(smallSizedIcons()));
341 menu->addAction(smallIcons);
343 QAction * normalIcons = new QAction(iconSizeGroup);
344 normalIcons->setText(qt_("Normal-sized icons"));
345 normalIcons->setCheckable(true);
346 QObject::connect(normalIcons, SIGNAL(triggered()),
347 parent, SLOT(normalSizedIcons()));
348 menu->addAction(normalIcons);
350 QAction * bigIcons = new QAction(iconSizeGroup);
351 bigIcons->setText(qt_("Big-sized icons"));
352 bigIcons->setCheckable(true);
353 QObject::connect(bigIcons, SIGNAL(triggered()),
354 parent, SLOT(bigSizedIcons()));
355 menu->addAction(bigIcons);
357 QAction * hugeIcons = new QAction(iconSizeGroup);
358 hugeIcons->setText(qt_("Huge-sized icons"));
359 hugeIcons->setCheckable(true);
360 QObject::connect(hugeIcons, SIGNAL(triggered()),
361 parent, SLOT(hugeSizedIcons()));
362 menu->addAction(hugeIcons);
364 QAction * giantIcons = new QAction(iconSizeGroup);
365 giantIcons->setText(qt_("Giant-sized icons"));
366 giantIcons->setCheckable(true);
367 QObject::connect(giantIcons, SIGNAL(triggered()),
368 parent, SLOT(giantSizedIcons()));
369 menu->addAction(giantIcons);
371 unsigned int cur = parent->iconSize().width();
372 if ( cur == parent->d.smallIconSize)
373 smallIcons->setChecked(true);
374 else if (cur == parent->d.normalIconSize)
375 normalIcons->setChecked(true);
376 else if (cur == parent->d.bigIconSize)
377 bigIcons->setChecked(true);
378 else if (cur == parent->d.hugeIconSize)
379 hugeIcons->setChecked(true);
380 else if (cur == parent->d.giantIconSize)
381 giantIcons->setChecked(true);
388 stack_widget_->setCurrentWidget(bg_widget_);
389 bg_widget_->setUpdatesEnabled(true);
390 bg_widget_->setFocus();
393 int tabWorkAreaCount()
395 return splitter_->count();
398 TabWorkArea * tabWorkArea(int i)
400 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
403 TabWorkArea * currentTabWorkArea()
405 int areas = tabWorkAreaCount();
407 // The first TabWorkArea is always the first one, if any.
408 return tabWorkArea(0);
410 for (int i = 0; i != areas; ++i) {
411 TabWorkArea * twa = tabWorkArea(i);
412 if (current_main_work_area_ == twa->currentWorkArea())
416 // None has the focus so we just take the first one.
417 return tabWorkArea(0);
420 int countWorkAreasOf(Buffer & buf)
422 int areas = tabWorkAreaCount();
424 for (int i = 0; i != areas; ++i) {
425 TabWorkArea * twa = tabWorkArea(i);
426 if (twa->workArea(buf))
432 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
434 if (processing_thread_watcher_.isRunning()) {
435 // we prefer to cancel this preview in order to keep a snappy
439 processing_thread_watcher_.setFuture(f);
444 GuiWorkArea * current_work_area_;
445 GuiWorkArea * current_main_work_area_;
446 QSplitter * splitter_;
447 QStackedWidget * stack_widget_;
448 BackgroundWidget * bg_widget_;
450 ToolbarMap toolbars_;
451 ProgressInterface* progress_;
452 /// The main layout box.
454 * \warning Don't Delete! The layout box is actually owned by
455 * whichever toolbar contains it. All the GuiView class needs is a
456 * means of accessing it.
458 * FIXME: replace that with a proper model so that we are not limited
459 * to only one dialog.
464 map<string, DialogPtr> dialogs_;
466 unsigned int smallIconSize;
467 unsigned int normalIconSize;
468 unsigned int bigIconSize;
469 unsigned int hugeIconSize;
470 unsigned int giantIconSize;
472 QTimer statusbar_timer_;
473 /// auto-saving of buffers
474 Timeout autosave_timeout_;
475 /// flag against a race condition due to multiclicks, see bug #1119
479 TocModels toc_models_;
482 QFutureWatcher<docstring> autosave_watcher_;
483 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
485 string last_export_format;
486 string processing_format;
488 static QSet<Buffer const *> busyBuffers;
489 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
492 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
495 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
497 // TODO syncFunc/previewFunc: use bind
498 bool asyncBufferProcessing(string const & argument,
499 Buffer const * used_buffer,
500 docstring const & msg,
501 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
502 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
503 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
505 QVector<GuiWorkArea*> guiWorkAreas();
508 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
511 GuiView::GuiView(int id)
512 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
513 command_execute_(false), minibuffer_focus_(false)
515 connect(this, SIGNAL(bufferViewChanged()),
516 this, SLOT(on_bufferViewChanged()));
518 // GuiToolbars *must* be initialised before the menu bar.
519 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
522 // set ourself as the current view. This is needed for the menu bar
523 // filling, at least for the static special menu item on Mac. Otherwise
524 // they are greyed out.
525 guiApp->setCurrentView(this);
527 // Fill up the menu bar.
528 guiApp->menus().fillMenuBar(menuBar(), this, true);
530 setCentralWidget(d.stack_widget_);
532 // Start autosave timer
533 if (lyxrc.autosave) {
534 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
535 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
536 d.autosave_timeout_.start();
538 connect(&d.statusbar_timer_, SIGNAL(timeout()),
539 this, SLOT(clearMessage()));
541 // We don't want to keep the window in memory if it is closed.
542 setAttribute(Qt::WA_DeleteOnClose, true);
544 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
545 // QIcon::fromTheme was introduced in Qt 4.6
546 #if (QT_VERSION >= 0x040600)
547 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
548 // since the icon is provided in the application bundle. We use a themed
549 // version when available and use the bundled one as fallback.
550 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
552 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
558 // use tabbed dock area for multiple docks
559 // (such as "source" and "messages")
560 setDockOptions(QMainWindow::ForceTabbedDocks);
563 setAcceptDrops(true);
565 // add busy indicator to statusbar
566 QLabel * busylabel = new QLabel(statusBar());
567 statusBar()->addPermanentWidget(busylabel);
568 search_mode mode = theGuiApp()->imageSearchMode();
569 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
570 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
571 busylabel->setMovie(busyanim);
575 connect(&d.processing_thread_watcher_, SIGNAL(started()),
576 busylabel, SLOT(show()));
577 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
578 busylabel, SLOT(hide()));
580 QFontMetrics const fm(statusBar()->fontMetrics());
581 int const roheight = max(int(d.normalIconSize), fm.height());
582 QSize const rosize(roheight, roheight);
583 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
584 read_only_ = new QLabel(statusBar());
585 read_only_->setPixmap(readonly);
586 read_only_->setScaledContents(true);
587 read_only_->setAlignment(Qt::AlignCenter);
589 statusBar()->addPermanentWidget(read_only_);
591 version_control_ = new QLabel(statusBar());
592 version_control_->setAlignment(Qt::AlignCenter);
593 version_control_->setFrameStyle(QFrame::StyledPanel);
594 version_control_->hide();
595 statusBar()->addPermanentWidget(version_control_);
597 statusBar()->setSizeGripEnabled(true);
600 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
601 SLOT(autoSaveThreadFinished()));
603 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
604 SLOT(processingThreadStarted()));
605 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
606 SLOT(processingThreadFinished()));
608 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
609 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
611 // Forbid too small unresizable window because it can happen
612 // with some window manager under X11.
613 setMinimumSize(300, 200);
615 if (lyxrc.allow_geometry_session) {
616 // Now take care of session management.
621 // no session handling, default to a sane size.
622 setGeometry(50, 50, 690, 510);
625 // clear session data if any.
627 settings.remove("views");
637 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
639 QVector<GuiWorkArea*> areas;
640 for (int i = 0; i < tabWorkAreaCount(); i++) {
641 TabWorkArea* ta = tabWorkArea(i);
642 for (int u = 0; u < ta->count(); u++) {
643 areas << ta->workArea(u);
649 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
650 string const & format)
652 docstring const fmt = formats.prettyName(format);
655 case Buffer::ExportSuccess:
656 msg = bformat(_("Successful export to format: %1$s"), fmt);
658 case Buffer::ExportCancel:
659 msg = _("Document export cancelled.");
661 case Buffer::ExportError:
662 case Buffer::ExportNoPathToFormat:
663 case Buffer::ExportTexPathHasSpaces:
664 case Buffer::ExportConverterError:
665 msg = bformat(_("Error while exporting format: %1$s"), fmt);
667 case Buffer::PreviewSuccess:
668 msg = bformat(_("Successful preview of format: %1$s"), fmt);
670 case Buffer::PreviewError:
671 msg = bformat(_("Error while previewing format: %1$s"), fmt);
678 void GuiView::processingThreadStarted()
683 void GuiView::processingThreadFinished()
685 QFutureWatcher<Buffer::ExportStatus> const * watcher =
686 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
688 Buffer::ExportStatus const status = watcher->result();
689 handleExportStatus(this, status, d.processing_format);
692 BufferView const * const bv = currentBufferView();
693 if (bv && !bv->buffer().errorList("Export").empty()) {
697 errors(d.last_export_format);
701 void GuiView::autoSaveThreadFinished()
703 QFutureWatcher<docstring> const * watcher =
704 static_cast<QFutureWatcher<docstring> const *>(sender());
705 message(watcher->result());
710 void GuiView::saveLayout() const
713 settings.beginGroup("views");
714 settings.beginGroup(QString::number(id_));
715 #if defined(Q_WS_X11) || defined(QPA_XCB)
716 settings.setValue("pos", pos());
717 settings.setValue("size", size());
719 settings.setValue("geometry", saveGeometry());
721 settings.setValue("layout", saveState(0));
722 settings.setValue("icon_size", iconSize());
726 void GuiView::saveUISettings() const
728 // Save the toolbar private states
729 ToolbarMap::iterator end = d.toolbars_.end();
730 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
731 it->second->saveSession();
732 // Now take care of all other dialogs
733 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
734 for (; it!= d.dialogs_.end(); ++it)
735 it->second->saveSession();
739 bool GuiView::restoreLayout()
742 settings.beginGroup("views");
743 settings.beginGroup(QString::number(id_));
744 QString const icon_key = "icon_size";
745 if (!settings.contains(icon_key))
748 //code below is skipped when when ~/.config/LyX is (re)created
749 QSize icon_size = settings.value(icon_key).toSize();
750 // Check whether session size changed.
751 if (icon_size.width() != int(d.smallIconSize) &&
752 icon_size.width() != int(d.normalIconSize) &&
753 icon_size.width() != int(d.bigIconSize) &&
754 icon_size.width() != int(d.hugeIconSize) &&
755 icon_size.width() != int(d.giantIconSize)) {
756 icon_size.setWidth(d.normalIconSize);
757 icon_size.setHeight(d.normalIconSize);
759 setIconSize(icon_size);
761 #if defined(Q_WS_X11) || defined(QPA_XCB)
762 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
763 QSize size = settings.value("size", QSize(690, 510)).toSize();
767 // Work-around for bug #6034: the window ends up in an undetermined
768 // state when trying to restore a maximized window when it is
769 // already maximized.
770 if (!(windowState() & Qt::WindowMaximized))
771 if (!restoreGeometry(settings.value("geometry").toByteArray()))
772 setGeometry(50, 50, 690, 510);
774 // Make sure layout is correctly oriented.
775 setLayoutDirection(qApp->layoutDirection());
777 // Allow the toc and view-source dock widget to be restored if needed.
779 if ((dialog = findOrBuild("toc", true)))
780 // see bug 5082. At least setup title and enabled state.
781 // Visibility will be adjusted by restoreState below.
782 dialog->prepareView();
783 if ((dialog = findOrBuild("view-source", true)))
784 dialog->prepareView();
785 if ((dialog = findOrBuild("progress", true)))
786 dialog->prepareView();
788 if (!restoreState(settings.value("layout").toByteArray(), 0))
791 // init the toolbars that have not been restored
792 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
793 Toolbars::Infos::iterator end = guiApp->toolbars().end();
794 for (; cit != end; ++cit) {
795 GuiToolbar * tb = toolbar(cit->name);
796 if (tb && !tb->isRestored())
797 initToolbar(cit->name);
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::constructToolbars()
818 ToolbarMap::iterator it = d.toolbars_.begin();
819 for (; it != d.toolbars_.end(); ++it)
823 // I don't like doing this here, but the standard toolbar
824 // destroys this object when it's destroyed itself (vfr)
825 d.layout_ = new LayoutBox(*this);
826 d.stack_widget_->addWidget(d.layout_);
827 d.layout_->move(0,0);
829 // extracts the toolbars from the backend
830 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
831 Toolbars::Infos::iterator end = guiApp->toolbars().end();
832 for (; cit != end; ++cit)
833 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
837 void GuiView::initToolbars()
839 // extracts the toolbars from the backend
840 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
841 Toolbars::Infos::iterator end = guiApp->toolbars().end();
842 for (; cit != end; ++cit)
843 initToolbar(cit->name);
847 void GuiView::initToolbar(string const & name)
849 GuiToolbar * tb = toolbar(name);
852 int const visibility = guiApp->toolbars().defaultVisibility(name);
853 bool newline = !(visibility & Toolbars::SAMEROW);
854 tb->setVisible(false);
855 tb->setVisibility(visibility);
857 if (visibility & Toolbars::TOP) {
859 addToolBarBreak(Qt::TopToolBarArea);
860 addToolBar(Qt::TopToolBarArea, tb);
863 if (visibility & Toolbars::BOTTOM) {
865 addToolBarBreak(Qt::BottomToolBarArea);
866 addToolBar(Qt::BottomToolBarArea, tb);
869 if (visibility & Toolbars::LEFT) {
871 addToolBarBreak(Qt::LeftToolBarArea);
872 addToolBar(Qt::LeftToolBarArea, tb);
875 if (visibility & Toolbars::RIGHT) {
877 addToolBarBreak(Qt::RightToolBarArea);
878 addToolBar(Qt::RightToolBarArea, tb);
881 if (visibility & Toolbars::ON)
882 tb->setVisible(true);
886 TocModels & GuiView::tocModels()
888 return d.toc_models_;
892 void GuiView::setFocus()
894 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
895 QMainWindow::setFocus();
899 bool GuiView::hasFocus() const
901 if (currentWorkArea())
902 return currentWorkArea()->hasFocus();
903 if (currentMainWorkArea())
904 return currentMainWorkArea()->hasFocus();
905 return d.bg_widget_->hasFocus();
909 void GuiView::focusInEvent(QFocusEvent * e)
911 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
912 QMainWindow::focusInEvent(e);
913 // Make sure guiApp points to the correct view.
914 guiApp->setCurrentView(this);
915 if (currentWorkArea())
916 currentWorkArea()->setFocus();
917 else if (currentMainWorkArea())
918 currentMainWorkArea()->setFocus();
920 d.bg_widget_->setFocus();
924 QMenu * GuiView::createPopupMenu()
926 return d.toolBarPopup(this);
930 void GuiView::showEvent(QShowEvent * e)
932 LYXERR(Debug::GUI, "Passed Geometry "
933 << size().height() << "x" << size().width()
934 << "+" << pos().x() << "+" << pos().y());
936 if (d.splitter_->count() == 0)
937 // No work area, switch to the background widget.
941 QMainWindow::showEvent(e);
945 bool GuiView::closeScheduled()
952 bool GuiView::prepareAllBuffersForLogout()
954 Buffer * first = theBufferList().first();
958 // First, iterate over all buffers and ask the users if unsaved
959 // changes should be saved.
960 // We cannot use a for loop as the buffer list cycles.
963 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
965 b = theBufferList().next(b);
966 } while (b != first);
968 // Next, save session state
969 // When a view/window was closed before without quitting LyX, there
970 // are already entries in the lastOpened list.
971 theSession().lastOpened().clear();
978 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
979 ** is responsibility of the container (e.g., dialog)
981 void GuiView::closeEvent(QCloseEvent * close_event)
983 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
985 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
986 Alert::warning(_("Exit LyX"),
987 _("LyX could not be closed because documents are being processed by LyX."));
988 close_event->setAccepted(false);
992 // If the user pressed the x (so we didn't call closeView
993 // programmatically), we want to clear all existing entries.
995 theSession().lastOpened().clear();
1000 // it can happen that this event arrives without selecting the view,
1001 // e.g. when clicking the close button on a background window.
1003 if (!closeWorkAreaAll()) {
1005 close_event->ignore();
1009 // Make sure that nothing will use this to be closed View.
1010 guiApp->unregisterView(this);
1012 if (isFullScreen()) {
1013 // Switch off fullscreen before closing.
1018 // Make sure the timer time out will not trigger a statusbar update.
1019 d.statusbar_timer_.stop();
1021 // Saving fullscreen requires additional tweaks in the toolbar code.
1022 // It wouldn't also work under linux natively.
1023 if (lyxrc.allow_geometry_session) {
1028 close_event->accept();
1032 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1034 if (event->mimeData()->hasUrls())
1036 /// \todo Ask lyx-devel is this is enough:
1037 /// if (event->mimeData()->hasFormat("text/plain"))
1038 /// event->acceptProposedAction();
1042 void GuiView::dropEvent(QDropEvent * event)
1044 QList<QUrl> files = event->mimeData()->urls();
1045 if (files.isEmpty())
1048 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1049 for (int i = 0; i != files.size(); ++i) {
1050 string const file = os::internal_path(fromqstr(
1051 files.at(i).toLocalFile()));
1055 string const ext = support::getExtension(file);
1056 vector<const Format *> found_formats;
1058 // Find all formats that have the correct extension.
1059 vector<const Format *> const & import_formats
1060 = theConverters().importableFormats();
1061 vector<const Format *>::const_iterator it = import_formats.begin();
1062 for (; it != import_formats.end(); ++it)
1063 if ((*it)->hasExtension(ext))
1064 found_formats.push_back(*it);
1067 if (found_formats.size() >= 1) {
1068 if (found_formats.size() > 1) {
1069 //FIXME: show a dialog to choose the correct importable format
1070 LYXERR(Debug::FILES,
1071 "Multiple importable formats found, selecting first");
1073 string const arg = found_formats[0]->name() + " " + file;
1074 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1077 //FIXME: do we have to explicitly check whether it's a lyx file?
1078 LYXERR(Debug::FILES,
1079 "No formats found, trying to open it as a lyx file");
1080 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1082 // add the functions to the queue
1083 guiApp->addToFuncRequestQueue(cmd);
1086 // now process the collected functions. We perform the events
1087 // asynchronously. This prevents potential problems in case the
1088 // BufferView is closed within an event.
1089 guiApp->processFuncRequestQueueAsync();
1093 void GuiView::message(docstring const & str)
1095 if (ForkedProcess::iAmAChild())
1098 // call is moved to GUI-thread by GuiProgress
1099 d.progress_->appendMessage(toqstr(str));
1103 void GuiView::clearMessageText()
1105 message(docstring());
1109 void GuiView::updateStatusBarMessage(QString const & str)
1111 statusBar()->showMessage(str);
1112 d.statusbar_timer_.stop();
1113 d.statusbar_timer_.start(3000);
1117 void GuiView::smallSizedIcons()
1119 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1123 void GuiView::normalSizedIcons()
1125 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1129 void GuiView::bigSizedIcons()
1131 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1135 void GuiView::hugeSizedIcons()
1137 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1141 void GuiView::giantSizedIcons()
1143 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1147 void GuiView::clearMessage()
1149 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1150 // the hasFocus function mostly returns false, even if the focus is on
1151 // a workarea in this view.
1155 d.statusbar_timer_.stop();
1159 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1161 if (wa != d.current_work_area_
1162 || wa->bufferView().buffer().isInternal())
1164 Buffer const & buf = wa->bufferView().buffer();
1165 // Set the windows title
1166 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1168 title += from_ascii(" - LyX");
1170 setWindowTitle(toqstr(title));
1171 // Sets the path for the window: this is used by OSX to
1172 // allow a context click on the title bar showing a menu
1173 // with the path up to the file
1174 setWindowFilePath(toqstr(buf.absFileName()));
1175 // Tell Qt whether the current document is changed
1176 setWindowModified(!buf.isClean());
1178 if (buf.isReadonly())
1183 if (buf.lyxvc().inUse()) {
1184 version_control_->show();
1185 if (buf.lyxvc().locking())
1186 version_control_->setText(
1187 toqstr(bformat(_("%1$s lock"),
1188 from_ascii(buf.lyxvc().vcname()))));
1190 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1192 version_control_->hide();
1196 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1198 if (d.current_work_area_)
1199 // disconnect the current work area from all slots
1200 QObject::disconnect(d.current_work_area_, 0, this, 0);
1202 disconnectBufferView();
1203 connectBufferView(wa->bufferView());
1204 connectBuffer(wa->bufferView().buffer());
1205 d.current_work_area_ = wa;
1206 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1207 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1208 QObject::connect(wa, SIGNAL(busy(bool)),
1209 this, SLOT(setBusy(bool)));
1210 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1211 this, SIGNAL(bufferViewChanged()));
1212 Q_EMIT updateWindowTitle(wa);
1213 Q_EMIT bufferViewChanged();
1217 void GuiView::on_bufferViewChanged()
1220 // Buffer-dependent dialogs must be updated. This is done here because
1221 // some dialogs require buffer()->text.
1226 void GuiView::on_lastWorkAreaRemoved()
1229 // We already are in a close event. Nothing more to do.
1232 if (d.splitter_->count() > 1)
1233 // We have a splitter so don't close anything.
1236 // Reset and updates the dialogs.
1237 Q_EMIT bufferViewChanged();
1242 if (lyxrc.open_buffers_in_tabs)
1243 // Nothing more to do, the window should stay open.
1246 if (guiApp->viewIds().size() > 1) {
1252 // On Mac we also close the last window because the application stay
1253 // resident in memory. On other platforms we don't close the last
1254 // window because this would quit the application.
1260 void GuiView::updateStatusBar()
1262 // let the user see the explicit message
1263 if (d.statusbar_timer_.isActive())
1270 void GuiView::showMessage()
1274 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1275 if (msg.isEmpty()) {
1276 BufferView const * bv = currentBufferView();
1278 msg = toqstr(bv->cursor().currentState());
1280 msg = qt_("Welcome to LyX!");
1282 statusBar()->showMessage(msg);
1286 bool GuiView::event(QEvent * e)
1290 // Useful debug code:
1291 //case QEvent::ActivationChange:
1292 //case QEvent::WindowDeactivate:
1293 //case QEvent::Paint:
1294 //case QEvent::Enter:
1295 //case QEvent::Leave:
1296 //case QEvent::HoverEnter:
1297 //case QEvent::HoverLeave:
1298 //case QEvent::HoverMove:
1299 //case QEvent::StatusTip:
1300 //case QEvent::DragEnter:
1301 //case QEvent::DragLeave:
1302 //case QEvent::Drop:
1305 case QEvent::WindowActivate: {
1306 GuiView * old_view = guiApp->currentView();
1307 if (this == old_view) {
1309 return QMainWindow::event(e);
1311 if (old_view && old_view->currentBufferView()) {
1312 // save current selection to the selection buffer to allow
1313 // middle-button paste in this window.
1314 cap::saveSelection(old_view->currentBufferView()->cursor());
1316 guiApp->setCurrentView(this);
1317 if (d.current_work_area_)
1318 on_currentWorkAreaChanged(d.current_work_area_);
1322 return QMainWindow::event(e);
1325 case QEvent::ShortcutOverride: {
1327 if (isFullScreen() && menuBar()->isHidden()) {
1328 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1329 // FIXME: we should also try to detect special LyX shortcut such as
1330 // Alt-P and Alt-M. Right now there is a hack in
1331 // GuiWorkArea::processKeySym() that hides again the menubar for
1333 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1335 return QMainWindow::event(e);
1338 return QMainWindow::event(e);
1342 return QMainWindow::event(e);
1346 void GuiView::resetWindowTitle()
1348 setWindowTitle(qt_("LyX"));
1351 bool GuiView::focusNextPrevChild(bool /*next*/)
1358 bool GuiView::busy() const
1364 void GuiView::setBusy(bool busy)
1366 bool const busy_before = busy_ > 0;
1367 busy ? ++busy_ : --busy_;
1368 if ((busy_ > 0) == busy_before)
1369 // busy state didn't change
1373 QApplication::setOverrideCursor(Qt::WaitCursor);
1376 QApplication::restoreOverrideCursor();
1381 void GuiView::resetCommandExecute()
1383 command_execute_ = false;
1388 double GuiView::pixelRatio() const
1390 #if QT_VERSION >= 0x050000
1391 return devicePixelRatio();
1398 GuiWorkArea * GuiView::workArea(int index)
1400 if (TabWorkArea * twa = d.currentTabWorkArea())
1401 if (index < twa->count())
1402 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1407 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1409 if (currentWorkArea()
1410 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1411 return currentWorkArea();
1412 if (TabWorkArea * twa = d.currentTabWorkArea())
1413 return twa->workArea(buffer);
1418 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1420 // Automatically create a TabWorkArea if there are none yet.
1421 TabWorkArea * tab_widget = d.splitter_->count()
1422 ? d.currentTabWorkArea() : addTabWorkArea();
1423 return tab_widget->addWorkArea(buffer, *this);
1427 TabWorkArea * GuiView::addTabWorkArea()
1429 TabWorkArea * twa = new TabWorkArea;
1430 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1431 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1432 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1433 this, SLOT(on_lastWorkAreaRemoved()));
1435 d.splitter_->addWidget(twa);
1436 d.stack_widget_->setCurrentWidget(d.splitter_);
1441 GuiWorkArea const * GuiView::currentWorkArea() const
1443 return d.current_work_area_;
1447 GuiWorkArea * GuiView::currentWorkArea()
1449 return d.current_work_area_;
1453 GuiWorkArea const * GuiView::currentMainWorkArea() const
1455 if (!d.currentTabWorkArea())
1457 return d.currentTabWorkArea()->currentWorkArea();
1461 GuiWorkArea * GuiView::currentMainWorkArea()
1463 if (!d.currentTabWorkArea())
1465 return d.currentTabWorkArea()->currentWorkArea();
1469 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1471 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1473 d.current_work_area_ = 0;
1475 Q_EMIT bufferViewChanged();
1479 // FIXME: I've no clue why this is here and why it accesses
1480 // theGuiApp()->currentView, which might be 0 (bug 6464).
1481 // See also 27525 (vfr).
1482 if (theGuiApp()->currentView() == this
1483 && theGuiApp()->currentView()->currentWorkArea() == wa)
1486 if (currentBufferView())
1487 cap::saveSelection(currentBufferView()->cursor());
1489 theGuiApp()->setCurrentView(this);
1490 d.current_work_area_ = wa;
1492 // We need to reset this now, because it will need to be
1493 // right if the tabWorkArea gets reset in the for loop. We
1494 // will change it back if we aren't in that case.
1495 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1496 d.current_main_work_area_ = wa;
1498 for (int i = 0; i != d.splitter_->count(); ++i) {
1499 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1500 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1501 << ", Current main wa: " << currentMainWorkArea());
1506 d.current_main_work_area_ = old_cmwa;
1508 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1509 on_currentWorkAreaChanged(wa);
1510 BufferView & bv = wa->bufferView();
1511 bv.cursor().fixIfBroken();
1513 wa->setUpdatesEnabled(true);
1514 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1518 void GuiView::removeWorkArea(GuiWorkArea * wa)
1520 LASSERT(wa, return);
1521 if (wa == d.current_work_area_) {
1523 disconnectBufferView();
1524 d.current_work_area_ = 0;
1525 d.current_main_work_area_ = 0;
1528 bool found_twa = false;
1529 for (int i = 0; i != d.splitter_->count(); ++i) {
1530 TabWorkArea * twa = d.tabWorkArea(i);
1531 if (twa->removeWorkArea(wa)) {
1532 // Found in this tab group, and deleted the GuiWorkArea.
1534 if (twa->count() != 0) {
1535 if (d.current_work_area_ == 0)
1536 // This means that we are closing the current GuiWorkArea, so
1537 // switch to the next GuiWorkArea in the found TabWorkArea.
1538 setCurrentWorkArea(twa->currentWorkArea());
1540 // No more WorkAreas in this tab group, so delete it.
1547 // It is not a tabbed work area (i.e., the search work area), so it
1548 // should be deleted by other means.
1549 LASSERT(found_twa, return);
1551 if (d.current_work_area_ == 0) {
1552 if (d.splitter_->count() != 0) {
1553 TabWorkArea * twa = d.currentTabWorkArea();
1554 setCurrentWorkArea(twa->currentWorkArea());
1556 // No more work areas, switch to the background widget.
1557 setCurrentWorkArea(0);
1563 LayoutBox * GuiView::getLayoutDialog() const
1569 void GuiView::updateLayoutList()
1572 d.layout_->updateContents(false);
1576 void GuiView::updateToolbars()
1578 ToolbarMap::iterator end = d.toolbars_.end();
1579 if (d.current_work_area_) {
1581 if (d.current_work_area_->bufferView().cursor().inMathed()
1582 && !d.current_work_area_->bufferView().cursor().inRegexped())
1583 context |= Toolbars::MATH;
1584 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1585 context |= Toolbars::TABLE;
1586 if (currentBufferView()->buffer().areChangesPresent()
1587 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1588 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1589 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1590 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1591 context |= Toolbars::REVIEW;
1592 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1593 context |= Toolbars::MATHMACROTEMPLATE;
1594 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1595 context |= Toolbars::IPA;
1596 if (command_execute_)
1597 context |= Toolbars::MINIBUFFER;
1598 if (minibuffer_focus_) {
1599 context |= Toolbars::MINIBUFFER_FOCUS;
1600 minibuffer_focus_ = false;
1603 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1604 it->second->update(context);
1606 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1607 it->second->update();
1611 void GuiView::setBuffer(Buffer * newBuffer)
1613 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1614 LASSERT(newBuffer, return);
1616 GuiWorkArea * wa = workArea(*newBuffer);
1619 newBuffer->masterBuffer()->updateBuffer();
1621 wa = addWorkArea(*newBuffer);
1622 // scroll to the position when the BufferView was last closed
1623 if (lyxrc.use_lastfilepos) {
1624 LastFilePosSection::FilePos filepos =
1625 theSession().lastFilePos().load(newBuffer->fileName());
1626 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1629 //Disconnect the old buffer...there's no new one.
1632 connectBuffer(*newBuffer);
1633 connectBufferView(wa->bufferView());
1634 setCurrentWorkArea(wa);
1638 void GuiView::connectBuffer(Buffer & buf)
1640 buf.setGuiDelegate(this);
1644 void GuiView::disconnectBuffer()
1646 if (d.current_work_area_)
1647 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1651 void GuiView::connectBufferView(BufferView & bv)
1653 bv.setGuiDelegate(this);
1657 void GuiView::disconnectBufferView()
1659 if (d.current_work_area_)
1660 d.current_work_area_->bufferView().setGuiDelegate(0);
1664 void GuiView::errors(string const & error_type, bool from_master)
1666 BufferView const * const bv = currentBufferView();
1670 #if EXPORT_in_THREAD
1671 // We are called with from_master == false by default, so we
1672 // have to figure out whether that is the case or not.
1673 ErrorList & el = bv->buffer().errorList(error_type);
1675 el = bv->buffer().masterBuffer()->errorList(error_type);
1679 ErrorList const & el = from_master ?
1680 bv->buffer().masterBuffer()->errorList(error_type) :
1681 bv->buffer().errorList(error_type);
1687 string data = error_type;
1689 data = "from_master|" + error_type;
1690 showDialog("errorlist", data);
1694 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1696 d.toc_models_.updateItem(toqstr(type), dit);
1700 void GuiView::structureChanged()
1702 // FIXME: This is slightly expensive, though less than the tocBackend update
1703 // (#9880). This also resets the view in the Toc Widget (#6675).
1704 d.toc_models_.reset(documentBufferView());
1705 // Navigator needs more than a simple update in this case. It needs to be
1707 updateDialog("toc", "");
1711 void GuiView::updateDialog(string const & name, string const & data)
1713 if (!isDialogVisible(name))
1716 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1717 if (it == d.dialogs_.end())
1720 Dialog * const dialog = it->second.get();
1721 if (dialog->isVisibleView())
1722 dialog->initialiseParams(data);
1726 BufferView * GuiView::documentBufferView()
1728 return currentMainWorkArea()
1729 ? ¤tMainWorkArea()->bufferView()
1734 BufferView const * GuiView::documentBufferView() const
1736 return currentMainWorkArea()
1737 ? ¤tMainWorkArea()->bufferView()
1742 BufferView * GuiView::currentBufferView()
1744 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1748 BufferView const * GuiView::currentBufferView() const
1750 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1754 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1755 Buffer const * orig, Buffer * clone)
1757 bool const success = clone->autoSave();
1759 busyBuffers.remove(orig);
1761 ? _("Automatic save done.")
1762 : _("Automatic save failed!");
1766 void GuiView::autoSave()
1768 LYXERR(Debug::INFO, "Running autoSave()");
1770 Buffer * buffer = documentBufferView()
1771 ? &documentBufferView()->buffer() : 0;
1773 resetAutosaveTimers();
1777 GuiViewPrivate::busyBuffers.insert(buffer);
1778 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1779 buffer, buffer->cloneBufferOnly());
1780 d.autosave_watcher_.setFuture(f);
1781 resetAutosaveTimers();
1785 void GuiView::resetAutosaveTimers()
1788 d.autosave_timeout_.restart();
1792 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1795 Buffer * buf = currentBufferView()
1796 ? ¤tBufferView()->buffer() : 0;
1797 Buffer * doc_buffer = documentBufferView()
1798 ? &(documentBufferView()->buffer()) : 0;
1801 /* In LyX/Mac, when a dialog is open, the menus of the
1802 application can still be accessed without giving focus to
1803 the main window. In this case, we want to disable the menu
1804 entries that are buffer-related.
1805 This code must not be used on Linux and Windows, since it
1806 would disable buffer-related entries when hovering over the
1807 menu (see bug #9574).
1809 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1815 // Check whether we need a buffer
1816 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1817 // no, exit directly
1818 flag.message(from_utf8(N_("Command not allowed with"
1819 "out any document open")));
1820 flag.setEnabled(false);
1824 if (cmd.origin() == FuncRequest::TOC) {
1825 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1826 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1827 flag.setEnabled(false);
1831 switch(cmd.action()) {
1832 case LFUN_BUFFER_IMPORT:
1835 case LFUN_MASTER_BUFFER_UPDATE:
1836 case LFUN_MASTER_BUFFER_VIEW:
1838 && (doc_buffer->parent() != 0
1839 || doc_buffer->hasChildren())
1840 && !d.processing_thread_watcher_.isRunning();
1843 case LFUN_BUFFER_UPDATE:
1844 case LFUN_BUFFER_VIEW: {
1845 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1849 string format = to_utf8(cmd.argument());
1850 if (cmd.argument().empty())
1851 format = doc_buffer->params().getDefaultOutputFormat();
1852 enable = doc_buffer->params().isExportableFormat(format);
1856 case LFUN_BUFFER_RELOAD:
1857 enable = doc_buffer && !doc_buffer->isUnnamed()
1858 && doc_buffer->fileName().exists()
1859 && (!doc_buffer->isClean()
1860 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1863 case LFUN_BUFFER_CHILD_OPEN:
1864 enable = doc_buffer != 0;
1867 case LFUN_BUFFER_WRITE:
1868 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1871 //FIXME: This LFUN should be moved to GuiApplication.
1872 case LFUN_BUFFER_WRITE_ALL: {
1873 // We enable the command only if there are some modified buffers
1874 Buffer * first = theBufferList().first();
1879 // We cannot use a for loop as the buffer list is a cycle.
1881 if (!b->isClean()) {
1885 b = theBufferList().next(b);
1886 } while (b != first);
1890 case LFUN_BUFFER_WRITE_AS:
1891 case LFUN_BUFFER_EXPORT_AS:
1892 enable = doc_buffer != 0;
1895 case LFUN_BUFFER_CLOSE:
1896 case LFUN_VIEW_CLOSE:
1897 enable = doc_buffer != 0;
1900 case LFUN_BUFFER_CLOSE_ALL:
1901 enable = theBufferList().last() != theBufferList().first();
1904 case LFUN_VIEW_SPLIT:
1905 if (cmd.getArg(0) == "vertical")
1906 enable = doc_buffer && (d.splitter_->count() == 1 ||
1907 d.splitter_->orientation() == Qt::Vertical);
1909 enable = doc_buffer && (d.splitter_->count() == 1 ||
1910 d.splitter_->orientation() == Qt::Horizontal);
1913 case LFUN_TAB_GROUP_CLOSE:
1914 enable = d.tabWorkAreaCount() > 1;
1917 case LFUN_TOOLBAR_TOGGLE: {
1918 string const name = cmd.getArg(0);
1919 if (GuiToolbar * t = toolbar(name))
1920 flag.setOnOff(t->isVisible());
1923 docstring const msg =
1924 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1930 case LFUN_DROP_LAYOUTS_CHOICE:
1934 case LFUN_UI_TOGGLE:
1935 flag.setOnOff(isFullScreen());
1938 case LFUN_DIALOG_DISCONNECT_INSET:
1941 case LFUN_DIALOG_HIDE:
1942 // FIXME: should we check if the dialog is shown?
1945 case LFUN_DIALOG_TOGGLE:
1946 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1947 // fall through to set "enable"
1948 case LFUN_DIALOG_SHOW: {
1949 string const name = cmd.getArg(0);
1951 enable = name == "aboutlyx"
1952 || name == "file" //FIXME: should be removed.
1954 || name == "texinfo"
1955 || name == "progress"
1956 || name == "compare";
1957 else if (name == "character" || name == "symbols"
1958 || name == "mathdelimiter" || name == "mathmatrix") {
1959 if (!buf || buf->isReadonly())
1962 Cursor const & cur = currentBufferView()->cursor();
1963 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1966 else if (name == "latexlog")
1967 enable = FileName(doc_buffer->logName()).isReadableFile();
1968 else if (name == "spellchecker")
1969 enable = theSpellChecker()
1970 && !doc_buffer->isReadonly()
1971 && !doc_buffer->text().empty();
1972 else if (name == "vclog")
1973 enable = doc_buffer->lyxvc().inUse();
1977 case LFUN_DIALOG_UPDATE: {
1978 string const name = cmd.getArg(0);
1980 enable = name == "prefs";
1984 case LFUN_COMMAND_EXECUTE:
1986 case LFUN_MENU_OPEN:
1987 // Nothing to check.
1990 case LFUN_COMPLETION_INLINE:
1991 if (!d.current_work_area_
1992 || !d.current_work_area_->completer().inlinePossible(
1993 currentBufferView()->cursor()))
1997 case LFUN_COMPLETION_POPUP:
1998 if (!d.current_work_area_
1999 || !d.current_work_area_->completer().popupPossible(
2000 currentBufferView()->cursor()))
2005 if (!d.current_work_area_
2006 || !d.current_work_area_->completer().inlinePossible(
2007 currentBufferView()->cursor()))
2011 case LFUN_COMPLETION_ACCEPT:
2012 if (!d.current_work_area_
2013 || (!d.current_work_area_->completer().popupVisible()
2014 && !d.current_work_area_->completer().inlineVisible()
2015 && !d.current_work_area_->completer().completionAvailable()))
2019 case LFUN_COMPLETION_CANCEL:
2020 if (!d.current_work_area_
2021 || (!d.current_work_area_->completer().popupVisible()
2022 && !d.current_work_area_->completer().inlineVisible()))
2026 case LFUN_BUFFER_ZOOM_OUT:
2027 case LFUN_BUFFER_ZOOM_IN: {
2028 // only diff between these two is that the default for ZOOM_OUT
2030 bool const neg_zoom =
2031 convert<int>(cmd.argument()) < 0 ||
2032 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2033 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2034 docstring const msg =
2035 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2039 enable = doc_buffer;
2042 case LFUN_BUFFER_MOVE_NEXT:
2043 case LFUN_BUFFER_MOVE_PREVIOUS:
2044 // we do not cycle when moving
2045 case LFUN_BUFFER_NEXT:
2046 case LFUN_BUFFER_PREVIOUS:
2047 // because we cycle, it doesn't matter whether on first or last
2048 enable = (d.currentTabWorkArea()->count() > 1);
2050 case LFUN_BUFFER_SWITCH:
2051 // toggle on the current buffer, but do not toggle off
2052 // the other ones (is that a good idea?)
2054 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2055 flag.setOnOff(true);
2058 case LFUN_VC_REGISTER:
2059 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2061 case LFUN_VC_RENAME:
2062 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2065 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2067 case LFUN_VC_CHECK_IN:
2068 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2070 case LFUN_VC_CHECK_OUT:
2071 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2073 case LFUN_VC_LOCKING_TOGGLE:
2074 enable = doc_buffer && !doc_buffer->isReadonly()
2075 && doc_buffer->lyxvc().lockingToggleEnabled();
2076 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2078 case LFUN_VC_REVERT:
2079 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2081 case LFUN_VC_UNDO_LAST:
2082 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2084 case LFUN_VC_REPO_UPDATE:
2085 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2087 case LFUN_VC_COMMAND: {
2088 if (cmd.argument().empty())
2090 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2094 case LFUN_VC_COMPARE:
2095 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2098 case LFUN_SERVER_GOTO_FILE_ROW:
2099 case LFUN_LYX_ACTIVATE:
2101 case LFUN_FORWARD_SEARCH:
2102 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2105 case LFUN_FILE_INSERT_PLAINTEXT:
2106 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2107 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2110 case LFUN_SPELLING_CONTINUOUSLY:
2111 flag.setOnOff(lyxrc.spellcheck_continuously);
2119 flag.setEnabled(false);
2125 static FileName selectTemplateFile()
2127 FileDialog dlg(qt_("Select template file"));
2128 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2129 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2131 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2132 QStringList(qt_("LyX Documents (*.lyx)")));
2134 if (result.first == FileDialog::Later)
2136 if (result.second.isEmpty())
2138 return FileName(fromqstr(result.second));
2142 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2146 Buffer * newBuffer = 0;
2148 newBuffer = checkAndLoadLyXFile(filename);
2149 } catch (ExceptionMessage const & e) {
2156 message(_("Document not loaded."));
2160 setBuffer(newBuffer);
2161 newBuffer->errors("Parse");
2164 theSession().lastFiles().add(filename);
2170 void GuiView::openDocument(string const & fname)
2172 string initpath = lyxrc.document_path;
2174 if (documentBufferView()) {
2175 string const trypath = documentBufferView()->buffer().filePath();
2176 // If directory is writeable, use this as default.
2177 if (FileName(trypath).isDirWritable())
2183 if (fname.empty()) {
2184 FileDialog dlg(qt_("Select document to open"));
2185 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2186 dlg.setButton2(qt_("Examples|#E#e"),
2187 toqstr(addPath(package().system_support().absFileName(), "examples")));
2189 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2190 FileDialog::Result result =
2191 dlg.open(toqstr(initpath), filter);
2193 if (result.first == FileDialog::Later)
2196 filename = fromqstr(result.second);
2198 // check selected filename
2199 if (filename.empty()) {
2200 message(_("Canceled."));
2206 // get absolute path of file and add ".lyx" to the filename if
2208 FileName const fullname =
2209 fileSearch(string(), filename, "lyx", support::may_not_exist);
2210 if (!fullname.empty())
2211 filename = fullname.absFileName();
2213 if (!fullname.onlyPath().isDirectory()) {
2214 Alert::warning(_("Invalid filename"),
2215 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2216 from_utf8(fullname.absFileName())));
2220 // if the file doesn't exist and isn't already open (bug 6645),
2221 // let the user create one
2222 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2223 !LyXVC::file_not_found_hook(fullname)) {
2224 // the user specifically chose this name. Believe him.
2225 Buffer * const b = newFile(filename, string(), true);
2231 docstring const disp_fn = makeDisplayPath(filename);
2232 message(bformat(_("Opening document %1$s..."), disp_fn));
2235 Buffer * buf = loadDocument(fullname);
2237 str2 = bformat(_("Document %1$s opened."), disp_fn);
2238 if (buf->lyxvc().inUse())
2239 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2240 " " + _("Version control detected.");
2242 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2247 // FIXME: clean that
2248 static bool import(GuiView * lv, FileName const & filename,
2249 string const & format, ErrorList & errorList)
2251 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2253 string loader_format;
2254 vector<string> loaders = theConverters().loaders();
2255 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2256 vector<string>::const_iterator it = loaders.begin();
2257 vector<string>::const_iterator en = loaders.end();
2258 for (; it != en; ++it) {
2259 if (!theConverters().isReachable(format, *it))
2262 string const tofile =
2263 support::changeExtension(filename.absFileName(),
2264 formats.extension(*it));
2265 if (!theConverters().convert(0, filename, FileName(tofile),
2266 filename, format, *it, errorList))
2268 loader_format = *it;
2271 if (loader_format.empty()) {
2272 frontend::Alert::error(_("Couldn't import file"),
2273 bformat(_("No information for importing the format %1$s."),
2274 formats.prettyName(format)));
2278 loader_format = format;
2280 if (loader_format == "lyx") {
2281 Buffer * buf = lv->loadDocument(lyxfile);
2285 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2289 bool as_paragraphs = loader_format == "textparagraph";
2290 string filename2 = (loader_format == format) ? filename.absFileName()
2291 : support::changeExtension(filename.absFileName(),
2292 formats.extension(loader_format));
2293 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2295 guiApp->setCurrentView(lv);
2296 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2303 void GuiView::importDocument(string const & argument)
2306 string filename = split(argument, format, ' ');
2308 LYXERR(Debug::INFO, format << " file: " << filename);
2310 // need user interaction
2311 if (filename.empty()) {
2312 string initpath = lyxrc.document_path;
2313 if (documentBufferView()) {
2314 string const trypath = documentBufferView()->buffer().filePath();
2315 // If directory is writeable, use this as default.
2316 if (FileName(trypath).isDirWritable())
2320 docstring const text = bformat(_("Select %1$s file to import"),
2321 formats.prettyName(format));
2323 FileDialog dlg(toqstr(text));
2324 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2325 dlg.setButton2(qt_("Examples|#E#e"),
2326 toqstr(addPath(package().system_support().absFileName(), "examples")));
2328 docstring filter = formats.prettyName(format);
2331 filter += from_utf8(formats.extensions(format));
2334 FileDialog::Result result =
2335 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2337 if (result.first == FileDialog::Later)
2340 filename = fromqstr(result.second);
2342 // check selected filename
2343 if (filename.empty())
2344 message(_("Canceled."));
2347 if (filename.empty())
2350 // get absolute path of file
2351 FileName const fullname(support::makeAbsPath(filename));
2353 // Can happen if the user entered a path into the dialog
2355 if (fullname.onlyFileName().empty()) {
2356 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2357 "Aborting import."),
2358 from_utf8(fullname.absFileName()));
2359 frontend::Alert::error(_("File name error"), msg);
2360 message(_("Canceled."));
2365 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2367 // Check if the document already is open
2368 Buffer * buf = theBufferList().getBuffer(lyxfile);
2371 if (!closeBuffer()) {
2372 message(_("Canceled."));
2377 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2379 // if the file exists already, and we didn't do
2380 // -i lyx thefile.lyx, warn
2381 if (lyxfile.exists() && fullname != lyxfile) {
2383 docstring text = bformat(_("The document %1$s already exists.\n\n"
2384 "Do you want to overwrite that document?"), displaypath);
2385 int const ret = Alert::prompt(_("Overwrite document?"),
2386 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2389 message(_("Canceled."));
2394 message(bformat(_("Importing %1$s..."), displaypath));
2395 ErrorList errorList;
2396 if (import(this, fullname, format, errorList))
2397 message(_("imported."));
2399 message(_("file not imported!"));
2401 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2405 void GuiView::newDocument(string const & filename, bool from_template)
2407 FileName initpath(lyxrc.document_path);
2408 if (documentBufferView()) {
2409 FileName const trypath(documentBufferView()->buffer().filePath());
2410 // If directory is writeable, use this as default.
2411 if (trypath.isDirWritable())
2415 string templatefile;
2416 if (from_template) {
2417 templatefile = selectTemplateFile().absFileName();
2418 if (templatefile.empty())
2423 if (filename.empty())
2424 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2426 b = newFile(filename, templatefile, true);
2431 // If no new document could be created, it is unsure
2432 // whether there is a valid BufferView.
2433 if (currentBufferView())
2434 // Ensure the cursor is correctly positioned on screen.
2435 currentBufferView()->showCursor();
2439 void GuiView::insertLyXFile(docstring const & fname)
2441 BufferView * bv = documentBufferView();
2446 FileName filename(to_utf8(fname));
2447 if (filename.empty()) {
2448 // Launch a file browser
2450 string initpath = lyxrc.document_path;
2451 string const trypath = bv->buffer().filePath();
2452 // If directory is writeable, use this as default.
2453 if (FileName(trypath).isDirWritable())
2457 FileDialog dlg(qt_("Select LyX document to insert"));
2458 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2459 dlg.setButton2(qt_("Examples|#E#e"),
2460 toqstr(addPath(package().system_support().absFileName(),
2463 FileDialog::Result result = dlg.open(toqstr(initpath),
2464 QStringList(qt_("LyX Documents (*.lyx)")));
2466 if (result.first == FileDialog::Later)
2470 filename.set(fromqstr(result.second));
2472 // check selected filename
2473 if (filename.empty()) {
2474 // emit message signal.
2475 message(_("Canceled."));
2480 bv->insertLyXFile(filename);
2481 bv->buffer().errors("Parse");
2485 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2487 FileName fname = b.fileName();
2488 FileName const oldname = fname;
2490 if (!newname.empty()) {
2492 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2494 // Switch to this Buffer.
2497 // No argument? Ask user through dialog.
2499 FileDialog dlg(qt_("Choose a filename to save document as"));
2500 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2501 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2503 if (!isLyXFileName(fname.absFileName()))
2504 fname.changeExtension(".lyx");
2506 FileDialog::Result result =
2507 dlg.save(toqstr(fname.onlyPath().absFileName()),
2508 QStringList(qt_("LyX Documents (*.lyx)")),
2509 toqstr(fname.onlyFileName()));
2511 if (result.first == FileDialog::Later)
2514 fname.set(fromqstr(result.second));
2519 if (!isLyXFileName(fname.absFileName()))
2520 fname.changeExtension(".lyx");
2523 // fname is now the new Buffer location.
2525 // if there is already a Buffer open with this name, we do not want
2526 // to have another one. (the second test makes sure we're not just
2527 // trying to overwrite ourselves, which is fine.)
2528 if (theBufferList().exists(fname) && fname != oldname
2529 && theBufferList().getBuffer(fname) != &b) {
2530 docstring const text =
2531 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2532 "Please close it before attempting to overwrite it.\n"
2533 "Do you want to choose a new filename?"),
2534 from_utf8(fname.absFileName()));
2535 int const ret = Alert::prompt(_("Chosen File Already Open"),
2536 text, 0, 1, _("&Rename"), _("&Cancel"));
2538 case 0: return renameBuffer(b, docstring(), kind);
2539 case 1: return false;
2544 bool const existsLocal = fname.exists();
2545 bool const existsInVC = LyXVC::fileInVC(fname);
2546 if (existsLocal || existsInVC) {
2547 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2548 if (kind != LV_WRITE_AS && existsInVC) {
2549 // renaming to a name that is already in VC
2551 docstring text = bformat(_("The document %1$s "
2552 "is already registered.\n\n"
2553 "Do you want to choose a new name?"),
2555 docstring const title = (kind == LV_VC_RENAME) ?
2556 _("Rename document?") : _("Copy document?");
2557 docstring const button = (kind == LV_VC_RENAME) ?
2558 _("&Rename") : _("&Copy");
2559 int const ret = Alert::prompt(title, text, 0, 1,
2560 button, _("&Cancel"));
2562 case 0: return renameBuffer(b, docstring(), kind);
2563 case 1: return false;
2568 docstring text = bformat(_("The document %1$s "
2569 "already exists.\n\n"
2570 "Do you want to overwrite that document?"),
2572 int const ret = Alert::prompt(_("Overwrite document?"),
2573 text, 0, 2, _("&Overwrite"),
2574 _("&Rename"), _("&Cancel"));
2577 case 1: return renameBuffer(b, docstring(), kind);
2578 case 2: return false;
2584 case LV_VC_RENAME: {
2585 string msg = b.lyxvc().rename(fname);
2588 message(from_utf8(msg));
2592 string msg = b.lyxvc().copy(fname);
2595 message(from_utf8(msg));
2601 // LyXVC created the file already in case of LV_VC_RENAME or
2602 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2603 // relative paths of included stuff right if we moved e.g. from
2604 // /a/b.lyx to /a/c/b.lyx.
2606 bool const saved = saveBuffer(b, fname);
2613 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2615 FileName fname = b.fileName();
2617 FileDialog dlg(qt_("Choose a filename to export the document as"));
2618 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2621 QString const anyformat = qt_("Guess from extension (*.*)");
2624 vector<Format const *> export_formats;
2625 for (Format const & f : formats)
2626 if (f.documentFormat())
2627 export_formats.push_back(&f);
2628 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2629 map<QString, string> fmap;
2632 for (Format const * f : export_formats) {
2633 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2634 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2636 from_ascii(f->extension())));
2637 types << loc_filter;
2638 fmap[loc_filter] = f->name();
2639 if (from_ascii(f->name()) == iformat) {
2640 filter = loc_filter;
2641 ext = f->extension();
2644 string ofname = fname.onlyFileName();
2646 ofname = support::changeExtension(ofname, ext);
2647 FileDialog::Result result =
2648 dlg.save(toqstr(fname.onlyPath().absFileName()),
2652 if (result.first != FileDialog::Chosen)
2656 fname.set(fromqstr(result.second));
2657 if (filter == anyformat)
2658 fmt_name = formats.getFormatFromExtension(fname.extension());
2660 fmt_name = fmap[filter];
2661 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2662 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2664 if (fmt_name.empty() || fname.empty())
2667 // fname is now the new Buffer location.
2668 if (FileName(fname).exists()) {
2669 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2670 docstring text = bformat(_("The document %1$s already "
2671 "exists.\n\nDo you want to "
2672 "overwrite that document?"),
2674 int const ret = Alert::prompt(_("Overwrite document?"),
2675 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2678 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2679 case 2: return false;
2683 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2686 return dr.dispatched();
2690 bool GuiView::saveBuffer(Buffer & b)
2692 return saveBuffer(b, FileName());
2696 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2698 if (workArea(b) && workArea(b)->inDialogMode())
2701 if (fn.empty() && b.isUnnamed())
2702 return renameBuffer(b, docstring());
2704 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2706 theSession().lastFiles().add(b.fileName());
2710 // Switch to this Buffer.
2713 // FIXME: we don't tell the user *WHY* the save failed !!
2714 docstring const file = makeDisplayPath(b.absFileName(), 30);
2715 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2716 "Do you want to rename the document and "
2717 "try again?"), file);
2718 int const ret = Alert::prompt(_("Rename and save?"),
2719 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2722 if (!renameBuffer(b, docstring()))
2731 return saveBuffer(b, fn);
2735 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2737 return closeWorkArea(wa, false);
2741 // We only want to close the buffer if it is not visible in other workareas
2742 // of the same view, nor in other views, and if this is not a child
2743 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2745 Buffer & buf = wa->bufferView().buffer();
2747 bool last_wa = d.countWorkAreasOf(buf) == 1
2748 && !inOtherView(buf) && !buf.parent();
2750 bool close_buffer = last_wa;
2753 if (lyxrc.close_buffer_with_last_view == "yes")
2755 else if (lyxrc.close_buffer_with_last_view == "no")
2756 close_buffer = false;
2759 if (buf.isUnnamed())
2760 file = from_utf8(buf.fileName().onlyFileName());
2762 file = buf.fileName().displayName(30);
2763 docstring const text = bformat(
2764 _("Last view on document %1$s is being closed.\n"
2765 "Would you like to close or hide the document?\n"
2767 "Hidden documents can be displayed back through\n"
2768 "the menu: View->Hidden->...\n"
2770 "To remove this question, set your preference in:\n"
2771 " Tools->Preferences->Look&Feel->UserInterface\n"
2773 int ret = Alert::prompt(_("Close or hide document?"),
2774 text, 0, 1, _("&Close"), _("&Hide"));
2775 close_buffer = (ret == 0);
2779 return closeWorkArea(wa, close_buffer);
2783 bool GuiView::closeBuffer()
2785 GuiWorkArea * wa = currentMainWorkArea();
2786 // coverity complained about this
2787 // it seems unnecessary, but perhaps is worth the check
2788 LASSERT(wa, return false);
2790 setCurrentWorkArea(wa);
2791 Buffer & buf = wa->bufferView().buffer();
2792 return closeWorkArea(wa, !buf.parent());
2796 void GuiView::writeSession() const {
2797 GuiWorkArea const * active_wa = currentMainWorkArea();
2798 for (int i = 0; i < d.splitter_->count(); ++i) {
2799 TabWorkArea * twa = d.tabWorkArea(i);
2800 for (int j = 0; j < twa->count(); ++j) {
2801 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2802 Buffer & buf = wa->bufferView().buffer();
2803 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2809 bool GuiView::closeBufferAll()
2811 // Close the workareas in all other views
2812 QList<int> const ids = guiApp->viewIds();
2813 for (int i = 0; i != ids.size(); ++i) {
2814 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2818 // Close our own workareas
2819 if (!closeWorkAreaAll())
2822 // Now close the hidden buffers. We prevent hidden buffers from being
2823 // dirty, so we can just close them.
2824 theBufferList().closeAll();
2829 bool GuiView::closeWorkAreaAll()
2831 setCurrentWorkArea(currentMainWorkArea());
2833 // We might be in a situation that there is still a tabWorkArea, but
2834 // there are no tabs anymore. This can happen when we get here after a
2835 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2836 // many TabWorkArea's have no documents anymore.
2839 // We have to call count() each time, because it can happen that
2840 // more than one splitter will disappear in one iteration (bug 5998).
2841 while (d.splitter_->count() > empty_twa) {
2842 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2844 if (twa->count() == 0)
2847 setCurrentWorkArea(twa->currentWorkArea());
2848 if (!closeTabWorkArea(twa))
2856 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2861 Buffer & buf = wa->bufferView().buffer();
2863 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2864 Alert::warning(_("Close document"),
2865 _("Document could not be closed because it is being processed by LyX."));
2870 return closeBuffer(buf);
2872 if (!inMultiTabs(wa))
2873 if (!saveBufferIfNeeded(buf, true))
2881 bool GuiView::closeBuffer(Buffer & buf)
2883 // If we are in a close_event all children will be closed in some time,
2884 // so no need to do it here. This will ensure that the children end up
2885 // in the session file in the correct order. If we close the master
2886 // buffer, we can close or release the child buffers here too.
2887 bool success = true;
2889 ListOfBuffers clist = buf.getChildren();
2890 ListOfBuffers::const_iterator it = clist.begin();
2891 ListOfBuffers::const_iterator const bend = clist.end();
2892 for (; it != bend; ++it) {
2893 Buffer * child_buf = *it;
2894 if (theBufferList().isOthersChild(&buf, child_buf)) {
2895 child_buf->setParent(0);
2899 // FIXME: should we look in other tabworkareas?
2900 // ANSWER: I don't think so. I've tested, and if the child is
2901 // open in some other window, it closes without a problem.
2902 GuiWorkArea * child_wa = workArea(*child_buf);
2904 success = closeWorkArea(child_wa, true);
2908 // In this case the child buffer is open but hidden.
2909 // It therefore should not (MUST NOT) be dirty!
2910 LATTEST(child_buf->isClean());
2911 theBufferList().release(child_buf);
2916 // goto bookmark to update bookmark pit.
2917 // FIXME: we should update only the bookmarks related to this buffer!
2918 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2919 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2920 guiApp->gotoBookmark(i+1, false, false);
2922 if (saveBufferIfNeeded(buf, false)) {
2923 buf.removeAutosaveFile();
2924 theBufferList().release(&buf);
2928 // open all children again to avoid a crash because of dangling
2929 // pointers (bug 6603)
2935 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2937 while (twa == d.currentTabWorkArea()) {
2938 twa->setCurrentIndex(twa->count() - 1);
2940 GuiWorkArea * wa = twa->currentWorkArea();
2941 Buffer & b = wa->bufferView().buffer();
2943 // We only want to close the buffer if the same buffer is not visible
2944 // in another view, and if this is not a child and if we are closing
2945 // a view (not a tabgroup).
2946 bool const close_buffer =
2947 !inOtherView(b) && !b.parent() && closing_;
2949 if (!closeWorkArea(wa, close_buffer))
2956 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2958 if (buf.isClean() || buf.paragraphs().empty())
2961 // Switch to this Buffer.
2966 if (buf.isUnnamed())
2967 file = from_utf8(buf.fileName().onlyFileName());
2969 file = buf.fileName().displayName(30);
2971 // Bring this window to top before asking questions.
2976 if (hiding && buf.isUnnamed()) {
2977 docstring const text = bformat(_("The document %1$s has not been "
2978 "saved yet.\n\nDo you want to save "
2979 "the document?"), file);
2980 ret = Alert::prompt(_("Save new document?"),
2981 text, 0, 1, _("&Save"), _("&Cancel"));
2985 docstring const text = bformat(_("The document %1$s has unsaved changes."
2986 "\n\nDo you want to save the document or discard the changes?"), file);
2987 ret = Alert::prompt(_("Save changed document?"),
2988 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2993 if (!saveBuffer(buf))
2997 // If we crash after this we could have no autosave file
2998 // but I guess this is really improbable (Jug).
2999 // Sometimes improbable things happen:
3000 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3001 // buf.removeAutosaveFile();
3003 // revert all changes
3014 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3016 Buffer & buf = wa->bufferView().buffer();
3018 for (int i = 0; i != d.splitter_->count(); ++i) {
3019 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3020 if (wa_ && wa_ != wa)
3023 return inOtherView(buf);
3027 bool GuiView::inOtherView(Buffer & buf)
3029 QList<int> const ids = guiApp->viewIds();
3031 for (int i = 0; i != ids.size(); ++i) {
3035 if (guiApp->view(ids[i]).workArea(buf))
3042 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3044 if (!documentBufferView())
3047 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3048 Buffer * const curbuf = &documentBufferView()->buffer();
3049 int nwa = twa->count();
3050 for (int i = 0; i < nwa; ++i) {
3051 if (&workArea(i)->bufferView().buffer() == curbuf) {
3053 if (np == NEXTBUFFER)
3054 next_index = (i == nwa - 1 ? 0 : i + 1);
3056 next_index = (i == 0 ? nwa - 1 : i - 1);
3058 twa->moveTab(i, next_index);
3060 setBuffer(&workArea(next_index)->bufferView().buffer());
3068 /// make sure the document is saved
3069 static bool ensureBufferClean(Buffer * buffer)
3071 LASSERT(buffer, return false);
3072 if (buffer->isClean() && !buffer->isUnnamed())
3075 docstring const file = buffer->fileName().displayName(30);
3078 if (!buffer->isUnnamed()) {
3079 text = bformat(_("The document %1$s has unsaved "
3080 "changes.\n\nDo you want to save "
3081 "the document?"), file);
3082 title = _("Save changed document?");
3085 text = bformat(_("The document %1$s has not been "
3086 "saved yet.\n\nDo you want to save "
3087 "the document?"), file);
3088 title = _("Save new document?");
3090 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3093 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3095 return buffer->isClean() && !buffer->isUnnamed();
3099 bool GuiView::reloadBuffer(Buffer & buf)
3101 Buffer::ReadStatus status = buf.reload();
3102 return status == Buffer::ReadSuccess;
3106 void GuiView::checkExternallyModifiedBuffers()
3108 BufferList::iterator bit = theBufferList().begin();
3109 BufferList::iterator const bend = theBufferList().end();
3110 for (; bit != bend; ++bit) {
3111 Buffer * buf = *bit;
3112 if (buf->fileName().exists()
3113 && buf->isExternallyModified(Buffer::checksum_method)) {
3114 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3115 " Reload now? Any local changes will be lost."),
3116 from_utf8(buf->absFileName()));
3117 int const ret = Alert::prompt(_("Reload externally changed document?"),
3118 text, 0, 1, _("&Reload"), _("&Cancel"));
3126 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3128 Buffer * buffer = documentBufferView()
3129 ? &(documentBufferView()->buffer()) : 0;
3131 switch (cmd.action()) {
3132 case LFUN_VC_REGISTER:
3133 if (!buffer || !ensureBufferClean(buffer))
3135 if (!buffer->lyxvc().inUse()) {
3136 if (buffer->lyxvc().registrer()) {
3137 reloadBuffer(*buffer);
3138 dr.clearMessageUpdate();
3143 case LFUN_VC_RENAME:
3144 case LFUN_VC_COPY: {
3145 if (!buffer || !ensureBufferClean(buffer))
3147 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3148 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3149 // Some changes are not yet committed.
3150 // We test here and not in getStatus(), since
3151 // this test is expensive.
3153 LyXVC::CommandResult ret =
3154 buffer->lyxvc().checkIn(log);
3156 if (ret == LyXVC::ErrorCommand ||
3157 ret == LyXVC::VCSuccess)
3158 reloadBuffer(*buffer);
3159 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3160 frontend::Alert::error(
3161 _("Revision control error."),
3162 _("Document could not be checked in."));
3166 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3167 LV_VC_RENAME : LV_VC_COPY;
3168 renameBuffer(*buffer, cmd.argument(), kind);
3173 case LFUN_VC_CHECK_IN:
3174 if (!buffer || !ensureBufferClean(buffer))
3176 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3178 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3180 // Only skip reloading if the checkin was cancelled or
3181 // an error occurred before the real checkin VCS command
3182 // was executed, since the VCS might have changed the
3183 // file even if it could not checkin successfully.
3184 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3185 reloadBuffer(*buffer);
3189 case LFUN_VC_CHECK_OUT:
3190 if (!buffer || !ensureBufferClean(buffer))
3192 if (buffer->lyxvc().inUse()) {
3193 dr.setMessage(buffer->lyxvc().checkOut());
3194 reloadBuffer(*buffer);
3198 case LFUN_VC_LOCKING_TOGGLE:
3199 LASSERT(buffer, return);
3200 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3202 if (buffer->lyxvc().inUse()) {
3203 string res = buffer->lyxvc().lockingToggle();
3205 frontend::Alert::error(_("Revision control error."),
3206 _("Error when setting the locking property."));
3209 reloadBuffer(*buffer);
3214 case LFUN_VC_REVERT:
3215 LASSERT(buffer, return);
3216 if (buffer->lyxvc().revert()) {
3217 reloadBuffer(*buffer);
3218 dr.clearMessageUpdate();
3222 case LFUN_VC_UNDO_LAST:
3223 LASSERT(buffer, return);
3224 buffer->lyxvc().undoLast();
3225 reloadBuffer(*buffer);
3226 dr.clearMessageUpdate();
3229 case LFUN_VC_REPO_UPDATE:
3230 LASSERT(buffer, return);
3231 if (ensureBufferClean(buffer)) {
3232 dr.setMessage(buffer->lyxvc().repoUpdate());
3233 checkExternallyModifiedBuffers();
3237 case LFUN_VC_COMMAND: {
3238 string flag = cmd.getArg(0);
3239 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3242 if (contains(flag, 'M')) {
3243 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3246 string path = cmd.getArg(1);
3247 if (contains(path, "$$p") && buffer)
3248 path = subst(path, "$$p", buffer->filePath());
3249 LYXERR(Debug::LYXVC, "Directory: " << path);
3251 if (!pp.isReadableDirectory()) {
3252 lyxerr << _("Directory is not accessible.") << endl;
3255 support::PathChanger p(pp);
3257 string command = cmd.getArg(2);
3258 if (command.empty())
3261 command = subst(command, "$$i", buffer->absFileName());
3262 command = subst(command, "$$p", buffer->filePath());
3264 command = subst(command, "$$m", to_utf8(message));
3265 LYXERR(Debug::LYXVC, "Command: " << command);
3267 one.startscript(Systemcall::Wait, command);
3271 if (contains(flag, 'I'))
3272 buffer->markDirty();
3273 if (contains(flag, 'R'))
3274 reloadBuffer(*buffer);
3279 case LFUN_VC_COMPARE: {
3280 if (cmd.argument().empty()) {
3281 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3285 string rev1 = cmd.getArg(0);
3289 // it seems safe to assume we have a buffer
3290 // coverity[FORWARD_NULL]
3291 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3294 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3295 f2 = buffer->absFileName();
3297 string rev2 = cmd.getArg(1);
3301 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3305 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3306 f1 << "\n" << f2 << "\n" );
3307 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3308 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3318 void GuiView::openChildDocument(string const & fname)
3320 LASSERT(documentBufferView(), return);
3321 Buffer & buffer = documentBufferView()->buffer();
3322 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3323 documentBufferView()->saveBookmark(false);
3325 if (theBufferList().exists(filename)) {
3326 child = theBufferList().getBuffer(filename);
3329 message(bformat(_("Opening child document %1$s..."),
3330 makeDisplayPath(filename.absFileName())));
3331 child = loadDocument(filename, false);
3333 // Set the parent name of the child document.
3334 // This makes insertion of citations and references in the child work,
3335 // when the target is in the parent or another child document.
3337 child->setParent(&buffer);
3341 bool GuiView::goToFileRow(string const & argument)
3345 size_t i = argument.find_last_of(' ');
3346 if (i != string::npos) {
3347 file_name = os::internal_path(trim(argument.substr(0, i)));
3348 istringstream is(argument.substr(i + 1));
3353 if (i == string::npos) {
3354 LYXERR0("Wrong argument: " << argument);
3358 string const abstmp = package().temp_dir().absFileName();
3359 string const realtmp = package().temp_dir().realPath();
3360 // We have to use os::path_prefix_is() here, instead of
3361 // simply prefixIs(), because the file name comes from
3362 // an external application and may need case adjustment.
3363 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3364 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3365 // Needed by inverse dvi search. If it is a file
3366 // in tmpdir, call the apropriated function.
3367 // If tmpdir is a symlink, we may have the real
3368 // path passed back, so we correct for that.
3369 if (!prefixIs(file_name, abstmp))
3370 file_name = subst(file_name, realtmp, abstmp);
3371 buf = theBufferList().getBufferFromTmp(file_name);
3373 // Must replace extension of the file to be .lyx
3374 // and get full path
3375 FileName const s = fileSearch(string(),
3376 support::changeExtension(file_name, ".lyx"), "lyx");
3377 // Either change buffer or load the file
3378 if (theBufferList().exists(s))
3379 buf = theBufferList().getBuffer(s);
3380 else if (s.exists()) {
3381 buf = loadDocument(s);
3386 _("File does not exist: %1$s"),
3387 makeDisplayPath(file_name)));
3393 _("No buffer for file: %1$s."),
3394 makeDisplayPath(file_name))
3399 bool success = documentBufferView()->setCursorFromRow(row);
3401 LYXERR(Debug::LATEX,
3402 "setCursorFromRow: invalid position for row " << row);
3403 frontend::Alert::error(_("Inverse Search Failed"),
3404 _("Invalid position requested by inverse search.\n"
3405 "You may need to update the viewed document."));
3412 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3414 Buffer::ExportStatus const status = func(format);
3416 // the cloning operation will have produced a clone of the entire set of
3417 // documents, starting from the master. so we must delete those.
3418 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3420 busyBuffers.remove(orig);
3425 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3427 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3428 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3432 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3434 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3435 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3439 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3441 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3442 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3446 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3447 string const & argument,
3448 Buffer const * used_buffer,
3449 docstring const & msg,
3450 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3451 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3452 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3457 string format = argument;
3459 format = used_buffer->params().getDefaultOutputFormat();
3460 processing_format = format;
3462 progress_->clearMessages();
3465 #if EXPORT_in_THREAD
3466 GuiViewPrivate::busyBuffers.insert(used_buffer);
3467 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3468 if (!cloned_buffer) {
3469 Alert::error(_("Export Error"),
3470 _("Error cloning the Buffer."));
3473 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3478 setPreviewFuture(f);
3479 last_export_format = used_buffer->params().bufferFormat();
3482 // We are asynchronous, so we don't know here anything about the success
3485 Buffer::ExportStatus status;
3487 status = (used_buffer->*syncFunc)(format, true);
3488 } else if (previewFunc) {
3489 status = (used_buffer->*previewFunc)(format);
3492 handleExportStatus(gv_, status, format);
3494 return (status == Buffer::ExportSuccess
3495 || status == Buffer::PreviewSuccess);
3499 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3501 BufferView * bv = currentBufferView();
3502 LASSERT(bv, return);
3504 // Let the current BufferView dispatch its own actions.
3505 bv->dispatch(cmd, dr);
3506 if (dr.dispatched())
3509 // Try with the document BufferView dispatch if any.
3510 BufferView * doc_bv = documentBufferView();
3511 if (doc_bv && doc_bv != bv) {
3512 doc_bv->dispatch(cmd, dr);
3513 if (dr.dispatched())
3517 // Then let the current Cursor dispatch its own actions.
3518 bv->cursor().dispatch(cmd);
3520 // update completion. We do it here and not in
3521 // processKeySym to avoid another redraw just for a
3522 // changed inline completion
3523 if (cmd.origin() == FuncRequest::KEYBOARD) {
3524 if (cmd.action() == LFUN_SELF_INSERT
3525 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3526 updateCompletion(bv->cursor(), true, true);
3527 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3528 updateCompletion(bv->cursor(), false, true);
3530 updateCompletion(bv->cursor(), false, false);
3533 dr = bv->cursor().result();
3537 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3539 BufferView * bv = currentBufferView();
3540 // By default we won't need any update.
3541 dr.screenUpdate(Update::None);
3542 // assume cmd will be dispatched
3543 dr.dispatched(true);
3545 Buffer * doc_buffer = documentBufferView()
3546 ? &(documentBufferView()->buffer()) : 0;
3548 if (cmd.origin() == FuncRequest::TOC) {
3549 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3550 // FIXME: do we need to pass a DispatchResult object here?
3551 toc->doDispatch(bv->cursor(), cmd);
3555 string const argument = to_utf8(cmd.argument());
3557 switch(cmd.action()) {
3558 case LFUN_BUFFER_CHILD_OPEN:
3559 openChildDocument(to_utf8(cmd.argument()));
3562 case LFUN_BUFFER_IMPORT:
3563 importDocument(to_utf8(cmd.argument()));
3566 case LFUN_BUFFER_EXPORT: {
3569 // GCC only sees strfwd.h when building merged
3570 if (::lyx::operator==(cmd.argument(), "custom")) {
3571 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3575 string const dest = cmd.getArg(1);
3576 FileName target_dir;
3577 if (!dest.empty() && FileName::isAbsolute(dest))
3578 target_dir = FileName(support::onlyPath(dest));
3580 target_dir = doc_buffer->fileName().onlyPath();
3582 if ((dest.empty() && doc_buffer->isUnnamed())
3583 || !target_dir.isDirWritable()) {
3584 exportBufferAs(*doc_buffer, cmd.argument());
3587 /* TODO/Review: Is it a problem to also export the children?
3588 See the update_unincluded flag */
3589 d.asyncBufferProcessing(argument,
3592 &GuiViewPrivate::exportAndDestroy,
3595 // TODO Inform user about success
3599 case LFUN_BUFFER_EXPORT_AS: {
3600 LASSERT(doc_buffer, break);
3601 docstring f = cmd.argument();
3603 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3604 exportBufferAs(*doc_buffer, f);
3608 case LFUN_BUFFER_UPDATE: {
3609 d.asyncBufferProcessing(argument,
3612 &GuiViewPrivate::compileAndDestroy,
3617 case LFUN_BUFFER_VIEW: {
3618 d.asyncBufferProcessing(argument,
3620 _("Previewing ..."),
3621 &GuiViewPrivate::previewAndDestroy,
3626 case LFUN_MASTER_BUFFER_UPDATE: {
3627 d.asyncBufferProcessing(argument,
3628 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3630 &GuiViewPrivate::compileAndDestroy,
3635 case LFUN_MASTER_BUFFER_VIEW: {
3636 d.asyncBufferProcessing(argument,
3637 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3639 &GuiViewPrivate::previewAndDestroy,
3640 0, &Buffer::preview);
3643 case LFUN_BUFFER_SWITCH: {
3644 string const file_name = to_utf8(cmd.argument());
3645 if (!FileName::isAbsolute(file_name)) {
3647 dr.setMessage(_("Absolute filename expected."));
3651 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3654 dr.setMessage(_("Document not loaded"));
3658 // Do we open or switch to the buffer in this view ?
3659 if (workArea(*buffer)
3660 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3665 // Look for the buffer in other views
3666 QList<int> const ids = guiApp->viewIds();
3668 for (; i != ids.size(); ++i) {
3669 GuiView & gv = guiApp->view(ids[i]);
3670 if (gv.workArea(*buffer)) {
3672 gv.activateWindow();
3674 gv.setBuffer(buffer);
3679 // If necessary, open a new window as a last resort
3680 if (i == ids.size()) {
3681 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3687 case LFUN_BUFFER_NEXT:
3688 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3691 case LFUN_BUFFER_MOVE_NEXT:
3692 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3695 case LFUN_BUFFER_PREVIOUS:
3696 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3699 case LFUN_BUFFER_MOVE_PREVIOUS:
3700 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3703 case LFUN_COMMAND_EXECUTE: {
3704 command_execute_ = true;
3705 minibuffer_focus_ = true;
3708 case LFUN_DROP_LAYOUTS_CHOICE:
3709 d.layout_->showPopup();
3712 case LFUN_MENU_OPEN:
3713 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3714 menu->exec(QCursor::pos());
3717 case LFUN_FILE_INSERT:
3718 insertLyXFile(cmd.argument());
3721 case LFUN_FILE_INSERT_PLAINTEXT:
3722 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3723 string const fname = to_utf8(cmd.argument());
3724 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3725 dr.setMessage(_("Absolute filename expected."));
3729 FileName filename(fname);
3730 if (fname.empty()) {
3731 FileDialog dlg(qt_("Select file to insert"));
3733 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3734 QStringList(qt_("All Files (*)")));
3736 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3737 dr.setMessage(_("Canceled."));
3741 filename.set(fromqstr(result.second));
3745 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3746 bv->dispatch(new_cmd, dr);
3751 case LFUN_BUFFER_RELOAD: {
3752 LASSERT(doc_buffer, break);
3755 if (!doc_buffer->isClean()) {
3756 docstring const file =
3757 makeDisplayPath(doc_buffer->absFileName(), 20);
3758 docstring text = bformat(_("Any changes will be lost. "
3759 "Are you sure you want to revert to the saved version "
3760 "of the document %1$s?"), file);
3761 ret = Alert::prompt(_("Revert to saved document?"),
3762 text, 1, 1, _("&Revert"), _("&Cancel"));
3766 doc_buffer->markClean();
3767 reloadBuffer(*doc_buffer);
3768 dr.forceBufferUpdate();
3773 case LFUN_BUFFER_WRITE:
3774 LASSERT(doc_buffer, break);
3775 saveBuffer(*doc_buffer);
3778 case LFUN_BUFFER_WRITE_AS:
3779 LASSERT(doc_buffer, break);
3780 renameBuffer(*doc_buffer, cmd.argument());
3783 case LFUN_BUFFER_WRITE_ALL: {
3784 Buffer * first = theBufferList().first();
3787 message(_("Saving all documents..."));
3788 // We cannot use a for loop as the buffer list cycles.
3791 if (!b->isClean()) {
3793 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3795 b = theBufferList().next(b);
3796 } while (b != first);
3797 dr.setMessage(_("All documents saved."));
3801 case LFUN_BUFFER_CLOSE:
3805 case LFUN_BUFFER_CLOSE_ALL:
3809 case LFUN_TOOLBAR_TOGGLE: {
3810 string const name = cmd.getArg(0);
3811 if (GuiToolbar * t = toolbar(name))
3816 case LFUN_DIALOG_UPDATE: {
3817 string const name = to_utf8(cmd.argument());
3818 if (name == "prefs" || name == "document")
3819 updateDialog(name, string());
3820 else if (name == "paragraph")
3821 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3822 else if (currentBufferView()) {
3823 Inset * inset = currentBufferView()->editedInset(name);
3824 // Can only update a dialog connected to an existing inset
3826 // FIXME: get rid of this indirection; GuiView ask the inset
3827 // if he is kind enough to update itself...
3828 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3829 //FIXME: pass DispatchResult here?
3830 inset->dispatch(currentBufferView()->cursor(), fr);
3836 case LFUN_DIALOG_TOGGLE: {
3837 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3838 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3839 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3843 case LFUN_DIALOG_DISCONNECT_INSET:
3844 disconnectDialog(to_utf8(cmd.argument()));
3847 case LFUN_DIALOG_HIDE: {
3848 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3852 case LFUN_DIALOG_SHOW: {
3853 string const name = cmd.getArg(0);
3854 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3856 if (name == "character") {
3857 data = freefont2string();
3859 showDialog("character", data);
3860 } else if (name == "latexlog") {
3861 Buffer::LogType type;
3862 string const logfile = doc_buffer->logName(&type);
3864 case Buffer::latexlog:
3867 case Buffer::buildlog:
3871 data += Lexer::quoteString(logfile);
3872 showDialog("log", data);
3873 } else if (name == "vclog") {
3874 string const data = "vc " +
3875 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3876 showDialog("log", data);
3877 } else if (name == "symbols") {
3878 data = bv->cursor().getEncoding()->name();
3880 showDialog("symbols", data);
3882 } else if (name == "prefs" && isFullScreen()) {
3883 lfunUiToggle("fullscreen");
3884 showDialog("prefs", data);
3886 showDialog(name, data);
3891 dr.setMessage(cmd.argument());
3894 case LFUN_UI_TOGGLE: {
3895 string arg = cmd.getArg(0);
3896 if (!lfunUiToggle(arg)) {
3897 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3898 dr.setMessage(bformat(msg, from_utf8(arg)));
3900 // Make sure the keyboard focus stays in the work area.
3905 case LFUN_VIEW_SPLIT: {
3906 LASSERT(doc_buffer, break);
3907 string const orientation = cmd.getArg(0);
3908 d.splitter_->setOrientation(orientation == "vertical"
3909 ? Qt::Vertical : Qt::Horizontal);
3910 TabWorkArea * twa = addTabWorkArea();
3911 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3912 setCurrentWorkArea(wa);
3915 case LFUN_TAB_GROUP_CLOSE:
3916 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3917 closeTabWorkArea(twa);
3918 d.current_work_area_ = 0;
3919 twa = d.currentTabWorkArea();
3920 // Switch to the next GuiWorkArea in the found TabWorkArea.
3922 // Make sure the work area is up to date.
3923 setCurrentWorkArea(twa->currentWorkArea());
3925 setCurrentWorkArea(0);
3930 case LFUN_VIEW_CLOSE:
3931 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3932 closeWorkArea(twa->currentWorkArea());
3933 d.current_work_area_ = 0;
3934 twa = d.currentTabWorkArea();
3935 // Switch to the next GuiWorkArea in the found TabWorkArea.
3937 // Make sure the work area is up to date.
3938 setCurrentWorkArea(twa->currentWorkArea());
3940 setCurrentWorkArea(0);
3945 case LFUN_COMPLETION_INLINE:
3946 if (d.current_work_area_)
3947 d.current_work_area_->completer().showInline();
3950 case LFUN_COMPLETION_POPUP:
3951 if (d.current_work_area_)
3952 d.current_work_area_->completer().showPopup();
3957 if (d.current_work_area_)
3958 d.current_work_area_->completer().tab();
3961 case LFUN_COMPLETION_CANCEL:
3962 if (d.current_work_area_) {
3963 if (d.current_work_area_->completer().popupVisible())
3964 d.current_work_area_->completer().hidePopup();
3966 d.current_work_area_->completer().hideInline();
3970 case LFUN_COMPLETION_ACCEPT:
3971 if (d.current_work_area_)
3972 d.current_work_area_->completer().activate();
3975 case LFUN_BUFFER_ZOOM_IN:
3976 case LFUN_BUFFER_ZOOM_OUT: {
3977 // use a signed temp to avoid overflow
3978 int zoom = lyxrc.zoom;
3979 if (cmd.argument().empty()) {
3980 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3985 zoom += convert<int>(cmd.argument());
3987 if (zoom < static_cast<int>(zoom_min_))
3991 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3993 // The global QPixmapCache is used in GuiPainter to cache text
3994 // painting so we must reset it.
3995 QPixmapCache::clear();
3996 guiApp->fontLoader().update();
3997 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4001 case LFUN_VC_REGISTER:
4002 case LFUN_VC_RENAME:
4004 case LFUN_VC_CHECK_IN:
4005 case LFUN_VC_CHECK_OUT:
4006 case LFUN_VC_REPO_UPDATE:
4007 case LFUN_VC_LOCKING_TOGGLE:
4008 case LFUN_VC_REVERT:
4009 case LFUN_VC_UNDO_LAST:
4010 case LFUN_VC_COMMAND:
4011 case LFUN_VC_COMPARE:
4012 dispatchVC(cmd, dr);
4015 case LFUN_SERVER_GOTO_FILE_ROW:
4016 if(goToFileRow(to_utf8(cmd.argument())))
4017 dr.screenUpdate(Update::Force | Update::FitCursor);
4020 case LFUN_LYX_ACTIVATE:
4024 case LFUN_FORWARD_SEARCH: {
4025 // it seems safe to assume we have a document buffer, since
4026 // getStatus wants one.
4027 // coverity[FORWARD_NULL]
4028 Buffer const * doc_master = doc_buffer->masterBuffer();
4029 FileName const path(doc_master->temppath());
4030 string const texname = doc_master->isChild(doc_buffer)
4031 ? DocFileName(changeExtension(
4032 doc_buffer->absFileName(),
4033 "tex")).mangledFileName()
4034 : doc_buffer->latexName();
4035 string const fulltexname =
4036 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4037 string const mastername =
4038 removeExtension(doc_master->latexName());
4039 FileName const dviname(addName(path.absFileName(),
4040 addExtension(mastername, "dvi")));
4041 FileName const pdfname(addName(path.absFileName(),
4042 addExtension(mastername, "pdf")));
4043 bool const have_dvi = dviname.exists();
4044 bool const have_pdf = pdfname.exists();
4045 if (!have_dvi && !have_pdf) {
4046 dr.setMessage(_("Please, preview the document first."));
4049 string outname = dviname.onlyFileName();
4050 string command = lyxrc.forward_search_dvi;
4051 if (!have_dvi || (have_pdf &&
4052 pdfname.lastModified() > dviname.lastModified())) {
4053 outname = pdfname.onlyFileName();
4054 command = lyxrc.forward_search_pdf;
4057 DocIterator cur = bv->cursor();
4058 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4059 LYXERR(Debug::ACTION, "Forward search: row:" << row
4061 if (row == -1 || command.empty()) {
4062 dr.setMessage(_("Couldn't proceed."));
4065 string texrow = convert<string>(row);
4067 command = subst(command, "$$n", texrow);
4068 command = subst(command, "$$f", fulltexname);
4069 command = subst(command, "$$t", texname);
4070 command = subst(command, "$$o", outname);
4072 PathChanger p(path);
4074 one.startscript(Systemcall::DontWait, command);
4078 case LFUN_SPELLING_CONTINUOUSLY:
4079 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4080 dr.screenUpdate(Update::Force | Update::FitCursor);
4084 // The LFUN must be for one of BufferView, Buffer or Cursor;
4086 dispatchToBufferView(cmd, dr);
4090 // Part of automatic menu appearance feature.
4091 if (isFullScreen()) {
4092 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4096 // Need to update bv because many LFUNs here might have destroyed it
4097 bv = currentBufferView();
4099 // Clear non-empty selections
4100 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4102 Cursor & cur = bv->cursor();
4103 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4104 cur.clearSelection();
4110 bool GuiView::lfunUiToggle(string const & ui_component)
4112 if (ui_component == "scrollbar") {
4113 // hide() is of no help
4114 if (d.current_work_area_->verticalScrollBarPolicy() ==
4115 Qt::ScrollBarAlwaysOff)
4117 d.current_work_area_->setVerticalScrollBarPolicy(
4118 Qt::ScrollBarAsNeeded);
4120 d.current_work_area_->setVerticalScrollBarPolicy(
4121 Qt::ScrollBarAlwaysOff);
4122 } else if (ui_component == "statusbar") {
4123 statusBar()->setVisible(!statusBar()->isVisible());
4124 } else if (ui_component == "menubar") {
4125 menuBar()->setVisible(!menuBar()->isVisible());
4127 if (ui_component == "frame") {
4129 getContentsMargins(&l, &t, &r, &b);
4130 //are the frames in default state?
4131 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4133 setContentsMargins(-2, -2, -2, -2);
4135 setContentsMargins(0, 0, 0, 0);
4138 if (ui_component == "fullscreen") {
4146 void GuiView::toggleFullScreen()
4148 if (isFullScreen()) {
4149 for (int i = 0; i != d.splitter_->count(); ++i)
4150 d.tabWorkArea(i)->setFullScreen(false);
4151 setContentsMargins(0, 0, 0, 0);
4152 setWindowState(windowState() ^ Qt::WindowFullScreen);
4155 statusBar()->show();
4158 hideDialogs("prefs", 0);
4159 for (int i = 0; i != d.splitter_->count(); ++i)
4160 d.tabWorkArea(i)->setFullScreen(true);
4161 setContentsMargins(-2, -2, -2, -2);
4163 setWindowState(windowState() ^ Qt::WindowFullScreen);
4164 if (lyxrc.full_screen_statusbar)
4165 statusBar()->hide();
4166 if (lyxrc.full_screen_menubar)
4168 if (lyxrc.full_screen_toolbars) {
4169 ToolbarMap::iterator end = d.toolbars_.end();
4170 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4175 // give dialogs like the TOC a chance to adapt
4180 Buffer const * GuiView::updateInset(Inset const * inset)
4185 Buffer const * inset_buffer = &(inset->buffer());
4187 for (int i = 0; i != d.splitter_->count(); ++i) {
4188 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4191 Buffer const * buffer = &(wa->bufferView().buffer());
4192 if (inset_buffer == buffer)
4193 wa->scheduleRedraw();
4195 return inset_buffer;
4199 void GuiView::restartCursor()
4201 /* When we move around, or type, it's nice to be able to see
4202 * the cursor immediately after the keypress.
4204 if (d.current_work_area_)
4205 d.current_work_area_->startBlinkingCursor();
4207 // Take this occasion to update the other GUI elements.
4213 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4215 if (d.current_work_area_)
4216 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4221 // This list should be kept in sync with the list of insets in
4222 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4223 // dialog should have the same name as the inset.
4224 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4225 // docs in LyXAction.cpp.
4227 char const * const dialognames[] = {
4229 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4230 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4231 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4232 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4233 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4234 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4235 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4236 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4238 char const * const * const end_dialognames =
4239 dialognames + (sizeof(dialognames) / sizeof(char *));
4243 cmpCStr(char const * name) : name_(name) {}
4244 bool operator()(char const * other) {
4245 return strcmp(other, name_) == 0;
4252 bool isValidName(string const & name)
4254 return find_if(dialognames, end_dialognames,
4255 cmpCStr(name.c_str())) != end_dialognames;
4261 void GuiView::resetDialogs()
4263 // Make sure that no LFUN uses any GuiView.
4264 guiApp->setCurrentView(0);
4268 constructToolbars();
4269 guiApp->menus().fillMenuBar(menuBar(), this, false);
4270 d.layout_->updateContents(true);
4271 // Now update controls with current buffer.
4272 guiApp->setCurrentView(this);
4278 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4280 if (!isValidName(name))
4283 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4285 if (it != d.dialogs_.end()) {
4287 it->second->hideView();
4288 return it->second.get();
4291 Dialog * dialog = build(name);
4292 d.dialogs_[name].reset(dialog);
4293 if (lyxrc.allow_geometry_session)
4294 dialog->restoreSession();
4301 void GuiView::showDialog(string const & name, string const & data,
4304 triggerShowDialog(toqstr(name), toqstr(data), inset);
4308 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4314 const string name = fromqstr(qname);
4315 const string data = fromqstr(qdata);
4319 Dialog * dialog = findOrBuild(name, false);
4321 bool const visible = dialog->isVisibleView();
4322 dialog->showData(data);
4323 if (inset && currentBufferView())
4324 currentBufferView()->editInset(name, inset);
4325 // We only set the focus to the new dialog if it was not yet
4326 // visible in order not to change the existing previous behaviour
4328 // activateWindow is needed for floating dockviews
4329 dialog->asQWidget()->raise();
4330 dialog->asQWidget()->activateWindow();
4331 dialog->asQWidget()->setFocus();
4335 catch (ExceptionMessage const & ex) {
4343 bool GuiView::isDialogVisible(string const & name) const
4345 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4346 if (it == d.dialogs_.end())
4348 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4352 void GuiView::hideDialog(string const & name, Inset * inset)
4354 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4355 if (it == d.dialogs_.end())
4359 if (!currentBufferView())
4361 if (inset != currentBufferView()->editedInset(name))
4365 Dialog * const dialog = it->second.get();
4366 if (dialog->isVisibleView())
4368 if (currentBufferView())
4369 currentBufferView()->editInset(name, 0);
4373 void GuiView::disconnectDialog(string const & name)
4375 if (!isValidName(name))
4377 if (currentBufferView())
4378 currentBufferView()->editInset(name, 0);
4382 void GuiView::hideAll() const
4384 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4385 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4387 for(; it != end; ++it)
4388 it->second->hideView();
4392 void GuiView::updateDialogs()
4394 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4395 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4397 for(; it != end; ++it) {
4398 Dialog * dialog = it->second.get();
4400 if (dialog->needBufferOpen() && !documentBufferView())
4401 hideDialog(fromqstr(dialog->name()), 0);
4402 else if (dialog->isVisibleView())
4403 dialog->checkStatus();
4410 Dialog * createDialog(GuiView & lv, string const & name);
4412 // will be replaced by a proper factory...
4413 Dialog * createGuiAbout(GuiView & lv);
4414 Dialog * createGuiBibtex(GuiView & lv);
4415 Dialog * createGuiChanges(GuiView & lv);
4416 Dialog * createGuiCharacter(GuiView & lv);
4417 Dialog * createGuiCitation(GuiView & lv);
4418 Dialog * createGuiCompare(GuiView & lv);
4419 Dialog * createGuiCompareHistory(GuiView & lv);
4420 Dialog * createGuiDelimiter(GuiView & lv);
4421 Dialog * createGuiDocument(GuiView & lv);
4422 Dialog * createGuiErrorList(GuiView & lv);
4423 Dialog * createGuiExternal(GuiView & lv);
4424 Dialog * createGuiGraphics(GuiView & lv);
4425 Dialog * createGuiInclude(GuiView & lv);
4426 Dialog * createGuiIndex(GuiView & lv);
4427 Dialog * createGuiListings(GuiView & lv);
4428 Dialog * createGuiLog(GuiView & lv);
4429 Dialog * createGuiMathMatrix(GuiView & lv);
4430 Dialog * createGuiNote(GuiView & lv);
4431 Dialog * createGuiParagraph(GuiView & lv);
4432 Dialog * createGuiPhantom(GuiView & lv);
4433 Dialog * createGuiPreferences(GuiView & lv);
4434 Dialog * createGuiPrint(GuiView & lv);
4435 Dialog * createGuiPrintindex(GuiView & lv);
4436 Dialog * createGuiRef(GuiView & lv);
4437 Dialog * createGuiSearch(GuiView & lv);
4438 Dialog * createGuiSearchAdv(GuiView & lv);
4439 Dialog * createGuiSendTo(GuiView & lv);
4440 Dialog * createGuiShowFile(GuiView & lv);
4441 Dialog * createGuiSpellchecker(GuiView & lv);
4442 Dialog * createGuiSymbols(GuiView & lv);
4443 Dialog * createGuiTabularCreate(GuiView & lv);
4444 Dialog * createGuiTexInfo(GuiView & lv);
4445 Dialog * createGuiToc(GuiView & lv);
4446 Dialog * createGuiThesaurus(GuiView & lv);
4447 Dialog * createGuiViewSource(GuiView & lv);
4448 Dialog * createGuiWrap(GuiView & lv);
4449 Dialog * createGuiProgressView(GuiView & lv);
4453 Dialog * GuiView::build(string const & name)
4455 LASSERT(isValidName(name), return 0);
4457 Dialog * dialog = createDialog(*this, name);
4461 if (name == "aboutlyx")
4462 return createGuiAbout(*this);
4463 if (name == "bibtex")
4464 return createGuiBibtex(*this);
4465 if (name == "changes")
4466 return createGuiChanges(*this);
4467 if (name == "character")
4468 return createGuiCharacter(*this);
4469 if (name == "citation")
4470 return createGuiCitation(*this);
4471 if (name == "compare")
4472 return createGuiCompare(*this);
4473 if (name == "comparehistory")
4474 return createGuiCompareHistory(*this);
4475 if (name == "document")
4476 return createGuiDocument(*this);
4477 if (name == "errorlist")
4478 return createGuiErrorList(*this);
4479 if (name == "external")
4480 return createGuiExternal(*this);
4482 return createGuiShowFile(*this);
4483 if (name == "findreplace")
4484 return createGuiSearch(*this);
4485 if (name == "findreplaceadv")
4486 return createGuiSearchAdv(*this);
4487 if (name == "graphics")
4488 return createGuiGraphics(*this);
4489 if (name == "include")
4490 return createGuiInclude(*this);
4491 if (name == "index")
4492 return createGuiIndex(*this);
4493 if (name == "index_print")
4494 return createGuiPrintindex(*this);
4495 if (name == "listings")
4496 return createGuiListings(*this);
4498 return createGuiLog(*this);
4499 if (name == "mathdelimiter")
4500 return createGuiDelimiter(*this);
4501 if (name == "mathmatrix")
4502 return createGuiMathMatrix(*this);
4504 return createGuiNote(*this);
4505 if (name == "paragraph")
4506 return createGuiParagraph(*this);
4507 if (name == "phantom")
4508 return createGuiPhantom(*this);
4509 if (name == "prefs")
4510 return createGuiPreferences(*this);
4512 return createGuiRef(*this);
4513 if (name == "sendto")
4514 return createGuiSendTo(*this);
4515 if (name == "spellchecker")
4516 return createGuiSpellchecker(*this);
4517 if (name == "symbols")
4518 return createGuiSymbols(*this);
4519 if (name == "tabularcreate")
4520 return createGuiTabularCreate(*this);
4521 if (name == "texinfo")
4522 return createGuiTexInfo(*this);
4523 if (name == "thesaurus")
4524 return createGuiThesaurus(*this);
4526 return createGuiToc(*this);
4527 if (name == "view-source")
4528 return createGuiViewSource(*this);
4530 return createGuiWrap(*this);
4531 if (name == "progress")
4532 return createGuiProgressView(*this);
4538 } // namespace frontend
4541 #include "moc_GuiView.cpp"