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(onBufferViewChanged()));
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 // connection of a signal to a signal
1206 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1207 this, SIGNAL(bufferViewChanged()));
1208 Q_EMIT updateWindowTitle(wa);
1209 Q_EMIT bufferViewChanged();
1213 void GuiView::onBufferViewChanged()
1216 // Buffer-dependent dialogs must be updated. This is done here because
1217 // some dialogs require buffer()->text.
1222 void GuiView::on_lastWorkAreaRemoved()
1225 // We already are in a close event. Nothing more to do.
1228 if (d.splitter_->count() > 1)
1229 // We have a splitter so don't close anything.
1232 // Reset and updates the dialogs.
1233 Q_EMIT bufferViewChanged();
1238 if (lyxrc.open_buffers_in_tabs)
1239 // Nothing more to do, the window should stay open.
1242 if (guiApp->viewIds().size() > 1) {
1248 // On Mac we also close the last window because the application stay
1249 // resident in memory. On other platforms we don't close the last
1250 // window because this would quit the application.
1256 void GuiView::updateStatusBar()
1258 // let the user see the explicit message
1259 if (d.statusbar_timer_.isActive())
1266 void GuiView::showMessage()
1270 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1271 if (msg.isEmpty()) {
1272 BufferView const * bv = currentBufferView();
1274 msg = toqstr(bv->cursor().currentState());
1276 msg = qt_("Welcome to LyX!");
1278 statusBar()->showMessage(msg);
1282 bool GuiView::event(QEvent * e)
1286 // Useful debug code:
1287 //case QEvent::ActivationChange:
1288 //case QEvent::WindowDeactivate:
1289 //case QEvent::Paint:
1290 //case QEvent::Enter:
1291 //case QEvent::Leave:
1292 //case QEvent::HoverEnter:
1293 //case QEvent::HoverLeave:
1294 //case QEvent::HoverMove:
1295 //case QEvent::StatusTip:
1296 //case QEvent::DragEnter:
1297 //case QEvent::DragLeave:
1298 //case QEvent::Drop:
1301 case QEvent::WindowActivate: {
1302 GuiView * old_view = guiApp->currentView();
1303 if (this == old_view) {
1305 return QMainWindow::event(e);
1307 if (old_view && old_view->currentBufferView()) {
1308 // save current selection to the selection buffer to allow
1309 // middle-button paste in this window.
1310 cap::saveSelection(old_view->currentBufferView()->cursor());
1312 guiApp->setCurrentView(this);
1313 if (d.current_work_area_)
1314 on_currentWorkAreaChanged(d.current_work_area_);
1318 return QMainWindow::event(e);
1321 case QEvent::ShortcutOverride: {
1323 if (isFullScreen() && menuBar()->isHidden()) {
1324 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1325 // FIXME: we should also try to detect special LyX shortcut such as
1326 // Alt-P and Alt-M. Right now there is a hack in
1327 // GuiWorkArea::processKeySym() that hides again the menubar for
1329 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1331 return QMainWindow::event(e);
1334 return QMainWindow::event(e);
1338 return QMainWindow::event(e);
1342 void GuiView::resetWindowTitle()
1344 setWindowTitle(qt_("LyX"));
1347 bool GuiView::focusNextPrevChild(bool /*next*/)
1354 bool GuiView::busy() const
1360 void GuiView::setBusy(bool busy)
1362 bool const busy_before = busy_ > 0;
1363 busy ? ++busy_ : --busy_;
1364 if ((busy_ > 0) == busy_before)
1365 // busy state didn't change
1369 QApplication::setOverrideCursor(Qt::WaitCursor);
1372 QApplication::restoreOverrideCursor();
1377 void GuiView::resetCommandExecute()
1379 command_execute_ = false;
1384 double GuiView::pixelRatio() const
1386 #if QT_VERSION >= 0x050000
1387 return devicePixelRatio();
1394 GuiWorkArea * GuiView::workArea(int index)
1396 if (TabWorkArea * twa = d.currentTabWorkArea())
1397 if (index < twa->count())
1398 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1403 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1405 if (currentWorkArea()
1406 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1407 return currentWorkArea();
1408 if (TabWorkArea * twa = d.currentTabWorkArea())
1409 return twa->workArea(buffer);
1414 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1416 // Automatically create a TabWorkArea if there are none yet.
1417 TabWorkArea * tab_widget = d.splitter_->count()
1418 ? d.currentTabWorkArea() : addTabWorkArea();
1419 return tab_widget->addWorkArea(buffer, *this);
1423 TabWorkArea * GuiView::addTabWorkArea()
1425 TabWorkArea * twa = new TabWorkArea;
1426 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1427 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1428 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1429 this, SLOT(on_lastWorkAreaRemoved()));
1431 d.splitter_->addWidget(twa);
1432 d.stack_widget_->setCurrentWidget(d.splitter_);
1437 GuiWorkArea const * GuiView::currentWorkArea() const
1439 return d.current_work_area_;
1443 GuiWorkArea * GuiView::currentWorkArea()
1445 return d.current_work_area_;
1449 GuiWorkArea const * GuiView::currentMainWorkArea() const
1451 if (!d.currentTabWorkArea())
1453 return d.currentTabWorkArea()->currentWorkArea();
1457 GuiWorkArea * GuiView::currentMainWorkArea()
1459 if (!d.currentTabWorkArea())
1461 return d.currentTabWorkArea()->currentWorkArea();
1465 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1467 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1469 d.current_work_area_ = 0;
1471 Q_EMIT bufferViewChanged();
1475 // FIXME: I've no clue why this is here and why it accesses
1476 // theGuiApp()->currentView, which might be 0 (bug 6464).
1477 // See also 27525 (vfr).
1478 if (theGuiApp()->currentView() == this
1479 && theGuiApp()->currentView()->currentWorkArea() == wa)
1482 if (currentBufferView())
1483 cap::saveSelection(currentBufferView()->cursor());
1485 theGuiApp()->setCurrentView(this);
1486 d.current_work_area_ = wa;
1488 // We need to reset this now, because it will need to be
1489 // right if the tabWorkArea gets reset in the for loop. We
1490 // will change it back if we aren't in that case.
1491 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1492 d.current_main_work_area_ = wa;
1494 for (int i = 0; i != d.splitter_->count(); ++i) {
1495 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1496 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1497 << ", Current main wa: " << currentMainWorkArea());
1502 d.current_main_work_area_ = old_cmwa;
1504 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1505 on_currentWorkAreaChanged(wa);
1506 BufferView & bv = wa->bufferView();
1507 bv.cursor().fixIfBroken();
1509 wa->setUpdatesEnabled(true);
1510 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1514 void GuiView::removeWorkArea(GuiWorkArea * wa)
1516 LASSERT(wa, return);
1517 if (wa == d.current_work_area_) {
1519 disconnectBufferView();
1520 d.current_work_area_ = 0;
1521 d.current_main_work_area_ = 0;
1524 bool found_twa = false;
1525 for (int i = 0; i != d.splitter_->count(); ++i) {
1526 TabWorkArea * twa = d.tabWorkArea(i);
1527 if (twa->removeWorkArea(wa)) {
1528 // Found in this tab group, and deleted the GuiWorkArea.
1530 if (twa->count() != 0) {
1531 if (d.current_work_area_ == 0)
1532 // This means that we are closing the current GuiWorkArea, so
1533 // switch to the next GuiWorkArea in the found TabWorkArea.
1534 setCurrentWorkArea(twa->currentWorkArea());
1536 // No more WorkAreas in this tab group, so delete it.
1543 // It is not a tabbed work area (i.e., the search work area), so it
1544 // should be deleted by other means.
1545 LASSERT(found_twa, return);
1547 if (d.current_work_area_ == 0) {
1548 if (d.splitter_->count() != 0) {
1549 TabWorkArea * twa = d.currentTabWorkArea();
1550 setCurrentWorkArea(twa->currentWorkArea());
1552 // No more work areas, switch to the background widget.
1553 setCurrentWorkArea(0);
1559 LayoutBox * GuiView::getLayoutDialog() const
1565 void GuiView::updateLayoutList()
1568 d.layout_->updateContents(false);
1572 void GuiView::updateToolbars()
1574 ToolbarMap::iterator end = d.toolbars_.end();
1575 if (d.current_work_area_) {
1577 if (d.current_work_area_->bufferView().cursor().inMathed()
1578 && !d.current_work_area_->bufferView().cursor().inRegexped())
1579 context |= Toolbars::MATH;
1580 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1581 context |= Toolbars::TABLE;
1582 if (currentBufferView()->buffer().areChangesPresent()
1583 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1584 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1585 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1586 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1587 context |= Toolbars::REVIEW;
1588 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1589 context |= Toolbars::MATHMACROTEMPLATE;
1590 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1591 context |= Toolbars::IPA;
1592 if (command_execute_)
1593 context |= Toolbars::MINIBUFFER;
1594 if (minibuffer_focus_) {
1595 context |= Toolbars::MINIBUFFER_FOCUS;
1596 minibuffer_focus_ = false;
1599 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1600 it->second->update(context);
1602 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1603 it->second->update();
1607 void GuiView::setBuffer(Buffer * newBuffer)
1609 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1610 LASSERT(newBuffer, return);
1612 GuiWorkArea * wa = workArea(*newBuffer);
1615 newBuffer->masterBuffer()->updateBuffer();
1617 wa = addWorkArea(*newBuffer);
1618 // scroll to the position when the BufferView was last closed
1619 if (lyxrc.use_lastfilepos) {
1620 LastFilePosSection::FilePos filepos =
1621 theSession().lastFilePos().load(newBuffer->fileName());
1622 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1625 //Disconnect the old buffer...there's no new one.
1628 connectBuffer(*newBuffer);
1629 connectBufferView(wa->bufferView());
1630 setCurrentWorkArea(wa);
1634 void GuiView::connectBuffer(Buffer & buf)
1636 buf.setGuiDelegate(this);
1640 void GuiView::disconnectBuffer()
1642 if (d.current_work_area_)
1643 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1647 void GuiView::connectBufferView(BufferView & bv)
1649 bv.setGuiDelegate(this);
1653 void GuiView::disconnectBufferView()
1655 if (d.current_work_area_)
1656 d.current_work_area_->bufferView().setGuiDelegate(0);
1660 void GuiView::errors(string const & error_type, bool from_master)
1662 BufferView const * const bv = currentBufferView();
1666 #if EXPORT_in_THREAD
1667 // We are called with from_master == false by default, so we
1668 // have to figure out whether that is the case or not.
1669 ErrorList & el = bv->buffer().errorList(error_type);
1671 el = bv->buffer().masterBuffer()->errorList(error_type);
1675 ErrorList const & el = from_master ?
1676 bv->buffer().masterBuffer()->errorList(error_type) :
1677 bv->buffer().errorList(error_type);
1683 string data = error_type;
1685 data = "from_master|" + error_type;
1686 showDialog("errorlist", data);
1690 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1692 d.toc_models_.updateItem(toqstr(type), dit);
1696 void GuiView::structureChanged()
1698 // FIXME: This is slightly expensive, though less than the tocBackend update
1699 // (#9880). This also resets the view in the Toc Widget (#6675).
1700 d.toc_models_.reset(documentBufferView());
1701 // Navigator needs more than a simple update in this case. It needs to be
1703 updateDialog("toc", "");
1707 void GuiView::updateDialog(string const & name, string const & data)
1709 if (!isDialogVisible(name))
1712 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1713 if (it == d.dialogs_.end())
1716 Dialog * const dialog = it->second.get();
1717 if (dialog->isVisibleView())
1718 dialog->initialiseParams(data);
1722 BufferView * GuiView::documentBufferView()
1724 return currentMainWorkArea()
1725 ? ¤tMainWorkArea()->bufferView()
1730 BufferView const * GuiView::documentBufferView() const
1732 return currentMainWorkArea()
1733 ? ¤tMainWorkArea()->bufferView()
1738 BufferView * GuiView::currentBufferView()
1740 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1744 BufferView const * GuiView::currentBufferView() const
1746 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1750 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1751 Buffer const * orig, Buffer * clone)
1753 bool const success = clone->autoSave();
1755 busyBuffers.remove(orig);
1757 ? _("Automatic save done.")
1758 : _("Automatic save failed!");
1762 void GuiView::autoSave()
1764 LYXERR(Debug::INFO, "Running autoSave()");
1766 Buffer * buffer = documentBufferView()
1767 ? &documentBufferView()->buffer() : 0;
1769 resetAutosaveTimers();
1773 GuiViewPrivate::busyBuffers.insert(buffer);
1774 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1775 buffer, buffer->cloneBufferOnly());
1776 d.autosave_watcher_.setFuture(f);
1777 resetAutosaveTimers();
1781 void GuiView::resetAutosaveTimers()
1784 d.autosave_timeout_.restart();
1788 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1791 Buffer * buf = currentBufferView()
1792 ? ¤tBufferView()->buffer() : 0;
1793 Buffer * doc_buffer = documentBufferView()
1794 ? &(documentBufferView()->buffer()) : 0;
1797 /* In LyX/Mac, when a dialog is open, the menus of the
1798 application can still be accessed without giving focus to
1799 the main window. In this case, we want to disable the menu
1800 entries that are buffer-related.
1801 This code must not be used on Linux and Windows, since it
1802 would disable buffer-related entries when hovering over the
1803 menu (see bug #9574).
1805 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1811 // Check whether we need a buffer
1812 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1813 // no, exit directly
1814 flag.message(from_utf8(N_("Command not allowed with"
1815 "out any document open")));
1816 flag.setEnabled(false);
1820 if (cmd.origin() == FuncRequest::TOC) {
1821 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1822 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1823 flag.setEnabled(false);
1827 switch(cmd.action()) {
1828 case LFUN_BUFFER_IMPORT:
1831 case LFUN_MASTER_BUFFER_UPDATE:
1832 case LFUN_MASTER_BUFFER_VIEW:
1834 && (doc_buffer->parent() != 0
1835 || doc_buffer->hasChildren())
1836 && !d.processing_thread_watcher_.isRunning();
1839 case LFUN_BUFFER_UPDATE:
1840 case LFUN_BUFFER_VIEW: {
1841 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1845 string format = to_utf8(cmd.argument());
1846 if (cmd.argument().empty())
1847 format = doc_buffer->params().getDefaultOutputFormat();
1848 enable = doc_buffer->params().isExportable(format, true);
1852 case LFUN_BUFFER_RELOAD:
1853 enable = doc_buffer && !doc_buffer->isUnnamed()
1854 && doc_buffer->fileName().exists()
1855 && (!doc_buffer->isClean()
1856 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1859 case LFUN_BUFFER_CHILD_OPEN:
1860 enable = doc_buffer != 0;
1863 case LFUN_BUFFER_WRITE:
1864 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1867 //FIXME: This LFUN should be moved to GuiApplication.
1868 case LFUN_BUFFER_WRITE_ALL: {
1869 // We enable the command only if there are some modified buffers
1870 Buffer * first = theBufferList().first();
1875 // We cannot use a for loop as the buffer list is a cycle.
1877 if (!b->isClean()) {
1881 b = theBufferList().next(b);
1882 } while (b != first);
1886 case LFUN_BUFFER_WRITE_AS:
1887 case LFUN_BUFFER_EXPORT_AS:
1888 enable = doc_buffer != 0;
1891 case LFUN_BUFFER_CLOSE:
1892 case LFUN_VIEW_CLOSE:
1893 enable = doc_buffer != 0;
1896 case LFUN_BUFFER_CLOSE_ALL:
1897 enable = theBufferList().last() != theBufferList().first();
1900 case LFUN_VIEW_SPLIT:
1901 if (cmd.getArg(0) == "vertical")
1902 enable = doc_buffer && (d.splitter_->count() == 1 ||
1903 d.splitter_->orientation() == Qt::Vertical);
1905 enable = doc_buffer && (d.splitter_->count() == 1 ||
1906 d.splitter_->orientation() == Qt::Horizontal);
1909 case LFUN_TAB_GROUP_CLOSE:
1910 enable = d.tabWorkAreaCount() > 1;
1913 case LFUN_TOOLBAR_TOGGLE: {
1914 string const name = cmd.getArg(0);
1915 if (GuiToolbar * t = toolbar(name))
1916 flag.setOnOff(t->isVisible());
1919 docstring const msg =
1920 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1926 case LFUN_DROP_LAYOUTS_CHOICE:
1930 case LFUN_UI_TOGGLE:
1931 flag.setOnOff(isFullScreen());
1934 case LFUN_DIALOG_DISCONNECT_INSET:
1937 case LFUN_DIALOG_HIDE:
1938 // FIXME: should we check if the dialog is shown?
1941 case LFUN_DIALOG_TOGGLE:
1942 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1943 // fall through to set "enable"
1944 case LFUN_DIALOG_SHOW: {
1945 string const name = cmd.getArg(0);
1947 enable = name == "aboutlyx"
1948 || name == "file" //FIXME: should be removed.
1950 || name == "texinfo"
1951 || name == "progress"
1952 || name == "compare";
1953 else if (name == "character" || name == "symbols"
1954 || name == "mathdelimiter" || name == "mathmatrix") {
1955 if (!buf || buf->isReadonly())
1958 Cursor const & cur = currentBufferView()->cursor();
1959 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1962 else if (name == "latexlog")
1963 enable = FileName(doc_buffer->logName()).isReadableFile();
1964 else if (name == "spellchecker")
1965 enable = theSpellChecker()
1966 && !doc_buffer->isReadonly()
1967 && !doc_buffer->text().empty();
1968 else if (name == "vclog")
1969 enable = doc_buffer->lyxvc().inUse();
1973 case LFUN_DIALOG_UPDATE: {
1974 string const name = cmd.getArg(0);
1976 enable = name == "prefs";
1980 case LFUN_COMMAND_EXECUTE:
1982 case LFUN_MENU_OPEN:
1983 // Nothing to check.
1986 case LFUN_COMPLETION_INLINE:
1987 if (!d.current_work_area_
1988 || !d.current_work_area_->completer().inlinePossible(
1989 currentBufferView()->cursor()))
1993 case LFUN_COMPLETION_POPUP:
1994 if (!d.current_work_area_
1995 || !d.current_work_area_->completer().popupPossible(
1996 currentBufferView()->cursor()))
2001 if (!d.current_work_area_
2002 || !d.current_work_area_->completer().inlinePossible(
2003 currentBufferView()->cursor()))
2007 case LFUN_COMPLETION_ACCEPT:
2008 if (!d.current_work_area_
2009 || (!d.current_work_area_->completer().popupVisible()
2010 && !d.current_work_area_->completer().inlineVisible()
2011 && !d.current_work_area_->completer().completionAvailable()))
2015 case LFUN_COMPLETION_CANCEL:
2016 if (!d.current_work_area_
2017 || (!d.current_work_area_->completer().popupVisible()
2018 && !d.current_work_area_->completer().inlineVisible()))
2022 case LFUN_BUFFER_ZOOM_OUT:
2023 case LFUN_BUFFER_ZOOM_IN: {
2024 // only diff between these two is that the default for ZOOM_OUT
2026 bool const neg_zoom =
2027 convert<int>(cmd.argument()) < 0 ||
2028 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2029 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
2030 docstring const msg =
2031 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2035 enable = doc_buffer;
2038 case LFUN_BUFFER_MOVE_NEXT:
2039 case LFUN_BUFFER_MOVE_PREVIOUS:
2040 // we do not cycle when moving
2041 case LFUN_BUFFER_NEXT:
2042 case LFUN_BUFFER_PREVIOUS:
2043 // because we cycle, it doesn't matter whether on first or last
2044 enable = (d.currentTabWorkArea()->count() > 1);
2046 case LFUN_BUFFER_SWITCH:
2047 // toggle on the current buffer, but do not toggle off
2048 // the other ones (is that a good idea?)
2050 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2051 flag.setOnOff(true);
2054 case LFUN_VC_REGISTER:
2055 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2057 case LFUN_VC_RENAME:
2058 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2061 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2063 case LFUN_VC_CHECK_IN:
2064 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2066 case LFUN_VC_CHECK_OUT:
2067 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2069 case LFUN_VC_LOCKING_TOGGLE:
2070 enable = doc_buffer && !doc_buffer->isReadonly()
2071 && doc_buffer->lyxvc().lockingToggleEnabled();
2072 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2074 case LFUN_VC_REVERT:
2075 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2077 case LFUN_VC_UNDO_LAST:
2078 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2080 case LFUN_VC_REPO_UPDATE:
2081 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2083 case LFUN_VC_COMMAND: {
2084 if (cmd.argument().empty())
2086 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2090 case LFUN_VC_COMPARE:
2091 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2094 case LFUN_SERVER_GOTO_FILE_ROW:
2095 case LFUN_LYX_ACTIVATE:
2097 case LFUN_FORWARD_SEARCH:
2098 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2101 case LFUN_FILE_INSERT_PLAINTEXT:
2102 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2103 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2106 case LFUN_SPELLING_CONTINUOUSLY:
2107 flag.setOnOff(lyxrc.spellcheck_continuously);
2115 flag.setEnabled(false);
2121 static FileName selectTemplateFile()
2123 FileDialog dlg(qt_("Select template file"));
2124 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2125 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2127 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2128 QStringList(qt_("LyX Documents (*.lyx)")));
2130 if (result.first == FileDialog::Later)
2132 if (result.second.isEmpty())
2134 return FileName(fromqstr(result.second));
2138 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2142 Buffer * newBuffer = 0;
2144 newBuffer = checkAndLoadLyXFile(filename);
2145 } catch (ExceptionMessage const & e) {
2152 message(_("Document not loaded."));
2156 setBuffer(newBuffer);
2157 newBuffer->errors("Parse");
2160 theSession().lastFiles().add(filename);
2166 void GuiView::openDocument(string const & fname)
2168 string initpath = lyxrc.document_path;
2170 if (documentBufferView()) {
2171 string const trypath = documentBufferView()->buffer().filePath();
2172 // If directory is writeable, use this as default.
2173 if (FileName(trypath).isDirWritable())
2179 if (fname.empty()) {
2180 FileDialog dlg(qt_("Select document to open"));
2181 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2182 dlg.setButton2(qt_("Examples|#E#e"),
2183 toqstr(addPath(package().system_support().absFileName(), "examples")));
2185 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2186 FileDialog::Result result =
2187 dlg.open(toqstr(initpath), filter);
2189 if (result.first == FileDialog::Later)
2192 filename = fromqstr(result.second);
2194 // check selected filename
2195 if (filename.empty()) {
2196 message(_("Canceled."));
2202 // get absolute path of file and add ".lyx" to the filename if
2204 FileName const fullname =
2205 fileSearch(string(), filename, "lyx", support::may_not_exist);
2206 if (!fullname.empty())
2207 filename = fullname.absFileName();
2209 if (!fullname.onlyPath().isDirectory()) {
2210 Alert::warning(_("Invalid filename"),
2211 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2212 from_utf8(fullname.absFileName())));
2216 // if the file doesn't exist and isn't already open (bug 6645),
2217 // let the user create one
2218 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2219 !LyXVC::file_not_found_hook(fullname)) {
2220 // the user specifically chose this name. Believe him.
2221 Buffer * const b = newFile(filename, string(), true);
2227 docstring const disp_fn = makeDisplayPath(filename);
2228 message(bformat(_("Opening document %1$s..."), disp_fn));
2231 Buffer * buf = loadDocument(fullname);
2233 str2 = bformat(_("Document %1$s opened."), disp_fn);
2234 if (buf->lyxvc().inUse())
2235 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2236 " " + _("Version control detected.");
2238 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2243 // FIXME: clean that
2244 static bool import(GuiView * lv, FileName const & filename,
2245 string const & format, ErrorList & errorList)
2247 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2249 string loader_format;
2250 vector<string> loaders = theConverters().loaders();
2251 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2252 vector<string>::const_iterator it = loaders.begin();
2253 vector<string>::const_iterator en = loaders.end();
2254 for (; it != en; ++it) {
2255 if (!theConverters().isReachable(format, *it))
2258 string const tofile =
2259 support::changeExtension(filename.absFileName(),
2260 formats.extension(*it));
2261 if (!theConverters().convert(0, filename, FileName(tofile),
2262 filename, format, *it, errorList))
2264 loader_format = *it;
2267 if (loader_format.empty()) {
2268 frontend::Alert::error(_("Couldn't import file"),
2269 bformat(_("No information for importing the format %1$s."),
2270 formats.prettyName(format)));
2274 loader_format = format;
2276 if (loader_format == "lyx") {
2277 Buffer * buf = lv->loadDocument(lyxfile);
2281 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2285 bool as_paragraphs = loader_format == "textparagraph";
2286 string filename2 = (loader_format == format) ? filename.absFileName()
2287 : support::changeExtension(filename.absFileName(),
2288 formats.extension(loader_format));
2289 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2291 guiApp->setCurrentView(lv);
2292 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2299 void GuiView::importDocument(string const & argument)
2302 string filename = split(argument, format, ' ');
2304 LYXERR(Debug::INFO, format << " file: " << filename);
2306 // need user interaction
2307 if (filename.empty()) {
2308 string initpath = lyxrc.document_path;
2309 if (documentBufferView()) {
2310 string const trypath = documentBufferView()->buffer().filePath();
2311 // If directory is writeable, use this as default.
2312 if (FileName(trypath).isDirWritable())
2316 docstring const text = bformat(_("Select %1$s file to import"),
2317 formats.prettyName(format));
2319 FileDialog dlg(toqstr(text));
2320 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2321 dlg.setButton2(qt_("Examples|#E#e"),
2322 toqstr(addPath(package().system_support().absFileName(), "examples")));
2324 docstring filter = formats.prettyName(format);
2327 filter += from_utf8(formats.extensions(format));
2330 FileDialog::Result result =
2331 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2333 if (result.first == FileDialog::Later)
2336 filename = fromqstr(result.second);
2338 // check selected filename
2339 if (filename.empty())
2340 message(_("Canceled."));
2343 if (filename.empty())
2346 // get absolute path of file
2347 FileName const fullname(support::makeAbsPath(filename));
2349 // Can happen if the user entered a path into the dialog
2351 if (fullname.onlyFileName().empty()) {
2352 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2353 "Aborting import."),
2354 from_utf8(fullname.absFileName()));
2355 frontend::Alert::error(_("File name error"), msg);
2356 message(_("Canceled."));
2361 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2363 // Check if the document already is open
2364 Buffer * buf = theBufferList().getBuffer(lyxfile);
2367 if (!closeBuffer()) {
2368 message(_("Canceled."));
2373 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2375 // if the file exists already, and we didn't do
2376 // -i lyx thefile.lyx, warn
2377 if (lyxfile.exists() && fullname != lyxfile) {
2379 docstring text = bformat(_("The document %1$s already exists.\n\n"
2380 "Do you want to overwrite that document?"), displaypath);
2381 int const ret = Alert::prompt(_("Overwrite document?"),
2382 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2385 message(_("Canceled."));
2390 message(bformat(_("Importing %1$s..."), displaypath));
2391 ErrorList errorList;
2392 if (import(this, fullname, format, errorList))
2393 message(_("imported."));
2395 message(_("file not imported!"));
2397 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2401 void GuiView::newDocument(string const & filename, bool from_template)
2403 FileName initpath(lyxrc.document_path);
2404 if (documentBufferView()) {
2405 FileName const trypath(documentBufferView()->buffer().filePath());
2406 // If directory is writeable, use this as default.
2407 if (trypath.isDirWritable())
2411 string templatefile;
2412 if (from_template) {
2413 templatefile = selectTemplateFile().absFileName();
2414 if (templatefile.empty())
2419 if (filename.empty())
2420 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2422 b = newFile(filename, templatefile, true);
2427 // If no new document could be created, it is unsure
2428 // whether there is a valid BufferView.
2429 if (currentBufferView())
2430 // Ensure the cursor is correctly positioned on screen.
2431 currentBufferView()->showCursor();
2435 void GuiView::insertLyXFile(docstring const & fname)
2437 BufferView * bv = documentBufferView();
2442 FileName filename(to_utf8(fname));
2443 if (filename.empty()) {
2444 // Launch a file browser
2446 string initpath = lyxrc.document_path;
2447 string const trypath = bv->buffer().filePath();
2448 // If directory is writeable, use this as default.
2449 if (FileName(trypath).isDirWritable())
2453 FileDialog dlg(qt_("Select LyX document to insert"));
2454 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2455 dlg.setButton2(qt_("Examples|#E#e"),
2456 toqstr(addPath(package().system_support().absFileName(),
2459 FileDialog::Result result = dlg.open(toqstr(initpath),
2460 QStringList(qt_("LyX Documents (*.lyx)")));
2462 if (result.first == FileDialog::Later)
2466 filename.set(fromqstr(result.second));
2468 // check selected filename
2469 if (filename.empty()) {
2470 // emit message signal.
2471 message(_("Canceled."));
2476 bv->insertLyXFile(filename);
2477 bv->buffer().errors("Parse");
2481 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2483 FileName fname = b.fileName();
2484 FileName const oldname = fname;
2486 if (!newname.empty()) {
2488 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2490 // Switch to this Buffer.
2493 // No argument? Ask user through dialog.
2495 FileDialog dlg(qt_("Choose a filename to save document as"));
2496 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2497 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2499 if (!isLyXFileName(fname.absFileName()))
2500 fname.changeExtension(".lyx");
2502 FileDialog::Result result =
2503 dlg.save(toqstr(fname.onlyPath().absFileName()),
2504 QStringList(qt_("LyX Documents (*.lyx)")),
2505 toqstr(fname.onlyFileName()));
2507 if (result.first == FileDialog::Later)
2510 fname.set(fromqstr(result.second));
2515 if (!isLyXFileName(fname.absFileName()))
2516 fname.changeExtension(".lyx");
2519 // fname is now the new Buffer location.
2521 // if there is already a Buffer open with this name, we do not want
2522 // to have another one. (the second test makes sure we're not just
2523 // trying to overwrite ourselves, which is fine.)
2524 if (theBufferList().exists(fname) && fname != oldname
2525 && theBufferList().getBuffer(fname) != &b) {
2526 docstring const text =
2527 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2528 "Please close it before attempting to overwrite it.\n"
2529 "Do you want to choose a new filename?"),
2530 from_utf8(fname.absFileName()));
2531 int const ret = Alert::prompt(_("Chosen File Already Open"),
2532 text, 0, 1, _("&Rename"), _("&Cancel"));
2534 case 0: return renameBuffer(b, docstring(), kind);
2535 case 1: return false;
2540 bool const existsLocal = fname.exists();
2541 bool const existsInVC = LyXVC::fileInVC(fname);
2542 if (existsLocal || existsInVC) {
2543 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2544 if (kind != LV_WRITE_AS && existsInVC) {
2545 // renaming to a name that is already in VC
2547 docstring text = bformat(_("The document %1$s "
2548 "is already registered.\n\n"
2549 "Do you want to choose a new name?"),
2551 docstring const title = (kind == LV_VC_RENAME) ?
2552 _("Rename document?") : _("Copy document?");
2553 docstring const button = (kind == LV_VC_RENAME) ?
2554 _("&Rename") : _("&Copy");
2555 int const ret = Alert::prompt(title, text, 0, 1,
2556 button, _("&Cancel"));
2558 case 0: return renameBuffer(b, docstring(), kind);
2559 case 1: return false;
2564 docstring text = bformat(_("The document %1$s "
2565 "already exists.\n\n"
2566 "Do you want to overwrite that document?"),
2568 int const ret = Alert::prompt(_("Overwrite document?"),
2569 text, 0, 2, _("&Overwrite"),
2570 _("&Rename"), _("&Cancel"));
2573 case 1: return renameBuffer(b, docstring(), kind);
2574 case 2: return false;
2580 case LV_VC_RENAME: {
2581 string msg = b.lyxvc().rename(fname);
2584 message(from_utf8(msg));
2588 string msg = b.lyxvc().copy(fname);
2591 message(from_utf8(msg));
2597 // LyXVC created the file already in case of LV_VC_RENAME or
2598 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2599 // relative paths of included stuff right if we moved e.g. from
2600 // /a/b.lyx to /a/c/b.lyx.
2602 bool const saved = saveBuffer(b, fname);
2609 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2611 FileName fname = b.fileName();
2613 FileDialog dlg(qt_("Choose a filename to export the document as"));
2614 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2617 QString const anyformat = qt_("Guess from extension (*.*)");
2620 vector<Format const *> export_formats;
2621 for (Format const & f : formats)
2622 if (f.documentFormat())
2623 export_formats.push_back(&f);
2624 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2625 map<QString, string> fmap;
2628 for (Format const * f : export_formats) {
2629 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2630 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2632 from_ascii(f->extension())));
2633 types << loc_filter;
2634 fmap[loc_filter] = f->name();
2635 if (from_ascii(f->name()) == iformat) {
2636 filter = loc_filter;
2637 ext = f->extension();
2640 string ofname = fname.onlyFileName();
2642 ofname = support::changeExtension(ofname, ext);
2643 FileDialog::Result result =
2644 dlg.save(toqstr(fname.onlyPath().absFileName()),
2648 if (result.first != FileDialog::Chosen)
2652 fname.set(fromqstr(result.second));
2653 if (filter == anyformat)
2654 fmt_name = formats.getFormatFromExtension(fname.extension());
2656 fmt_name = fmap[filter];
2657 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2658 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2660 if (fmt_name.empty() || fname.empty())
2663 // fname is now the new Buffer location.
2664 if (FileName(fname).exists()) {
2665 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2666 docstring text = bformat(_("The document %1$s already "
2667 "exists.\n\nDo you want to "
2668 "overwrite that document?"),
2670 int const ret = Alert::prompt(_("Overwrite document?"),
2671 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2674 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2675 case 2: return false;
2679 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2682 return dr.dispatched();
2686 bool GuiView::saveBuffer(Buffer & b)
2688 return saveBuffer(b, FileName());
2692 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2694 if (workArea(b) && workArea(b)->inDialogMode())
2697 if (fn.empty() && b.isUnnamed())
2698 return renameBuffer(b, docstring());
2700 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2702 theSession().lastFiles().add(b.fileName());
2706 // Switch to this Buffer.
2709 // FIXME: we don't tell the user *WHY* the save failed !!
2710 docstring const file = makeDisplayPath(b.absFileName(), 30);
2711 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2712 "Do you want to rename the document and "
2713 "try again?"), file);
2714 int const ret = Alert::prompt(_("Rename and save?"),
2715 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2718 if (!renameBuffer(b, docstring()))
2727 return saveBuffer(b, fn);
2731 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2733 return closeWorkArea(wa, false);
2737 // We only want to close the buffer if it is not visible in other workareas
2738 // of the same view, nor in other views, and if this is not a child
2739 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2741 Buffer & buf = wa->bufferView().buffer();
2743 bool last_wa = d.countWorkAreasOf(buf) == 1
2744 && !inOtherView(buf) && !buf.parent();
2746 bool close_buffer = last_wa;
2749 if (lyxrc.close_buffer_with_last_view == "yes")
2751 else if (lyxrc.close_buffer_with_last_view == "no")
2752 close_buffer = false;
2755 if (buf.isUnnamed())
2756 file = from_utf8(buf.fileName().onlyFileName());
2758 file = buf.fileName().displayName(30);
2759 docstring const text = bformat(
2760 _("Last view on document %1$s is being closed.\n"
2761 "Would you like to close or hide the document?\n"
2763 "Hidden documents can be displayed back through\n"
2764 "the menu: View->Hidden->...\n"
2766 "To remove this question, set your preference in:\n"
2767 " Tools->Preferences->Look&Feel->UserInterface\n"
2769 int ret = Alert::prompt(_("Close or hide document?"),
2770 text, 0, 1, _("&Close"), _("&Hide"));
2771 close_buffer = (ret == 0);
2775 return closeWorkArea(wa, close_buffer);
2779 bool GuiView::closeBuffer()
2781 GuiWorkArea * wa = currentMainWorkArea();
2782 // coverity complained about this
2783 // it seems unnecessary, but perhaps is worth the check
2784 LASSERT(wa, return false);
2786 setCurrentWorkArea(wa);
2787 Buffer & buf = wa->bufferView().buffer();
2788 return closeWorkArea(wa, !buf.parent());
2792 void GuiView::writeSession() const {
2793 GuiWorkArea const * active_wa = currentMainWorkArea();
2794 for (int i = 0; i < d.splitter_->count(); ++i) {
2795 TabWorkArea * twa = d.tabWorkArea(i);
2796 for (int j = 0; j < twa->count(); ++j) {
2797 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2798 Buffer & buf = wa->bufferView().buffer();
2799 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2805 bool GuiView::closeBufferAll()
2807 // Close the workareas in all other views
2808 QList<int> const ids = guiApp->viewIds();
2809 for (int i = 0; i != ids.size(); ++i) {
2810 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2814 // Close our own workareas
2815 if (!closeWorkAreaAll())
2818 // Now close the hidden buffers. We prevent hidden buffers from being
2819 // dirty, so we can just close them.
2820 theBufferList().closeAll();
2825 bool GuiView::closeWorkAreaAll()
2827 setCurrentWorkArea(currentMainWorkArea());
2829 // We might be in a situation that there is still a tabWorkArea, but
2830 // there are no tabs anymore. This can happen when we get here after a
2831 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2832 // many TabWorkArea's have no documents anymore.
2835 // We have to call count() each time, because it can happen that
2836 // more than one splitter will disappear in one iteration (bug 5998).
2837 while (d.splitter_->count() > empty_twa) {
2838 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2840 if (twa->count() == 0)
2843 setCurrentWorkArea(twa->currentWorkArea());
2844 if (!closeTabWorkArea(twa))
2852 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2857 Buffer & buf = wa->bufferView().buffer();
2859 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2860 Alert::warning(_("Close document"),
2861 _("Document could not be closed because it is being processed by LyX."));
2866 return closeBuffer(buf);
2868 if (!inMultiTabs(wa))
2869 if (!saveBufferIfNeeded(buf, true))
2877 bool GuiView::closeBuffer(Buffer & buf)
2879 // If we are in a close_event all children will be closed in some time,
2880 // so no need to do it here. This will ensure that the children end up
2881 // in the session file in the correct order. If we close the master
2882 // buffer, we can close or release the child buffers here too.
2883 bool success = true;
2885 ListOfBuffers clist = buf.getChildren();
2886 ListOfBuffers::const_iterator it = clist.begin();
2887 ListOfBuffers::const_iterator const bend = clist.end();
2888 for (; it != bend; ++it) {
2889 Buffer * child_buf = *it;
2890 if (theBufferList().isOthersChild(&buf, child_buf)) {
2891 child_buf->setParent(0);
2895 // FIXME: should we look in other tabworkareas?
2896 // ANSWER: I don't think so. I've tested, and if the child is
2897 // open in some other window, it closes without a problem.
2898 GuiWorkArea * child_wa = workArea(*child_buf);
2900 success = closeWorkArea(child_wa, true);
2904 // In this case the child buffer is open but hidden.
2905 // It therefore should not (MUST NOT) be dirty!
2906 LATTEST(child_buf->isClean());
2907 theBufferList().release(child_buf);
2912 // goto bookmark to update bookmark pit.
2913 // FIXME: we should update only the bookmarks related to this buffer!
2914 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2915 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2916 guiApp->gotoBookmark(i+1, false, false);
2918 if (saveBufferIfNeeded(buf, false)) {
2919 buf.removeAutosaveFile();
2920 theBufferList().release(&buf);
2924 // open all children again to avoid a crash because of dangling
2925 // pointers (bug 6603)
2931 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2933 while (twa == d.currentTabWorkArea()) {
2934 twa->setCurrentIndex(twa->count() - 1);
2936 GuiWorkArea * wa = twa->currentWorkArea();
2937 Buffer & b = wa->bufferView().buffer();
2939 // We only want to close the buffer if the same buffer is not visible
2940 // in another view, and if this is not a child and if we are closing
2941 // a view (not a tabgroup).
2942 bool const close_buffer =
2943 !inOtherView(b) && !b.parent() && closing_;
2945 if (!closeWorkArea(wa, close_buffer))
2952 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2954 if (buf.isClean() || buf.paragraphs().empty())
2957 // Switch to this Buffer.
2962 if (buf.isUnnamed())
2963 file = from_utf8(buf.fileName().onlyFileName());
2965 file = buf.fileName().displayName(30);
2967 // Bring this window to top before asking questions.
2972 if (hiding && buf.isUnnamed()) {
2973 docstring const text = bformat(_("The document %1$s has not been "
2974 "saved yet.\n\nDo you want to save "
2975 "the document?"), file);
2976 ret = Alert::prompt(_("Save new document?"),
2977 text, 0, 1, _("&Save"), _("&Cancel"));
2981 docstring const text = bformat(_("The document %1$s has unsaved changes."
2982 "\n\nDo you want to save the document or discard the changes?"), file);
2983 ret = Alert::prompt(_("Save changed document?"),
2984 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2989 if (!saveBuffer(buf))
2993 // If we crash after this we could have no autosave file
2994 // but I guess this is really improbable (Jug).
2995 // Sometimes improbable things happen:
2996 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2997 // buf.removeAutosaveFile();
2999 // revert all changes
3010 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3012 Buffer & buf = wa->bufferView().buffer();
3014 for (int i = 0; i != d.splitter_->count(); ++i) {
3015 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3016 if (wa_ && wa_ != wa)
3019 return inOtherView(buf);
3023 bool GuiView::inOtherView(Buffer & buf)
3025 QList<int> const ids = guiApp->viewIds();
3027 for (int i = 0; i != ids.size(); ++i) {
3031 if (guiApp->view(ids[i]).workArea(buf))
3038 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3040 if (!documentBufferView())
3043 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3044 Buffer * const curbuf = &documentBufferView()->buffer();
3045 int nwa = twa->count();
3046 for (int i = 0; i < nwa; ++i) {
3047 if (&workArea(i)->bufferView().buffer() == curbuf) {
3049 if (np == NEXTBUFFER)
3050 next_index = (i == nwa - 1 ? 0 : i + 1);
3052 next_index = (i == 0 ? nwa - 1 : i - 1);
3054 twa->moveTab(i, next_index);
3056 setBuffer(&workArea(next_index)->bufferView().buffer());
3064 /// make sure the document is saved
3065 static bool ensureBufferClean(Buffer * buffer)
3067 LASSERT(buffer, return false);
3068 if (buffer->isClean() && !buffer->isUnnamed())
3071 docstring const file = buffer->fileName().displayName(30);
3074 if (!buffer->isUnnamed()) {
3075 text = bformat(_("The document %1$s has unsaved "
3076 "changes.\n\nDo you want to save "
3077 "the document?"), file);
3078 title = _("Save changed document?");
3081 text = bformat(_("The document %1$s has not been "
3082 "saved yet.\n\nDo you want to save "
3083 "the document?"), file);
3084 title = _("Save new document?");
3086 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3089 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3091 return buffer->isClean() && !buffer->isUnnamed();
3095 bool GuiView::reloadBuffer(Buffer & buf)
3097 Buffer::ReadStatus status = buf.reload();
3098 return status == Buffer::ReadSuccess;
3102 void GuiView::checkExternallyModifiedBuffers()
3104 BufferList::iterator bit = theBufferList().begin();
3105 BufferList::iterator const bend = theBufferList().end();
3106 for (; bit != bend; ++bit) {
3107 Buffer * buf = *bit;
3108 if (buf->fileName().exists()
3109 && buf->isExternallyModified(Buffer::checksum_method)) {
3110 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3111 " Reload now? Any local changes will be lost."),
3112 from_utf8(buf->absFileName()));
3113 int const ret = Alert::prompt(_("Reload externally changed document?"),
3114 text, 0, 1, _("&Reload"), _("&Cancel"));
3122 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3124 Buffer * buffer = documentBufferView()
3125 ? &(documentBufferView()->buffer()) : 0;
3127 switch (cmd.action()) {
3128 case LFUN_VC_REGISTER:
3129 if (!buffer || !ensureBufferClean(buffer))
3131 if (!buffer->lyxvc().inUse()) {
3132 if (buffer->lyxvc().registrer()) {
3133 reloadBuffer(*buffer);
3134 dr.clearMessageUpdate();
3139 case LFUN_VC_RENAME:
3140 case LFUN_VC_COPY: {
3141 if (!buffer || !ensureBufferClean(buffer))
3143 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3144 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3145 // Some changes are not yet committed.
3146 // We test here and not in getStatus(), since
3147 // this test is expensive.
3149 LyXVC::CommandResult ret =
3150 buffer->lyxvc().checkIn(log);
3152 if (ret == LyXVC::ErrorCommand ||
3153 ret == LyXVC::VCSuccess)
3154 reloadBuffer(*buffer);
3155 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3156 frontend::Alert::error(
3157 _("Revision control error."),
3158 _("Document could not be checked in."));
3162 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3163 LV_VC_RENAME : LV_VC_COPY;
3164 renameBuffer(*buffer, cmd.argument(), kind);
3169 case LFUN_VC_CHECK_IN:
3170 if (!buffer || !ensureBufferClean(buffer))
3172 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3174 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3176 // Only skip reloading if the checkin was cancelled or
3177 // an error occurred before the real checkin VCS command
3178 // was executed, since the VCS might have changed the
3179 // file even if it could not checkin successfully.
3180 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3181 reloadBuffer(*buffer);
3185 case LFUN_VC_CHECK_OUT:
3186 if (!buffer || !ensureBufferClean(buffer))
3188 if (buffer->lyxvc().inUse()) {
3189 dr.setMessage(buffer->lyxvc().checkOut());
3190 reloadBuffer(*buffer);
3194 case LFUN_VC_LOCKING_TOGGLE:
3195 LASSERT(buffer, return);
3196 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3198 if (buffer->lyxvc().inUse()) {
3199 string res = buffer->lyxvc().lockingToggle();
3201 frontend::Alert::error(_("Revision control error."),
3202 _("Error when setting the locking property."));
3205 reloadBuffer(*buffer);
3210 case LFUN_VC_REVERT:
3211 LASSERT(buffer, return);
3212 if (buffer->lyxvc().revert()) {
3213 reloadBuffer(*buffer);
3214 dr.clearMessageUpdate();
3218 case LFUN_VC_UNDO_LAST:
3219 LASSERT(buffer, return);
3220 buffer->lyxvc().undoLast();
3221 reloadBuffer(*buffer);
3222 dr.clearMessageUpdate();
3225 case LFUN_VC_REPO_UPDATE:
3226 LASSERT(buffer, return);
3227 if (ensureBufferClean(buffer)) {
3228 dr.setMessage(buffer->lyxvc().repoUpdate());
3229 checkExternallyModifiedBuffers();
3233 case LFUN_VC_COMMAND: {
3234 string flag = cmd.getArg(0);
3235 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3238 if (contains(flag, 'M')) {
3239 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3242 string path = cmd.getArg(1);
3243 if (contains(path, "$$p") && buffer)
3244 path = subst(path, "$$p", buffer->filePath());
3245 LYXERR(Debug::LYXVC, "Directory: " << path);
3247 if (!pp.isReadableDirectory()) {
3248 lyxerr << _("Directory is not accessible.") << endl;
3251 support::PathChanger p(pp);
3253 string command = cmd.getArg(2);
3254 if (command.empty())
3257 command = subst(command, "$$i", buffer->absFileName());
3258 command = subst(command, "$$p", buffer->filePath());
3260 command = subst(command, "$$m", to_utf8(message));
3261 LYXERR(Debug::LYXVC, "Command: " << command);
3263 one.startscript(Systemcall::Wait, command);
3267 if (contains(flag, 'I'))
3268 buffer->markDirty();
3269 if (contains(flag, 'R'))
3270 reloadBuffer(*buffer);
3275 case LFUN_VC_COMPARE: {
3276 if (cmd.argument().empty()) {
3277 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3281 string rev1 = cmd.getArg(0);
3285 // it seems safe to assume we have a buffer
3286 // coverity[FORWARD_NULL]
3287 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3290 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3291 f2 = buffer->absFileName();
3293 string rev2 = cmd.getArg(1);
3297 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3301 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3302 f1 << "\n" << f2 << "\n" );
3303 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3304 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3314 void GuiView::openChildDocument(string const & fname)
3316 LASSERT(documentBufferView(), return);
3317 Buffer & buffer = documentBufferView()->buffer();
3318 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3319 documentBufferView()->saveBookmark(false);
3321 if (theBufferList().exists(filename)) {
3322 child = theBufferList().getBuffer(filename);
3325 message(bformat(_("Opening child document %1$s..."),
3326 makeDisplayPath(filename.absFileName())));
3327 child = loadDocument(filename, false);
3329 // Set the parent name of the child document.
3330 // This makes insertion of citations and references in the child work,
3331 // when the target is in the parent or another child document.
3333 child->setParent(&buffer);
3337 bool GuiView::goToFileRow(string const & argument)
3341 size_t i = argument.find_last_of(' ');
3342 if (i != string::npos) {
3343 file_name = os::internal_path(trim(argument.substr(0, i)));
3344 istringstream is(argument.substr(i + 1));
3349 if (i == string::npos) {
3350 LYXERR0("Wrong argument: " << argument);
3354 string const abstmp = package().temp_dir().absFileName();
3355 string const realtmp = package().temp_dir().realPath();
3356 // We have to use os::path_prefix_is() here, instead of
3357 // simply prefixIs(), because the file name comes from
3358 // an external application and may need case adjustment.
3359 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3360 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3361 // Needed by inverse dvi search. If it is a file
3362 // in tmpdir, call the apropriated function.
3363 // If tmpdir is a symlink, we may have the real
3364 // path passed back, so we correct for that.
3365 if (!prefixIs(file_name, abstmp))
3366 file_name = subst(file_name, realtmp, abstmp);
3367 buf = theBufferList().getBufferFromTmp(file_name);
3369 // Must replace extension of the file to be .lyx
3370 // and get full path
3371 FileName const s = fileSearch(string(),
3372 support::changeExtension(file_name, ".lyx"), "lyx");
3373 // Either change buffer or load the file
3374 if (theBufferList().exists(s))
3375 buf = theBufferList().getBuffer(s);
3376 else if (s.exists()) {
3377 buf = loadDocument(s);
3382 _("File does not exist: %1$s"),
3383 makeDisplayPath(file_name)));
3389 _("No buffer for file: %1$s."),
3390 makeDisplayPath(file_name))
3395 bool success = documentBufferView()->setCursorFromRow(row);
3397 LYXERR(Debug::LATEX,
3398 "setCursorFromRow: invalid position for row " << row);
3399 frontend::Alert::error(_("Inverse Search Failed"),
3400 _("Invalid position requested by inverse search.\n"
3401 "You may need to update the viewed document."));
3408 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3410 Buffer::ExportStatus const status = func(format);
3412 // the cloning operation will have produced a clone of the entire set of
3413 // documents, starting from the master. so we must delete those.
3414 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3416 busyBuffers.remove(orig);
3421 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3423 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3424 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3428 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3430 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3431 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3435 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3437 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3438 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3442 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3443 string const & argument,
3444 Buffer const * used_buffer,
3445 docstring const & msg,
3446 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3447 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3448 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3453 string format = argument;
3455 format = used_buffer->params().getDefaultOutputFormat();
3456 processing_format = format;
3458 progress_->clearMessages();
3461 #if EXPORT_in_THREAD
3462 GuiViewPrivate::busyBuffers.insert(used_buffer);
3463 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3464 if (!cloned_buffer) {
3465 Alert::error(_("Export Error"),
3466 _("Error cloning the Buffer."));
3469 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3474 setPreviewFuture(f);
3475 last_export_format = used_buffer->params().bufferFormat();
3478 // We are asynchronous, so we don't know here anything about the success
3481 Buffer::ExportStatus status;
3483 status = (used_buffer->*syncFunc)(format, true);
3484 } else if (previewFunc) {
3485 status = (used_buffer->*previewFunc)(format);
3488 handleExportStatus(gv_, status, format);
3490 return (status == Buffer::ExportSuccess
3491 || status == Buffer::PreviewSuccess);
3495 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3497 BufferView * bv = currentBufferView();
3498 LASSERT(bv, return);
3500 // Let the current BufferView dispatch its own actions.
3501 bv->dispatch(cmd, dr);
3502 if (dr.dispatched())
3505 // Try with the document BufferView dispatch if any.
3506 BufferView * doc_bv = documentBufferView();
3507 if (doc_bv && doc_bv != bv) {
3508 doc_bv->dispatch(cmd, dr);
3509 if (dr.dispatched())
3513 // Then let the current Cursor dispatch its own actions.
3514 bv->cursor().dispatch(cmd);
3516 // update completion. We do it here and not in
3517 // processKeySym to avoid another redraw just for a
3518 // changed inline completion
3519 if (cmd.origin() == FuncRequest::KEYBOARD) {
3520 if (cmd.action() == LFUN_SELF_INSERT
3521 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3522 updateCompletion(bv->cursor(), true, true);
3523 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3524 updateCompletion(bv->cursor(), false, true);
3526 updateCompletion(bv->cursor(), false, false);
3529 dr = bv->cursor().result();
3533 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3535 BufferView * bv = currentBufferView();
3536 // By default we won't need any update.
3537 dr.screenUpdate(Update::None);
3538 // assume cmd will be dispatched
3539 dr.dispatched(true);
3541 Buffer * doc_buffer = documentBufferView()
3542 ? &(documentBufferView()->buffer()) : 0;
3544 if (cmd.origin() == FuncRequest::TOC) {
3545 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3546 // FIXME: do we need to pass a DispatchResult object here?
3547 toc->doDispatch(bv->cursor(), cmd);
3551 string const argument = to_utf8(cmd.argument());
3553 switch(cmd.action()) {
3554 case LFUN_BUFFER_CHILD_OPEN:
3555 openChildDocument(to_utf8(cmd.argument()));
3558 case LFUN_BUFFER_IMPORT:
3559 importDocument(to_utf8(cmd.argument()));
3562 case LFUN_BUFFER_EXPORT: {
3565 // GCC only sees strfwd.h when building merged
3566 if (::lyx::operator==(cmd.argument(), "custom")) {
3567 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3571 string const dest = cmd.getArg(1);
3572 FileName target_dir;
3573 if (!dest.empty() && FileName::isAbsolute(dest))
3574 target_dir = FileName(support::onlyPath(dest));
3576 target_dir = doc_buffer->fileName().onlyPath();
3578 if ((dest.empty() && doc_buffer->isUnnamed())
3579 || !target_dir.isDirWritable()) {
3580 exportBufferAs(*doc_buffer, cmd.argument());
3583 /* TODO/Review: Is it a problem to also export the children?
3584 See the update_unincluded flag */
3585 d.asyncBufferProcessing(argument,
3588 &GuiViewPrivate::exportAndDestroy,
3591 // TODO Inform user about success
3595 case LFUN_BUFFER_EXPORT_AS: {
3596 LASSERT(doc_buffer, break);
3597 docstring f = cmd.argument();
3599 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3600 exportBufferAs(*doc_buffer, f);
3604 case LFUN_BUFFER_UPDATE: {
3605 d.asyncBufferProcessing(argument,
3608 &GuiViewPrivate::compileAndDestroy,
3613 case LFUN_BUFFER_VIEW: {
3614 d.asyncBufferProcessing(argument,
3616 _("Previewing ..."),
3617 &GuiViewPrivate::previewAndDestroy,
3622 case LFUN_MASTER_BUFFER_UPDATE: {
3623 d.asyncBufferProcessing(argument,
3624 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3626 &GuiViewPrivate::compileAndDestroy,
3631 case LFUN_MASTER_BUFFER_VIEW: {
3632 d.asyncBufferProcessing(argument,
3633 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3635 &GuiViewPrivate::previewAndDestroy,
3636 0, &Buffer::preview);
3639 case LFUN_BUFFER_SWITCH: {
3640 string const file_name = to_utf8(cmd.argument());
3641 if (!FileName::isAbsolute(file_name)) {
3643 dr.setMessage(_("Absolute filename expected."));
3647 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3650 dr.setMessage(_("Document not loaded"));
3654 // Do we open or switch to the buffer in this view ?
3655 if (workArea(*buffer)
3656 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3661 // Look for the buffer in other views
3662 QList<int> const ids = guiApp->viewIds();
3664 for (; i != ids.size(); ++i) {
3665 GuiView & gv = guiApp->view(ids[i]);
3666 if (gv.workArea(*buffer)) {
3668 gv.activateWindow();
3670 gv.setBuffer(buffer);
3675 // If necessary, open a new window as a last resort
3676 if (i == ids.size()) {
3677 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3683 case LFUN_BUFFER_NEXT:
3684 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3687 case LFUN_BUFFER_MOVE_NEXT:
3688 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3691 case LFUN_BUFFER_PREVIOUS:
3692 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3695 case LFUN_BUFFER_MOVE_PREVIOUS:
3696 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3699 case LFUN_COMMAND_EXECUTE: {
3700 command_execute_ = true;
3701 minibuffer_focus_ = true;
3704 case LFUN_DROP_LAYOUTS_CHOICE:
3705 d.layout_->showPopup();
3708 case LFUN_MENU_OPEN:
3709 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3710 menu->exec(QCursor::pos());
3713 case LFUN_FILE_INSERT:
3714 insertLyXFile(cmd.argument());
3717 case LFUN_FILE_INSERT_PLAINTEXT:
3718 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3719 string const fname = to_utf8(cmd.argument());
3720 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3721 dr.setMessage(_("Absolute filename expected."));
3725 FileName filename(fname);
3726 if (fname.empty()) {
3727 FileDialog dlg(qt_("Select file to insert"));
3729 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3730 QStringList(qt_("All Files (*)")));
3732 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3733 dr.setMessage(_("Canceled."));
3737 filename.set(fromqstr(result.second));
3741 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3742 bv->dispatch(new_cmd, dr);
3747 case LFUN_BUFFER_RELOAD: {
3748 LASSERT(doc_buffer, break);
3751 if (!doc_buffer->isClean()) {
3752 docstring const file =
3753 makeDisplayPath(doc_buffer->absFileName(), 20);
3754 docstring text = bformat(_("Any changes will be lost. "
3755 "Are you sure you want to revert to the saved version "
3756 "of the document %1$s?"), file);
3757 ret = Alert::prompt(_("Revert to saved document?"),
3758 text, 1, 1, _("&Revert"), _("&Cancel"));
3762 doc_buffer->markClean();
3763 reloadBuffer(*doc_buffer);
3764 dr.forceBufferUpdate();
3769 case LFUN_BUFFER_WRITE:
3770 LASSERT(doc_buffer, break);
3771 saveBuffer(*doc_buffer);
3774 case LFUN_BUFFER_WRITE_AS:
3775 LASSERT(doc_buffer, break);
3776 renameBuffer(*doc_buffer, cmd.argument());
3779 case LFUN_BUFFER_WRITE_ALL: {
3780 Buffer * first = theBufferList().first();
3783 message(_("Saving all documents..."));
3784 // We cannot use a for loop as the buffer list cycles.
3787 if (!b->isClean()) {
3789 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3791 b = theBufferList().next(b);
3792 } while (b != first);
3793 dr.setMessage(_("All documents saved."));
3797 case LFUN_BUFFER_CLOSE:
3801 case LFUN_BUFFER_CLOSE_ALL:
3805 case LFUN_TOOLBAR_TOGGLE: {
3806 string const name = cmd.getArg(0);
3807 if (GuiToolbar * t = toolbar(name))
3812 case LFUN_DIALOG_UPDATE: {
3813 string const name = to_utf8(cmd.argument());
3814 if (name == "prefs" || name == "document")
3815 updateDialog(name, string());
3816 else if (name == "paragraph")
3817 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3818 else if (currentBufferView()) {
3819 Inset * inset = currentBufferView()->editedInset(name);
3820 // Can only update a dialog connected to an existing inset
3822 // FIXME: get rid of this indirection; GuiView ask the inset
3823 // if he is kind enough to update itself...
3824 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3825 //FIXME: pass DispatchResult here?
3826 inset->dispatch(currentBufferView()->cursor(), fr);
3832 case LFUN_DIALOG_TOGGLE: {
3833 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3834 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3835 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3839 case LFUN_DIALOG_DISCONNECT_INSET:
3840 disconnectDialog(to_utf8(cmd.argument()));
3843 case LFUN_DIALOG_HIDE: {
3844 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3848 case LFUN_DIALOG_SHOW: {
3849 string const name = cmd.getArg(0);
3850 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3852 if (name == "character") {
3853 data = freefont2string();
3855 showDialog("character", data);
3856 } else if (name == "latexlog") {
3857 Buffer::LogType type;
3858 string const logfile = doc_buffer->logName(&type);
3860 case Buffer::latexlog:
3863 case Buffer::buildlog:
3867 data += Lexer::quoteString(logfile);
3868 showDialog("log", data);
3869 } else if (name == "vclog") {
3870 string const data = "vc " +
3871 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3872 showDialog("log", data);
3873 } else if (name == "symbols") {
3874 data = bv->cursor().getEncoding()->name();
3876 showDialog("symbols", data);
3878 } else if (name == "prefs" && isFullScreen()) {
3879 lfunUiToggle("fullscreen");
3880 showDialog("prefs", data);
3882 showDialog(name, data);
3887 dr.setMessage(cmd.argument());
3890 case LFUN_UI_TOGGLE: {
3891 string arg = cmd.getArg(0);
3892 if (!lfunUiToggle(arg)) {
3893 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3894 dr.setMessage(bformat(msg, from_utf8(arg)));
3896 // Make sure the keyboard focus stays in the work area.
3901 case LFUN_VIEW_SPLIT: {
3902 LASSERT(doc_buffer, break);
3903 string const orientation = cmd.getArg(0);
3904 d.splitter_->setOrientation(orientation == "vertical"
3905 ? Qt::Vertical : Qt::Horizontal);
3906 TabWorkArea * twa = addTabWorkArea();
3907 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3908 setCurrentWorkArea(wa);
3911 case LFUN_TAB_GROUP_CLOSE:
3912 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3913 closeTabWorkArea(twa);
3914 d.current_work_area_ = 0;
3915 twa = d.currentTabWorkArea();
3916 // Switch to the next GuiWorkArea in the found TabWorkArea.
3918 // Make sure the work area is up to date.
3919 setCurrentWorkArea(twa->currentWorkArea());
3921 setCurrentWorkArea(0);
3926 case LFUN_VIEW_CLOSE:
3927 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3928 closeWorkArea(twa->currentWorkArea());
3929 d.current_work_area_ = 0;
3930 twa = d.currentTabWorkArea();
3931 // Switch to the next GuiWorkArea in the found TabWorkArea.
3933 // Make sure the work area is up to date.
3934 setCurrentWorkArea(twa->currentWorkArea());
3936 setCurrentWorkArea(0);
3941 case LFUN_COMPLETION_INLINE:
3942 if (d.current_work_area_)
3943 d.current_work_area_->completer().showInline();
3946 case LFUN_COMPLETION_POPUP:
3947 if (d.current_work_area_)
3948 d.current_work_area_->completer().showPopup();
3953 if (d.current_work_area_)
3954 d.current_work_area_->completer().tab();
3957 case LFUN_COMPLETION_CANCEL:
3958 if (d.current_work_area_) {
3959 if (d.current_work_area_->completer().popupVisible())
3960 d.current_work_area_->completer().hidePopup();
3962 d.current_work_area_->completer().hideInline();
3966 case LFUN_COMPLETION_ACCEPT:
3967 if (d.current_work_area_)
3968 d.current_work_area_->completer().activate();
3971 case LFUN_BUFFER_ZOOM_IN:
3972 case LFUN_BUFFER_ZOOM_OUT: {
3973 // use a signed temp to avoid overflow
3974 int zoom = lyxrc.zoom;
3975 if (cmd.argument().empty()) {
3976 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3981 zoom += convert<int>(cmd.argument());
3983 if (zoom < static_cast<int>(zoom_min_))
3987 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3989 // The global QPixmapCache is used in GuiPainter to cache text
3990 // painting so we must reset it.
3991 QPixmapCache::clear();
3992 guiApp->fontLoader().update();
3993 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3997 case LFUN_VC_REGISTER:
3998 case LFUN_VC_RENAME:
4000 case LFUN_VC_CHECK_IN:
4001 case LFUN_VC_CHECK_OUT:
4002 case LFUN_VC_REPO_UPDATE:
4003 case LFUN_VC_LOCKING_TOGGLE:
4004 case LFUN_VC_REVERT:
4005 case LFUN_VC_UNDO_LAST:
4006 case LFUN_VC_COMMAND:
4007 case LFUN_VC_COMPARE:
4008 dispatchVC(cmd, dr);
4011 case LFUN_SERVER_GOTO_FILE_ROW:
4012 if(goToFileRow(to_utf8(cmd.argument())))
4013 dr.screenUpdate(Update::Force | Update::FitCursor);
4016 case LFUN_LYX_ACTIVATE:
4020 case LFUN_FORWARD_SEARCH: {
4021 // it seems safe to assume we have a document buffer, since
4022 // getStatus wants one.
4023 // coverity[FORWARD_NULL]
4024 Buffer const * doc_master = doc_buffer->masterBuffer();
4025 FileName const path(doc_master->temppath());
4026 string const texname = doc_master->isChild(doc_buffer)
4027 ? DocFileName(changeExtension(
4028 doc_buffer->absFileName(),
4029 "tex")).mangledFileName()
4030 : doc_buffer->latexName();
4031 string const fulltexname =
4032 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4033 string const mastername =
4034 removeExtension(doc_master->latexName());
4035 FileName const dviname(addName(path.absFileName(),
4036 addExtension(mastername, "dvi")));
4037 FileName const pdfname(addName(path.absFileName(),
4038 addExtension(mastername, "pdf")));
4039 bool const have_dvi = dviname.exists();
4040 bool const have_pdf = pdfname.exists();
4041 if (!have_dvi && !have_pdf) {
4042 dr.setMessage(_("Please, preview the document first."));
4045 string outname = dviname.onlyFileName();
4046 string command = lyxrc.forward_search_dvi;
4047 if (!have_dvi || (have_pdf &&
4048 pdfname.lastModified() > dviname.lastModified())) {
4049 outname = pdfname.onlyFileName();
4050 command = lyxrc.forward_search_pdf;
4053 DocIterator cur = bv->cursor();
4054 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4055 LYXERR(Debug::ACTION, "Forward search: row:" << row
4057 if (row == -1 || command.empty()) {
4058 dr.setMessage(_("Couldn't proceed."));
4061 string texrow = convert<string>(row);
4063 command = subst(command, "$$n", texrow);
4064 command = subst(command, "$$f", fulltexname);
4065 command = subst(command, "$$t", texname);
4066 command = subst(command, "$$o", outname);
4068 PathChanger p(path);
4070 one.startscript(Systemcall::DontWait, command);
4074 case LFUN_SPELLING_CONTINUOUSLY:
4075 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4076 dr.screenUpdate(Update::Force);
4080 // The LFUN must be for one of BufferView, Buffer or Cursor;
4082 dispatchToBufferView(cmd, dr);
4086 // Part of automatic menu appearance feature.
4087 if (isFullScreen()) {
4088 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4092 // Need to update bv because many LFUNs here might have destroyed it
4093 bv = currentBufferView();
4095 // Clear non-empty selections
4096 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4098 Cursor & cur = bv->cursor();
4099 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4100 cur.clearSelection();
4106 bool GuiView::lfunUiToggle(string const & ui_component)
4108 if (ui_component == "scrollbar") {
4109 // hide() is of no help
4110 if (d.current_work_area_->verticalScrollBarPolicy() ==
4111 Qt::ScrollBarAlwaysOff)
4113 d.current_work_area_->setVerticalScrollBarPolicy(
4114 Qt::ScrollBarAsNeeded);
4116 d.current_work_area_->setVerticalScrollBarPolicy(
4117 Qt::ScrollBarAlwaysOff);
4118 } else if (ui_component == "statusbar") {
4119 statusBar()->setVisible(!statusBar()->isVisible());
4120 } else if (ui_component == "menubar") {
4121 menuBar()->setVisible(!menuBar()->isVisible());
4123 if (ui_component == "frame") {
4125 getContentsMargins(&l, &t, &r, &b);
4126 //are the frames in default state?
4127 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4129 setContentsMargins(-2, -2, -2, -2);
4131 setContentsMargins(0, 0, 0, 0);
4134 if (ui_component == "fullscreen") {
4142 void GuiView::toggleFullScreen()
4144 if (isFullScreen()) {
4145 for (int i = 0; i != d.splitter_->count(); ++i)
4146 d.tabWorkArea(i)->setFullScreen(false);
4147 setContentsMargins(0, 0, 0, 0);
4148 setWindowState(windowState() ^ Qt::WindowFullScreen);
4151 statusBar()->show();
4154 hideDialogs("prefs", 0);
4155 for (int i = 0; i != d.splitter_->count(); ++i)
4156 d.tabWorkArea(i)->setFullScreen(true);
4157 setContentsMargins(-2, -2, -2, -2);
4159 setWindowState(windowState() ^ Qt::WindowFullScreen);
4160 if (lyxrc.full_screen_statusbar)
4161 statusBar()->hide();
4162 if (lyxrc.full_screen_menubar)
4164 if (lyxrc.full_screen_toolbars) {
4165 ToolbarMap::iterator end = d.toolbars_.end();
4166 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4171 // give dialogs like the TOC a chance to adapt
4176 Buffer const * GuiView::updateInset(Inset const * inset)
4181 Buffer const * inset_buffer = &(inset->buffer());
4183 for (int i = 0; i != d.splitter_->count(); ++i) {
4184 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4187 Buffer const * buffer = &(wa->bufferView().buffer());
4188 if (inset_buffer == buffer)
4189 wa->scheduleRedraw();
4191 return inset_buffer;
4195 void GuiView::restartCursor()
4197 /* When we move around, or type, it's nice to be able to see
4198 * the cursor immediately after the keypress.
4200 if (d.current_work_area_)
4201 d.current_work_area_->startBlinkingCursor();
4203 // Take this occasion to update the other GUI elements.
4209 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4211 if (d.current_work_area_)
4212 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4217 // This list should be kept in sync with the list of insets in
4218 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4219 // dialog should have the same name as the inset.
4220 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4221 // docs in LyXAction.cpp.
4223 char const * const dialognames[] = {
4225 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4226 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4227 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4228 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4229 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4230 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4231 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4232 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4234 char const * const * const end_dialognames =
4235 dialognames + (sizeof(dialognames) / sizeof(char *));
4239 cmpCStr(char const * name) : name_(name) {}
4240 bool operator()(char const * other) {
4241 return strcmp(other, name_) == 0;
4248 bool isValidName(string const & name)
4250 return find_if(dialognames, end_dialognames,
4251 cmpCStr(name.c_str())) != end_dialognames;
4257 void GuiView::resetDialogs()
4259 // Make sure that no LFUN uses any GuiView.
4260 guiApp->setCurrentView(0);
4264 constructToolbars();
4265 guiApp->menus().fillMenuBar(menuBar(), this, false);
4266 d.layout_->updateContents(true);
4267 // Now update controls with current buffer.
4268 guiApp->setCurrentView(this);
4274 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4276 if (!isValidName(name))
4279 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4281 if (it != d.dialogs_.end()) {
4283 it->second->hideView();
4284 return it->second.get();
4287 Dialog * dialog = build(name);
4288 d.dialogs_[name].reset(dialog);
4289 if (lyxrc.allow_geometry_session)
4290 dialog->restoreSession();
4297 void GuiView::showDialog(string const & name, string const & data,
4300 triggerShowDialog(toqstr(name), toqstr(data), inset);
4304 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4310 const string name = fromqstr(qname);
4311 const string data = fromqstr(qdata);
4315 Dialog * dialog = findOrBuild(name, false);
4317 bool const visible = dialog->isVisibleView();
4318 dialog->showData(data);
4319 if (inset && currentBufferView())
4320 currentBufferView()->editInset(name, inset);
4321 // We only set the focus to the new dialog if it was not yet
4322 // visible in order not to change the existing previous behaviour
4324 // activateWindow is needed for floating dockviews
4325 dialog->asQWidget()->raise();
4326 dialog->asQWidget()->activateWindow();
4327 dialog->asQWidget()->setFocus();
4331 catch (ExceptionMessage const & ex) {
4339 bool GuiView::isDialogVisible(string const & name) const
4341 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4342 if (it == d.dialogs_.end())
4344 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4348 void GuiView::hideDialog(string const & name, Inset * inset)
4350 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4351 if (it == d.dialogs_.end())
4355 if (!currentBufferView())
4357 if (inset != currentBufferView()->editedInset(name))
4361 Dialog * const dialog = it->second.get();
4362 if (dialog->isVisibleView())
4364 if (currentBufferView())
4365 currentBufferView()->editInset(name, 0);
4369 void GuiView::disconnectDialog(string const & name)
4371 if (!isValidName(name))
4373 if (currentBufferView())
4374 currentBufferView()->editInset(name, 0);
4378 void GuiView::hideAll() const
4380 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4381 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4383 for(; it != end; ++it)
4384 it->second->hideView();
4388 void GuiView::updateDialogs()
4390 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4391 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4393 for(; it != end; ++it) {
4394 Dialog * dialog = it->second.get();
4396 if (dialog->needBufferOpen() && !documentBufferView())
4397 hideDialog(fromqstr(dialog->name()), 0);
4398 else if (dialog->isVisibleView())
4399 dialog->checkStatus();
4406 Dialog * createDialog(GuiView & lv, string const & name);
4408 // will be replaced by a proper factory...
4409 Dialog * createGuiAbout(GuiView & lv);
4410 Dialog * createGuiBibtex(GuiView & lv);
4411 Dialog * createGuiChanges(GuiView & lv);
4412 Dialog * createGuiCharacter(GuiView & lv);
4413 Dialog * createGuiCitation(GuiView & lv);
4414 Dialog * createGuiCompare(GuiView & lv);
4415 Dialog * createGuiCompareHistory(GuiView & lv);
4416 Dialog * createGuiDelimiter(GuiView & lv);
4417 Dialog * createGuiDocument(GuiView & lv);
4418 Dialog * createGuiErrorList(GuiView & lv);
4419 Dialog * createGuiExternal(GuiView & lv);
4420 Dialog * createGuiGraphics(GuiView & lv);
4421 Dialog * createGuiInclude(GuiView & lv);
4422 Dialog * createGuiIndex(GuiView & lv);
4423 Dialog * createGuiListings(GuiView & lv);
4424 Dialog * createGuiLog(GuiView & lv);
4425 Dialog * createGuiMathMatrix(GuiView & lv);
4426 Dialog * createGuiNote(GuiView & lv);
4427 Dialog * createGuiParagraph(GuiView & lv);
4428 Dialog * createGuiPhantom(GuiView & lv);
4429 Dialog * createGuiPreferences(GuiView & lv);
4430 Dialog * createGuiPrint(GuiView & lv);
4431 Dialog * createGuiPrintindex(GuiView & lv);
4432 Dialog * createGuiRef(GuiView & lv);
4433 Dialog * createGuiSearch(GuiView & lv);
4434 Dialog * createGuiSearchAdv(GuiView & lv);
4435 Dialog * createGuiSendTo(GuiView & lv);
4436 Dialog * createGuiShowFile(GuiView & lv);
4437 Dialog * createGuiSpellchecker(GuiView & lv);
4438 Dialog * createGuiSymbols(GuiView & lv);
4439 Dialog * createGuiTabularCreate(GuiView & lv);
4440 Dialog * createGuiTexInfo(GuiView & lv);
4441 Dialog * createGuiToc(GuiView & lv);
4442 Dialog * createGuiThesaurus(GuiView & lv);
4443 Dialog * createGuiViewSource(GuiView & lv);
4444 Dialog * createGuiWrap(GuiView & lv);
4445 Dialog * createGuiProgressView(GuiView & lv);
4449 Dialog * GuiView::build(string const & name)
4451 LASSERT(isValidName(name), return 0);
4453 Dialog * dialog = createDialog(*this, name);
4457 if (name == "aboutlyx")
4458 return createGuiAbout(*this);
4459 if (name == "bibtex")
4460 return createGuiBibtex(*this);
4461 if (name == "changes")
4462 return createGuiChanges(*this);
4463 if (name == "character")
4464 return createGuiCharacter(*this);
4465 if (name == "citation")
4466 return createGuiCitation(*this);
4467 if (name == "compare")
4468 return createGuiCompare(*this);
4469 if (name == "comparehistory")
4470 return createGuiCompareHistory(*this);
4471 if (name == "document")
4472 return createGuiDocument(*this);
4473 if (name == "errorlist")
4474 return createGuiErrorList(*this);
4475 if (name == "external")
4476 return createGuiExternal(*this);
4478 return createGuiShowFile(*this);
4479 if (name == "findreplace")
4480 return createGuiSearch(*this);
4481 if (name == "findreplaceadv")
4482 return createGuiSearchAdv(*this);
4483 if (name == "graphics")
4484 return createGuiGraphics(*this);
4485 if (name == "include")
4486 return createGuiInclude(*this);
4487 if (name == "index")
4488 return createGuiIndex(*this);
4489 if (name == "index_print")
4490 return createGuiPrintindex(*this);
4491 if (name == "listings")
4492 return createGuiListings(*this);
4494 return createGuiLog(*this);
4495 if (name == "mathdelimiter")
4496 return createGuiDelimiter(*this);
4497 if (name == "mathmatrix")
4498 return createGuiMathMatrix(*this);
4500 return createGuiNote(*this);
4501 if (name == "paragraph")
4502 return createGuiParagraph(*this);
4503 if (name == "phantom")
4504 return createGuiPhantom(*this);
4505 if (name == "prefs")
4506 return createGuiPreferences(*this);
4508 return createGuiRef(*this);
4509 if (name == "sendto")
4510 return createGuiSendTo(*this);
4511 if (name == "spellchecker")
4512 return createGuiSpellchecker(*this);
4513 if (name == "symbols")
4514 return createGuiSymbols(*this);
4515 if (name == "tabularcreate")
4516 return createGuiTabularCreate(*this);
4517 if (name == "texinfo")
4518 return createGuiTexInfo(*this);
4519 if (name == "thesaurus")
4520 return createGuiThesaurus(*this);
4522 return createGuiToc(*this);
4523 if (name == "view-source")
4524 return createGuiViewSource(*this);
4526 return createGuiWrap(*this);
4527 if (name == "progress")
4528 return createGuiProgressView(*this);
4534 } // namespace frontend
4537 #include "moc_GuiView.cpp"