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.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #include <QSvgRenderer>
114 #include <QtConcurrentRun>
122 // sync with GuiAlert.cpp
123 #define EXPORT_in_THREAD 1
126 #include "support/bind.h"
130 #ifdef HAVE_SYS_TIME_H
131 # include <sys/time.h>
139 using namespace lyx::support;
143 using support::addExtension;
144 using support::changeExtension;
145 using support::removeExtension;
151 class BackgroundWidget : public QWidget
154 BackgroundWidget(int width, int height)
155 : width_(width), height_(height)
157 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
158 if (!lyxrc.show_banner)
160 /// The text to be written on top of the pixmap
161 QString const text = lyx_version ?
162 qt_("version ") + lyx_version : qt_("unknown version");
163 #if QT_VERSION >= 0x050000
164 QString imagedir = "images/";
165 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
166 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
167 if (svgRenderer.isValid()) {
168 splash_ = QPixmap(splashSize());
169 QPainter painter(&splash_);
170 svgRenderer.render(&painter);
171 splash_.setDevicePixelRatio(pixelRatio());
173 splash_ = getPixmap("images/", "banner", "png");
176 splash_ = getPixmap("images/", "banner", "svgz,png");
179 QPainter pain(&splash_);
180 pain.setPen(QColor(0, 0, 0));
181 qreal const fsize = fontSize();
182 QPointF const position = textPosition();
184 "widget pixel ratio: " << pixelRatio() <<
185 " splash pixel ratio: " << splashPixelRatio() <<
186 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
188 // The font used to display the version info
189 font.setStyleHint(QFont::SansSerif);
190 font.setWeight(QFont::Bold);
191 font.setPointSizeF(fsize);
193 pain.drawText(position, text);
194 setFocusPolicy(Qt::StrongFocus);
197 void paintEvent(QPaintEvent *)
199 int const w = width_;
200 int const h = height_;
201 int const x = (width() - w) / 2;
202 int const y = (height() - h) / 2;
204 "widget pixel ratio: " << pixelRatio() <<
205 " splash pixel ratio: " << splashPixelRatio() <<
206 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
208 pain.drawPixmap(x, y, w, h, splash_);
211 void keyPressEvent(QKeyEvent * ev)
214 setKeySymbol(&sym, ev);
216 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
228 /// Current ratio between physical pixels and device-independent pixels
229 double pixelRatio() const {
230 #if QT_VERSION >= 0x050000
231 return devicePixelRatio();
237 qreal fontSize() const {
238 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
241 QPointF textPosition() const {
242 return QPointF(width_/2 - 18, height_/2 + 45);
245 QSize splashSize() const {
247 static_cast<unsigned int>(width_ * pixelRatio()),
248 static_cast<unsigned int>(height_ * pixelRatio()));
251 /// Ratio between physical pixels and device-independent pixels of splash image
252 double splashPixelRatio() const {
253 #if QT_VERSION >= 0x050000
254 return splash_.devicePixelRatio();
262 /// Toolbar store providing access to individual toolbars by name.
263 typedef map<string, GuiToolbar *> ToolbarMap;
265 typedef shared_ptr<Dialog> DialogPtr;
270 class GuiView::GuiViewPrivate
273 GuiViewPrivate(GuiViewPrivate const &);
274 void operator=(GuiViewPrivate const &);
276 GuiViewPrivate(GuiView * gv)
277 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
278 layout_(0), autosave_timeout_(5000),
281 // hardcode here the platform specific icon size
282 smallIconSize = 16; // scaling problems
283 normalIconSize = 20; // ok, default if iconsize.png is missing
284 bigIconSize = 26; // better for some math icons
285 hugeIconSize = 32; // better for hires displays
288 // if it exists, use width of iconsize.png as normal size
289 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
290 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
292 QImage image(toqstr(fn.absFileName()));
293 if (image.width() < int(smallIconSize))
294 normalIconSize = smallIconSize;
295 else if (image.width() > int(giantIconSize))
296 normalIconSize = giantIconSize;
298 normalIconSize = image.width();
301 splitter_ = new QSplitter;
302 bg_widget_ = new BackgroundWidget(400, 250);
303 stack_widget_ = new QStackedWidget;
304 stack_widget_->addWidget(bg_widget_);
305 stack_widget_->addWidget(splitter_);
308 // TODO cleanup, remove the singleton, handle multiple Windows?
309 progress_ = ProgressInterface::instance();
310 if (!dynamic_cast<GuiProgress*>(progress_)) {
311 progress_ = new GuiProgress; // TODO who deletes it
312 ProgressInterface::setInstance(progress_);
315 dynamic_cast<GuiProgress*>(progress_),
316 SIGNAL(updateStatusBarMessage(QString const&)),
317 gv, SLOT(updateStatusBarMessage(QString const&)));
319 dynamic_cast<GuiProgress*>(progress_),
320 SIGNAL(clearMessageText()),
321 gv, SLOT(clearMessageText()));
328 delete stack_widget_;
331 QMenu * toolBarPopup(GuiView * parent)
333 // FIXME: translation
334 QMenu * menu = new QMenu(parent);
335 QActionGroup * iconSizeGroup = new QActionGroup(parent);
337 QAction * smallIcons = new QAction(iconSizeGroup);
338 smallIcons->setText(qt_("Small-sized icons"));
339 smallIcons->setCheckable(true);
340 QObject::connect(smallIcons, SIGNAL(triggered()),
341 parent, SLOT(smallSizedIcons()));
342 menu->addAction(smallIcons);
344 QAction * normalIcons = new QAction(iconSizeGroup);
345 normalIcons->setText(qt_("Normal-sized icons"));
346 normalIcons->setCheckable(true);
347 QObject::connect(normalIcons, SIGNAL(triggered()),
348 parent, SLOT(normalSizedIcons()));
349 menu->addAction(normalIcons);
351 QAction * bigIcons = new QAction(iconSizeGroup);
352 bigIcons->setText(qt_("Big-sized icons"));
353 bigIcons->setCheckable(true);
354 QObject::connect(bigIcons, SIGNAL(triggered()),
355 parent, SLOT(bigSizedIcons()));
356 menu->addAction(bigIcons);
358 QAction * hugeIcons = new QAction(iconSizeGroup);
359 hugeIcons->setText(qt_("Huge-sized icons"));
360 hugeIcons->setCheckable(true);
361 QObject::connect(hugeIcons, SIGNAL(triggered()),
362 parent, SLOT(hugeSizedIcons()));
363 menu->addAction(hugeIcons);
365 QAction * giantIcons = new QAction(iconSizeGroup);
366 giantIcons->setText(qt_("Giant-sized icons"));
367 giantIcons->setCheckable(true);
368 QObject::connect(giantIcons, SIGNAL(triggered()),
369 parent, SLOT(giantSizedIcons()));
370 menu->addAction(giantIcons);
372 unsigned int cur = parent->iconSize().width();
373 if ( cur == parent->d.smallIconSize)
374 smallIcons->setChecked(true);
375 else if (cur == parent->d.normalIconSize)
376 normalIcons->setChecked(true);
377 else if (cur == parent->d.bigIconSize)
378 bigIcons->setChecked(true);
379 else if (cur == parent->d.hugeIconSize)
380 hugeIcons->setChecked(true);
381 else if (cur == parent->d.giantIconSize)
382 giantIcons->setChecked(true);
389 stack_widget_->setCurrentWidget(bg_widget_);
390 bg_widget_->setUpdatesEnabled(true);
391 bg_widget_->setFocus();
394 int tabWorkAreaCount()
396 return splitter_->count();
399 TabWorkArea * tabWorkArea(int i)
401 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
404 TabWorkArea * currentTabWorkArea()
406 int areas = tabWorkAreaCount();
408 // The first TabWorkArea is always the first one, if any.
409 return tabWorkArea(0);
411 for (int i = 0; i != areas; ++i) {
412 TabWorkArea * twa = tabWorkArea(i);
413 if (current_main_work_area_ == twa->currentWorkArea())
417 // None has the focus so we just take the first one.
418 return tabWorkArea(0);
421 int countWorkAreasOf(Buffer & buf)
423 int areas = tabWorkAreaCount();
425 for (int i = 0; i != areas; ++i) {
426 TabWorkArea * twa = tabWorkArea(i);
427 if (twa->workArea(buf))
433 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
435 if (processing_thread_watcher_.isRunning()) {
436 // we prefer to cancel this preview in order to keep a snappy
440 processing_thread_watcher_.setFuture(f);
445 GuiWorkArea * current_work_area_;
446 GuiWorkArea * current_main_work_area_;
447 QSplitter * splitter_;
448 QStackedWidget * stack_widget_;
449 BackgroundWidget * bg_widget_;
451 ToolbarMap toolbars_;
452 ProgressInterface* progress_;
453 /// The main layout box.
455 * \warning Don't Delete! The layout box is actually owned by
456 * whichever toolbar contains it. All the GuiView class needs is a
457 * means of accessing it.
459 * FIXME: replace that with a proper model so that we are not limited
460 * to only one dialog.
465 map<string, DialogPtr> dialogs_;
467 unsigned int smallIconSize;
468 unsigned int normalIconSize;
469 unsigned int bigIconSize;
470 unsigned int hugeIconSize;
471 unsigned int giantIconSize;
473 QTimer statusbar_timer_;
474 /// auto-saving of buffers
475 Timeout autosave_timeout_;
476 /// flag against a race condition due to multiclicks, see bug #1119
480 TocModels toc_models_;
483 QFutureWatcher<docstring> autosave_watcher_;
484 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
486 string last_export_format;
487 string processing_format;
489 static QSet<Buffer const *> busyBuffers;
490 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
492 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
493 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
496 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
498 // TODO syncFunc/previewFunc: use bind
499 bool asyncBufferProcessing(string const & argument,
500 Buffer const * used_buffer,
501 docstring const & msg,
502 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
503 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
504 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
506 QVector<GuiWorkArea*> guiWorkAreas();
509 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
512 GuiView::GuiView(int id)
513 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
514 command_execute_(false), minibuffer_focus_(false)
516 // GuiToolbars *must* be initialised before the menu bar.
517 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
533 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
534 d.autosave_timeout_.start();
536 connect(&d.statusbar_timer_, SIGNAL(timeout()),
537 this, SLOT(clearMessage()));
539 // We don't want to keep the window in memory if it is closed.
540 setAttribute(Qt::WA_DeleteOnClose, true);
542 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
543 // QIcon::fromTheme was introduced in Qt 4.6
544 #if (QT_VERSION >= 0x040600)
545 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
546 // since the icon is provided in the application bundle. We use a themed
547 // version when available and use the bundled one as fallback.
548 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
556 // use tabbed dock area for multiple docks
557 // (such as "source" and "messages")
558 setDockOptions(QMainWindow::ForceTabbedDocks);
561 setAcceptDrops(true);
563 // add busy indicator to statusbar
564 QLabel * busylabel = new QLabel(statusBar());
565 statusBar()->addPermanentWidget(busylabel);
566 search_mode mode = theGuiApp()->imageSearchMode();
567 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
568 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
569 busylabel->setMovie(busyanim);
573 connect(&d.processing_thread_watcher_, SIGNAL(started()),
574 busylabel, SLOT(show()));
575 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
576 busylabel, SLOT(hide()));
578 QFontMetrics const fm(statusBar()->fontMetrics());
579 int const roheight = max(int(d.normalIconSize), fm.height());
580 QSize const rosize(roheight, roheight);
581 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
582 read_only_ = new QLabel(statusBar());
583 read_only_->setPixmap(readonly);
584 read_only_->setScaledContents(true);
585 read_only_->setAlignment(Qt::AlignCenter);
587 statusBar()->addPermanentWidget(read_only_);
589 version_control_ = new QLabel(statusBar());
590 version_control_->setAlignment(Qt::AlignCenter);
591 version_control_->setFrameStyle(QFrame::StyledPanel);
592 version_control_->hide();
593 statusBar()->addPermanentWidget(version_control_);
595 statusBar()->setSizeGripEnabled(true);
598 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
599 SLOT(autoSaveThreadFinished()));
601 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
602 SLOT(processingThreadStarted()));
603 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
604 SLOT(processingThreadFinished()));
606 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
607 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
609 // Forbid too small unresizable window because it can happen
610 // with some window manager under X11.
611 setMinimumSize(300, 200);
613 if (lyxrc.allow_geometry_session) {
614 // Now take care of session management.
619 // no session handling, default to a sane size.
620 setGeometry(50, 50, 690, 510);
623 // clear session data if any.
625 settings.remove("views");
635 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
637 QVector<GuiWorkArea*> areas;
638 for (int i = 0; i < tabWorkAreaCount(); i++) {
639 TabWorkArea* ta = tabWorkArea(i);
640 for (int u = 0; u < ta->count(); u++) {
641 areas << ta->workArea(u);
647 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
648 string const & format)
650 docstring const fmt = formats.prettyName(format);
653 case Buffer::ExportSuccess:
654 msg = bformat(_("Successful export to format: %1$s"), fmt);
656 case Buffer::ExportCancel:
657 msg = _("Document export cancelled.");
659 case Buffer::ExportError:
660 case Buffer::ExportNoPathToFormat:
661 case Buffer::ExportTexPathHasSpaces:
662 case Buffer::ExportConverterError:
663 msg = bformat(_("Error while exporting format: %1$s"), fmt);
665 case Buffer::PreviewSuccess:
666 msg = bformat(_("Successful preview of format: %1$s"), fmt);
668 case Buffer::PreviewError:
669 msg = bformat(_("Error while previewing format: %1$s"), fmt);
676 void GuiView::processingThreadStarted()
681 void GuiView::processingThreadFinished()
683 QFutureWatcher<Buffer::ExportStatus> const * watcher =
684 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
686 Buffer::ExportStatus const status = watcher->result();
687 handleExportStatus(this, status, d.processing_format);
690 BufferView const * const bv = currentBufferView();
691 if (bv && !bv->buffer().errorList("Export").empty()) {
695 errors(d.last_export_format);
699 void GuiView::autoSaveThreadFinished()
701 QFutureWatcher<docstring> const * watcher =
702 static_cast<QFutureWatcher<docstring> const *>(sender());
703 message(watcher->result());
708 void GuiView::saveLayout() const
711 settings.beginGroup("views");
712 settings.beginGroup(QString::number(id_));
713 #if defined(Q_WS_X11) || defined(QPA_XCB)
714 settings.setValue("pos", pos());
715 settings.setValue("size", size());
717 settings.setValue("geometry", saveGeometry());
719 settings.setValue("layout", saveState(0));
720 settings.setValue("icon_size", iconSize());
724 void GuiView::saveUISettings() const
726 // Save the toolbar private states
727 ToolbarMap::iterator end = d.toolbars_.end();
728 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
729 it->second->saveSession();
730 // Now take care of all other dialogs
731 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
732 for (; it!= d.dialogs_.end(); ++it)
733 it->second->saveSession();
737 bool GuiView::restoreLayout()
740 settings.beginGroup("views");
741 settings.beginGroup(QString::number(id_));
742 QString const icon_key = "icon_size";
743 if (!settings.contains(icon_key))
746 //code below is skipped when when ~/.config/LyX is (re)created
747 QSize icon_size = settings.value(icon_key).toSize();
748 // Check whether session size changed.
749 if (icon_size.width() != int(d.smallIconSize) &&
750 icon_size.width() != int(d.normalIconSize) &&
751 icon_size.width() != int(d.bigIconSize) &&
752 icon_size.width() != int(d.hugeIconSize) &&
753 icon_size.width() != int(d.giantIconSize)) {
754 icon_size.setWidth(d.normalIconSize);
755 icon_size.setHeight(d.normalIconSize);
757 setIconSize(icon_size);
759 #if defined(Q_WS_X11) || defined(QPA_XCB)
760 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
761 QSize size = settings.value("size", QSize(690, 510)).toSize();
765 // Work-around for bug #6034: the window ends up in an undetermined
766 // state when trying to restore a maximized window when it is
767 // already maximized.
768 if (!(windowState() & Qt::WindowMaximized))
769 if (!restoreGeometry(settings.value("geometry").toByteArray()))
770 setGeometry(50, 50, 690, 510);
772 // Make sure layout is correctly oriented.
773 setLayoutDirection(qApp->layoutDirection());
775 // Allow the toc and view-source dock widget to be restored if needed.
777 if ((dialog = findOrBuild("toc", true)))
778 // see bug 5082. At least setup title and enabled state.
779 // Visibility will be adjusted by restoreState below.
780 dialog->prepareView();
781 if ((dialog = findOrBuild("view-source", true)))
782 dialog->prepareView();
783 if ((dialog = findOrBuild("progress", true)))
784 dialog->prepareView();
786 if (!restoreState(settings.value("layout").toByteArray(), 0))
789 // init the toolbars that have not been restored
790 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
791 Toolbars::Infos::iterator end = guiApp->toolbars().end();
792 for (; cit != end; ++cit) {
793 GuiToolbar * tb = toolbar(cit->name);
794 if (tb && !tb->isRestored())
795 initToolbar(cit->name);
803 GuiToolbar * GuiView::toolbar(string const & name)
805 ToolbarMap::iterator it = d.toolbars_.find(name);
806 if (it != d.toolbars_.end())
809 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
814 void GuiView::constructToolbars()
816 ToolbarMap::iterator it = d.toolbars_.begin();
817 for (; it != d.toolbars_.end(); ++it)
821 // I don't like doing this here, but the standard toolbar
822 // destroys this object when it's destroyed itself (vfr)
823 d.layout_ = new LayoutBox(*this);
824 d.stack_widget_->addWidget(d.layout_);
825 d.layout_->move(0,0);
827 // extracts the toolbars from the backend
828 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
829 Toolbars::Infos::iterator end = guiApp->toolbars().end();
830 for (; cit != end; ++cit)
831 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
835 void GuiView::initToolbars()
837 // extracts the toolbars from the backend
838 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
839 Toolbars::Infos::iterator end = guiApp->toolbars().end();
840 for (; cit != end; ++cit)
841 initToolbar(cit->name);
845 void GuiView::initToolbar(string const & name)
847 GuiToolbar * tb = toolbar(name);
850 int const visibility = guiApp->toolbars().defaultVisibility(name);
851 bool newline = !(visibility & Toolbars::SAMEROW);
852 tb->setVisible(false);
853 tb->setVisibility(visibility);
855 if (visibility & Toolbars::TOP) {
857 addToolBarBreak(Qt::TopToolBarArea);
858 addToolBar(Qt::TopToolBarArea, tb);
861 if (visibility & Toolbars::BOTTOM) {
863 addToolBarBreak(Qt::BottomToolBarArea);
864 addToolBar(Qt::BottomToolBarArea, tb);
867 if (visibility & Toolbars::LEFT) {
869 addToolBarBreak(Qt::LeftToolBarArea);
870 addToolBar(Qt::LeftToolBarArea, tb);
873 if (visibility & Toolbars::RIGHT) {
875 addToolBarBreak(Qt::RightToolBarArea);
876 addToolBar(Qt::RightToolBarArea, tb);
879 if (visibility & Toolbars::ON)
880 tb->setVisible(true);
884 TocModels & GuiView::tocModels()
886 return d.toc_models_;
890 void GuiView::setFocus()
892 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
893 QMainWindow::setFocus();
897 bool GuiView::hasFocus() const
899 if (currentWorkArea())
900 return currentWorkArea()->hasFocus();
901 if (currentMainWorkArea())
902 return currentMainWorkArea()->hasFocus();
903 return d.bg_widget_->hasFocus();
907 void GuiView::focusInEvent(QFocusEvent * e)
909 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
910 QMainWindow::focusInEvent(e);
911 // Make sure guiApp points to the correct view.
912 guiApp->setCurrentView(this);
913 if (currentWorkArea())
914 currentWorkArea()->setFocus();
915 else if (currentMainWorkArea())
916 currentMainWorkArea()->setFocus();
918 d.bg_widget_->setFocus();
922 QMenu * GuiView::createPopupMenu()
924 return d.toolBarPopup(this);
928 void GuiView::showEvent(QShowEvent * e)
930 LYXERR(Debug::GUI, "Passed Geometry "
931 << size().height() << "x" << size().width()
932 << "+" << pos().x() << "+" << pos().y());
934 if (d.splitter_->count() == 0)
935 // No work area, switch to the background widget.
939 QMainWindow::showEvent(e);
943 bool GuiView::closeScheduled()
950 bool GuiView::prepareAllBuffersForLogout()
952 Buffer * first = theBufferList().first();
956 // First, iterate over all buffers and ask the users if unsaved
957 // changes should be saved.
958 // We cannot use a for loop as the buffer list cycles.
961 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
963 b = theBufferList().next(b);
964 } while (b != first);
966 // Next, save session state
967 // When a view/window was closed before without quitting LyX, there
968 // are already entries in the lastOpened list.
969 theSession().lastOpened().clear();
976 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
977 ** is responsibility of the container (e.g., dialog)
979 void GuiView::closeEvent(QCloseEvent * close_event)
981 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
983 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
984 Alert::warning(_("Exit LyX"),
985 _("LyX could not be closed because documents are being processed by LyX."));
986 close_event->setAccepted(false);
990 // If the user pressed the x (so we didn't call closeView
991 // programmatically), we want to clear all existing entries.
993 theSession().lastOpened().clear();
998 // it can happen that this event arrives without selecting the view,
999 // e.g. when clicking the close button on a background window.
1001 if (!closeWorkAreaAll()) {
1003 close_event->ignore();
1007 // Make sure that nothing will use this to be closed View.
1008 guiApp->unregisterView(this);
1010 if (isFullScreen()) {
1011 // Switch off fullscreen before closing.
1016 // Make sure the timer time out will not trigger a statusbar update.
1017 d.statusbar_timer_.stop();
1019 // Saving fullscreen requires additional tweaks in the toolbar code.
1020 // It wouldn't also work under linux natively.
1021 if (lyxrc.allow_geometry_session) {
1026 close_event->accept();
1030 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1032 if (event->mimeData()->hasUrls())
1034 /// \todo Ask lyx-devel is this is enough:
1035 /// if (event->mimeData()->hasFormat("text/plain"))
1036 /// event->acceptProposedAction();
1040 void GuiView::dropEvent(QDropEvent * event)
1042 QList<QUrl> files = event->mimeData()->urls();
1043 if (files.isEmpty())
1046 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1047 for (int i = 0; i != files.size(); ++i) {
1048 string const file = os::internal_path(fromqstr(
1049 files.at(i).toLocalFile()));
1053 string const ext = support::getExtension(file);
1054 vector<const Format *> found_formats;
1056 // Find all formats that have the correct extension.
1057 vector<const Format *> const & import_formats
1058 = theConverters().importableFormats();
1059 vector<const Format *>::const_iterator it = import_formats.begin();
1060 for (; it != import_formats.end(); ++it)
1061 if ((*it)->hasExtension(ext))
1062 found_formats.push_back(*it);
1065 if (found_formats.size() >= 1) {
1066 if (found_formats.size() > 1) {
1067 //FIXME: show a dialog to choose the correct importable format
1068 LYXERR(Debug::FILES,
1069 "Multiple importable formats found, selecting first");
1071 string const arg = found_formats[0]->name() + " " + file;
1072 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1075 //FIXME: do we have to explicitly check whether it's a lyx file?
1076 LYXERR(Debug::FILES,
1077 "No formats found, trying to open it as a lyx file");
1078 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1080 // add the functions to the queue
1081 guiApp->addToFuncRequestQueue(cmd);
1084 // now process the collected functions. We perform the events
1085 // asynchronously. This prevents potential problems in case the
1086 // BufferView is closed within an event.
1087 guiApp->processFuncRequestQueueAsync();
1091 void GuiView::message(docstring const & str)
1093 if (ForkedProcess::iAmAChild())
1096 // call is moved to GUI-thread by GuiProgress
1097 d.progress_->appendMessage(toqstr(str));
1101 void GuiView::clearMessageText()
1103 message(docstring());
1107 void GuiView::updateStatusBarMessage(QString const & str)
1109 statusBar()->showMessage(str);
1110 d.statusbar_timer_.stop();
1111 d.statusbar_timer_.start(3000);
1115 void GuiView::smallSizedIcons()
1117 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1121 void GuiView::normalSizedIcons()
1123 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1127 void GuiView::bigSizedIcons()
1129 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1133 void GuiView::hugeSizedIcons()
1135 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1139 void GuiView::giantSizedIcons()
1141 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1145 void GuiView::clearMessage()
1147 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1148 // the hasFocus function mostly returns false, even if the focus is on
1149 // a workarea in this view.
1153 d.statusbar_timer_.stop();
1157 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1159 if (wa != d.current_work_area_
1160 || wa->bufferView().buffer().isInternal())
1162 Buffer const & buf = wa->bufferView().buffer();
1163 // Set the windows title
1164 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1166 title += from_ascii(" - LyX");
1168 setWindowTitle(toqstr(title));
1169 // Sets the path for the window: this is used by OSX to
1170 // allow a context click on the title bar showing a menu
1171 // with the path up to the file
1172 setWindowFilePath(toqstr(buf.absFileName()));
1173 // Tell Qt whether the current document is changed
1174 setWindowModified(!buf.isClean());
1176 if (buf.isReadonly())
1181 if (buf.lyxvc().inUse()) {
1182 version_control_->show();
1183 if (buf.lyxvc().locking())
1184 version_control_->setText(
1185 toqstr(bformat(_("%1$s lock"),
1186 from_ascii(buf.lyxvc().vcname()))));
1188 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1190 version_control_->hide();
1194 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1196 if (d.current_work_area_)
1197 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1198 this, SLOT(setBusy(bool)));
1200 disconnectBufferView();
1201 connectBufferView(wa->bufferView());
1202 connectBuffer(wa->bufferView().buffer());
1203 d.current_work_area_ = wa;
1204 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1205 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1206 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1207 updateWindowTitle(wa);
1211 // The document settings needs to be reinitialised.
1212 updateDialog("document", "");
1214 // Buffer-dependent dialogs must be updated. This is done here because
1215 // some dialogs require buffer()->text.
1220 void GuiView::on_lastWorkAreaRemoved()
1223 // We already are in a close event. Nothing more to do.
1226 if (d.splitter_->count() > 1)
1227 // We have a splitter so don't close anything.
1230 // Reset and updates the dialogs.
1231 d.toc_models_.reset(0);
1232 updateDialog("document", "");
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 BufferView & bv = d.current_work_area_->bufferView();
1315 connectBufferView(bv);
1316 connectBuffer(bv.buffer());
1317 // The document structure, name and dialogs might have
1318 // changed in another view.
1320 // The document settings needs to be reinitialised.
1321 updateDialog("document", "");
1327 return QMainWindow::event(e);
1330 case QEvent::ShortcutOverride: {
1332 if (isFullScreen() && menuBar()->isHidden()) {
1333 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1334 // FIXME: we should also try to detect special LyX shortcut such as
1335 // Alt-P and Alt-M. Right now there is a hack in
1336 // GuiWorkArea::processKeySym() that hides again the menubar for
1338 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1340 return QMainWindow::event(e);
1343 return QMainWindow::event(e);
1347 return QMainWindow::event(e);
1351 void GuiView::resetWindowTitle()
1353 setWindowTitle(qt_("LyX"));
1356 bool GuiView::focusNextPrevChild(bool /*next*/)
1363 bool GuiView::busy() const
1369 void GuiView::setBusy(bool busy)
1371 bool const busy_before = busy_ > 0;
1372 busy ? ++busy_ : --busy_;
1373 if ((busy_ > 0) == busy_before)
1374 // busy state didn't change
1378 QApplication::setOverrideCursor(Qt::WaitCursor);
1381 QApplication::restoreOverrideCursor();
1386 void GuiView::resetCommandExecute()
1388 command_execute_ = false;
1393 double GuiView::pixelRatio() const
1395 #if QT_VERSION >= 0x050000
1396 return devicePixelRatio();
1403 GuiWorkArea * GuiView::workArea(int index)
1405 if (TabWorkArea * twa = d.currentTabWorkArea())
1406 if (index < twa->count())
1407 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1412 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1414 if (currentWorkArea()
1415 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1416 return currentWorkArea();
1417 if (TabWorkArea * twa = d.currentTabWorkArea())
1418 return twa->workArea(buffer);
1423 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1425 // Automatically create a TabWorkArea if there are none yet.
1426 TabWorkArea * tab_widget = d.splitter_->count()
1427 ? d.currentTabWorkArea() : addTabWorkArea();
1428 return tab_widget->addWorkArea(buffer, *this);
1432 TabWorkArea * GuiView::addTabWorkArea()
1434 TabWorkArea * twa = new TabWorkArea;
1435 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1436 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1437 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1438 this, SLOT(on_lastWorkAreaRemoved()));
1440 d.splitter_->addWidget(twa);
1441 d.stack_widget_->setCurrentWidget(d.splitter_);
1446 GuiWorkArea const * GuiView::currentWorkArea() const
1448 return d.current_work_area_;
1452 GuiWorkArea * GuiView::currentWorkArea()
1454 return d.current_work_area_;
1458 GuiWorkArea const * GuiView::currentMainWorkArea() const
1460 if (!d.currentTabWorkArea())
1462 return d.currentTabWorkArea()->currentWorkArea();
1466 GuiWorkArea * GuiView::currentMainWorkArea()
1468 if (!d.currentTabWorkArea())
1470 return d.currentTabWorkArea()->currentWorkArea();
1474 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1476 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1478 d.current_work_area_ = 0;
1483 // FIXME: I've no clue why this is here and why it accesses
1484 // theGuiApp()->currentView, which might be 0 (bug 6464).
1485 // See also 27525 (vfr).
1486 if (theGuiApp()->currentView() == this
1487 && theGuiApp()->currentView()->currentWorkArea() == wa)
1490 if (currentBufferView())
1491 cap::saveSelection(currentBufferView()->cursor());
1493 theGuiApp()->setCurrentView(this);
1494 d.current_work_area_ = wa;
1496 // We need to reset this now, because it will need to be
1497 // right if the tabWorkArea gets reset in the for loop. We
1498 // will change it back if we aren't in that case.
1499 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1500 d.current_main_work_area_ = wa;
1502 for (int i = 0; i != d.splitter_->count(); ++i) {
1503 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1504 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1505 << ", Current main wa: " << currentMainWorkArea());
1510 d.current_main_work_area_ = old_cmwa;
1512 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1513 on_currentWorkAreaChanged(wa);
1514 BufferView & bv = wa->bufferView();
1515 bv.cursor().fixIfBroken();
1517 wa->setUpdatesEnabled(true);
1518 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1522 void GuiView::removeWorkArea(GuiWorkArea * wa)
1524 LASSERT(wa, return);
1525 if (wa == d.current_work_area_) {
1527 disconnectBufferView();
1528 d.current_work_area_ = 0;
1529 d.current_main_work_area_ = 0;
1532 bool found_twa = false;
1533 for (int i = 0; i != d.splitter_->count(); ++i) {
1534 TabWorkArea * twa = d.tabWorkArea(i);
1535 if (twa->removeWorkArea(wa)) {
1536 // Found in this tab group, and deleted the GuiWorkArea.
1538 if (twa->count() != 0) {
1539 if (d.current_work_area_ == 0)
1540 // This means that we are closing the current GuiWorkArea, so
1541 // switch to the next GuiWorkArea in the found TabWorkArea.
1542 setCurrentWorkArea(twa->currentWorkArea());
1544 // No more WorkAreas in this tab group, so delete it.
1551 // It is not a tabbed work area (i.e., the search work area), so it
1552 // should be deleted by other means.
1553 LASSERT(found_twa, return);
1555 if (d.current_work_area_ == 0) {
1556 if (d.splitter_->count() != 0) {
1557 TabWorkArea * twa = d.currentTabWorkArea();
1558 setCurrentWorkArea(twa->currentWorkArea());
1560 // No more work areas, switch to the background widget.
1561 setCurrentWorkArea(0);
1567 LayoutBox * GuiView::getLayoutDialog() const
1573 void GuiView::updateLayoutList()
1576 d.layout_->updateContents(false);
1580 void GuiView::updateToolbars()
1582 ToolbarMap::iterator end = d.toolbars_.end();
1583 if (d.current_work_area_) {
1585 if (d.current_work_area_->bufferView().cursor().inMathed()
1586 && !d.current_work_area_->bufferView().cursor().inRegexped())
1587 context |= Toolbars::MATH;
1588 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1589 context |= Toolbars::TABLE;
1590 if (currentBufferView()->buffer().areChangesPresent()
1591 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1592 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1593 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1594 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1595 context |= Toolbars::REVIEW;
1596 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1597 context |= Toolbars::MATHMACROTEMPLATE;
1598 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1599 context |= Toolbars::IPA;
1600 if (command_execute_)
1601 context |= Toolbars::MINIBUFFER;
1602 if (minibuffer_focus_) {
1603 context |= Toolbars::MINIBUFFER_FOCUS;
1604 minibuffer_focus_ = false;
1607 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1608 it->second->update(context);
1610 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1611 it->second->update();
1615 void GuiView::setBuffer(Buffer * newBuffer)
1617 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1618 LASSERT(newBuffer, return);
1620 GuiWorkArea * wa = workArea(*newBuffer);
1623 newBuffer->masterBuffer()->updateBuffer();
1625 wa = addWorkArea(*newBuffer);
1626 // scroll to the position when the BufferView was last closed
1627 if (lyxrc.use_lastfilepos) {
1628 LastFilePosSection::FilePos filepos =
1629 theSession().lastFilePos().load(newBuffer->fileName());
1630 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1633 //Disconnect the old buffer...there's no new one.
1636 connectBuffer(*newBuffer);
1637 connectBufferView(wa->bufferView());
1638 setCurrentWorkArea(wa);
1642 void GuiView::connectBuffer(Buffer & buf)
1644 buf.setGuiDelegate(this);
1648 void GuiView::disconnectBuffer()
1650 if (d.current_work_area_)
1651 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1655 void GuiView::connectBufferView(BufferView & bv)
1657 bv.setGuiDelegate(this);
1661 void GuiView::disconnectBufferView()
1663 if (d.current_work_area_)
1664 d.current_work_area_->bufferView().setGuiDelegate(0);
1668 void GuiView::errors(string const & error_type, bool from_master)
1670 BufferView const * const bv = currentBufferView();
1674 #if EXPORT_in_THREAD
1675 // We are called with from_master == false by default, so we
1676 // have to figure out whether that is the case or not.
1677 ErrorList & el = bv->buffer().errorList(error_type);
1679 el = bv->buffer().masterBuffer()->errorList(error_type);
1683 ErrorList const & el = from_master ?
1684 bv->buffer().masterBuffer()->errorList(error_type) :
1685 bv->buffer().errorList(error_type);
1691 string data = error_type;
1693 data = "from_master|" + error_type;
1694 showDialog("errorlist", data);
1698 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1700 d.toc_models_.updateItem(toqstr(type), dit);
1704 void GuiView::structureChanged()
1706 // FIXME: This is slightly expensive, though less than the tocBackend update
1707 // (#9880). This also resets the view in the Toc Widget (#6675).
1708 d.toc_models_.reset(documentBufferView());
1709 // Navigator needs more than a simple update in this case. It needs to be
1711 updateDialog("toc", "");
1715 void GuiView::updateDialog(string const & name, string const & data)
1717 if (!isDialogVisible(name))
1720 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1721 if (it == d.dialogs_.end())
1724 Dialog * const dialog = it->second.get();
1725 if (dialog->isVisibleView())
1726 dialog->initialiseParams(data);
1730 BufferView * GuiView::documentBufferView()
1732 return currentMainWorkArea()
1733 ? ¤tMainWorkArea()->bufferView()
1738 BufferView const * GuiView::documentBufferView() const
1740 return currentMainWorkArea()
1741 ? ¤tMainWorkArea()->bufferView()
1746 BufferView * GuiView::currentBufferView()
1748 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1752 BufferView const * GuiView::currentBufferView() const
1754 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1758 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1759 Buffer const * orig, Buffer * clone)
1761 bool const success = clone->autoSave();
1763 busyBuffers.remove(orig);
1765 ? _("Automatic save done.")
1766 : _("Automatic save failed!");
1770 void GuiView::autoSave()
1772 LYXERR(Debug::INFO, "Running autoSave()");
1774 Buffer * buffer = documentBufferView()
1775 ? &documentBufferView()->buffer() : 0;
1777 resetAutosaveTimers();
1781 GuiViewPrivate::busyBuffers.insert(buffer);
1782 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1783 buffer, buffer->cloneBufferOnly());
1784 d.autosave_watcher_.setFuture(f);
1785 resetAutosaveTimers();
1789 void GuiView::resetAutosaveTimers()
1792 d.autosave_timeout_.restart();
1796 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1799 Buffer * buf = currentBufferView()
1800 ? ¤tBufferView()->buffer() : 0;
1801 Buffer * doc_buffer = documentBufferView()
1802 ? &(documentBufferView()->buffer()) : 0;
1805 /* In LyX/Mac, when a dialog is open, the menus of the
1806 application can still be accessed without giving focus to
1807 the main window. In this case, we want to disable the menu
1808 entries that are buffer-related.
1809 This code must not be used on Linux and Windows, since it
1810 would disable buffer-related entries when hovering over the
1811 menu (see bug #9574).
1813 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1819 // Check whether we need a buffer
1820 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1821 // no, exit directly
1822 flag.message(from_utf8(N_("Command not allowed with"
1823 "out any document open")));
1824 flag.setEnabled(false);
1828 if (cmd.origin() == FuncRequest::TOC) {
1829 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1830 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1831 flag.setEnabled(false);
1835 switch(cmd.action()) {
1836 case LFUN_BUFFER_IMPORT:
1839 case LFUN_MASTER_BUFFER_UPDATE:
1840 case LFUN_MASTER_BUFFER_VIEW:
1842 && (doc_buffer->parent() != 0
1843 || doc_buffer->hasChildren())
1844 && !d.processing_thread_watcher_.isRunning();
1847 case LFUN_BUFFER_UPDATE:
1848 case LFUN_BUFFER_VIEW: {
1849 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1853 string format = to_utf8(cmd.argument());
1854 if (cmd.argument().empty())
1855 format = doc_buffer->params().getDefaultOutputFormat();
1856 enable = doc_buffer->params().isExportableFormat(format);
1860 case LFUN_BUFFER_RELOAD:
1861 enable = doc_buffer && !doc_buffer->isUnnamed()
1862 && doc_buffer->fileName().exists()
1863 && (!doc_buffer->isClean()
1864 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1867 case LFUN_BUFFER_CHILD_OPEN:
1868 enable = doc_buffer != 0;
1871 case LFUN_BUFFER_WRITE:
1872 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1875 //FIXME: This LFUN should be moved to GuiApplication.
1876 case LFUN_BUFFER_WRITE_ALL: {
1877 // We enable the command only if there are some modified buffers
1878 Buffer * first = theBufferList().first();
1883 // We cannot use a for loop as the buffer list is a cycle.
1885 if (!b->isClean()) {
1889 b = theBufferList().next(b);
1890 } while (b != first);
1894 case LFUN_BUFFER_WRITE_AS:
1895 case LFUN_BUFFER_EXPORT_AS:
1896 enable = doc_buffer != 0;
1899 case LFUN_BUFFER_CLOSE:
1900 case LFUN_VIEW_CLOSE:
1901 enable = doc_buffer != 0;
1904 case LFUN_BUFFER_CLOSE_ALL:
1905 enable = theBufferList().last() != theBufferList().first();
1908 case LFUN_VIEW_SPLIT:
1909 if (cmd.getArg(0) == "vertical")
1910 enable = doc_buffer && (d.splitter_->count() == 1 ||
1911 d.splitter_->orientation() == Qt::Vertical);
1913 enable = doc_buffer && (d.splitter_->count() == 1 ||
1914 d.splitter_->orientation() == Qt::Horizontal);
1917 case LFUN_TAB_GROUP_CLOSE:
1918 enable = d.tabWorkAreaCount() > 1;
1921 case LFUN_TOOLBAR_TOGGLE: {
1922 string const name = cmd.getArg(0);
1923 if (GuiToolbar * t = toolbar(name))
1924 flag.setOnOff(t->isVisible());
1927 docstring const msg =
1928 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1934 case LFUN_DROP_LAYOUTS_CHOICE:
1938 case LFUN_UI_TOGGLE:
1939 flag.setOnOff(isFullScreen());
1942 case LFUN_DIALOG_DISCONNECT_INSET:
1945 case LFUN_DIALOG_HIDE:
1946 // FIXME: should we check if the dialog is shown?
1949 case LFUN_DIALOG_TOGGLE:
1950 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1951 // fall through to set "enable"
1952 case LFUN_DIALOG_SHOW: {
1953 string const name = cmd.getArg(0);
1955 enable = name == "aboutlyx"
1956 || name == "file" //FIXME: should be removed.
1958 || name == "texinfo"
1959 || name == "progress"
1960 || name == "compare";
1961 else if (name == "character" || name == "symbols"
1962 || name == "mathdelimiter" || name == "mathmatrix") {
1963 if (!buf || buf->isReadonly())
1966 Cursor const & cur = currentBufferView()->cursor();
1967 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1970 else if (name == "latexlog")
1971 enable = FileName(doc_buffer->logName()).isReadableFile();
1972 else if (name == "spellchecker")
1973 enable = theSpellChecker()
1974 && !doc_buffer->isReadonly()
1975 && !doc_buffer->text().empty();
1976 else if (name == "vclog")
1977 enable = doc_buffer->lyxvc().inUse();
1981 case LFUN_DIALOG_UPDATE: {
1982 string const name = cmd.getArg(0);
1984 enable = name == "prefs";
1988 case LFUN_COMMAND_EXECUTE:
1990 case LFUN_MENU_OPEN:
1991 // Nothing to check.
1994 case LFUN_COMPLETION_INLINE:
1995 if (!d.current_work_area_
1996 || !d.current_work_area_->completer().inlinePossible(
1997 currentBufferView()->cursor()))
2001 case LFUN_COMPLETION_POPUP:
2002 if (!d.current_work_area_
2003 || !d.current_work_area_->completer().popupPossible(
2004 currentBufferView()->cursor()))
2009 if (!d.current_work_area_
2010 || !d.current_work_area_->completer().inlinePossible(
2011 currentBufferView()->cursor()))
2015 case LFUN_COMPLETION_ACCEPT:
2016 if (!d.current_work_area_
2017 || (!d.current_work_area_->completer().popupVisible()
2018 && !d.current_work_area_->completer().inlineVisible()
2019 && !d.current_work_area_->completer().completionAvailable()))
2023 case LFUN_COMPLETION_CANCEL:
2024 if (!d.current_work_area_
2025 || (!d.current_work_area_->completer().popupVisible()
2026 && !d.current_work_area_->completer().inlineVisible()))
2030 case LFUN_BUFFER_ZOOM_OUT:
2031 case LFUN_BUFFER_ZOOM_IN: {
2032 // only diff between these two is that the default for ZOOM_OUT
2034 bool const neg_zoom =
2035 convert<int>(cmd.argument()) < 0 ||
2036 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2037 if (lyxrc.zoom <= 10 && neg_zoom) {
2038 flag.message(_("Zoom level cannot be less than 10%."));
2041 enable = doc_buffer;
2044 case LFUN_BUFFER_MOVE_NEXT:
2045 case LFUN_BUFFER_MOVE_PREVIOUS:
2046 // we do not cycle when moving
2047 case LFUN_BUFFER_NEXT:
2048 case LFUN_BUFFER_PREVIOUS:
2049 // because we cycle, it doesn't matter whether on first or last
2050 enable = (d.currentTabWorkArea()->count() > 1);
2052 case LFUN_BUFFER_SWITCH:
2053 // toggle on the current buffer, but do not toggle off
2054 // the other ones (is that a good idea?)
2056 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2057 flag.setOnOff(true);
2060 case LFUN_VC_REGISTER:
2061 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2063 case LFUN_VC_RENAME:
2064 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2067 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2069 case LFUN_VC_CHECK_IN:
2070 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2072 case LFUN_VC_CHECK_OUT:
2073 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2075 case LFUN_VC_LOCKING_TOGGLE:
2076 enable = doc_buffer && !doc_buffer->isReadonly()
2077 && doc_buffer->lyxvc().lockingToggleEnabled();
2078 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2080 case LFUN_VC_REVERT:
2081 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2083 case LFUN_VC_UNDO_LAST:
2084 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2086 case LFUN_VC_REPO_UPDATE:
2087 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2089 case LFUN_VC_COMMAND: {
2090 if (cmd.argument().empty())
2092 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2096 case LFUN_VC_COMPARE:
2097 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2100 case LFUN_SERVER_GOTO_FILE_ROW:
2101 case LFUN_LYX_ACTIVATE:
2103 case LFUN_FORWARD_SEARCH:
2104 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2107 case LFUN_FILE_INSERT_PLAINTEXT:
2108 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2109 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2112 case LFUN_SPELLING_CONTINUOUSLY:
2113 flag.setOnOff(lyxrc.spellcheck_continuously);
2121 flag.setEnabled(false);
2127 static FileName selectTemplateFile()
2129 FileDialog dlg(qt_("Select template file"));
2130 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2131 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2133 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2134 QStringList(qt_("LyX Documents (*.lyx)")));
2136 if (result.first == FileDialog::Later)
2138 if (result.second.isEmpty())
2140 return FileName(fromqstr(result.second));
2144 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2148 Buffer * newBuffer = 0;
2150 newBuffer = checkAndLoadLyXFile(filename);
2151 } catch (ExceptionMessage const & e) {
2158 message(_("Document not loaded."));
2162 setBuffer(newBuffer);
2163 newBuffer->errors("Parse");
2166 theSession().lastFiles().add(filename);
2172 void GuiView::openDocument(string const & fname)
2174 string initpath = lyxrc.document_path;
2176 if (documentBufferView()) {
2177 string const trypath = documentBufferView()->buffer().filePath();
2178 // If directory is writeable, use this as default.
2179 if (FileName(trypath).isDirWritable())
2185 if (fname.empty()) {
2186 FileDialog dlg(qt_("Select document to open"));
2187 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2188 dlg.setButton2(qt_("Examples|#E#e"),
2189 toqstr(addPath(package().system_support().absFileName(), "examples")));
2191 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2192 FileDialog::Result result =
2193 dlg.open(toqstr(initpath), filter);
2195 if (result.first == FileDialog::Later)
2198 filename = fromqstr(result.second);
2200 // check selected filename
2201 if (filename.empty()) {
2202 message(_("Canceled."));
2208 // get absolute path of file and add ".lyx" to the filename if
2210 FileName const fullname =
2211 fileSearch(string(), filename, "lyx", support::may_not_exist);
2212 if (!fullname.empty())
2213 filename = fullname.absFileName();
2215 if (!fullname.onlyPath().isDirectory()) {
2216 Alert::warning(_("Invalid filename"),
2217 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2218 from_utf8(fullname.absFileName())));
2222 // if the file doesn't exist and isn't already open (bug 6645),
2223 // let the user create one
2224 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2225 !LyXVC::file_not_found_hook(fullname)) {
2226 // the user specifically chose this name. Believe him.
2227 Buffer * const b = newFile(filename, string(), true);
2233 docstring const disp_fn = makeDisplayPath(filename);
2234 message(bformat(_("Opening document %1$s..."), disp_fn));
2237 Buffer * buf = loadDocument(fullname);
2239 str2 = bformat(_("Document %1$s opened."), disp_fn);
2240 if (buf->lyxvc().inUse())
2241 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2242 " " + _("Version control detected.");
2244 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2249 // FIXME: clean that
2250 static bool import(GuiView * lv, FileName const & filename,
2251 string const & format, ErrorList & errorList)
2253 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2255 string loader_format;
2256 vector<string> loaders = theConverters().loaders();
2257 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2258 vector<string>::const_iterator it = loaders.begin();
2259 vector<string>::const_iterator en = loaders.end();
2260 for (; it != en; ++it) {
2261 if (!theConverters().isReachable(format, *it))
2264 string const tofile =
2265 support::changeExtension(filename.absFileName(),
2266 formats.extension(*it));
2267 if (!theConverters().convert(0, filename, FileName(tofile),
2268 filename, format, *it, errorList))
2270 loader_format = *it;
2273 if (loader_format.empty()) {
2274 frontend::Alert::error(_("Couldn't import file"),
2275 bformat(_("No information for importing the format %1$s."),
2276 formats.prettyName(format)));
2280 loader_format = format;
2282 if (loader_format == "lyx") {
2283 Buffer * buf = lv->loadDocument(lyxfile);
2287 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2291 bool as_paragraphs = loader_format == "textparagraph";
2292 string filename2 = (loader_format == format) ? filename.absFileName()
2293 : support::changeExtension(filename.absFileName(),
2294 formats.extension(loader_format));
2295 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2297 guiApp->setCurrentView(lv);
2298 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2305 void GuiView::importDocument(string const & argument)
2308 string filename = split(argument, format, ' ');
2310 LYXERR(Debug::INFO, format << " file: " << filename);
2312 // need user interaction
2313 if (filename.empty()) {
2314 string initpath = lyxrc.document_path;
2315 if (documentBufferView()) {
2316 string const trypath = documentBufferView()->buffer().filePath();
2317 // If directory is writeable, use this as default.
2318 if (FileName(trypath).isDirWritable())
2322 docstring const text = bformat(_("Select %1$s file to import"),
2323 formats.prettyName(format));
2325 FileDialog dlg(toqstr(text));
2326 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2327 dlg.setButton2(qt_("Examples|#E#e"),
2328 toqstr(addPath(package().system_support().absFileName(), "examples")));
2330 docstring filter = formats.prettyName(format);
2333 filter += from_utf8(formats.extensions(format));
2336 FileDialog::Result result =
2337 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2339 if (result.first == FileDialog::Later)
2342 filename = fromqstr(result.second);
2344 // check selected filename
2345 if (filename.empty())
2346 message(_("Canceled."));
2349 if (filename.empty())
2352 // get absolute path of file
2353 FileName const fullname(support::makeAbsPath(filename));
2355 // Can happen if the user entered a path into the dialog
2357 if (fullname.onlyFileName().empty()) {
2358 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2359 "Aborting import."),
2360 from_utf8(fullname.absFileName()));
2361 frontend::Alert::error(_("File name error"), msg);
2362 message(_("Canceled."));
2367 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2369 // Check if the document already is open
2370 Buffer * buf = theBufferList().getBuffer(lyxfile);
2373 if (!closeBuffer()) {
2374 message(_("Canceled."));
2379 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2381 // if the file exists already, and we didn't do
2382 // -i lyx thefile.lyx, warn
2383 if (lyxfile.exists() && fullname != lyxfile) {
2385 docstring text = bformat(_("The document %1$s already exists.\n\n"
2386 "Do you want to overwrite that document?"), displaypath);
2387 int const ret = Alert::prompt(_("Overwrite document?"),
2388 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2391 message(_("Canceled."));
2396 message(bformat(_("Importing %1$s..."), displaypath));
2397 ErrorList errorList;
2398 if (import(this, fullname, format, errorList))
2399 message(_("imported."));
2401 message(_("file not imported!"));
2403 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2407 void GuiView::newDocument(string const & filename, bool from_template)
2409 FileName initpath(lyxrc.document_path);
2410 if (documentBufferView()) {
2411 FileName const trypath(documentBufferView()->buffer().filePath());
2412 // If directory is writeable, use this as default.
2413 if (trypath.isDirWritable())
2417 string templatefile;
2418 if (from_template) {
2419 templatefile = selectTemplateFile().absFileName();
2420 if (templatefile.empty())
2425 if (filename.empty())
2426 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2428 b = newFile(filename, templatefile, true);
2433 // If no new document could be created, it is unsure
2434 // whether there is a valid BufferView.
2435 if (currentBufferView())
2436 // Ensure the cursor is correctly positioned on screen.
2437 currentBufferView()->showCursor();
2441 void GuiView::insertLyXFile(docstring const & fname)
2443 BufferView * bv = documentBufferView();
2448 FileName filename(to_utf8(fname));
2449 if (filename.empty()) {
2450 // Launch a file browser
2452 string initpath = lyxrc.document_path;
2453 string const trypath = bv->buffer().filePath();
2454 // If directory is writeable, use this as default.
2455 if (FileName(trypath).isDirWritable())
2459 FileDialog dlg(qt_("Select LyX document to insert"));
2460 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2461 dlg.setButton2(qt_("Examples|#E#e"),
2462 toqstr(addPath(package().system_support().absFileName(),
2465 FileDialog::Result result = dlg.open(toqstr(initpath),
2466 QStringList(qt_("LyX Documents (*.lyx)")));
2468 if (result.first == FileDialog::Later)
2472 filename.set(fromqstr(result.second));
2474 // check selected filename
2475 if (filename.empty()) {
2476 // emit message signal.
2477 message(_("Canceled."));
2482 bv->insertLyXFile(filename);
2483 bv->buffer().errors("Parse");
2487 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2489 FileName fname = b.fileName();
2490 FileName const oldname = fname;
2492 if (!newname.empty()) {
2494 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2496 // Switch to this Buffer.
2499 // No argument? Ask user through dialog.
2501 FileDialog dlg(qt_("Choose a filename to save document as"));
2502 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2503 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2505 if (!isLyXFileName(fname.absFileName()))
2506 fname.changeExtension(".lyx");
2508 FileDialog::Result result =
2509 dlg.save(toqstr(fname.onlyPath().absFileName()),
2510 QStringList(qt_("LyX Documents (*.lyx)")),
2511 toqstr(fname.onlyFileName()));
2513 if (result.first == FileDialog::Later)
2516 fname.set(fromqstr(result.second));
2521 if (!isLyXFileName(fname.absFileName()))
2522 fname.changeExtension(".lyx");
2525 // fname is now the new Buffer location.
2527 // if there is already a Buffer open with this name, we do not want
2528 // to have another one. (the second test makes sure we're not just
2529 // trying to overwrite ourselves, which is fine.)
2530 if (theBufferList().exists(fname) && fname != oldname
2531 && theBufferList().getBuffer(fname) != &b) {
2532 docstring const text =
2533 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2534 "Please close it before attempting to overwrite it.\n"
2535 "Do you want to choose a new filename?"),
2536 from_utf8(fname.absFileName()));
2537 int const ret = Alert::prompt(_("Chosen File Already Open"),
2538 text, 0, 1, _("&Rename"), _("&Cancel"));
2540 case 0: return renameBuffer(b, docstring(), kind);
2541 case 1: return false;
2546 bool const existsLocal = fname.exists();
2547 bool const existsInVC = LyXVC::fileInVC(fname);
2548 if (existsLocal || existsInVC) {
2549 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2550 if (kind != LV_WRITE_AS && existsInVC) {
2551 // renaming to a name that is already in VC
2553 docstring text = bformat(_("The document %1$s "
2554 "is already registered.\n\n"
2555 "Do you want to choose a new name?"),
2557 docstring const title = (kind == LV_VC_RENAME) ?
2558 _("Rename document?") : _("Copy document?");
2559 docstring const button = (kind == LV_VC_RENAME) ?
2560 _("&Rename") : _("&Copy");
2561 int const ret = Alert::prompt(title, text, 0, 1,
2562 button, _("&Cancel"));
2564 case 0: return renameBuffer(b, docstring(), kind);
2565 case 1: return false;
2570 docstring text = bformat(_("The document %1$s "
2571 "already exists.\n\n"
2572 "Do you want to overwrite that document?"),
2574 int const ret = Alert::prompt(_("Overwrite document?"),
2575 text, 0, 2, _("&Overwrite"),
2576 _("&Rename"), _("&Cancel"));
2579 case 1: return renameBuffer(b, docstring(), kind);
2580 case 2: return false;
2586 case LV_VC_RENAME: {
2587 string msg = b.lyxvc().rename(fname);
2590 message(from_utf8(msg));
2594 string msg = b.lyxvc().copy(fname);
2597 message(from_utf8(msg));
2603 // LyXVC created the file already in case of LV_VC_RENAME or
2604 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2605 // relative paths of included stuff right if we moved e.g. from
2606 // /a/b.lyx to /a/c/b.lyx.
2608 bool const saved = saveBuffer(b, fname);
2615 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2617 FileName fname = b.fileName();
2619 FileDialog dlg(qt_("Choose a filename to export the document as"));
2620 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2623 QString const anyformat = qt_("Guess from extension (*.*)");
2626 vector<Format const *> export_formats;
2627 for (Format const & f : formats)
2628 if (f.documentFormat())
2629 export_formats.push_back(&f);
2630 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2631 map<QString, string> fmap;
2634 for (Format const * f : export_formats) {
2635 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2636 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2638 from_ascii(f->extension())));
2639 types << loc_filter;
2640 fmap[loc_filter] = f->name();
2641 if (from_ascii(f->name()) == iformat) {
2642 filter = loc_filter;
2643 ext = f->extension();
2646 string ofname = fname.onlyFileName();
2648 ofname = support::changeExtension(ofname, ext);
2649 FileDialog::Result result =
2650 dlg.save(toqstr(fname.onlyPath().absFileName()),
2654 if (result.first != FileDialog::Chosen)
2658 fname.set(fromqstr(result.second));
2659 if (filter == anyformat)
2660 fmt_name = formats.getFormatFromExtension(fname.extension());
2662 fmt_name = fmap[filter];
2663 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2664 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2666 if (fmt_name.empty() || fname.empty())
2669 // fname is now the new Buffer location.
2670 if (FileName(fname).exists()) {
2671 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2672 docstring text = bformat(_("The document %1$s already "
2673 "exists.\n\nDo you want to "
2674 "overwrite that document?"),
2676 int const ret = Alert::prompt(_("Overwrite document?"),
2677 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2680 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2681 case 2: return false;
2685 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2688 return dr.dispatched();
2692 bool GuiView::saveBuffer(Buffer & b)
2694 return saveBuffer(b, FileName());
2698 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2700 if (workArea(b) && workArea(b)->inDialogMode())
2703 if (fn.empty() && b.isUnnamed())
2704 return renameBuffer(b, docstring());
2706 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2708 theSession().lastFiles().add(b.fileName());
2712 // Switch to this Buffer.
2715 // FIXME: we don't tell the user *WHY* the save failed !!
2716 docstring const file = makeDisplayPath(b.absFileName(), 30);
2717 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2718 "Do you want to rename the document and "
2719 "try again?"), file);
2720 int const ret = Alert::prompt(_("Rename and save?"),
2721 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2724 if (!renameBuffer(b, docstring()))
2733 return saveBuffer(b, fn);
2737 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2739 return closeWorkArea(wa, false);
2743 // We only want to close the buffer if it is not visible in other workareas
2744 // of the same view, nor in other views, and if this is not a child
2745 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2747 Buffer & buf = wa->bufferView().buffer();
2749 bool last_wa = d.countWorkAreasOf(buf) == 1
2750 && !inOtherView(buf) && !buf.parent();
2752 bool close_buffer = last_wa;
2755 if (lyxrc.close_buffer_with_last_view == "yes")
2757 else if (lyxrc.close_buffer_with_last_view == "no")
2758 close_buffer = false;
2761 if (buf.isUnnamed())
2762 file = from_utf8(buf.fileName().onlyFileName());
2764 file = buf.fileName().displayName(30);
2765 docstring const text = bformat(
2766 _("Last view on document %1$s is being closed.\n"
2767 "Would you like to close or hide the document?\n"
2769 "Hidden documents can be displayed back through\n"
2770 "the menu: View->Hidden->...\n"
2772 "To remove this question, set your preference in:\n"
2773 " Tools->Preferences->Look&Feel->UserInterface\n"
2775 int ret = Alert::prompt(_("Close or hide document?"),
2776 text, 0, 1, _("&Close"), _("&Hide"));
2777 close_buffer = (ret == 0);
2781 return closeWorkArea(wa, close_buffer);
2785 bool GuiView::closeBuffer()
2787 GuiWorkArea * wa = currentMainWorkArea();
2788 // coverity complained about this
2789 // it seems unnecessary, but perhaps is worth the check
2790 LASSERT(wa, return false);
2792 setCurrentWorkArea(wa);
2793 Buffer & buf = wa->bufferView().buffer();
2794 return closeWorkArea(wa, !buf.parent());
2798 void GuiView::writeSession() const {
2799 GuiWorkArea const * active_wa = currentMainWorkArea();
2800 for (int i = 0; i < d.splitter_->count(); ++i) {
2801 TabWorkArea * twa = d.tabWorkArea(i);
2802 for (int j = 0; j < twa->count(); ++j) {
2803 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2804 Buffer & buf = wa->bufferView().buffer();
2805 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2811 bool GuiView::closeBufferAll()
2813 // Close the workareas in all other views
2814 QList<int> const ids = guiApp->viewIds();
2815 for (int i = 0; i != ids.size(); ++i) {
2816 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2820 // Close our own workareas
2821 if (!closeWorkAreaAll())
2824 // Now close the hidden buffers. We prevent hidden buffers from being
2825 // dirty, so we can just close them.
2826 theBufferList().closeAll();
2831 bool GuiView::closeWorkAreaAll()
2833 setCurrentWorkArea(currentMainWorkArea());
2835 // We might be in a situation that there is still a tabWorkArea, but
2836 // there are no tabs anymore. This can happen when we get here after a
2837 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2838 // many TabWorkArea's have no documents anymore.
2841 // We have to call count() each time, because it can happen that
2842 // more than one splitter will disappear in one iteration (bug 5998).
2843 while (d.splitter_->count() > empty_twa) {
2844 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2846 if (twa->count() == 0)
2849 setCurrentWorkArea(twa->currentWorkArea());
2850 if (!closeTabWorkArea(twa))
2858 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2863 Buffer & buf = wa->bufferView().buffer();
2865 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2866 Alert::warning(_("Close document"),
2867 _("Document could not be closed because it is being processed by LyX."));
2872 return closeBuffer(buf);
2874 if (!inMultiTabs(wa))
2875 if (!saveBufferIfNeeded(buf, true))
2883 bool GuiView::closeBuffer(Buffer & buf)
2885 // If we are in a close_event all children will be closed in some time,
2886 // so no need to do it here. This will ensure that the children end up
2887 // in the session file in the correct order. If we close the master
2888 // buffer, we can close or release the child buffers here too.
2889 bool success = true;
2891 ListOfBuffers clist = buf.getChildren();
2892 ListOfBuffers::const_iterator it = clist.begin();
2893 ListOfBuffers::const_iterator const bend = clist.end();
2894 for (; it != bend; ++it) {
2895 Buffer * child_buf = *it;
2896 if (theBufferList().isOthersChild(&buf, child_buf)) {
2897 child_buf->setParent(0);
2901 // FIXME: should we look in other tabworkareas?
2902 // ANSWER: I don't think so. I've tested, and if the child is
2903 // open in some other window, it closes without a problem.
2904 GuiWorkArea * child_wa = workArea(*child_buf);
2906 success = closeWorkArea(child_wa, true);
2910 // In this case the child buffer is open but hidden.
2911 // It therefore should not (MUST NOT) be dirty!
2912 LATTEST(child_buf->isClean());
2913 theBufferList().release(child_buf);
2918 // goto bookmark to update bookmark pit.
2919 // FIXME: we should update only the bookmarks related to this buffer!
2920 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2921 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2922 guiApp->gotoBookmark(i+1, false, false);
2924 if (saveBufferIfNeeded(buf, false)) {
2925 buf.removeAutosaveFile();
2926 theBufferList().release(&buf);
2930 // open all children again to avoid a crash because of dangling
2931 // pointers (bug 6603)
2937 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2939 while (twa == d.currentTabWorkArea()) {
2940 twa->setCurrentIndex(twa->count() - 1);
2942 GuiWorkArea * wa = twa->currentWorkArea();
2943 Buffer & b = wa->bufferView().buffer();
2945 // We only want to close the buffer if the same buffer is not visible
2946 // in another view, and if this is not a child and if we are closing
2947 // a view (not a tabgroup).
2948 bool const close_buffer =
2949 !inOtherView(b) && !b.parent() && closing_;
2951 if (!closeWorkArea(wa, close_buffer))
2958 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2960 if (buf.isClean() || buf.paragraphs().empty())
2963 // Switch to this Buffer.
2968 if (buf.isUnnamed())
2969 file = from_utf8(buf.fileName().onlyFileName());
2971 file = buf.fileName().displayName(30);
2973 // Bring this window to top before asking questions.
2978 if (hiding && buf.isUnnamed()) {
2979 docstring const text = bformat(_("The document %1$s has not been "
2980 "saved yet.\n\nDo you want to save "
2981 "the document?"), file);
2982 ret = Alert::prompt(_("Save new document?"),
2983 text, 0, 1, _("&Save"), _("&Cancel"));
2987 docstring const text = bformat(_("The document %1$s has unsaved changes."
2988 "\n\nDo you want to save the document or discard the changes?"), file);
2989 ret = Alert::prompt(_("Save changed document?"),
2990 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2995 if (!saveBuffer(buf))
2999 // If we crash after this we could have no autosave file
3000 // but I guess this is really improbable (Jug).
3001 // Sometimes improbable things happen:
3002 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3003 // buf.removeAutosaveFile();
3005 // revert all changes
3016 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3018 Buffer & buf = wa->bufferView().buffer();
3020 for (int i = 0; i != d.splitter_->count(); ++i) {
3021 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3022 if (wa_ && wa_ != wa)
3025 return inOtherView(buf);
3029 bool GuiView::inOtherView(Buffer & buf)
3031 QList<int> const ids = guiApp->viewIds();
3033 for (int i = 0; i != ids.size(); ++i) {
3037 if (guiApp->view(ids[i]).workArea(buf))
3044 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3046 if (!documentBufferView())
3049 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3050 Buffer * const curbuf = &documentBufferView()->buffer();
3051 int nwa = twa->count();
3052 for (int i = 0; i < nwa; ++i) {
3053 if (&workArea(i)->bufferView().buffer() == curbuf) {
3055 if (np == NEXTBUFFER)
3056 next_index = (i == nwa - 1 ? 0 : i + 1);
3058 next_index = (i == 0 ? nwa - 1 : i - 1);
3060 twa->moveTab(i, next_index);
3062 setBuffer(&workArea(next_index)->bufferView().buffer());
3070 /// make sure the document is saved
3071 static bool ensureBufferClean(Buffer * buffer)
3073 LASSERT(buffer, return false);
3074 if (buffer->isClean() && !buffer->isUnnamed())
3077 docstring const file = buffer->fileName().displayName(30);
3080 if (!buffer->isUnnamed()) {
3081 text = bformat(_("The document %1$s has unsaved "
3082 "changes.\n\nDo you want to save "
3083 "the document?"), file);
3084 title = _("Save changed document?");
3087 text = bformat(_("The document %1$s has not been "
3088 "saved yet.\n\nDo you want to save "
3089 "the document?"), file);
3090 title = _("Save new document?");
3092 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3095 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3097 return buffer->isClean() && !buffer->isUnnamed();
3101 bool GuiView::reloadBuffer(Buffer & buf)
3103 Buffer::ReadStatus status = buf.reload();
3104 return status == Buffer::ReadSuccess;
3108 void GuiView::checkExternallyModifiedBuffers()
3110 BufferList::iterator bit = theBufferList().begin();
3111 BufferList::iterator const bend = theBufferList().end();
3112 for (; bit != bend; ++bit) {
3113 Buffer * buf = *bit;
3114 if (buf->fileName().exists()
3115 && buf->isExternallyModified(Buffer::checksum_method)) {
3116 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3117 " Reload now? Any local changes will be lost."),
3118 from_utf8(buf->absFileName()));
3119 int const ret = Alert::prompt(_("Reload externally changed document?"),
3120 text, 0, 1, _("&Reload"), _("&Cancel"));
3128 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3130 Buffer * buffer = documentBufferView()
3131 ? &(documentBufferView()->buffer()) : 0;
3133 switch (cmd.action()) {
3134 case LFUN_VC_REGISTER:
3135 if (!buffer || !ensureBufferClean(buffer))
3137 if (!buffer->lyxvc().inUse()) {
3138 if (buffer->lyxvc().registrer()) {
3139 reloadBuffer(*buffer);
3140 dr.clearMessageUpdate();
3145 case LFUN_VC_RENAME:
3146 case LFUN_VC_COPY: {
3147 if (!buffer || !ensureBufferClean(buffer))
3149 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3150 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3151 // Some changes are not yet committed.
3152 // We test here and not in getStatus(), since
3153 // this test is expensive.
3155 LyXVC::CommandResult ret =
3156 buffer->lyxvc().checkIn(log);
3158 if (ret == LyXVC::ErrorCommand ||
3159 ret == LyXVC::VCSuccess)
3160 reloadBuffer(*buffer);
3161 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3162 frontend::Alert::error(
3163 _("Revision control error."),
3164 _("Document could not be checked in."));
3168 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3169 LV_VC_RENAME : LV_VC_COPY;
3170 renameBuffer(*buffer, cmd.argument(), kind);
3175 case LFUN_VC_CHECK_IN:
3176 if (!buffer || !ensureBufferClean(buffer))
3178 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3180 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3182 // Only skip reloading if the checkin was cancelled or
3183 // an error occurred before the real checkin VCS command
3184 // was executed, since the VCS might have changed the
3185 // file even if it could not checkin successfully.
3186 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3187 reloadBuffer(*buffer);
3191 case LFUN_VC_CHECK_OUT:
3192 if (!buffer || !ensureBufferClean(buffer))
3194 if (buffer->lyxvc().inUse()) {
3195 dr.setMessage(buffer->lyxvc().checkOut());
3196 reloadBuffer(*buffer);
3200 case LFUN_VC_LOCKING_TOGGLE:
3201 LASSERT(buffer, return);
3202 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3204 if (buffer->lyxvc().inUse()) {
3205 string res = buffer->lyxvc().lockingToggle();
3207 frontend::Alert::error(_("Revision control error."),
3208 _("Error when setting the locking property."));
3211 reloadBuffer(*buffer);
3216 case LFUN_VC_REVERT:
3217 LASSERT(buffer, return);
3218 if (buffer->lyxvc().revert()) {
3219 reloadBuffer(*buffer);
3220 dr.clearMessageUpdate();
3224 case LFUN_VC_UNDO_LAST:
3225 LASSERT(buffer, return);
3226 buffer->lyxvc().undoLast();
3227 reloadBuffer(*buffer);
3228 dr.clearMessageUpdate();
3231 case LFUN_VC_REPO_UPDATE:
3232 LASSERT(buffer, return);
3233 if (ensureBufferClean(buffer)) {
3234 dr.setMessage(buffer->lyxvc().repoUpdate());
3235 checkExternallyModifiedBuffers();
3239 case LFUN_VC_COMMAND: {
3240 string flag = cmd.getArg(0);
3241 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3244 if (contains(flag, 'M')) {
3245 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3248 string path = cmd.getArg(1);
3249 if (contains(path, "$$p") && buffer)
3250 path = subst(path, "$$p", buffer->filePath());
3251 LYXERR(Debug::LYXVC, "Directory: " << path);
3253 if (!pp.isReadableDirectory()) {
3254 lyxerr << _("Directory is not accessible.") << endl;
3257 support::PathChanger p(pp);
3259 string command = cmd.getArg(2);
3260 if (command.empty())
3263 command = subst(command, "$$i", buffer->absFileName());
3264 command = subst(command, "$$p", buffer->filePath());
3266 command = subst(command, "$$m", to_utf8(message));
3267 LYXERR(Debug::LYXVC, "Command: " << command);
3269 one.startscript(Systemcall::Wait, command);
3273 if (contains(flag, 'I'))
3274 buffer->markDirty();
3275 if (contains(flag, 'R'))
3276 reloadBuffer(*buffer);
3281 case LFUN_VC_COMPARE: {
3282 if (cmd.argument().empty()) {
3283 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3287 string rev1 = cmd.getArg(0);
3291 // it seems safe to assume we have a buffer
3292 // coverity[FORWARD_NULL]
3293 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3296 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3297 f2 = buffer->absFileName();
3299 string rev2 = cmd.getArg(1);
3303 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3307 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3308 f1 << "\n" << f2 << "\n" );
3309 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3310 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3320 void GuiView::openChildDocument(string const & fname)
3322 LASSERT(documentBufferView(), return);
3323 Buffer & buffer = documentBufferView()->buffer();
3324 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3325 documentBufferView()->saveBookmark(false);
3327 if (theBufferList().exists(filename)) {
3328 child = theBufferList().getBuffer(filename);
3331 message(bformat(_("Opening child document %1$s..."),
3332 makeDisplayPath(filename.absFileName())));
3333 child = loadDocument(filename, false);
3335 // Set the parent name of the child document.
3336 // This makes insertion of citations and references in the child work,
3337 // when the target is in the parent or another child document.
3339 child->setParent(&buffer);
3343 bool GuiView::goToFileRow(string const & argument)
3347 size_t i = argument.find_last_of(' ');
3348 if (i != string::npos) {
3349 file_name = os::internal_path(trim(argument.substr(0, i)));
3350 istringstream is(argument.substr(i + 1));
3355 if (i == string::npos) {
3356 LYXERR0("Wrong argument: " << argument);
3360 string const abstmp = package().temp_dir().absFileName();
3361 string const realtmp = package().temp_dir().realPath();
3362 // We have to use os::path_prefix_is() here, instead of
3363 // simply prefixIs(), because the file name comes from
3364 // an external application and may need case adjustment.
3365 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3366 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3367 // Needed by inverse dvi search. If it is a file
3368 // in tmpdir, call the apropriated function.
3369 // If tmpdir is a symlink, we may have the real
3370 // path passed back, so we correct for that.
3371 if (!prefixIs(file_name, abstmp))
3372 file_name = subst(file_name, realtmp, abstmp);
3373 buf = theBufferList().getBufferFromTmp(file_name);
3375 // Must replace extension of the file to be .lyx
3376 // and get full path
3377 FileName const s = fileSearch(string(),
3378 support::changeExtension(file_name, ".lyx"), "lyx");
3379 // Either change buffer or load the file
3380 if (theBufferList().exists(s))
3381 buf = theBufferList().getBuffer(s);
3382 else if (s.exists()) {
3383 buf = loadDocument(s);
3388 _("File does not exist: %1$s"),
3389 makeDisplayPath(file_name)));
3395 _("No buffer for file: %1$s."),
3396 makeDisplayPath(file_name))
3401 documentBufferView()->setCursorFromRow(row);
3407 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3409 Buffer::ExportStatus const status = func(format);
3411 // the cloning operation will have produced a clone of the entire set of
3412 // documents, starting from the master. so we must delete those.
3413 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3415 busyBuffers.remove(orig);
3420 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3422 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3423 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3427 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3429 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3430 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3434 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3436 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3437 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3441 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3442 string const & argument,
3443 Buffer const * used_buffer,
3444 docstring const & msg,
3445 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3446 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3447 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3452 string format = argument;
3454 format = used_buffer->params().getDefaultOutputFormat();
3455 processing_format = format;
3457 progress_->clearMessages();
3460 #if EXPORT_in_THREAD
3461 GuiViewPrivate::busyBuffers.insert(used_buffer);
3462 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3463 if (!cloned_buffer) {
3464 Alert::error(_("Export Error"),
3465 _("Error cloning the Buffer."));
3468 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3473 setPreviewFuture(f);
3474 last_export_format = used_buffer->params().bufferFormat();
3477 // We are asynchronous, so we don't know here anything about the success
3480 Buffer::ExportStatus status;
3482 status = (used_buffer->*syncFunc)(format, true);
3483 } else if (previewFunc) {
3484 status = (used_buffer->*previewFunc)(format);
3487 handleExportStatus(gv_, status, format);
3489 return (status == Buffer::ExportSuccess
3490 || status == Buffer::PreviewSuccess);
3494 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3496 BufferView * bv = currentBufferView();
3497 LASSERT(bv, return);
3499 // Let the current BufferView dispatch its own actions.
3500 bv->dispatch(cmd, dr);
3501 if (dr.dispatched())
3504 // Try with the document BufferView dispatch if any.
3505 BufferView * doc_bv = documentBufferView();
3506 if (doc_bv && doc_bv != bv) {
3507 doc_bv->dispatch(cmd, dr);
3508 if (dr.dispatched())
3512 // Then let the current Cursor dispatch its own actions.
3513 bv->cursor().dispatch(cmd);
3515 // update completion. We do it here and not in
3516 // processKeySym to avoid another redraw just for a
3517 // changed inline completion
3518 if (cmd.origin() == FuncRequest::KEYBOARD) {
3519 if (cmd.action() == LFUN_SELF_INSERT
3520 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3521 updateCompletion(bv->cursor(), true, true);
3522 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3523 updateCompletion(bv->cursor(), false, true);
3525 updateCompletion(bv->cursor(), false, false);
3528 dr = bv->cursor().result();
3532 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3534 BufferView * bv = currentBufferView();
3535 // By default we won't need any update.
3536 dr.screenUpdate(Update::None);
3537 // assume cmd will be dispatched
3538 dr.dispatched(true);
3540 Buffer * doc_buffer = documentBufferView()
3541 ? &(documentBufferView()->buffer()) : 0;
3543 if (cmd.origin() == FuncRequest::TOC) {
3544 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3545 // FIXME: do we need to pass a DispatchResult object here?
3546 toc->doDispatch(bv->cursor(), cmd);
3550 string const argument = to_utf8(cmd.argument());
3552 switch(cmd.action()) {
3553 case LFUN_BUFFER_CHILD_OPEN:
3554 openChildDocument(to_utf8(cmd.argument()));
3557 case LFUN_BUFFER_IMPORT:
3558 importDocument(to_utf8(cmd.argument()));
3561 case LFUN_BUFFER_EXPORT: {
3564 // GCC only sees strfwd.h when building merged
3565 if (::lyx::operator==(cmd.argument(), "custom")) {
3566 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3570 string const dest = cmd.getArg(1);
3571 FileName target_dir;
3572 if (!dest.empty() && FileName::isAbsolute(dest))
3573 target_dir = FileName(support::onlyPath(dest));
3575 target_dir = doc_buffer->fileName().onlyPath();
3577 if ((dest.empty() && doc_buffer->isUnnamed())
3578 || !target_dir.isDirWritable()) {
3579 exportBufferAs(*doc_buffer, cmd.argument());
3582 /* TODO/Review: Is it a problem to also export the children?
3583 See the update_unincluded flag */
3584 d.asyncBufferProcessing(argument,
3587 &GuiViewPrivate::exportAndDestroy,
3590 // TODO Inform user about success
3594 case LFUN_BUFFER_EXPORT_AS: {
3595 LASSERT(doc_buffer, break);
3596 docstring f = cmd.argument();
3598 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3599 exportBufferAs(*doc_buffer, f);
3603 case LFUN_BUFFER_UPDATE: {
3604 d.asyncBufferProcessing(argument,
3607 &GuiViewPrivate::compileAndDestroy,
3612 case LFUN_BUFFER_VIEW: {
3613 d.asyncBufferProcessing(argument,
3615 _("Previewing ..."),
3616 &GuiViewPrivate::previewAndDestroy,
3621 case LFUN_MASTER_BUFFER_UPDATE: {
3622 d.asyncBufferProcessing(argument,
3623 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3625 &GuiViewPrivate::compileAndDestroy,
3630 case LFUN_MASTER_BUFFER_VIEW: {
3631 d.asyncBufferProcessing(argument,
3632 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3634 &GuiViewPrivate::previewAndDestroy,
3635 0, &Buffer::preview);
3638 case LFUN_BUFFER_SWITCH: {
3639 string const file_name = to_utf8(cmd.argument());
3640 if (!FileName::isAbsolute(file_name)) {
3642 dr.setMessage(_("Absolute filename expected."));
3646 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3649 dr.setMessage(_("Document not loaded"));
3653 // Do we open or switch to the buffer in this view ?
3654 if (workArea(*buffer)
3655 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3660 // Look for the buffer in other views
3661 QList<int> const ids = guiApp->viewIds();
3663 for (; i != ids.size(); ++i) {
3664 GuiView & gv = guiApp->view(ids[i]);
3665 if (gv.workArea(*buffer)) {
3667 gv.activateWindow();
3669 gv.setBuffer(buffer);
3674 // If necessary, open a new window as a last resort
3675 if (i == ids.size()) {
3676 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3682 case LFUN_BUFFER_NEXT:
3683 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3686 case LFUN_BUFFER_MOVE_NEXT:
3687 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3690 case LFUN_BUFFER_PREVIOUS:
3691 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3694 case LFUN_BUFFER_MOVE_PREVIOUS:
3695 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3698 case LFUN_COMMAND_EXECUTE: {
3699 command_execute_ = true;
3700 minibuffer_focus_ = true;
3703 case LFUN_DROP_LAYOUTS_CHOICE:
3704 d.layout_->showPopup();
3707 case LFUN_MENU_OPEN:
3708 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3709 menu->exec(QCursor::pos());
3712 case LFUN_FILE_INSERT:
3713 insertLyXFile(cmd.argument());
3716 case LFUN_FILE_INSERT_PLAINTEXT:
3717 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3718 string const fname = to_utf8(cmd.argument());
3719 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3720 dr.setMessage(_("Absolute filename expected."));
3724 FileName filename(fname);
3725 if (fname.empty()) {
3726 FileDialog dlg(qt_("Select file to insert"));
3728 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3729 QStringList(qt_("All Files (*)")));
3731 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3732 dr.setMessage(_("Canceled."));
3736 filename.set(fromqstr(result.second));
3740 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3741 bv->dispatch(new_cmd, dr);
3746 case LFUN_BUFFER_RELOAD: {
3747 LASSERT(doc_buffer, break);
3750 if (!doc_buffer->isClean()) {
3751 docstring const file =
3752 makeDisplayPath(doc_buffer->absFileName(), 20);
3753 docstring text = bformat(_("Any changes will be lost. "
3754 "Are you sure you want to revert to the saved version "
3755 "of the document %1$s?"), file);
3756 ret = Alert::prompt(_("Revert to saved document?"),
3757 text, 1, 1, _("&Revert"), _("&Cancel"));
3761 doc_buffer->markClean();
3762 reloadBuffer(*doc_buffer);
3763 dr.forceBufferUpdate();
3768 case LFUN_BUFFER_WRITE:
3769 LASSERT(doc_buffer, break);
3770 saveBuffer(*doc_buffer);
3773 case LFUN_BUFFER_WRITE_AS:
3774 LASSERT(doc_buffer, break);
3775 renameBuffer(*doc_buffer, cmd.argument());
3778 case LFUN_BUFFER_WRITE_ALL: {
3779 Buffer * first = theBufferList().first();
3782 message(_("Saving all documents..."));
3783 // We cannot use a for loop as the buffer list cycles.
3786 if (!b->isClean()) {
3788 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3790 b = theBufferList().next(b);
3791 } while (b != first);
3792 dr.setMessage(_("All documents saved."));
3796 case LFUN_BUFFER_CLOSE:
3800 case LFUN_BUFFER_CLOSE_ALL:
3804 case LFUN_TOOLBAR_TOGGLE: {
3805 string const name = cmd.getArg(0);
3806 if (GuiToolbar * t = toolbar(name))
3811 case LFUN_DIALOG_UPDATE: {
3812 string const name = to_utf8(cmd.argument());
3813 if (name == "prefs" || name == "document")
3814 updateDialog(name, string());
3815 else if (name == "paragraph")
3816 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3817 else if (currentBufferView()) {
3818 Inset * inset = currentBufferView()->editedInset(name);
3819 // Can only update a dialog connected to an existing inset
3821 // FIXME: get rid of this indirection; GuiView ask the inset
3822 // if he is kind enough to update itself...
3823 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3824 //FIXME: pass DispatchResult here?
3825 inset->dispatch(currentBufferView()->cursor(), fr);
3831 case LFUN_DIALOG_TOGGLE: {
3832 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3833 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3834 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3838 case LFUN_DIALOG_DISCONNECT_INSET:
3839 disconnectDialog(to_utf8(cmd.argument()));
3842 case LFUN_DIALOG_HIDE: {
3843 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3847 case LFUN_DIALOG_SHOW: {
3848 string const name = cmd.getArg(0);
3849 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3851 if (name == "character") {
3852 data = freefont2string();
3854 showDialog("character", data);
3855 } else if (name == "latexlog") {
3856 Buffer::LogType type;
3857 string const logfile = doc_buffer->logName(&type);
3859 case Buffer::latexlog:
3862 case Buffer::buildlog:
3866 data += Lexer::quoteString(logfile);
3867 showDialog("log", data);
3868 } else if (name == "vclog") {
3869 string const data = "vc " +
3870 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3871 showDialog("log", data);
3872 } else if (name == "symbols") {
3873 data = bv->cursor().getEncoding()->name();
3875 showDialog("symbols", data);
3877 } else if (name == "prefs" && isFullScreen()) {
3878 lfunUiToggle("fullscreen");
3879 showDialog("prefs", data);
3881 showDialog(name, data);
3886 dr.setMessage(cmd.argument());
3889 case LFUN_UI_TOGGLE: {
3890 string arg = cmd.getArg(0);
3891 if (!lfunUiToggle(arg)) {
3892 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3893 dr.setMessage(bformat(msg, from_utf8(arg)));
3895 // Make sure the keyboard focus stays in the work area.
3900 case LFUN_VIEW_SPLIT: {
3901 LASSERT(doc_buffer, break);
3902 string const orientation = cmd.getArg(0);
3903 d.splitter_->setOrientation(orientation == "vertical"
3904 ? Qt::Vertical : Qt::Horizontal);
3905 TabWorkArea * twa = addTabWorkArea();
3906 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3907 setCurrentWorkArea(wa);
3910 case LFUN_TAB_GROUP_CLOSE:
3911 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3912 closeTabWorkArea(twa);
3913 d.current_work_area_ = 0;
3914 twa = d.currentTabWorkArea();
3915 // Switch to the next GuiWorkArea in the found TabWorkArea.
3917 // Make sure the work area is up to date.
3918 setCurrentWorkArea(twa->currentWorkArea());
3920 setCurrentWorkArea(0);
3925 case LFUN_VIEW_CLOSE:
3926 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3927 closeWorkArea(twa->currentWorkArea());
3928 d.current_work_area_ = 0;
3929 twa = d.currentTabWorkArea();
3930 // Switch to the next GuiWorkArea in the found TabWorkArea.
3932 // Make sure the work area is up to date.
3933 setCurrentWorkArea(twa->currentWorkArea());
3935 setCurrentWorkArea(0);
3940 case LFUN_COMPLETION_INLINE:
3941 if (d.current_work_area_)
3942 d.current_work_area_->completer().showInline();
3945 case LFUN_COMPLETION_POPUP:
3946 if (d.current_work_area_)
3947 d.current_work_area_->completer().showPopup();
3952 if (d.current_work_area_)
3953 d.current_work_area_->completer().tab();
3956 case LFUN_COMPLETION_CANCEL:
3957 if (d.current_work_area_) {
3958 if (d.current_work_area_->completer().popupVisible())
3959 d.current_work_area_->completer().hidePopup();
3961 d.current_work_area_->completer().hideInline();
3965 case LFUN_COMPLETION_ACCEPT:
3966 if (d.current_work_area_)
3967 d.current_work_area_->completer().activate();
3970 case LFUN_BUFFER_ZOOM_IN:
3971 case LFUN_BUFFER_ZOOM_OUT: {
3972 // use a signed temp to avoid overflow
3973 int zoom = lyxrc.zoom;
3974 if (cmd.argument().empty()) {
3975 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3980 zoom += convert<int>(cmd.argument());
3986 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3988 // The global QPixmapCache is used in GuiPainter to cache text
3989 // painting so we must reset it.
3990 QPixmapCache::clear();
3991 guiApp->fontLoader().update();
3992 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3996 case LFUN_VC_REGISTER:
3997 case LFUN_VC_RENAME:
3999 case LFUN_VC_CHECK_IN:
4000 case LFUN_VC_CHECK_OUT:
4001 case LFUN_VC_REPO_UPDATE:
4002 case LFUN_VC_LOCKING_TOGGLE:
4003 case LFUN_VC_REVERT:
4004 case LFUN_VC_UNDO_LAST:
4005 case LFUN_VC_COMMAND:
4006 case LFUN_VC_COMPARE:
4007 dispatchVC(cmd, dr);
4010 case LFUN_SERVER_GOTO_FILE_ROW:
4011 goToFileRow(to_utf8(cmd.argument()));
4014 case LFUN_LYX_ACTIVATE:
4018 case LFUN_FORWARD_SEARCH: {
4019 // it seems safe to assume we have a document buffer, since
4020 // getStatus wants one.
4021 // coverity[FORWARD_NULL]
4022 Buffer const * doc_master = doc_buffer->masterBuffer();
4023 FileName const path(doc_master->temppath());
4024 string const texname = doc_master->isChild(doc_buffer)
4025 ? DocFileName(changeExtension(
4026 doc_buffer->absFileName(),
4027 "tex")).mangledFileName()
4028 : doc_buffer->latexName();
4029 string const fulltexname =
4030 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4031 string const mastername =
4032 removeExtension(doc_master->latexName());
4033 FileName const dviname(addName(path.absFileName(),
4034 addExtension(mastername, "dvi")));
4035 FileName const pdfname(addName(path.absFileName(),
4036 addExtension(mastername, "pdf")));
4037 bool const have_dvi = dviname.exists();
4038 bool const have_pdf = pdfname.exists();
4039 if (!have_dvi && !have_pdf) {
4040 dr.setMessage(_("Please, preview the document first."));
4043 string outname = dviname.onlyFileName();
4044 string command = lyxrc.forward_search_dvi;
4045 if (!have_dvi || (have_pdf &&
4046 pdfname.lastModified() > dviname.lastModified())) {
4047 outname = pdfname.onlyFileName();
4048 command = lyxrc.forward_search_pdf;
4051 DocIterator cur = bv->cursor();
4052 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4053 LYXERR(Debug::ACTION, "Forward search: row:" << row
4055 if (row == -1 || command.empty()) {
4056 dr.setMessage(_("Couldn't proceed."));
4059 string texrow = convert<string>(row);
4061 command = subst(command, "$$n", texrow);
4062 command = subst(command, "$$f", fulltexname);
4063 command = subst(command, "$$t", texname);
4064 command = subst(command, "$$o", outname);
4066 PathChanger p(path);
4068 one.startscript(Systemcall::DontWait, command);
4072 case LFUN_SPELLING_CONTINUOUSLY:
4073 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4074 dr.screenUpdate(Update::Force | Update::FitCursor);
4078 // The LFUN must be for one of BufferView, Buffer or Cursor;
4080 dispatchToBufferView(cmd, dr);
4084 // Part of automatic menu appearance feature.
4085 if (isFullScreen()) {
4086 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4090 // Need to update bv because many LFUNs here might have destroyed it
4091 bv = currentBufferView();
4093 // Clear non-empty selections
4094 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4096 Cursor & cur = bv->cursor();
4097 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4098 cur.clearSelection();
4104 bool GuiView::lfunUiToggle(string const & ui_component)
4106 if (ui_component == "scrollbar") {
4107 // hide() is of no help
4108 if (d.current_work_area_->verticalScrollBarPolicy() ==
4109 Qt::ScrollBarAlwaysOff)
4111 d.current_work_area_->setVerticalScrollBarPolicy(
4112 Qt::ScrollBarAsNeeded);
4114 d.current_work_area_->setVerticalScrollBarPolicy(
4115 Qt::ScrollBarAlwaysOff);
4116 } else if (ui_component == "statusbar") {
4117 statusBar()->setVisible(!statusBar()->isVisible());
4118 } else if (ui_component == "menubar") {
4119 menuBar()->setVisible(!menuBar()->isVisible());
4121 if (ui_component == "frame") {
4123 getContentsMargins(&l, &t, &r, &b);
4124 //are the frames in default state?
4125 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4127 setContentsMargins(-2, -2, -2, -2);
4129 setContentsMargins(0, 0, 0, 0);
4132 if (ui_component == "fullscreen") {
4140 void GuiView::toggleFullScreen()
4142 if (isFullScreen()) {
4143 for (int i = 0; i != d.splitter_->count(); ++i)
4144 d.tabWorkArea(i)->setFullScreen(false);
4145 setContentsMargins(0, 0, 0, 0);
4146 setWindowState(windowState() ^ Qt::WindowFullScreen);
4149 statusBar()->show();
4152 hideDialogs("prefs", 0);
4153 for (int i = 0; i != d.splitter_->count(); ++i)
4154 d.tabWorkArea(i)->setFullScreen(true);
4155 setContentsMargins(-2, -2, -2, -2);
4157 setWindowState(windowState() ^ Qt::WindowFullScreen);
4158 if (lyxrc.full_screen_statusbar)
4159 statusBar()->hide();
4160 if (lyxrc.full_screen_menubar)
4162 if (lyxrc.full_screen_toolbars) {
4163 ToolbarMap::iterator end = d.toolbars_.end();
4164 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4169 // give dialogs like the TOC a chance to adapt
4174 Buffer const * GuiView::updateInset(Inset const * inset)
4179 Buffer const * inset_buffer = &(inset->buffer());
4181 for (int i = 0; i != d.splitter_->count(); ++i) {
4182 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4185 Buffer const * buffer = &(wa->bufferView().buffer());
4186 if (inset_buffer == buffer)
4187 wa->scheduleRedraw();
4189 return inset_buffer;
4193 void GuiView::restartCursor()
4195 /* When we move around, or type, it's nice to be able to see
4196 * the cursor immediately after the keypress.
4198 if (d.current_work_area_)
4199 d.current_work_area_->startBlinkingCursor();
4201 // Take this occasion to update the other GUI elements.
4207 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4209 if (d.current_work_area_)
4210 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4215 // This list should be kept in sync with the list of insets in
4216 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4217 // dialog should have the same name as the inset.
4218 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4219 // docs in LyXAction.cpp.
4221 char const * const dialognames[] = {
4223 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4224 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4225 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4226 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4227 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4228 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4229 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4230 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4232 char const * const * const end_dialognames =
4233 dialognames + (sizeof(dialognames) / sizeof(char *));
4237 cmpCStr(char const * name) : name_(name) {}
4238 bool operator()(char const * other) {
4239 return strcmp(other, name_) == 0;
4246 bool isValidName(string const & name)
4248 return find_if(dialognames, end_dialognames,
4249 cmpCStr(name.c_str())) != end_dialognames;
4255 void GuiView::resetDialogs()
4257 // Make sure that no LFUN uses any GuiView.
4258 guiApp->setCurrentView(0);
4262 constructToolbars();
4263 guiApp->menus().fillMenuBar(menuBar(), this, false);
4264 d.layout_->updateContents(true);
4265 // Now update controls with current buffer.
4266 guiApp->setCurrentView(this);
4272 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4274 if (!isValidName(name))
4277 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4279 if (it != d.dialogs_.end()) {
4281 it->second->hideView();
4282 return it->second.get();
4285 Dialog * dialog = build(name);
4286 d.dialogs_[name].reset(dialog);
4287 if (lyxrc.allow_geometry_session)
4288 dialog->restoreSession();
4295 void GuiView::showDialog(string const & name, string const & data,
4298 triggerShowDialog(toqstr(name), toqstr(data), inset);
4302 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4308 const string name = fromqstr(qname);
4309 const string data = fromqstr(qdata);
4313 Dialog * dialog = findOrBuild(name, false);
4315 bool const visible = dialog->isVisibleView();
4316 dialog->showData(data);
4317 if (inset && currentBufferView())
4318 currentBufferView()->editInset(name, inset);
4319 // We only set the focus to the new dialog if it was not yet
4320 // visible in order not to change the existing previous behaviour
4322 // activateWindow is needed for floating dockviews
4323 dialog->asQWidget()->raise();
4324 dialog->asQWidget()->activateWindow();
4325 dialog->asQWidget()->setFocus();
4329 catch (ExceptionMessage const & ex) {
4337 bool GuiView::isDialogVisible(string const & name) const
4339 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4340 if (it == d.dialogs_.end())
4342 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4346 void GuiView::hideDialog(string const & name, Inset * inset)
4348 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4349 if (it == d.dialogs_.end())
4353 if (!currentBufferView())
4355 if (inset != currentBufferView()->editedInset(name))
4359 Dialog * const dialog = it->second.get();
4360 if (dialog->isVisibleView())
4362 if (currentBufferView())
4363 currentBufferView()->editInset(name, 0);
4367 void GuiView::disconnectDialog(string const & name)
4369 if (!isValidName(name))
4371 if (currentBufferView())
4372 currentBufferView()->editInset(name, 0);
4376 void GuiView::hideAll() const
4378 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4379 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4381 for(; it != end; ++it)
4382 it->second->hideView();
4386 void GuiView::updateDialogs()
4388 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4389 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4391 for(; it != end; ++it) {
4392 Dialog * dialog = it->second.get();
4394 if (dialog->needBufferOpen() && !documentBufferView())
4395 hideDialog(fromqstr(dialog->name()), 0);
4396 else if (dialog->isVisibleView())
4397 dialog->checkStatus();
4404 Dialog * createDialog(GuiView & lv, string const & name);
4406 // will be replaced by a proper factory...
4407 Dialog * createGuiAbout(GuiView & lv);
4408 Dialog * createGuiBibtex(GuiView & lv);
4409 Dialog * createGuiChanges(GuiView & lv);
4410 Dialog * createGuiCharacter(GuiView & lv);
4411 Dialog * createGuiCitation(GuiView & lv);
4412 Dialog * createGuiCompare(GuiView & lv);
4413 Dialog * createGuiCompareHistory(GuiView & lv);
4414 Dialog * createGuiDelimiter(GuiView & lv);
4415 Dialog * createGuiDocument(GuiView & lv);
4416 Dialog * createGuiErrorList(GuiView & lv);
4417 Dialog * createGuiExternal(GuiView & lv);
4418 Dialog * createGuiGraphics(GuiView & lv);
4419 Dialog * createGuiInclude(GuiView & lv);
4420 Dialog * createGuiIndex(GuiView & lv);
4421 Dialog * createGuiListings(GuiView & lv);
4422 Dialog * createGuiLog(GuiView & lv);
4423 Dialog * createGuiMathMatrix(GuiView & lv);
4424 Dialog * createGuiNote(GuiView & lv);
4425 Dialog * createGuiParagraph(GuiView & lv);
4426 Dialog * createGuiPhantom(GuiView & lv);
4427 Dialog * createGuiPreferences(GuiView & lv);
4428 Dialog * createGuiPrint(GuiView & lv);
4429 Dialog * createGuiPrintindex(GuiView & lv);
4430 Dialog * createGuiRef(GuiView & lv);
4431 Dialog * createGuiSearch(GuiView & lv);
4432 Dialog * createGuiSearchAdv(GuiView & lv);
4433 Dialog * createGuiSendTo(GuiView & lv);
4434 Dialog * createGuiShowFile(GuiView & lv);
4435 Dialog * createGuiSpellchecker(GuiView & lv);
4436 Dialog * createGuiSymbols(GuiView & lv);
4437 Dialog * createGuiTabularCreate(GuiView & lv);
4438 Dialog * createGuiTexInfo(GuiView & lv);
4439 Dialog * createGuiToc(GuiView & lv);
4440 Dialog * createGuiThesaurus(GuiView & lv);
4441 Dialog * createGuiViewSource(GuiView & lv);
4442 Dialog * createGuiWrap(GuiView & lv);
4443 Dialog * createGuiProgressView(GuiView & lv);
4447 Dialog * GuiView::build(string const & name)
4449 LASSERT(isValidName(name), return 0);
4451 Dialog * dialog = createDialog(*this, name);
4455 if (name == "aboutlyx")
4456 return createGuiAbout(*this);
4457 if (name == "bibtex")
4458 return createGuiBibtex(*this);
4459 if (name == "changes")
4460 return createGuiChanges(*this);
4461 if (name == "character")
4462 return createGuiCharacter(*this);
4463 if (name == "citation")
4464 return createGuiCitation(*this);
4465 if (name == "compare")
4466 return createGuiCompare(*this);
4467 if (name == "comparehistory")
4468 return createGuiCompareHistory(*this);
4469 if (name == "document")
4470 return createGuiDocument(*this);
4471 if (name == "errorlist")
4472 return createGuiErrorList(*this);
4473 if (name == "external")
4474 return createGuiExternal(*this);
4476 return createGuiShowFile(*this);
4477 if (name == "findreplace")
4478 return createGuiSearch(*this);
4479 if (name == "findreplaceadv")
4480 return createGuiSearchAdv(*this);
4481 if (name == "graphics")
4482 return createGuiGraphics(*this);
4483 if (name == "include")
4484 return createGuiInclude(*this);
4485 if (name == "index")
4486 return createGuiIndex(*this);
4487 if (name == "index_print")
4488 return createGuiPrintindex(*this);
4489 if (name == "listings")
4490 return createGuiListings(*this);
4492 return createGuiLog(*this);
4493 if (name == "mathdelimiter")
4494 return createGuiDelimiter(*this);
4495 if (name == "mathmatrix")
4496 return createGuiMathMatrix(*this);
4498 return createGuiNote(*this);
4499 if (name == "paragraph")
4500 return createGuiParagraph(*this);
4501 if (name == "phantom")
4502 return createGuiPhantom(*this);
4503 if (name == "prefs")
4504 return createGuiPreferences(*this);
4506 return createGuiRef(*this);
4507 if (name == "sendto")
4508 return createGuiSendTo(*this);
4509 if (name == "spellchecker")
4510 return createGuiSpellchecker(*this);
4511 if (name == "symbols")
4512 return createGuiSymbols(*this);
4513 if (name == "tabularcreate")
4514 return createGuiTabularCreate(*this);
4515 if (name == "texinfo")
4516 return createGuiTexInfo(*this);
4517 if (name == "thesaurus")
4518 return createGuiThesaurus(*this);
4520 return createGuiToc(*this);
4521 if (name == "view-source")
4522 return createGuiViewSource(*this);
4524 return createGuiWrap(*this);
4525 if (name == "progress")
4526 return createGuiProgressView(*this);
4532 } // namespace frontend
4535 #include "moc_GuiView.cpp"