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 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1187 version_control_->hide();
1191 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1193 if (d.current_work_area_)
1194 // disconnect the current work area from all slots
1195 QObject::disconnect(d.current_work_area_, 0, this, 0);
1197 disconnectBufferView();
1198 connectBufferView(wa->bufferView());
1199 connectBuffer(wa->bufferView().buffer());
1200 d.current_work_area_ = wa;
1201 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1202 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1203 QObject::connect(wa, SIGNAL(busy(bool)),
1204 this, SLOT(setBusy(bool)));
1205 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1206 this, SIGNAL(bufferViewChanged()));
1207 Q_EMIT updateWindowTitle(wa);
1208 Q_EMIT bufferViewChanged();
1212 void GuiView::on_bufferViewChanged()
1215 // Buffer-dependent dialogs must be updated. This is done here because
1216 // some dialogs require buffer()->text.
1221 void GuiView::on_lastWorkAreaRemoved()
1224 // We already are in a close event. Nothing more to do.
1227 if (d.splitter_->count() > 1)
1228 // We have a splitter so don't close anything.
1231 // Reset and updates the dialogs.
1232 Q_EMIT bufferViewChanged();
1237 if (lyxrc.open_buffers_in_tabs)
1238 // Nothing more to do, the window should stay open.
1241 if (guiApp->viewIds().size() > 1) {
1247 // On Mac we also close the last window because the application stay
1248 // resident in memory. On other platforms we don't close the last
1249 // window because this would quit the application.
1255 void GuiView::updateStatusBar()
1257 // let the user see the explicit message
1258 if (d.statusbar_timer_.isActive())
1265 void GuiView::showMessage()
1269 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1270 if (msg.isEmpty()) {
1271 BufferView const * bv = currentBufferView();
1273 msg = toqstr(bv->cursor().currentState());
1275 msg = qt_("Welcome to LyX!");
1277 statusBar()->showMessage(msg);
1281 bool GuiView::event(QEvent * e)
1285 // Useful debug code:
1286 //case QEvent::ActivationChange:
1287 //case QEvent::WindowDeactivate:
1288 //case QEvent::Paint:
1289 //case QEvent::Enter:
1290 //case QEvent::Leave:
1291 //case QEvent::HoverEnter:
1292 //case QEvent::HoverLeave:
1293 //case QEvent::HoverMove:
1294 //case QEvent::StatusTip:
1295 //case QEvent::DragEnter:
1296 //case QEvent::DragLeave:
1297 //case QEvent::Drop:
1300 case QEvent::WindowActivate: {
1301 GuiView * old_view = guiApp->currentView();
1302 if (this == old_view) {
1304 return QMainWindow::event(e);
1306 if (old_view && old_view->currentBufferView()) {
1307 // save current selection to the selection buffer to allow
1308 // middle-button paste in this window.
1309 cap::saveSelection(old_view->currentBufferView()->cursor());
1311 guiApp->setCurrentView(this);
1312 if (d.current_work_area_)
1313 on_currentWorkAreaChanged(d.current_work_area_);
1317 return QMainWindow::event(e);
1320 case QEvent::ShortcutOverride: {
1322 if (isFullScreen() && menuBar()->isHidden()) {
1323 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1324 // FIXME: we should also try to detect special LyX shortcut such as
1325 // Alt-P and Alt-M. Right now there is a hack in
1326 // GuiWorkArea::processKeySym() that hides again the menubar for
1328 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1330 return QMainWindow::event(e);
1333 return QMainWindow::event(e);
1337 return QMainWindow::event(e);
1341 void GuiView::resetWindowTitle()
1343 setWindowTitle(qt_("LyX"));
1346 bool GuiView::focusNextPrevChild(bool /*next*/)
1353 bool GuiView::busy() const
1359 void GuiView::setBusy(bool busy)
1361 bool const busy_before = busy_ > 0;
1362 busy ? ++busy_ : --busy_;
1363 if ((busy_ > 0) == busy_before)
1364 // busy state didn't change
1368 QApplication::setOverrideCursor(Qt::WaitCursor);
1371 QApplication::restoreOverrideCursor();
1376 void GuiView::resetCommandExecute()
1378 command_execute_ = false;
1383 double GuiView::pixelRatio() const
1385 #if QT_VERSION >= 0x050000
1386 return devicePixelRatio();
1393 GuiWorkArea * GuiView::workArea(int index)
1395 if (TabWorkArea * twa = d.currentTabWorkArea())
1396 if (index < twa->count())
1397 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1402 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1404 if (currentWorkArea()
1405 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1406 return currentWorkArea();
1407 if (TabWorkArea * twa = d.currentTabWorkArea())
1408 return twa->workArea(buffer);
1413 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1415 // Automatically create a TabWorkArea if there are none yet.
1416 TabWorkArea * tab_widget = d.splitter_->count()
1417 ? d.currentTabWorkArea() : addTabWorkArea();
1418 return tab_widget->addWorkArea(buffer, *this);
1422 TabWorkArea * GuiView::addTabWorkArea()
1424 TabWorkArea * twa = new TabWorkArea;
1425 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1426 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1427 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1428 this, SLOT(on_lastWorkAreaRemoved()));
1430 d.splitter_->addWidget(twa);
1431 d.stack_widget_->setCurrentWidget(d.splitter_);
1436 GuiWorkArea const * GuiView::currentWorkArea() const
1438 return d.current_work_area_;
1442 GuiWorkArea * GuiView::currentWorkArea()
1444 return d.current_work_area_;
1448 GuiWorkArea const * GuiView::currentMainWorkArea() const
1450 if (!d.currentTabWorkArea())
1452 return d.currentTabWorkArea()->currentWorkArea();
1456 GuiWorkArea * GuiView::currentMainWorkArea()
1458 if (!d.currentTabWorkArea())
1460 return d.currentTabWorkArea()->currentWorkArea();
1464 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1466 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1468 d.current_work_area_ = 0;
1470 Q_EMIT bufferViewChanged();
1474 // FIXME: I've no clue why this is here and why it accesses
1475 // theGuiApp()->currentView, which might be 0 (bug 6464).
1476 // See also 27525 (vfr).
1477 if (theGuiApp()->currentView() == this
1478 && theGuiApp()->currentView()->currentWorkArea() == wa)
1481 if (currentBufferView())
1482 cap::saveSelection(currentBufferView()->cursor());
1484 theGuiApp()->setCurrentView(this);
1485 d.current_work_area_ = wa;
1487 // We need to reset this now, because it will need to be
1488 // right if the tabWorkArea gets reset in the for loop. We
1489 // will change it back if we aren't in that case.
1490 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1491 d.current_main_work_area_ = wa;
1493 for (int i = 0; i != d.splitter_->count(); ++i) {
1494 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1495 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1496 << ", Current main wa: " << currentMainWorkArea());
1501 d.current_main_work_area_ = old_cmwa;
1503 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1504 on_currentWorkAreaChanged(wa);
1505 BufferView & bv = wa->bufferView();
1506 bv.cursor().fixIfBroken();
1508 wa->setUpdatesEnabled(true);
1509 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1513 void GuiView::removeWorkArea(GuiWorkArea * wa)
1515 LASSERT(wa, return);
1516 if (wa == d.current_work_area_) {
1518 disconnectBufferView();
1519 d.current_work_area_ = 0;
1520 d.current_main_work_area_ = 0;
1523 bool found_twa = false;
1524 for (int i = 0; i != d.splitter_->count(); ++i) {
1525 TabWorkArea * twa = d.tabWorkArea(i);
1526 if (twa->removeWorkArea(wa)) {
1527 // Found in this tab group, and deleted the GuiWorkArea.
1529 if (twa->count() != 0) {
1530 if (d.current_work_area_ == 0)
1531 // This means that we are closing the current GuiWorkArea, so
1532 // switch to the next GuiWorkArea in the found TabWorkArea.
1533 setCurrentWorkArea(twa->currentWorkArea());
1535 // No more WorkAreas in this tab group, so delete it.
1542 // It is not a tabbed work area (i.e., the search work area), so it
1543 // should be deleted by other means.
1544 LASSERT(found_twa, return);
1546 if (d.current_work_area_ == 0) {
1547 if (d.splitter_->count() != 0) {
1548 TabWorkArea * twa = d.currentTabWorkArea();
1549 setCurrentWorkArea(twa->currentWorkArea());
1551 // No more work areas, switch to the background widget.
1552 setCurrentWorkArea(0);
1558 LayoutBox * GuiView::getLayoutDialog() const
1564 void GuiView::updateLayoutList()
1567 d.layout_->updateContents(false);
1571 void GuiView::updateToolbars()
1573 ToolbarMap::iterator end = d.toolbars_.end();
1574 if (d.current_work_area_) {
1576 if (d.current_work_area_->bufferView().cursor().inMathed()
1577 && !d.current_work_area_->bufferView().cursor().inRegexped())
1578 context |= Toolbars::MATH;
1579 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1580 context |= Toolbars::TABLE;
1581 if (currentBufferView()->buffer().areChangesPresent()
1582 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1583 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1584 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1585 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1586 context |= Toolbars::REVIEW;
1587 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1588 context |= Toolbars::MATHMACROTEMPLATE;
1589 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1590 context |= Toolbars::IPA;
1591 if (command_execute_)
1592 context |= Toolbars::MINIBUFFER;
1593 if (minibuffer_focus_) {
1594 context |= Toolbars::MINIBUFFER_FOCUS;
1595 minibuffer_focus_ = false;
1598 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1599 it->second->update(context);
1601 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1602 it->second->update();
1606 void GuiView::setBuffer(Buffer * newBuffer)
1608 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1609 LASSERT(newBuffer, return);
1611 GuiWorkArea * wa = workArea(*newBuffer);
1614 newBuffer->masterBuffer()->updateBuffer();
1616 wa = addWorkArea(*newBuffer);
1617 // scroll to the position when the BufferView was last closed
1618 if (lyxrc.use_lastfilepos) {
1619 LastFilePosSection::FilePos filepos =
1620 theSession().lastFilePos().load(newBuffer->fileName());
1621 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1624 //Disconnect the old buffer...there's no new one.
1627 connectBuffer(*newBuffer);
1628 connectBufferView(wa->bufferView());
1629 setCurrentWorkArea(wa);
1633 void GuiView::connectBuffer(Buffer & buf)
1635 buf.setGuiDelegate(this);
1639 void GuiView::disconnectBuffer()
1641 if (d.current_work_area_)
1642 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1646 void GuiView::connectBufferView(BufferView & bv)
1648 bv.setGuiDelegate(this);
1652 void GuiView::disconnectBufferView()
1654 if (d.current_work_area_)
1655 d.current_work_area_->bufferView().setGuiDelegate(0);
1659 void GuiView::errors(string const & error_type, bool from_master)
1661 BufferView const * const bv = currentBufferView();
1665 #if EXPORT_in_THREAD
1666 // We are called with from_master == false by default, so we
1667 // have to figure out whether that is the case or not.
1668 ErrorList & el = bv->buffer().errorList(error_type);
1670 el = bv->buffer().masterBuffer()->errorList(error_type);
1674 ErrorList const & el = from_master ?
1675 bv->buffer().masterBuffer()->errorList(error_type) :
1676 bv->buffer().errorList(error_type);
1682 string data = error_type;
1684 data = "from_master|" + error_type;
1685 showDialog("errorlist", data);
1689 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1691 d.toc_models_.updateItem(toqstr(type), dit);
1695 void GuiView::structureChanged()
1697 // FIXME: This is slightly expensive, though less than the tocBackend update
1698 // (#9880). This also resets the view in the Toc Widget (#6675).
1699 d.toc_models_.reset(documentBufferView());
1700 // Navigator needs more than a simple update in this case. It needs to be
1702 updateDialog("toc", "");
1706 void GuiView::updateDialog(string const & name, string const & data)
1708 if (!isDialogVisible(name))
1711 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1712 if (it == d.dialogs_.end())
1715 Dialog * const dialog = it->second.get();
1716 if (dialog->isVisibleView())
1717 dialog->initialiseParams(data);
1721 BufferView * GuiView::documentBufferView()
1723 return currentMainWorkArea()
1724 ? ¤tMainWorkArea()->bufferView()
1729 BufferView const * GuiView::documentBufferView() const
1731 return currentMainWorkArea()
1732 ? ¤tMainWorkArea()->bufferView()
1737 BufferView * GuiView::currentBufferView()
1739 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1743 BufferView const * GuiView::currentBufferView() const
1745 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1749 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1750 Buffer const * orig, Buffer * clone)
1752 bool const success = clone->autoSave();
1754 busyBuffers.remove(orig);
1756 ? _("Automatic save done.")
1757 : _("Automatic save failed!");
1761 void GuiView::autoSave()
1763 LYXERR(Debug::INFO, "Running autoSave()");
1765 Buffer * buffer = documentBufferView()
1766 ? &documentBufferView()->buffer() : 0;
1768 resetAutosaveTimers();
1772 GuiViewPrivate::busyBuffers.insert(buffer);
1773 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1774 buffer, buffer->cloneBufferOnly());
1775 d.autosave_watcher_.setFuture(f);
1776 resetAutosaveTimers();
1780 void GuiView::resetAutosaveTimers()
1783 d.autosave_timeout_.restart();
1787 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1790 Buffer * buf = currentBufferView()
1791 ? ¤tBufferView()->buffer() : 0;
1792 Buffer * doc_buffer = documentBufferView()
1793 ? &(documentBufferView()->buffer()) : 0;
1796 /* In LyX/Mac, when a dialog is open, the menus of the
1797 application can still be accessed without giving focus to
1798 the main window. In this case, we want to disable the menu
1799 entries that are buffer-related.
1800 This code must not be used on Linux and Windows, since it
1801 would disable buffer-related entries when hovering over the
1802 menu (see bug #9574).
1804 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1810 // Check whether we need a buffer
1811 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1812 // no, exit directly
1813 flag.message(from_utf8(N_("Command not allowed with"
1814 "out any document open")));
1815 flag.setEnabled(false);
1819 if (cmd.origin() == FuncRequest::TOC) {
1820 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1821 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1822 flag.setEnabled(false);
1826 switch(cmd.action()) {
1827 case LFUN_BUFFER_IMPORT:
1830 case LFUN_MASTER_BUFFER_UPDATE:
1831 case LFUN_MASTER_BUFFER_VIEW:
1833 && (doc_buffer->parent() != 0
1834 || doc_buffer->hasChildren())
1835 && !d.processing_thread_watcher_.isRunning();
1838 case LFUN_BUFFER_UPDATE:
1839 case LFUN_BUFFER_VIEW: {
1840 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1844 string format = to_utf8(cmd.argument());
1845 if (cmd.argument().empty())
1846 format = doc_buffer->params().getDefaultOutputFormat();
1847 enable = doc_buffer->params().isExportable(format, true);
1851 case LFUN_BUFFER_RELOAD:
1852 enable = doc_buffer && !doc_buffer->isUnnamed()
1853 && doc_buffer->fileName().exists()
1854 && (!doc_buffer->isClean()
1855 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1858 case LFUN_BUFFER_CHILD_OPEN:
1859 enable = doc_buffer != 0;
1862 case LFUN_BUFFER_WRITE:
1863 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1866 //FIXME: This LFUN should be moved to GuiApplication.
1867 case LFUN_BUFFER_WRITE_ALL: {
1868 // We enable the command only if there are some modified buffers
1869 Buffer * first = theBufferList().first();
1874 // We cannot use a for loop as the buffer list is a cycle.
1876 if (!b->isClean()) {
1880 b = theBufferList().next(b);
1881 } while (b != first);
1885 case LFUN_BUFFER_WRITE_AS:
1886 case LFUN_BUFFER_EXPORT_AS:
1887 enable = doc_buffer != 0;
1890 case LFUN_BUFFER_CLOSE:
1891 case LFUN_VIEW_CLOSE:
1892 enable = doc_buffer != 0;
1895 case LFUN_BUFFER_CLOSE_ALL:
1896 enable = theBufferList().last() != theBufferList().first();
1899 case LFUN_VIEW_SPLIT:
1900 if (cmd.getArg(0) == "vertical")
1901 enable = doc_buffer && (d.splitter_->count() == 1 ||
1902 d.splitter_->orientation() == Qt::Vertical);
1904 enable = doc_buffer && (d.splitter_->count() == 1 ||
1905 d.splitter_->orientation() == Qt::Horizontal);
1908 case LFUN_TAB_GROUP_CLOSE:
1909 enable = d.tabWorkAreaCount() > 1;
1912 case LFUN_TOOLBAR_TOGGLE: {
1913 string const name = cmd.getArg(0);
1914 if (GuiToolbar * t = toolbar(name))
1915 flag.setOnOff(t->isVisible());
1918 docstring const msg =
1919 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1925 case LFUN_DROP_LAYOUTS_CHOICE:
1929 case LFUN_UI_TOGGLE:
1930 flag.setOnOff(isFullScreen());
1933 case LFUN_DIALOG_DISCONNECT_INSET:
1936 case LFUN_DIALOG_HIDE:
1937 // FIXME: should we check if the dialog is shown?
1940 case LFUN_DIALOG_TOGGLE:
1941 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1942 // fall through to set "enable"
1943 case LFUN_DIALOG_SHOW: {
1944 string const name = cmd.getArg(0);
1946 enable = name == "aboutlyx"
1947 || name == "file" //FIXME: should be removed.
1949 || name == "texinfo"
1950 || name == "progress"
1951 || name == "compare";
1952 else if (name == "character" || name == "symbols"
1953 || name == "mathdelimiter" || name == "mathmatrix") {
1954 if (!buf || buf->isReadonly())
1957 Cursor const & cur = currentBufferView()->cursor();
1958 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1961 else if (name == "latexlog")
1962 enable = FileName(doc_buffer->logName()).isReadableFile();
1963 else if (name == "spellchecker")
1964 enable = theSpellChecker()
1965 && !doc_buffer->isReadonly()
1966 && !doc_buffer->text().empty();
1967 else if (name == "vclog")
1968 enable = doc_buffer->lyxvc().inUse();
1972 case LFUN_DIALOG_UPDATE: {
1973 string const name = cmd.getArg(0);
1975 enable = name == "prefs";
1979 case LFUN_COMMAND_EXECUTE:
1981 case LFUN_MENU_OPEN:
1982 // Nothing to check.
1985 case LFUN_COMPLETION_INLINE:
1986 if (!d.current_work_area_
1987 || !d.current_work_area_->completer().inlinePossible(
1988 currentBufferView()->cursor()))
1992 case LFUN_COMPLETION_POPUP:
1993 if (!d.current_work_area_
1994 || !d.current_work_area_->completer().popupPossible(
1995 currentBufferView()->cursor()))
2000 if (!d.current_work_area_
2001 || !d.current_work_area_->completer().inlinePossible(
2002 currentBufferView()->cursor()))
2006 case LFUN_COMPLETION_ACCEPT:
2007 if (!d.current_work_area_
2008 || (!d.current_work_area_->completer().popupVisible()
2009 && !d.current_work_area_->completer().inlineVisible()
2010 && !d.current_work_area_->completer().completionAvailable()))
2014 case LFUN_COMPLETION_CANCEL:
2015 if (!d.current_work_area_
2016 || (!d.current_work_area_->completer().popupVisible()
2017 && !d.current_work_area_->completer().inlineVisible()))
2021 case LFUN_BUFFER_ZOOM_OUT:
2022 case LFUN_BUFFER_ZOOM_IN: {
2023 // only diff between these two is that the default for ZOOM_OUT
2025 bool const neg_zoom =
2026 convert<int>(cmd.argument()) < 0 ||
2027 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2028 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2029 docstring const msg =
2030 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2034 enable = doc_buffer;
2037 case LFUN_BUFFER_MOVE_NEXT:
2038 case LFUN_BUFFER_MOVE_PREVIOUS:
2039 // we do not cycle when moving
2040 case LFUN_BUFFER_NEXT:
2041 case LFUN_BUFFER_PREVIOUS:
2042 // because we cycle, it doesn't matter whether on first or last
2043 enable = (d.currentTabWorkArea()->count() > 1);
2045 case LFUN_BUFFER_SWITCH:
2046 // toggle on the current buffer, but do not toggle off
2047 // the other ones (is that a good idea?)
2049 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2050 flag.setOnOff(true);
2053 case LFUN_VC_REGISTER:
2054 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2056 case LFUN_VC_RENAME:
2057 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2060 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2062 case LFUN_VC_CHECK_IN:
2063 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2065 case LFUN_VC_CHECK_OUT:
2066 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2068 case LFUN_VC_LOCKING_TOGGLE:
2069 enable = doc_buffer && !doc_buffer->isReadonly()
2070 && doc_buffer->lyxvc().lockingToggleEnabled();
2071 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2073 case LFUN_VC_REVERT:
2074 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2076 case LFUN_VC_UNDO_LAST:
2077 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2079 case LFUN_VC_REPO_UPDATE:
2080 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2082 case LFUN_VC_COMMAND: {
2083 if (cmd.argument().empty())
2085 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2089 case LFUN_VC_COMPARE:
2090 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2093 case LFUN_SERVER_GOTO_FILE_ROW:
2094 case LFUN_LYX_ACTIVATE:
2096 case LFUN_FORWARD_SEARCH:
2097 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2100 case LFUN_FILE_INSERT_PLAINTEXT:
2101 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2102 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2105 case LFUN_SPELLING_CONTINUOUSLY:
2106 flag.setOnOff(lyxrc.spellcheck_continuously);
2114 flag.setEnabled(false);
2120 static FileName selectTemplateFile()
2122 FileDialog dlg(qt_("Select template file"));
2123 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2124 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2126 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2127 QStringList(qt_("LyX Documents (*.lyx)")));
2129 if (result.first == FileDialog::Later)
2131 if (result.second.isEmpty())
2133 return FileName(fromqstr(result.second));
2137 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2141 Buffer * newBuffer = 0;
2143 newBuffer = checkAndLoadLyXFile(filename);
2144 } catch (ExceptionMessage const & e) {
2151 message(_("Document not loaded."));
2155 setBuffer(newBuffer);
2156 newBuffer->errors("Parse");
2159 theSession().lastFiles().add(filename);
2165 void GuiView::openDocument(string const & fname)
2167 string initpath = lyxrc.document_path;
2169 if (documentBufferView()) {
2170 string const trypath = documentBufferView()->buffer().filePath();
2171 // If directory is writeable, use this as default.
2172 if (FileName(trypath).isDirWritable())
2178 if (fname.empty()) {
2179 FileDialog dlg(qt_("Select document to open"));
2180 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2181 dlg.setButton2(qt_("Examples|#E#e"),
2182 toqstr(addPath(package().system_support().absFileName(), "examples")));
2184 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2185 FileDialog::Result result =
2186 dlg.open(toqstr(initpath), filter);
2188 if (result.first == FileDialog::Later)
2191 filename = fromqstr(result.second);
2193 // check selected filename
2194 if (filename.empty()) {
2195 message(_("Canceled."));
2201 // get absolute path of file and add ".lyx" to the filename if
2203 FileName const fullname =
2204 fileSearch(string(), filename, "lyx", support::may_not_exist);
2205 if (!fullname.empty())
2206 filename = fullname.absFileName();
2208 if (!fullname.onlyPath().isDirectory()) {
2209 Alert::warning(_("Invalid filename"),
2210 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2211 from_utf8(fullname.absFileName())));
2215 // if the file doesn't exist and isn't already open (bug 6645),
2216 // let the user create one
2217 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2218 !LyXVC::file_not_found_hook(fullname)) {
2219 // the user specifically chose this name. Believe him.
2220 Buffer * const b = newFile(filename, string(), true);
2226 docstring const disp_fn = makeDisplayPath(filename);
2227 message(bformat(_("Opening document %1$s..."), disp_fn));
2230 Buffer * buf = loadDocument(fullname);
2232 str2 = bformat(_("Document %1$s opened."), disp_fn);
2233 if (buf->lyxvc().inUse())
2234 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2235 " " + _("Version control detected.");
2237 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2242 // FIXME: clean that
2243 static bool import(GuiView * lv, FileName const & filename,
2244 string const & format, ErrorList & errorList)
2246 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2248 string loader_format;
2249 vector<string> loaders = theConverters().loaders();
2250 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2251 vector<string>::const_iterator it = loaders.begin();
2252 vector<string>::const_iterator en = loaders.end();
2253 for (; it != en; ++it) {
2254 if (!theConverters().isReachable(format, *it))
2257 string const tofile =
2258 support::changeExtension(filename.absFileName(),
2259 formats.extension(*it));
2260 if (!theConverters().convert(0, filename, FileName(tofile),
2261 filename, format, *it, errorList))
2263 loader_format = *it;
2266 if (loader_format.empty()) {
2267 frontend::Alert::error(_("Couldn't import file"),
2268 bformat(_("No information for importing the format %1$s."),
2269 formats.prettyName(format)));
2273 loader_format = format;
2275 if (loader_format == "lyx") {
2276 Buffer * buf = lv->loadDocument(lyxfile);
2280 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2284 bool as_paragraphs = loader_format == "textparagraph";
2285 string filename2 = (loader_format == format) ? filename.absFileName()
2286 : support::changeExtension(filename.absFileName(),
2287 formats.extension(loader_format));
2288 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2290 guiApp->setCurrentView(lv);
2291 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2298 void GuiView::importDocument(string const & argument)
2301 string filename = split(argument, format, ' ');
2303 LYXERR(Debug::INFO, format << " file: " << filename);
2305 // need user interaction
2306 if (filename.empty()) {
2307 string initpath = lyxrc.document_path;
2308 if (documentBufferView()) {
2309 string const trypath = documentBufferView()->buffer().filePath();
2310 // If directory is writeable, use this as default.
2311 if (FileName(trypath).isDirWritable())
2315 docstring const text = bformat(_("Select %1$s file to import"),
2316 formats.prettyName(format));
2318 FileDialog dlg(toqstr(text));
2319 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2320 dlg.setButton2(qt_("Examples|#E#e"),
2321 toqstr(addPath(package().system_support().absFileName(), "examples")));
2323 docstring filter = formats.prettyName(format);
2326 filter += from_utf8(formats.extensions(format));
2329 FileDialog::Result result =
2330 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2332 if (result.first == FileDialog::Later)
2335 filename = fromqstr(result.second);
2337 // check selected filename
2338 if (filename.empty())
2339 message(_("Canceled."));
2342 if (filename.empty())
2345 // get absolute path of file
2346 FileName const fullname(support::makeAbsPath(filename));
2348 // Can happen if the user entered a path into the dialog
2350 if (fullname.onlyFileName().empty()) {
2351 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2352 "Aborting import."),
2353 from_utf8(fullname.absFileName()));
2354 frontend::Alert::error(_("File name error"), msg);
2355 message(_("Canceled."));
2360 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2362 // Check if the document already is open
2363 Buffer * buf = theBufferList().getBuffer(lyxfile);
2366 if (!closeBuffer()) {
2367 message(_("Canceled."));
2372 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2374 // if the file exists already, and we didn't do
2375 // -i lyx thefile.lyx, warn
2376 if (lyxfile.exists() && fullname != lyxfile) {
2378 docstring text = bformat(_("The document %1$s already exists.\n\n"
2379 "Do you want to overwrite that document?"), displaypath);
2380 int const ret = Alert::prompt(_("Overwrite document?"),
2381 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2384 message(_("Canceled."));
2389 message(bformat(_("Importing %1$s..."), displaypath));
2390 ErrorList errorList;
2391 if (import(this, fullname, format, errorList))
2392 message(_("imported."));
2394 message(_("file not imported!"));
2396 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2400 void GuiView::newDocument(string const & filename, bool from_template)
2402 FileName initpath(lyxrc.document_path);
2403 if (documentBufferView()) {
2404 FileName const trypath(documentBufferView()->buffer().filePath());
2405 // If directory is writeable, use this as default.
2406 if (trypath.isDirWritable())
2410 string templatefile;
2411 if (from_template) {
2412 templatefile = selectTemplateFile().absFileName();
2413 if (templatefile.empty())
2418 if (filename.empty())
2419 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2421 b = newFile(filename, templatefile, true);
2426 // If no new document could be created, it is unsure
2427 // whether there is a valid BufferView.
2428 if (currentBufferView())
2429 // Ensure the cursor is correctly positioned on screen.
2430 currentBufferView()->showCursor();
2434 void GuiView::insertLyXFile(docstring const & fname)
2436 BufferView * bv = documentBufferView();
2441 FileName filename(to_utf8(fname));
2442 if (filename.empty()) {
2443 // Launch a file browser
2445 string initpath = lyxrc.document_path;
2446 string const trypath = bv->buffer().filePath();
2447 // If directory is writeable, use this as default.
2448 if (FileName(trypath).isDirWritable())
2452 FileDialog dlg(qt_("Select LyX document to insert"));
2453 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2454 dlg.setButton2(qt_("Examples|#E#e"),
2455 toqstr(addPath(package().system_support().absFileName(),
2458 FileDialog::Result result = dlg.open(toqstr(initpath),
2459 QStringList(qt_("LyX Documents (*.lyx)")));
2461 if (result.first == FileDialog::Later)
2465 filename.set(fromqstr(result.second));
2467 // check selected filename
2468 if (filename.empty()) {
2469 // emit message signal.
2470 message(_("Canceled."));
2475 bv->insertLyXFile(filename);
2476 bv->buffer().errors("Parse");
2480 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2482 FileName fname = b.fileName();
2483 FileName const oldname = fname;
2485 if (!newname.empty()) {
2487 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2489 // Switch to this Buffer.
2492 // No argument? Ask user through dialog.
2494 FileDialog dlg(qt_("Choose a filename to save document as"));
2495 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2496 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2498 if (!isLyXFileName(fname.absFileName()))
2499 fname.changeExtension(".lyx");
2501 FileDialog::Result result =
2502 dlg.save(toqstr(fname.onlyPath().absFileName()),
2503 QStringList(qt_("LyX Documents (*.lyx)")),
2504 toqstr(fname.onlyFileName()));
2506 if (result.first == FileDialog::Later)
2509 fname.set(fromqstr(result.second));
2514 if (!isLyXFileName(fname.absFileName()))
2515 fname.changeExtension(".lyx");
2518 // fname is now the new Buffer location.
2520 // if there is already a Buffer open with this name, we do not want
2521 // to have another one. (the second test makes sure we're not just
2522 // trying to overwrite ourselves, which is fine.)
2523 if (theBufferList().exists(fname) && fname != oldname
2524 && theBufferList().getBuffer(fname) != &b) {
2525 docstring const text =
2526 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2527 "Please close it before attempting to overwrite it.\n"
2528 "Do you want to choose a new filename?"),
2529 from_utf8(fname.absFileName()));
2530 int const ret = Alert::prompt(_("Chosen File Already Open"),
2531 text, 0, 1, _("&Rename"), _("&Cancel"));
2533 case 0: return renameBuffer(b, docstring(), kind);
2534 case 1: return false;
2539 bool const existsLocal = fname.exists();
2540 bool const existsInVC = LyXVC::fileInVC(fname);
2541 if (existsLocal || existsInVC) {
2542 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2543 if (kind != LV_WRITE_AS && existsInVC) {
2544 // renaming to a name that is already in VC
2546 docstring text = bformat(_("The document %1$s "
2547 "is already registered.\n\n"
2548 "Do you want to choose a new name?"),
2550 docstring const title = (kind == LV_VC_RENAME) ?
2551 _("Rename document?") : _("Copy document?");
2552 docstring const button = (kind == LV_VC_RENAME) ?
2553 _("&Rename") : _("&Copy");
2554 int const ret = Alert::prompt(title, text, 0, 1,
2555 button, _("&Cancel"));
2557 case 0: return renameBuffer(b, docstring(), kind);
2558 case 1: return false;
2563 docstring text = bformat(_("The document %1$s "
2564 "already exists.\n\n"
2565 "Do you want to overwrite that document?"),
2567 int const ret = Alert::prompt(_("Overwrite document?"),
2568 text, 0, 2, _("&Overwrite"),
2569 _("&Rename"), _("&Cancel"));
2572 case 1: return renameBuffer(b, docstring(), kind);
2573 case 2: return false;
2579 case LV_VC_RENAME: {
2580 string msg = b.lyxvc().rename(fname);
2583 message(from_utf8(msg));
2587 string msg = b.lyxvc().copy(fname);
2590 message(from_utf8(msg));
2596 // LyXVC created the file already in case of LV_VC_RENAME or
2597 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2598 // relative paths of included stuff right if we moved e.g. from
2599 // /a/b.lyx to /a/c/b.lyx.
2601 bool const saved = saveBuffer(b, fname);
2608 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2610 FileName fname = b.fileName();
2612 FileDialog dlg(qt_("Choose a filename to export the document as"));
2613 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2616 QString const anyformat = qt_("Guess from extension (*.*)");
2619 vector<Format const *> export_formats;
2620 for (Format const & f : formats)
2621 if (f.documentFormat())
2622 export_formats.push_back(&f);
2623 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2624 map<QString, string> fmap;
2627 for (Format const * f : export_formats) {
2628 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2629 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2631 from_ascii(f->extension())));
2632 types << loc_filter;
2633 fmap[loc_filter] = f->name();
2634 if (from_ascii(f->name()) == iformat) {
2635 filter = loc_filter;
2636 ext = f->extension();
2639 string ofname = fname.onlyFileName();
2641 ofname = support::changeExtension(ofname, ext);
2642 FileDialog::Result result =
2643 dlg.save(toqstr(fname.onlyPath().absFileName()),
2647 if (result.first != FileDialog::Chosen)
2651 fname.set(fromqstr(result.second));
2652 if (filter == anyformat)
2653 fmt_name = formats.getFormatFromExtension(fname.extension());
2655 fmt_name = fmap[filter];
2656 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2657 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2659 if (fmt_name.empty() || fname.empty())
2662 // fname is now the new Buffer location.
2663 if (FileName(fname).exists()) {
2664 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2665 docstring text = bformat(_("The document %1$s already "
2666 "exists.\n\nDo you want to "
2667 "overwrite that document?"),
2669 int const ret = Alert::prompt(_("Overwrite document?"),
2670 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2673 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2674 case 2: return false;
2678 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2681 return dr.dispatched();
2685 bool GuiView::saveBuffer(Buffer & b)
2687 return saveBuffer(b, FileName());
2691 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2693 if (workArea(b) && workArea(b)->inDialogMode())
2696 if (fn.empty() && b.isUnnamed())
2697 return renameBuffer(b, docstring());
2699 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2701 theSession().lastFiles().add(b.fileName());
2705 // Switch to this Buffer.
2708 // FIXME: we don't tell the user *WHY* the save failed !!
2709 docstring const file = makeDisplayPath(b.absFileName(), 30);
2710 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2711 "Do you want to rename the document and "
2712 "try again?"), file);
2713 int const ret = Alert::prompt(_("Rename and save?"),
2714 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2717 if (!renameBuffer(b, docstring()))
2726 return saveBuffer(b, fn);
2730 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2732 return closeWorkArea(wa, false);
2736 // We only want to close the buffer if it is not visible in other workareas
2737 // of the same view, nor in other views, and if this is not a child
2738 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2740 Buffer & buf = wa->bufferView().buffer();
2742 bool last_wa = d.countWorkAreasOf(buf) == 1
2743 && !inOtherView(buf) && !buf.parent();
2745 bool close_buffer = last_wa;
2748 if (lyxrc.close_buffer_with_last_view == "yes")
2750 else if (lyxrc.close_buffer_with_last_view == "no")
2751 close_buffer = false;
2754 if (buf.isUnnamed())
2755 file = from_utf8(buf.fileName().onlyFileName());
2757 file = buf.fileName().displayName(30);
2758 docstring const text = bformat(
2759 _("Last view on document %1$s is being closed.\n"
2760 "Would you like to close or hide the document?\n"
2762 "Hidden documents can be displayed back through\n"
2763 "the menu: View->Hidden->...\n"
2765 "To remove this question, set your preference in:\n"
2766 " Tools->Preferences->Look&Feel->UserInterface\n"
2768 int ret = Alert::prompt(_("Close or hide document?"),
2769 text, 0, 1, _("&Close"), _("&Hide"));
2770 close_buffer = (ret == 0);
2774 return closeWorkArea(wa, close_buffer);
2778 bool GuiView::closeBuffer()
2780 GuiWorkArea * wa = currentMainWorkArea();
2781 // coverity complained about this
2782 // it seems unnecessary, but perhaps is worth the check
2783 LASSERT(wa, return false);
2785 setCurrentWorkArea(wa);
2786 Buffer & buf = wa->bufferView().buffer();
2787 return closeWorkArea(wa, !buf.parent());
2791 void GuiView::writeSession() const {
2792 GuiWorkArea const * active_wa = currentMainWorkArea();
2793 for (int i = 0; i < d.splitter_->count(); ++i) {
2794 TabWorkArea * twa = d.tabWorkArea(i);
2795 for (int j = 0; j < twa->count(); ++j) {
2796 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2797 Buffer & buf = wa->bufferView().buffer();
2798 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2804 bool GuiView::closeBufferAll()
2806 // Close the workareas in all other views
2807 QList<int> const ids = guiApp->viewIds();
2808 for (int i = 0; i != ids.size(); ++i) {
2809 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2813 // Close our own workareas
2814 if (!closeWorkAreaAll())
2817 // Now close the hidden buffers. We prevent hidden buffers from being
2818 // dirty, so we can just close them.
2819 theBufferList().closeAll();
2824 bool GuiView::closeWorkAreaAll()
2826 setCurrentWorkArea(currentMainWorkArea());
2828 // We might be in a situation that there is still a tabWorkArea, but
2829 // there are no tabs anymore. This can happen when we get here after a
2830 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2831 // many TabWorkArea's have no documents anymore.
2834 // We have to call count() each time, because it can happen that
2835 // more than one splitter will disappear in one iteration (bug 5998).
2836 while (d.splitter_->count() > empty_twa) {
2837 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2839 if (twa->count() == 0)
2842 setCurrentWorkArea(twa->currentWorkArea());
2843 if (!closeTabWorkArea(twa))
2851 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2856 Buffer & buf = wa->bufferView().buffer();
2858 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2859 Alert::warning(_("Close document"),
2860 _("Document could not be closed because it is being processed by LyX."));
2865 return closeBuffer(buf);
2867 if (!inMultiTabs(wa))
2868 if (!saveBufferIfNeeded(buf, true))
2876 bool GuiView::closeBuffer(Buffer & buf)
2878 // If we are in a close_event all children will be closed in some time,
2879 // so no need to do it here. This will ensure that the children end up
2880 // in the session file in the correct order. If we close the master
2881 // buffer, we can close or release the child buffers here too.
2882 bool success = true;
2884 ListOfBuffers clist = buf.getChildren();
2885 ListOfBuffers::const_iterator it = clist.begin();
2886 ListOfBuffers::const_iterator const bend = clist.end();
2887 for (; it != bend; ++it) {
2888 Buffer * child_buf = *it;
2889 if (theBufferList().isOthersChild(&buf, child_buf)) {
2890 child_buf->setParent(0);
2894 // FIXME: should we look in other tabworkareas?
2895 // ANSWER: I don't think so. I've tested, and if the child is
2896 // open in some other window, it closes without a problem.
2897 GuiWorkArea * child_wa = workArea(*child_buf);
2899 success = closeWorkArea(child_wa, true);
2903 // In this case the child buffer is open but hidden.
2904 // It therefore should not (MUST NOT) be dirty!
2905 LATTEST(child_buf->isClean());
2906 theBufferList().release(child_buf);
2911 // goto bookmark to update bookmark pit.
2912 // FIXME: we should update only the bookmarks related to this buffer!
2913 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2914 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2915 guiApp->gotoBookmark(i+1, false, false);
2917 if (saveBufferIfNeeded(buf, false)) {
2918 buf.removeAutosaveFile();
2919 theBufferList().release(&buf);
2923 // open all children again to avoid a crash because of dangling
2924 // pointers (bug 6603)
2930 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2932 while (twa == d.currentTabWorkArea()) {
2933 twa->setCurrentIndex(twa->count() - 1);
2935 GuiWorkArea * wa = twa->currentWorkArea();
2936 Buffer & b = wa->bufferView().buffer();
2938 // We only want to close the buffer if the same buffer is not visible
2939 // in another view, and if this is not a child and if we are closing
2940 // a view (not a tabgroup).
2941 bool const close_buffer =
2942 !inOtherView(b) && !b.parent() && closing_;
2944 if (!closeWorkArea(wa, close_buffer))
2951 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2953 if (buf.isClean() || buf.paragraphs().empty())
2956 // Switch to this Buffer.
2961 if (buf.isUnnamed())
2962 file = from_utf8(buf.fileName().onlyFileName());
2964 file = buf.fileName().displayName(30);
2966 // Bring this window to top before asking questions.
2971 if (hiding && buf.isUnnamed()) {
2972 docstring const text = bformat(_("The document %1$s has not been "
2973 "saved yet.\n\nDo you want to save "
2974 "the document?"), file);
2975 ret = Alert::prompt(_("Save new document?"),
2976 text, 0, 1, _("&Save"), _("&Cancel"));
2980 docstring const text = bformat(_("The document %1$s has unsaved changes."
2981 "\n\nDo you want to save the document or discard the changes?"), file);
2982 ret = Alert::prompt(_("Save changed document?"),
2983 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2988 if (!saveBuffer(buf))
2992 // If we crash after this we could have no autosave file
2993 // but I guess this is really improbable (Jug).
2994 // Sometimes improbable things happen:
2995 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2996 // buf.removeAutosaveFile();
2998 // revert all changes
3009 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3011 Buffer & buf = wa->bufferView().buffer();
3013 for (int i = 0; i != d.splitter_->count(); ++i) {
3014 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3015 if (wa_ && wa_ != wa)
3018 return inOtherView(buf);
3022 bool GuiView::inOtherView(Buffer & buf)
3024 QList<int> const ids = guiApp->viewIds();
3026 for (int i = 0; i != ids.size(); ++i) {
3030 if (guiApp->view(ids[i]).workArea(buf))
3037 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3039 if (!documentBufferView())
3042 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3043 Buffer * const curbuf = &documentBufferView()->buffer();
3044 int nwa = twa->count();
3045 for (int i = 0; i < nwa; ++i) {
3046 if (&workArea(i)->bufferView().buffer() == curbuf) {
3048 if (np == NEXTBUFFER)
3049 next_index = (i == nwa - 1 ? 0 : i + 1);
3051 next_index = (i == 0 ? nwa - 1 : i - 1);
3053 twa->moveTab(i, next_index);
3055 setBuffer(&workArea(next_index)->bufferView().buffer());
3063 /// make sure the document is saved
3064 static bool ensureBufferClean(Buffer * buffer)
3066 LASSERT(buffer, return false);
3067 if (buffer->isClean() && !buffer->isUnnamed())
3070 docstring const file = buffer->fileName().displayName(30);
3073 if (!buffer->isUnnamed()) {
3074 text = bformat(_("The document %1$s has unsaved "
3075 "changes.\n\nDo you want to save "
3076 "the document?"), file);
3077 title = _("Save changed document?");
3080 text = bformat(_("The document %1$s has not been "
3081 "saved yet.\n\nDo you want to save "
3082 "the document?"), file);
3083 title = _("Save new document?");
3085 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3088 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3090 return buffer->isClean() && !buffer->isUnnamed();
3094 bool GuiView::reloadBuffer(Buffer & buf)
3096 Buffer::ReadStatus status = buf.reload();
3097 return status == Buffer::ReadSuccess;
3101 void GuiView::checkExternallyModifiedBuffers()
3103 BufferList::iterator bit = theBufferList().begin();
3104 BufferList::iterator const bend = theBufferList().end();
3105 for (; bit != bend; ++bit) {
3106 Buffer * buf = *bit;
3107 if (buf->fileName().exists()
3108 && buf->isExternallyModified(Buffer::checksum_method)) {
3109 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3110 " Reload now? Any local changes will be lost."),
3111 from_utf8(buf->absFileName()));
3112 int const ret = Alert::prompt(_("Reload externally changed document?"),
3113 text, 0, 1, _("&Reload"), _("&Cancel"));
3121 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3123 Buffer * buffer = documentBufferView()
3124 ? &(documentBufferView()->buffer()) : 0;
3126 switch (cmd.action()) {
3127 case LFUN_VC_REGISTER:
3128 if (!buffer || !ensureBufferClean(buffer))
3130 if (!buffer->lyxvc().inUse()) {
3131 if (buffer->lyxvc().registrer()) {
3132 reloadBuffer(*buffer);
3133 dr.clearMessageUpdate();
3138 case LFUN_VC_RENAME:
3139 case LFUN_VC_COPY: {
3140 if (!buffer || !ensureBufferClean(buffer))
3142 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3143 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3144 // Some changes are not yet committed.
3145 // We test here and not in getStatus(), since
3146 // this test is expensive.
3148 LyXVC::CommandResult ret =
3149 buffer->lyxvc().checkIn(log);
3151 if (ret == LyXVC::ErrorCommand ||
3152 ret == LyXVC::VCSuccess)
3153 reloadBuffer(*buffer);
3154 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3155 frontend::Alert::error(
3156 _("Revision control error."),
3157 _("Document could not be checked in."));
3161 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3162 LV_VC_RENAME : LV_VC_COPY;
3163 renameBuffer(*buffer, cmd.argument(), kind);
3168 case LFUN_VC_CHECK_IN:
3169 if (!buffer || !ensureBufferClean(buffer))
3171 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3173 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3175 // Only skip reloading if the checkin was cancelled or
3176 // an error occurred before the real checkin VCS command
3177 // was executed, since the VCS might have changed the
3178 // file even if it could not checkin successfully.
3179 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3180 reloadBuffer(*buffer);
3184 case LFUN_VC_CHECK_OUT:
3185 if (!buffer || !ensureBufferClean(buffer))
3187 if (buffer->lyxvc().inUse()) {
3188 dr.setMessage(buffer->lyxvc().checkOut());
3189 reloadBuffer(*buffer);
3193 case LFUN_VC_LOCKING_TOGGLE:
3194 LASSERT(buffer, return);
3195 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3197 if (buffer->lyxvc().inUse()) {
3198 string res = buffer->lyxvc().lockingToggle();
3200 frontend::Alert::error(_("Revision control error."),
3201 _("Error when setting the locking property."));
3204 reloadBuffer(*buffer);
3209 case LFUN_VC_REVERT:
3210 LASSERT(buffer, return);
3211 if (buffer->lyxvc().revert()) {
3212 reloadBuffer(*buffer);
3213 dr.clearMessageUpdate();
3217 case LFUN_VC_UNDO_LAST:
3218 LASSERT(buffer, return);
3219 buffer->lyxvc().undoLast();
3220 reloadBuffer(*buffer);
3221 dr.clearMessageUpdate();
3224 case LFUN_VC_REPO_UPDATE:
3225 LASSERT(buffer, return);
3226 if (ensureBufferClean(buffer)) {
3227 dr.setMessage(buffer->lyxvc().repoUpdate());
3228 checkExternallyModifiedBuffers();
3232 case LFUN_VC_COMMAND: {
3233 string flag = cmd.getArg(0);
3234 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3237 if (contains(flag, 'M')) {
3238 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3241 string path = cmd.getArg(1);
3242 if (contains(path, "$$p") && buffer)
3243 path = subst(path, "$$p", buffer->filePath());
3244 LYXERR(Debug::LYXVC, "Directory: " << path);
3246 if (!pp.isReadableDirectory()) {
3247 lyxerr << _("Directory is not accessible.") << endl;
3250 support::PathChanger p(pp);
3252 string command = cmd.getArg(2);
3253 if (command.empty())
3256 command = subst(command, "$$i", buffer->absFileName());
3257 command = subst(command, "$$p", buffer->filePath());
3259 command = subst(command, "$$m", to_utf8(message));
3260 LYXERR(Debug::LYXVC, "Command: " << command);
3262 one.startscript(Systemcall::Wait, command);
3266 if (contains(flag, 'I'))
3267 buffer->markDirty();
3268 if (contains(flag, 'R'))
3269 reloadBuffer(*buffer);
3274 case LFUN_VC_COMPARE: {
3275 if (cmd.argument().empty()) {
3276 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3280 string rev1 = cmd.getArg(0);
3284 // it seems safe to assume we have a buffer
3285 // coverity[FORWARD_NULL]
3286 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3289 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3290 f2 = buffer->absFileName();
3292 string rev2 = cmd.getArg(1);
3296 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3300 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3301 f1 << "\n" << f2 << "\n" );
3302 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3303 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3313 void GuiView::openChildDocument(string const & fname)
3315 LASSERT(documentBufferView(), return);
3316 Buffer & buffer = documentBufferView()->buffer();
3317 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3318 documentBufferView()->saveBookmark(false);
3320 if (theBufferList().exists(filename)) {
3321 child = theBufferList().getBuffer(filename);
3324 message(bformat(_("Opening child document %1$s..."),
3325 makeDisplayPath(filename.absFileName())));
3326 child = loadDocument(filename, false);
3328 // Set the parent name of the child document.
3329 // This makes insertion of citations and references in the child work,
3330 // when the target is in the parent or another child document.
3332 child->setParent(&buffer);
3336 bool GuiView::goToFileRow(string const & argument)
3340 size_t i = argument.find_last_of(' ');
3341 if (i != string::npos) {
3342 file_name = os::internal_path(trim(argument.substr(0, i)));
3343 istringstream is(argument.substr(i + 1));
3348 if (i == string::npos) {
3349 LYXERR0("Wrong argument: " << argument);
3353 string const abstmp = package().temp_dir().absFileName();
3354 string const realtmp = package().temp_dir().realPath();
3355 // We have to use os::path_prefix_is() here, instead of
3356 // simply prefixIs(), because the file name comes from
3357 // an external application and may need case adjustment.
3358 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3359 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3360 // Needed by inverse dvi search. If it is a file
3361 // in tmpdir, call the apropriated function.
3362 // If tmpdir is a symlink, we may have the real
3363 // path passed back, so we correct for that.
3364 if (!prefixIs(file_name, abstmp))
3365 file_name = subst(file_name, realtmp, abstmp);
3366 buf = theBufferList().getBufferFromTmp(file_name);
3368 // Must replace extension of the file to be .lyx
3369 // and get full path
3370 FileName const s = fileSearch(string(),
3371 support::changeExtension(file_name, ".lyx"), "lyx");
3372 // Either change buffer or load the file
3373 if (theBufferList().exists(s))
3374 buf = theBufferList().getBuffer(s);
3375 else if (s.exists()) {
3376 buf = loadDocument(s);
3381 _("File does not exist: %1$s"),
3382 makeDisplayPath(file_name)));
3388 _("No buffer for file: %1$s."),
3389 makeDisplayPath(file_name))
3394 bool success = documentBufferView()->setCursorFromRow(row);
3396 LYXERR(Debug::LATEX,
3397 "setCursorFromRow: invalid position for row " << row);
3398 frontend::Alert::error(_("Inverse Search Failed"),
3399 _("Invalid position requested by inverse search.\n"
3400 "You may need to update the viewed document."));
3407 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3409 Buffer::ExportStatus const status = func(format);
3411 // the cloning operation will have produced a clone of the entire set of
3412 // documents, starting from the master. so we must delete those.
3413 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3415 busyBuffers.remove(orig);
3420 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3422 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3423 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3427 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3429 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3430 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3434 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3436 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3437 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3441 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3442 string const & argument,
3443 Buffer const * used_buffer,
3444 docstring const & msg,
3445 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3446 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3447 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3452 string format = argument;
3454 format = used_buffer->params().getDefaultOutputFormat();
3455 processing_format = format;
3457 progress_->clearMessages();
3460 #if EXPORT_in_THREAD
3461 GuiViewPrivate::busyBuffers.insert(used_buffer);
3462 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3463 if (!cloned_buffer) {
3464 Alert::error(_("Export Error"),
3465 _("Error cloning the Buffer."));
3468 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3473 setPreviewFuture(f);
3474 last_export_format = used_buffer->params().bufferFormat();
3477 // We are asynchronous, so we don't know here anything about the success
3480 Buffer::ExportStatus status;
3482 status = (used_buffer->*syncFunc)(format, true);
3483 } else if (previewFunc) {
3484 status = (used_buffer->*previewFunc)(format);
3487 handleExportStatus(gv_, status, format);
3489 return (status == Buffer::ExportSuccess
3490 || status == Buffer::PreviewSuccess);
3494 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3496 BufferView * bv = currentBufferView();
3497 LASSERT(bv, return);
3499 // Let the current BufferView dispatch its own actions.
3500 bv->dispatch(cmd, dr);
3501 if (dr.dispatched())
3504 // Try with the document BufferView dispatch if any.
3505 BufferView * doc_bv = documentBufferView();
3506 if (doc_bv && doc_bv != bv) {
3507 doc_bv->dispatch(cmd, dr);
3508 if (dr.dispatched())
3512 // Then let the current Cursor dispatch its own actions.
3513 bv->cursor().dispatch(cmd);
3515 // update completion. We do it here and not in
3516 // processKeySym to avoid another redraw just for a
3517 // changed inline completion
3518 if (cmd.origin() == FuncRequest::KEYBOARD) {
3519 if (cmd.action() == LFUN_SELF_INSERT
3520 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3521 updateCompletion(bv->cursor(), true, true);
3522 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3523 updateCompletion(bv->cursor(), false, true);
3525 updateCompletion(bv->cursor(), false, false);
3528 dr = bv->cursor().result();
3532 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3534 BufferView * bv = currentBufferView();
3535 // By default we won't need any update.
3536 dr.screenUpdate(Update::None);
3537 // assume cmd will be dispatched
3538 dr.dispatched(true);
3540 Buffer * doc_buffer = documentBufferView()
3541 ? &(documentBufferView()->buffer()) : 0;
3543 if (cmd.origin() == FuncRequest::TOC) {
3544 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3545 // FIXME: do we need to pass a DispatchResult object here?
3546 toc->doDispatch(bv->cursor(), cmd);
3550 string const argument = to_utf8(cmd.argument());
3552 switch(cmd.action()) {
3553 case LFUN_BUFFER_CHILD_OPEN:
3554 openChildDocument(to_utf8(cmd.argument()));
3557 case LFUN_BUFFER_IMPORT:
3558 importDocument(to_utf8(cmd.argument()));
3561 case LFUN_BUFFER_EXPORT: {
3564 // GCC only sees strfwd.h when building merged
3565 if (::lyx::operator==(cmd.argument(), "custom")) {
3566 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3570 string const dest = cmd.getArg(1);
3571 FileName target_dir;
3572 if (!dest.empty() && FileName::isAbsolute(dest))
3573 target_dir = FileName(support::onlyPath(dest));
3575 target_dir = doc_buffer->fileName().onlyPath();
3577 if ((dest.empty() && doc_buffer->isUnnamed())
3578 || !target_dir.isDirWritable()) {
3579 exportBufferAs(*doc_buffer, cmd.argument());
3582 /* TODO/Review: Is it a problem to also export the children?
3583 See the update_unincluded flag */
3584 d.asyncBufferProcessing(argument,
3587 &GuiViewPrivate::exportAndDestroy,
3590 // TODO Inform user about success
3594 case LFUN_BUFFER_EXPORT_AS: {
3595 LASSERT(doc_buffer, break);
3596 docstring f = cmd.argument();
3598 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3599 exportBufferAs(*doc_buffer, f);
3603 case LFUN_BUFFER_UPDATE: {
3604 d.asyncBufferProcessing(argument,
3607 &GuiViewPrivate::compileAndDestroy,
3612 case LFUN_BUFFER_VIEW: {
3613 d.asyncBufferProcessing(argument,
3615 _("Previewing ..."),
3616 &GuiViewPrivate::previewAndDestroy,
3621 case LFUN_MASTER_BUFFER_UPDATE: {
3622 d.asyncBufferProcessing(argument,
3623 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3625 &GuiViewPrivate::compileAndDestroy,
3630 case LFUN_MASTER_BUFFER_VIEW: {
3631 d.asyncBufferProcessing(argument,
3632 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3634 &GuiViewPrivate::previewAndDestroy,
3635 0, &Buffer::preview);
3638 case LFUN_BUFFER_SWITCH: {
3639 string const file_name = to_utf8(cmd.argument());
3640 if (!FileName::isAbsolute(file_name)) {
3642 dr.setMessage(_("Absolute filename expected."));
3646 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3649 dr.setMessage(_("Document not loaded"));
3653 // Do we open or switch to the buffer in this view ?
3654 if (workArea(*buffer)
3655 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3660 // Look for the buffer in other views
3661 QList<int> const ids = guiApp->viewIds();
3663 for (; i != ids.size(); ++i) {
3664 GuiView & gv = guiApp->view(ids[i]);
3665 if (gv.workArea(*buffer)) {
3667 gv.activateWindow();
3669 gv.setBuffer(buffer);
3674 // If necessary, open a new window as a last resort
3675 if (i == ids.size()) {
3676 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3682 case LFUN_BUFFER_NEXT:
3683 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3686 case LFUN_BUFFER_MOVE_NEXT:
3687 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3690 case LFUN_BUFFER_PREVIOUS:
3691 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3694 case LFUN_BUFFER_MOVE_PREVIOUS:
3695 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3698 case LFUN_COMMAND_EXECUTE: {
3699 command_execute_ = true;
3700 minibuffer_focus_ = true;
3703 case LFUN_DROP_LAYOUTS_CHOICE:
3704 d.layout_->showPopup();
3707 case LFUN_MENU_OPEN:
3708 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3709 menu->exec(QCursor::pos());
3712 case LFUN_FILE_INSERT:
3713 insertLyXFile(cmd.argument());
3716 case LFUN_FILE_INSERT_PLAINTEXT:
3717 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3718 string const fname = to_utf8(cmd.argument());
3719 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3720 dr.setMessage(_("Absolute filename expected."));
3724 FileName filename(fname);
3725 if (fname.empty()) {
3726 FileDialog dlg(qt_("Select file to insert"));
3728 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3729 QStringList(qt_("All Files (*)")));
3731 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3732 dr.setMessage(_("Canceled."));
3736 filename.set(fromqstr(result.second));
3740 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3741 bv->dispatch(new_cmd, dr);
3746 case LFUN_BUFFER_RELOAD: {
3747 LASSERT(doc_buffer, break);
3750 if (!doc_buffer->isClean()) {
3751 docstring const file =
3752 makeDisplayPath(doc_buffer->absFileName(), 20);
3753 docstring text = bformat(_("Any changes will be lost. "
3754 "Are you sure you want to revert to the saved version "
3755 "of the document %1$s?"), file);
3756 ret = Alert::prompt(_("Revert to saved document?"),
3757 text, 1, 1, _("&Revert"), _("&Cancel"));
3761 doc_buffer->markClean();
3762 reloadBuffer(*doc_buffer);
3763 dr.forceBufferUpdate();
3768 case LFUN_BUFFER_WRITE:
3769 LASSERT(doc_buffer, break);
3770 saveBuffer(*doc_buffer);
3773 case LFUN_BUFFER_WRITE_AS:
3774 LASSERT(doc_buffer, break);
3775 renameBuffer(*doc_buffer, cmd.argument());
3778 case LFUN_BUFFER_WRITE_ALL: {
3779 Buffer * first = theBufferList().first();
3782 message(_("Saving all documents..."));
3783 // We cannot use a for loop as the buffer list cycles.
3786 if (!b->isClean()) {
3788 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3790 b = theBufferList().next(b);
3791 } while (b != first);
3792 dr.setMessage(_("All documents saved."));
3796 case LFUN_BUFFER_CLOSE:
3800 case LFUN_BUFFER_CLOSE_ALL:
3804 case LFUN_TOOLBAR_TOGGLE: {
3805 string const name = cmd.getArg(0);
3806 if (GuiToolbar * t = toolbar(name))
3811 case LFUN_DIALOG_UPDATE: {
3812 string const name = to_utf8(cmd.argument());
3813 if (name == "prefs" || name == "document")
3814 updateDialog(name, string());
3815 else if (name == "paragraph")
3816 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3817 else if (currentBufferView()) {
3818 Inset * inset = currentBufferView()->editedInset(name);
3819 // Can only update a dialog connected to an existing inset
3821 // FIXME: get rid of this indirection; GuiView ask the inset
3822 // if he is kind enough to update itself...
3823 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3824 //FIXME: pass DispatchResult here?
3825 inset->dispatch(currentBufferView()->cursor(), fr);
3831 case LFUN_DIALOG_TOGGLE: {
3832 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3833 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3834 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3838 case LFUN_DIALOG_DISCONNECT_INSET:
3839 disconnectDialog(to_utf8(cmd.argument()));
3842 case LFUN_DIALOG_HIDE: {
3843 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3847 case LFUN_DIALOG_SHOW: {
3848 string const name = cmd.getArg(0);
3849 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3851 if (name == "character") {
3852 data = freefont2string();
3854 showDialog("character", data);
3855 } else if (name == "latexlog") {
3856 Buffer::LogType type;
3857 string const logfile = doc_buffer->logName(&type);
3859 case Buffer::latexlog:
3862 case Buffer::buildlog:
3866 data += Lexer::quoteString(logfile);
3867 showDialog("log", data);
3868 } else if (name == "vclog") {
3869 string const data = "vc " +
3870 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3871 showDialog("log", data);
3872 } else if (name == "symbols") {
3873 data = bv->cursor().getEncoding()->name();
3875 showDialog("symbols", data);
3877 } else if (name == "prefs" && isFullScreen()) {
3878 lfunUiToggle("fullscreen");
3879 showDialog("prefs", data);
3881 showDialog(name, data);
3886 dr.setMessage(cmd.argument());
3889 case LFUN_UI_TOGGLE: {
3890 string arg = cmd.getArg(0);
3891 if (!lfunUiToggle(arg)) {
3892 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3893 dr.setMessage(bformat(msg, from_utf8(arg)));
3895 // Make sure the keyboard focus stays in the work area.
3900 case LFUN_VIEW_SPLIT: {
3901 LASSERT(doc_buffer, break);
3902 string const orientation = cmd.getArg(0);
3903 d.splitter_->setOrientation(orientation == "vertical"
3904 ? Qt::Vertical : Qt::Horizontal);
3905 TabWorkArea * twa = addTabWorkArea();
3906 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3907 setCurrentWorkArea(wa);
3910 case LFUN_TAB_GROUP_CLOSE:
3911 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3912 closeTabWorkArea(twa);
3913 d.current_work_area_ = 0;
3914 twa = d.currentTabWorkArea();
3915 // Switch to the next GuiWorkArea in the found TabWorkArea.
3917 // Make sure the work area is up to date.
3918 setCurrentWorkArea(twa->currentWorkArea());
3920 setCurrentWorkArea(0);
3925 case LFUN_VIEW_CLOSE:
3926 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3927 closeWorkArea(twa->currentWorkArea());
3928 d.current_work_area_ = 0;
3929 twa = d.currentTabWorkArea();
3930 // Switch to the next GuiWorkArea in the found TabWorkArea.
3932 // Make sure the work area is up to date.
3933 setCurrentWorkArea(twa->currentWorkArea());
3935 setCurrentWorkArea(0);
3940 case LFUN_COMPLETION_INLINE:
3941 if (d.current_work_area_)
3942 d.current_work_area_->completer().showInline();
3945 case LFUN_COMPLETION_POPUP:
3946 if (d.current_work_area_)
3947 d.current_work_area_->completer().showPopup();
3952 if (d.current_work_area_)
3953 d.current_work_area_->completer().tab();
3956 case LFUN_COMPLETION_CANCEL:
3957 if (d.current_work_area_) {
3958 if (d.current_work_area_->completer().popupVisible())
3959 d.current_work_area_->completer().hidePopup();
3961 d.current_work_area_->completer().hideInline();
3965 case LFUN_COMPLETION_ACCEPT:
3966 if (d.current_work_area_)
3967 d.current_work_area_->completer().activate();
3970 case LFUN_BUFFER_ZOOM_IN:
3971 case LFUN_BUFFER_ZOOM_OUT: {
3972 // use a signed temp to avoid overflow
3973 int zoom = lyxrc.zoom;
3974 if (cmd.argument().empty()) {
3975 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3980 zoom += convert<int>(cmd.argument());
3982 if (zoom < static_cast<int>(zoom_min_))
3986 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3988 // The global QPixmapCache is used in GuiPainter to cache text
3989 // painting so we must reset it.
3990 QPixmapCache::clear();
3991 guiApp->fontLoader().update();
3992 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3996 case LFUN_VC_REGISTER:
3997 case LFUN_VC_RENAME:
3999 case LFUN_VC_CHECK_IN:
4000 case LFUN_VC_CHECK_OUT:
4001 case LFUN_VC_REPO_UPDATE:
4002 case LFUN_VC_LOCKING_TOGGLE:
4003 case LFUN_VC_REVERT:
4004 case LFUN_VC_UNDO_LAST:
4005 case LFUN_VC_COMMAND:
4006 case LFUN_VC_COMPARE:
4007 dispatchVC(cmd, dr);
4010 case LFUN_SERVER_GOTO_FILE_ROW:
4011 if(goToFileRow(to_utf8(cmd.argument())))
4012 dr.screenUpdate(Update::Force | Update::FitCursor);
4015 case LFUN_LYX_ACTIVATE:
4019 case LFUN_FORWARD_SEARCH: {
4020 // it seems safe to assume we have a document buffer, since
4021 // getStatus wants one.
4022 // coverity[FORWARD_NULL]
4023 Buffer const * doc_master = doc_buffer->masterBuffer();
4024 FileName const path(doc_master->temppath());
4025 string const texname = doc_master->isChild(doc_buffer)
4026 ? DocFileName(changeExtension(
4027 doc_buffer->absFileName(),
4028 "tex")).mangledFileName()
4029 : doc_buffer->latexName();
4030 string const fulltexname =
4031 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4032 string const mastername =
4033 removeExtension(doc_master->latexName());
4034 FileName const dviname(addName(path.absFileName(),
4035 addExtension(mastername, "dvi")));
4036 FileName const pdfname(addName(path.absFileName(),
4037 addExtension(mastername, "pdf")));
4038 bool const have_dvi = dviname.exists();
4039 bool const have_pdf = pdfname.exists();
4040 if (!have_dvi && !have_pdf) {
4041 dr.setMessage(_("Please, preview the document first."));
4044 string outname = dviname.onlyFileName();
4045 string command = lyxrc.forward_search_dvi;
4046 if (!have_dvi || (have_pdf &&
4047 pdfname.lastModified() > dviname.lastModified())) {
4048 outname = pdfname.onlyFileName();
4049 command = lyxrc.forward_search_pdf;
4052 DocIterator cur = bv->cursor();
4053 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4054 LYXERR(Debug::ACTION, "Forward search: row:" << row
4056 if (row == -1 || command.empty()) {
4057 dr.setMessage(_("Couldn't proceed."));
4060 string texrow = convert<string>(row);
4062 command = subst(command, "$$n", texrow);
4063 command = subst(command, "$$f", fulltexname);
4064 command = subst(command, "$$t", texname);
4065 command = subst(command, "$$o", outname);
4067 PathChanger p(path);
4069 one.startscript(Systemcall::DontWait, command);
4073 case LFUN_SPELLING_CONTINUOUSLY:
4074 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4075 dr.screenUpdate(Update::Force);
4079 // The LFUN must be for one of BufferView, Buffer or Cursor;
4081 dispatchToBufferView(cmd, dr);
4085 // Part of automatic menu appearance feature.
4086 if (isFullScreen()) {
4087 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4091 // Need to update bv because many LFUNs here might have destroyed it
4092 bv = currentBufferView();
4094 // Clear non-empty selections
4095 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4097 Cursor & cur = bv->cursor();
4098 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4099 cur.clearSelection();
4105 bool GuiView::lfunUiToggle(string const & ui_component)
4107 if (ui_component == "scrollbar") {
4108 // hide() is of no help
4109 if (d.current_work_area_->verticalScrollBarPolicy() ==
4110 Qt::ScrollBarAlwaysOff)
4112 d.current_work_area_->setVerticalScrollBarPolicy(
4113 Qt::ScrollBarAsNeeded);
4115 d.current_work_area_->setVerticalScrollBarPolicy(
4116 Qt::ScrollBarAlwaysOff);
4117 } else if (ui_component == "statusbar") {
4118 statusBar()->setVisible(!statusBar()->isVisible());
4119 } else if (ui_component == "menubar") {
4120 menuBar()->setVisible(!menuBar()->isVisible());
4122 if (ui_component == "frame") {
4124 getContentsMargins(&l, &t, &r, &b);
4125 //are the frames in default state?
4126 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4128 setContentsMargins(-2, -2, -2, -2);
4130 setContentsMargins(0, 0, 0, 0);
4133 if (ui_component == "fullscreen") {
4141 void GuiView::toggleFullScreen()
4143 if (isFullScreen()) {
4144 for (int i = 0; i != d.splitter_->count(); ++i)
4145 d.tabWorkArea(i)->setFullScreen(false);
4146 setContentsMargins(0, 0, 0, 0);
4147 setWindowState(windowState() ^ Qt::WindowFullScreen);
4150 statusBar()->show();
4153 hideDialogs("prefs", 0);
4154 for (int i = 0; i != d.splitter_->count(); ++i)
4155 d.tabWorkArea(i)->setFullScreen(true);
4156 setContentsMargins(-2, -2, -2, -2);
4158 setWindowState(windowState() ^ Qt::WindowFullScreen);
4159 if (lyxrc.full_screen_statusbar)
4160 statusBar()->hide();
4161 if (lyxrc.full_screen_menubar)
4163 if (lyxrc.full_screen_toolbars) {
4164 ToolbarMap::iterator end = d.toolbars_.end();
4165 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4170 // give dialogs like the TOC a chance to adapt
4175 Buffer const * GuiView::updateInset(Inset const * inset)
4180 Buffer const * inset_buffer = &(inset->buffer());
4182 for (int i = 0; i != d.splitter_->count(); ++i) {
4183 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4186 Buffer const * buffer = &(wa->bufferView().buffer());
4187 if (inset_buffer == buffer)
4188 wa->scheduleRedraw();
4190 return inset_buffer;
4194 void GuiView::restartCursor()
4196 /* When we move around, or type, it's nice to be able to see
4197 * the cursor immediately after the keypress.
4199 if (d.current_work_area_)
4200 d.current_work_area_->startBlinkingCursor();
4202 // Take this occasion to update the other GUI elements.
4208 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4210 if (d.current_work_area_)
4211 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4216 // This list should be kept in sync with the list of insets in
4217 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4218 // dialog should have the same name as the inset.
4219 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4220 // docs in LyXAction.cpp.
4222 char const * const dialognames[] = {
4224 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4225 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4226 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4227 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4228 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4229 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4230 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4231 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4233 char const * const * const end_dialognames =
4234 dialognames + (sizeof(dialognames) / sizeof(char *));
4238 cmpCStr(char const * name) : name_(name) {}
4239 bool operator()(char const * other) {
4240 return strcmp(other, name_) == 0;
4247 bool isValidName(string const & name)
4249 return find_if(dialognames, end_dialognames,
4250 cmpCStr(name.c_str())) != end_dialognames;
4256 void GuiView::resetDialogs()
4258 // Make sure that no LFUN uses any GuiView.
4259 guiApp->setCurrentView(0);
4263 constructToolbars();
4264 guiApp->menus().fillMenuBar(menuBar(), this, false);
4265 d.layout_->updateContents(true);
4266 // Now update controls with current buffer.
4267 guiApp->setCurrentView(this);
4273 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4275 if (!isValidName(name))
4278 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4280 if (it != d.dialogs_.end()) {
4282 it->second->hideView();
4283 return it->second.get();
4286 Dialog * dialog = build(name);
4287 d.dialogs_[name].reset(dialog);
4288 if (lyxrc.allow_geometry_session)
4289 dialog->restoreSession();
4296 void GuiView::showDialog(string const & name, string const & data,
4299 triggerShowDialog(toqstr(name), toqstr(data), inset);
4303 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4309 const string name = fromqstr(qname);
4310 const string data = fromqstr(qdata);
4314 Dialog * dialog = findOrBuild(name, false);
4316 bool const visible = dialog->isVisibleView();
4317 dialog->showData(data);
4318 if (inset && currentBufferView())
4319 currentBufferView()->editInset(name, inset);
4320 // We only set the focus to the new dialog if it was not yet
4321 // visible in order not to change the existing previous behaviour
4323 // activateWindow is needed for floating dockviews
4324 dialog->asQWidget()->raise();
4325 dialog->asQWidget()->activateWindow();
4326 dialog->asQWidget()->setFocus();
4330 catch (ExceptionMessage const & ex) {
4338 bool GuiView::isDialogVisible(string const & name) const
4340 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4341 if (it == d.dialogs_.end())
4343 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4347 void GuiView::hideDialog(string const & name, Inset * inset)
4349 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4350 if (it == d.dialogs_.end())
4354 if (!currentBufferView())
4356 if (inset != currentBufferView()->editedInset(name))
4360 Dialog * const dialog = it->second.get();
4361 if (dialog->isVisibleView())
4363 if (currentBufferView())
4364 currentBufferView()->editInset(name, 0);
4368 void GuiView::disconnectDialog(string const & name)
4370 if (!isValidName(name))
4372 if (currentBufferView())
4373 currentBufferView()->editInset(name, 0);
4377 void GuiView::hideAll() const
4379 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4380 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4382 for(; it != end; ++it)
4383 it->second->hideView();
4387 void GuiView::updateDialogs()
4389 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4390 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4392 for(; it != end; ++it) {
4393 Dialog * dialog = it->second.get();
4395 if (dialog->needBufferOpen() && !documentBufferView())
4396 hideDialog(fromqstr(dialog->name()), 0);
4397 else if (dialog->isVisibleView())
4398 dialog->checkStatus();
4405 Dialog * createDialog(GuiView & lv, string const & name);
4407 // will be replaced by a proper factory...
4408 Dialog * createGuiAbout(GuiView & lv);
4409 Dialog * createGuiBibtex(GuiView & lv);
4410 Dialog * createGuiChanges(GuiView & lv);
4411 Dialog * createGuiCharacter(GuiView & lv);
4412 Dialog * createGuiCitation(GuiView & lv);
4413 Dialog * createGuiCompare(GuiView & lv);
4414 Dialog * createGuiCompareHistory(GuiView & lv);
4415 Dialog * createGuiDelimiter(GuiView & lv);
4416 Dialog * createGuiDocument(GuiView & lv);
4417 Dialog * createGuiErrorList(GuiView & lv);
4418 Dialog * createGuiExternal(GuiView & lv);
4419 Dialog * createGuiGraphics(GuiView & lv);
4420 Dialog * createGuiInclude(GuiView & lv);
4421 Dialog * createGuiIndex(GuiView & lv);
4422 Dialog * createGuiListings(GuiView & lv);
4423 Dialog * createGuiLog(GuiView & lv);
4424 Dialog * createGuiMathMatrix(GuiView & lv);
4425 Dialog * createGuiNote(GuiView & lv);
4426 Dialog * createGuiParagraph(GuiView & lv);
4427 Dialog * createGuiPhantom(GuiView & lv);
4428 Dialog * createGuiPreferences(GuiView & lv);
4429 Dialog * createGuiPrint(GuiView & lv);
4430 Dialog * createGuiPrintindex(GuiView & lv);
4431 Dialog * createGuiRef(GuiView & lv);
4432 Dialog * createGuiSearch(GuiView & lv);
4433 Dialog * createGuiSearchAdv(GuiView & lv);
4434 Dialog * createGuiSendTo(GuiView & lv);
4435 Dialog * createGuiShowFile(GuiView & lv);
4436 Dialog * createGuiSpellchecker(GuiView & lv);
4437 Dialog * createGuiSymbols(GuiView & lv);
4438 Dialog * createGuiTabularCreate(GuiView & lv);
4439 Dialog * createGuiTexInfo(GuiView & lv);
4440 Dialog * createGuiToc(GuiView & lv);
4441 Dialog * createGuiThesaurus(GuiView & lv);
4442 Dialog * createGuiViewSource(GuiView & lv);
4443 Dialog * createGuiWrap(GuiView & lv);
4444 Dialog * createGuiProgressView(GuiView & lv);
4448 Dialog * GuiView::build(string const & name)
4450 LASSERT(isValidName(name), return 0);
4452 Dialog * dialog = createDialog(*this, name);
4456 if (name == "aboutlyx")
4457 return createGuiAbout(*this);
4458 if (name == "bibtex")
4459 return createGuiBibtex(*this);
4460 if (name == "changes")
4461 return createGuiChanges(*this);
4462 if (name == "character")
4463 return createGuiCharacter(*this);
4464 if (name == "citation")
4465 return createGuiCitation(*this);
4466 if (name == "compare")
4467 return createGuiCompare(*this);
4468 if (name == "comparehistory")
4469 return createGuiCompareHistory(*this);
4470 if (name == "document")
4471 return createGuiDocument(*this);
4472 if (name == "errorlist")
4473 return createGuiErrorList(*this);
4474 if (name == "external")
4475 return createGuiExternal(*this);
4477 return createGuiShowFile(*this);
4478 if (name == "findreplace")
4479 return createGuiSearch(*this);
4480 if (name == "findreplaceadv")
4481 return createGuiSearchAdv(*this);
4482 if (name == "graphics")
4483 return createGuiGraphics(*this);
4484 if (name == "include")
4485 return createGuiInclude(*this);
4486 if (name == "index")
4487 return createGuiIndex(*this);
4488 if (name == "index_print")
4489 return createGuiPrintindex(*this);
4490 if (name == "listings")
4491 return createGuiListings(*this);
4493 return createGuiLog(*this);
4494 if (name == "mathdelimiter")
4495 return createGuiDelimiter(*this);
4496 if (name == "mathmatrix")
4497 return createGuiMathMatrix(*this);
4499 return createGuiNote(*this);
4500 if (name == "paragraph")
4501 return createGuiParagraph(*this);
4502 if (name == "phantom")
4503 return createGuiPhantom(*this);
4504 if (name == "prefs")
4505 return createGuiPreferences(*this);
4507 return createGuiRef(*this);
4508 if (name == "sendto")
4509 return createGuiSendTo(*this);
4510 if (name == "spellchecker")
4511 return createGuiSpellchecker(*this);
4512 if (name == "symbols")
4513 return createGuiSymbols(*this);
4514 if (name == "tabularcreate")
4515 return createGuiTabularCreate(*this);
4516 if (name == "texinfo")
4517 return createGuiTexInfo(*this);
4518 if (name == "thesaurus")
4519 return createGuiThesaurus(*this);
4521 return createGuiToc(*this);
4522 if (name == "view-source")
4523 return createGuiViewSource(*this);
4525 return createGuiWrap(*this);
4526 if (name == "progress")
4527 return createGuiProgressView(*this);
4533 } // namespace frontend
4536 #include "moc_GuiView.cpp"