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 <= zoom_min_ && neg_zoom) {
2038 docstring const msg =
2039 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2043 enable = doc_buffer;
2046 case LFUN_BUFFER_MOVE_NEXT:
2047 case LFUN_BUFFER_MOVE_PREVIOUS:
2048 // we do not cycle when moving
2049 case LFUN_BUFFER_NEXT:
2050 case LFUN_BUFFER_PREVIOUS:
2051 // because we cycle, it doesn't matter whether on first or last
2052 enable = (d.currentTabWorkArea()->count() > 1);
2054 case LFUN_BUFFER_SWITCH:
2055 // toggle on the current buffer, but do not toggle off
2056 // the other ones (is that a good idea?)
2058 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2059 flag.setOnOff(true);
2062 case LFUN_VC_REGISTER:
2063 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2065 case LFUN_VC_RENAME:
2066 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2069 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2071 case LFUN_VC_CHECK_IN:
2072 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2074 case LFUN_VC_CHECK_OUT:
2075 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2077 case LFUN_VC_LOCKING_TOGGLE:
2078 enable = doc_buffer && !doc_buffer->isReadonly()
2079 && doc_buffer->lyxvc().lockingToggleEnabled();
2080 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2082 case LFUN_VC_REVERT:
2083 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2085 case LFUN_VC_UNDO_LAST:
2086 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2088 case LFUN_VC_REPO_UPDATE:
2089 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2091 case LFUN_VC_COMMAND: {
2092 if (cmd.argument().empty())
2094 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2098 case LFUN_VC_COMPARE:
2099 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2102 case LFUN_SERVER_GOTO_FILE_ROW:
2103 case LFUN_LYX_ACTIVATE:
2105 case LFUN_FORWARD_SEARCH:
2106 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2109 case LFUN_FILE_INSERT_PLAINTEXT:
2110 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2111 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2114 case LFUN_SPELLING_CONTINUOUSLY:
2115 flag.setOnOff(lyxrc.spellcheck_continuously);
2123 flag.setEnabled(false);
2129 static FileName selectTemplateFile()
2131 FileDialog dlg(qt_("Select template file"));
2132 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2133 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2135 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2136 QStringList(qt_("LyX Documents (*.lyx)")));
2138 if (result.first == FileDialog::Later)
2140 if (result.second.isEmpty())
2142 return FileName(fromqstr(result.second));
2146 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2150 Buffer * newBuffer = 0;
2152 newBuffer = checkAndLoadLyXFile(filename);
2153 } catch (ExceptionMessage const & e) {
2160 message(_("Document not loaded."));
2164 setBuffer(newBuffer);
2165 newBuffer->errors("Parse");
2168 theSession().lastFiles().add(filename);
2174 void GuiView::openDocument(string const & fname)
2176 string initpath = lyxrc.document_path;
2178 if (documentBufferView()) {
2179 string const trypath = documentBufferView()->buffer().filePath();
2180 // If directory is writeable, use this as default.
2181 if (FileName(trypath).isDirWritable())
2187 if (fname.empty()) {
2188 FileDialog dlg(qt_("Select document to open"));
2189 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2190 dlg.setButton2(qt_("Examples|#E#e"),
2191 toqstr(addPath(package().system_support().absFileName(), "examples")));
2193 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2194 FileDialog::Result result =
2195 dlg.open(toqstr(initpath), filter);
2197 if (result.first == FileDialog::Later)
2200 filename = fromqstr(result.second);
2202 // check selected filename
2203 if (filename.empty()) {
2204 message(_("Canceled."));
2210 // get absolute path of file and add ".lyx" to the filename if
2212 FileName const fullname =
2213 fileSearch(string(), filename, "lyx", support::may_not_exist);
2214 if (!fullname.empty())
2215 filename = fullname.absFileName();
2217 if (!fullname.onlyPath().isDirectory()) {
2218 Alert::warning(_("Invalid filename"),
2219 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2220 from_utf8(fullname.absFileName())));
2224 // if the file doesn't exist and isn't already open (bug 6645),
2225 // let the user create one
2226 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2227 !LyXVC::file_not_found_hook(fullname)) {
2228 // the user specifically chose this name. Believe him.
2229 Buffer * const b = newFile(filename, string(), true);
2235 docstring const disp_fn = makeDisplayPath(filename);
2236 message(bformat(_("Opening document %1$s..."), disp_fn));
2239 Buffer * buf = loadDocument(fullname);
2241 str2 = bformat(_("Document %1$s opened."), disp_fn);
2242 if (buf->lyxvc().inUse())
2243 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2244 " " + _("Version control detected.");
2246 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2251 // FIXME: clean that
2252 static bool import(GuiView * lv, FileName const & filename,
2253 string const & format, ErrorList & errorList)
2255 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2257 string loader_format;
2258 vector<string> loaders = theConverters().loaders();
2259 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2260 vector<string>::const_iterator it = loaders.begin();
2261 vector<string>::const_iterator en = loaders.end();
2262 for (; it != en; ++it) {
2263 if (!theConverters().isReachable(format, *it))
2266 string const tofile =
2267 support::changeExtension(filename.absFileName(),
2268 formats.extension(*it));
2269 if (!theConverters().convert(0, filename, FileName(tofile),
2270 filename, format, *it, errorList))
2272 loader_format = *it;
2275 if (loader_format.empty()) {
2276 frontend::Alert::error(_("Couldn't import file"),
2277 bformat(_("No information for importing the format %1$s."),
2278 formats.prettyName(format)));
2282 loader_format = format;
2284 if (loader_format == "lyx") {
2285 Buffer * buf = lv->loadDocument(lyxfile);
2289 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2293 bool as_paragraphs = loader_format == "textparagraph";
2294 string filename2 = (loader_format == format) ? filename.absFileName()
2295 : support::changeExtension(filename.absFileName(),
2296 formats.extension(loader_format));
2297 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2299 guiApp->setCurrentView(lv);
2300 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2307 void GuiView::importDocument(string const & argument)
2310 string filename = split(argument, format, ' ');
2312 LYXERR(Debug::INFO, format << " file: " << filename);
2314 // need user interaction
2315 if (filename.empty()) {
2316 string initpath = lyxrc.document_path;
2317 if (documentBufferView()) {
2318 string const trypath = documentBufferView()->buffer().filePath();
2319 // If directory is writeable, use this as default.
2320 if (FileName(trypath).isDirWritable())
2324 docstring const text = bformat(_("Select %1$s file to import"),
2325 formats.prettyName(format));
2327 FileDialog dlg(toqstr(text));
2328 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2329 dlg.setButton2(qt_("Examples|#E#e"),
2330 toqstr(addPath(package().system_support().absFileName(), "examples")));
2332 docstring filter = formats.prettyName(format);
2335 filter += from_utf8(formats.extensions(format));
2338 FileDialog::Result result =
2339 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2341 if (result.first == FileDialog::Later)
2344 filename = fromqstr(result.second);
2346 // check selected filename
2347 if (filename.empty())
2348 message(_("Canceled."));
2351 if (filename.empty())
2354 // get absolute path of file
2355 FileName const fullname(support::makeAbsPath(filename));
2357 // Can happen if the user entered a path into the dialog
2359 if (fullname.onlyFileName().empty()) {
2360 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2361 "Aborting import."),
2362 from_utf8(fullname.absFileName()));
2363 frontend::Alert::error(_("File name error"), msg);
2364 message(_("Canceled."));
2369 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2371 // Check if the document already is open
2372 Buffer * buf = theBufferList().getBuffer(lyxfile);
2375 if (!closeBuffer()) {
2376 message(_("Canceled."));
2381 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2383 // if the file exists already, and we didn't do
2384 // -i lyx thefile.lyx, warn
2385 if (lyxfile.exists() && fullname != lyxfile) {
2387 docstring text = bformat(_("The document %1$s already exists.\n\n"
2388 "Do you want to overwrite that document?"), displaypath);
2389 int const ret = Alert::prompt(_("Overwrite document?"),
2390 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2393 message(_("Canceled."));
2398 message(bformat(_("Importing %1$s..."), displaypath));
2399 ErrorList errorList;
2400 if (import(this, fullname, format, errorList))
2401 message(_("imported."));
2403 message(_("file not imported!"));
2405 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2409 void GuiView::newDocument(string const & filename, bool from_template)
2411 FileName initpath(lyxrc.document_path);
2412 if (documentBufferView()) {
2413 FileName const trypath(documentBufferView()->buffer().filePath());
2414 // If directory is writeable, use this as default.
2415 if (trypath.isDirWritable())
2419 string templatefile;
2420 if (from_template) {
2421 templatefile = selectTemplateFile().absFileName();
2422 if (templatefile.empty())
2427 if (filename.empty())
2428 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2430 b = newFile(filename, templatefile, true);
2435 // If no new document could be created, it is unsure
2436 // whether there is a valid BufferView.
2437 if (currentBufferView())
2438 // Ensure the cursor is correctly positioned on screen.
2439 currentBufferView()->showCursor();
2443 void GuiView::insertLyXFile(docstring const & fname)
2445 BufferView * bv = documentBufferView();
2450 FileName filename(to_utf8(fname));
2451 if (filename.empty()) {
2452 // Launch a file browser
2454 string initpath = lyxrc.document_path;
2455 string const trypath = bv->buffer().filePath();
2456 // If directory is writeable, use this as default.
2457 if (FileName(trypath).isDirWritable())
2461 FileDialog dlg(qt_("Select LyX document to insert"));
2462 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2463 dlg.setButton2(qt_("Examples|#E#e"),
2464 toqstr(addPath(package().system_support().absFileName(),
2467 FileDialog::Result result = dlg.open(toqstr(initpath),
2468 QStringList(qt_("LyX Documents (*.lyx)")));
2470 if (result.first == FileDialog::Later)
2474 filename.set(fromqstr(result.second));
2476 // check selected filename
2477 if (filename.empty()) {
2478 // emit message signal.
2479 message(_("Canceled."));
2484 bv->insertLyXFile(filename);
2485 bv->buffer().errors("Parse");
2489 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2491 FileName fname = b.fileName();
2492 FileName const oldname = fname;
2494 if (!newname.empty()) {
2496 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2498 // Switch to this Buffer.
2501 // No argument? Ask user through dialog.
2503 FileDialog dlg(qt_("Choose a filename to save document as"));
2504 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2505 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2507 if (!isLyXFileName(fname.absFileName()))
2508 fname.changeExtension(".lyx");
2510 FileDialog::Result result =
2511 dlg.save(toqstr(fname.onlyPath().absFileName()),
2512 QStringList(qt_("LyX Documents (*.lyx)")),
2513 toqstr(fname.onlyFileName()));
2515 if (result.first == FileDialog::Later)
2518 fname.set(fromqstr(result.second));
2523 if (!isLyXFileName(fname.absFileName()))
2524 fname.changeExtension(".lyx");
2527 // fname is now the new Buffer location.
2529 // if there is already a Buffer open with this name, we do not want
2530 // to have another one. (the second test makes sure we're not just
2531 // trying to overwrite ourselves, which is fine.)
2532 if (theBufferList().exists(fname) && fname != oldname
2533 && theBufferList().getBuffer(fname) != &b) {
2534 docstring const text =
2535 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2536 "Please close it before attempting to overwrite it.\n"
2537 "Do you want to choose a new filename?"),
2538 from_utf8(fname.absFileName()));
2539 int const ret = Alert::prompt(_("Chosen File Already Open"),
2540 text, 0, 1, _("&Rename"), _("&Cancel"));
2542 case 0: return renameBuffer(b, docstring(), kind);
2543 case 1: return false;
2548 bool const existsLocal = fname.exists();
2549 bool const existsInVC = LyXVC::fileInVC(fname);
2550 if (existsLocal || existsInVC) {
2551 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2552 if (kind != LV_WRITE_AS && existsInVC) {
2553 // renaming to a name that is already in VC
2555 docstring text = bformat(_("The document %1$s "
2556 "is already registered.\n\n"
2557 "Do you want to choose a new name?"),
2559 docstring const title = (kind == LV_VC_RENAME) ?
2560 _("Rename document?") : _("Copy document?");
2561 docstring const button = (kind == LV_VC_RENAME) ?
2562 _("&Rename") : _("&Copy");
2563 int const ret = Alert::prompt(title, text, 0, 1,
2564 button, _("&Cancel"));
2566 case 0: return renameBuffer(b, docstring(), kind);
2567 case 1: return false;
2572 docstring text = bformat(_("The document %1$s "
2573 "already exists.\n\n"
2574 "Do you want to overwrite that document?"),
2576 int const ret = Alert::prompt(_("Overwrite document?"),
2577 text, 0, 2, _("&Overwrite"),
2578 _("&Rename"), _("&Cancel"));
2581 case 1: return renameBuffer(b, docstring(), kind);
2582 case 2: return false;
2588 case LV_VC_RENAME: {
2589 string msg = b.lyxvc().rename(fname);
2592 message(from_utf8(msg));
2596 string msg = b.lyxvc().copy(fname);
2599 message(from_utf8(msg));
2605 // LyXVC created the file already in case of LV_VC_RENAME or
2606 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2607 // relative paths of included stuff right if we moved e.g. from
2608 // /a/b.lyx to /a/c/b.lyx.
2610 bool const saved = saveBuffer(b, fname);
2617 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2619 FileName fname = b.fileName();
2621 FileDialog dlg(qt_("Choose a filename to export the document as"));
2622 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2625 QString const anyformat = qt_("Guess from extension (*.*)");
2628 vector<Format const *> export_formats;
2629 for (Format const & f : formats)
2630 if (f.documentFormat())
2631 export_formats.push_back(&f);
2632 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2633 map<QString, string> fmap;
2636 for (Format const * f : export_formats) {
2637 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2638 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2640 from_ascii(f->extension())));
2641 types << loc_filter;
2642 fmap[loc_filter] = f->name();
2643 if (from_ascii(f->name()) == iformat) {
2644 filter = loc_filter;
2645 ext = f->extension();
2648 string ofname = fname.onlyFileName();
2650 ofname = support::changeExtension(ofname, ext);
2651 FileDialog::Result result =
2652 dlg.save(toqstr(fname.onlyPath().absFileName()),
2656 if (result.first != FileDialog::Chosen)
2660 fname.set(fromqstr(result.second));
2661 if (filter == anyformat)
2662 fmt_name = formats.getFormatFromExtension(fname.extension());
2664 fmt_name = fmap[filter];
2665 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2666 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2668 if (fmt_name.empty() || fname.empty())
2671 // fname is now the new Buffer location.
2672 if (FileName(fname).exists()) {
2673 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2674 docstring text = bformat(_("The document %1$s already "
2675 "exists.\n\nDo you want to "
2676 "overwrite that document?"),
2678 int const ret = Alert::prompt(_("Overwrite document?"),
2679 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2682 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2683 case 2: return false;
2687 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2690 return dr.dispatched();
2694 bool GuiView::saveBuffer(Buffer & b)
2696 return saveBuffer(b, FileName());
2700 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2702 if (workArea(b) && workArea(b)->inDialogMode())
2705 if (fn.empty() && b.isUnnamed())
2706 return renameBuffer(b, docstring());
2708 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2710 theSession().lastFiles().add(b.fileName());
2714 // Switch to this Buffer.
2717 // FIXME: we don't tell the user *WHY* the save failed !!
2718 docstring const file = makeDisplayPath(b.absFileName(), 30);
2719 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2720 "Do you want to rename the document and "
2721 "try again?"), file);
2722 int const ret = Alert::prompt(_("Rename and save?"),
2723 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2726 if (!renameBuffer(b, docstring()))
2735 return saveBuffer(b, fn);
2739 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2741 return closeWorkArea(wa, false);
2745 // We only want to close the buffer if it is not visible in other workareas
2746 // of the same view, nor in other views, and if this is not a child
2747 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2749 Buffer & buf = wa->bufferView().buffer();
2751 bool last_wa = d.countWorkAreasOf(buf) == 1
2752 && !inOtherView(buf) && !buf.parent();
2754 bool close_buffer = last_wa;
2757 if (lyxrc.close_buffer_with_last_view == "yes")
2759 else if (lyxrc.close_buffer_with_last_view == "no")
2760 close_buffer = false;
2763 if (buf.isUnnamed())
2764 file = from_utf8(buf.fileName().onlyFileName());
2766 file = buf.fileName().displayName(30);
2767 docstring const text = bformat(
2768 _("Last view on document %1$s is being closed.\n"
2769 "Would you like to close or hide the document?\n"
2771 "Hidden documents can be displayed back through\n"
2772 "the menu: View->Hidden->...\n"
2774 "To remove this question, set your preference in:\n"
2775 " Tools->Preferences->Look&Feel->UserInterface\n"
2777 int ret = Alert::prompt(_("Close or hide document?"),
2778 text, 0, 1, _("&Close"), _("&Hide"));
2779 close_buffer = (ret == 0);
2783 return closeWorkArea(wa, close_buffer);
2787 bool GuiView::closeBuffer()
2789 GuiWorkArea * wa = currentMainWorkArea();
2790 // coverity complained about this
2791 // it seems unnecessary, but perhaps is worth the check
2792 LASSERT(wa, return false);
2794 setCurrentWorkArea(wa);
2795 Buffer & buf = wa->bufferView().buffer();
2796 return closeWorkArea(wa, !buf.parent());
2800 void GuiView::writeSession() const {
2801 GuiWorkArea const * active_wa = currentMainWorkArea();
2802 for (int i = 0; i < d.splitter_->count(); ++i) {
2803 TabWorkArea * twa = d.tabWorkArea(i);
2804 for (int j = 0; j < twa->count(); ++j) {
2805 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2806 Buffer & buf = wa->bufferView().buffer();
2807 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2813 bool GuiView::closeBufferAll()
2815 // Close the workareas in all other views
2816 QList<int> const ids = guiApp->viewIds();
2817 for (int i = 0; i != ids.size(); ++i) {
2818 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2822 // Close our own workareas
2823 if (!closeWorkAreaAll())
2826 // Now close the hidden buffers. We prevent hidden buffers from being
2827 // dirty, so we can just close them.
2828 theBufferList().closeAll();
2833 bool GuiView::closeWorkAreaAll()
2835 setCurrentWorkArea(currentMainWorkArea());
2837 // We might be in a situation that there is still a tabWorkArea, but
2838 // there are no tabs anymore. This can happen when we get here after a
2839 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2840 // many TabWorkArea's have no documents anymore.
2843 // We have to call count() each time, because it can happen that
2844 // more than one splitter will disappear in one iteration (bug 5998).
2845 while (d.splitter_->count() > empty_twa) {
2846 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2848 if (twa->count() == 0)
2851 setCurrentWorkArea(twa->currentWorkArea());
2852 if (!closeTabWorkArea(twa))
2860 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2865 Buffer & buf = wa->bufferView().buffer();
2867 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2868 Alert::warning(_("Close document"),
2869 _("Document could not be closed because it is being processed by LyX."));
2874 return closeBuffer(buf);
2876 if (!inMultiTabs(wa))
2877 if (!saveBufferIfNeeded(buf, true))
2885 bool GuiView::closeBuffer(Buffer & buf)
2887 // If we are in a close_event all children will be closed in some time,
2888 // so no need to do it here. This will ensure that the children end up
2889 // in the session file in the correct order. If we close the master
2890 // buffer, we can close or release the child buffers here too.
2891 bool success = true;
2893 ListOfBuffers clist = buf.getChildren();
2894 ListOfBuffers::const_iterator it = clist.begin();
2895 ListOfBuffers::const_iterator const bend = clist.end();
2896 for (; it != bend; ++it) {
2897 Buffer * child_buf = *it;
2898 if (theBufferList().isOthersChild(&buf, child_buf)) {
2899 child_buf->setParent(0);
2903 // FIXME: should we look in other tabworkareas?
2904 // ANSWER: I don't think so. I've tested, and if the child is
2905 // open in some other window, it closes without a problem.
2906 GuiWorkArea * child_wa = workArea(*child_buf);
2908 success = closeWorkArea(child_wa, true);
2912 // In this case the child buffer is open but hidden.
2913 // It therefore should not (MUST NOT) be dirty!
2914 LATTEST(child_buf->isClean());
2915 theBufferList().release(child_buf);
2920 // goto bookmark to update bookmark pit.
2921 // FIXME: we should update only the bookmarks related to this buffer!
2922 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2923 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2924 guiApp->gotoBookmark(i+1, false, false);
2926 if (saveBufferIfNeeded(buf, false)) {
2927 buf.removeAutosaveFile();
2928 theBufferList().release(&buf);
2932 // open all children again to avoid a crash because of dangling
2933 // pointers (bug 6603)
2939 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2941 while (twa == d.currentTabWorkArea()) {
2942 twa->setCurrentIndex(twa->count() - 1);
2944 GuiWorkArea * wa = twa->currentWorkArea();
2945 Buffer & b = wa->bufferView().buffer();
2947 // We only want to close the buffer if the same buffer is not visible
2948 // in another view, and if this is not a child and if we are closing
2949 // a view (not a tabgroup).
2950 bool const close_buffer =
2951 !inOtherView(b) && !b.parent() && closing_;
2953 if (!closeWorkArea(wa, close_buffer))
2960 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2962 if (buf.isClean() || buf.paragraphs().empty())
2965 // Switch to this Buffer.
2970 if (buf.isUnnamed())
2971 file = from_utf8(buf.fileName().onlyFileName());
2973 file = buf.fileName().displayName(30);
2975 // Bring this window to top before asking questions.
2980 if (hiding && buf.isUnnamed()) {
2981 docstring const text = bformat(_("The document %1$s has not been "
2982 "saved yet.\n\nDo you want to save "
2983 "the document?"), file);
2984 ret = Alert::prompt(_("Save new document?"),
2985 text, 0, 1, _("&Save"), _("&Cancel"));
2989 docstring const text = bformat(_("The document %1$s has unsaved changes."
2990 "\n\nDo you want to save the document or discard the changes?"), file);
2991 ret = Alert::prompt(_("Save changed document?"),
2992 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2997 if (!saveBuffer(buf))
3001 // If we crash after this we could have no autosave file
3002 // but I guess this is really improbable (Jug).
3003 // Sometimes improbable things happen:
3004 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3005 // buf.removeAutosaveFile();
3007 // revert all changes
3018 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3020 Buffer & buf = wa->bufferView().buffer();
3022 for (int i = 0; i != d.splitter_->count(); ++i) {
3023 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3024 if (wa_ && wa_ != wa)
3027 return inOtherView(buf);
3031 bool GuiView::inOtherView(Buffer & buf)
3033 QList<int> const ids = guiApp->viewIds();
3035 for (int i = 0; i != ids.size(); ++i) {
3039 if (guiApp->view(ids[i]).workArea(buf))
3046 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3048 if (!documentBufferView())
3051 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3052 Buffer * const curbuf = &documentBufferView()->buffer();
3053 int nwa = twa->count();
3054 for (int i = 0; i < nwa; ++i) {
3055 if (&workArea(i)->bufferView().buffer() == curbuf) {
3057 if (np == NEXTBUFFER)
3058 next_index = (i == nwa - 1 ? 0 : i + 1);
3060 next_index = (i == 0 ? nwa - 1 : i - 1);
3062 twa->moveTab(i, next_index);
3064 setBuffer(&workArea(next_index)->bufferView().buffer());
3072 /// make sure the document is saved
3073 static bool ensureBufferClean(Buffer * buffer)
3075 LASSERT(buffer, return false);
3076 if (buffer->isClean() && !buffer->isUnnamed())
3079 docstring const file = buffer->fileName().displayName(30);
3082 if (!buffer->isUnnamed()) {
3083 text = bformat(_("The document %1$s has unsaved "
3084 "changes.\n\nDo you want to save "
3085 "the document?"), file);
3086 title = _("Save changed document?");
3089 text = bformat(_("The document %1$s has not been "
3090 "saved yet.\n\nDo you want to save "
3091 "the document?"), file);
3092 title = _("Save new document?");
3094 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3097 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3099 return buffer->isClean() && !buffer->isUnnamed();
3103 bool GuiView::reloadBuffer(Buffer & buf)
3105 Buffer::ReadStatus status = buf.reload();
3106 return status == Buffer::ReadSuccess;
3110 void GuiView::checkExternallyModifiedBuffers()
3112 BufferList::iterator bit = theBufferList().begin();
3113 BufferList::iterator const bend = theBufferList().end();
3114 for (; bit != bend; ++bit) {
3115 Buffer * buf = *bit;
3116 if (buf->fileName().exists()
3117 && buf->isExternallyModified(Buffer::checksum_method)) {
3118 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3119 " Reload now? Any local changes will be lost."),
3120 from_utf8(buf->absFileName()));
3121 int const ret = Alert::prompt(_("Reload externally changed document?"),
3122 text, 0, 1, _("&Reload"), _("&Cancel"));
3130 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3132 Buffer * buffer = documentBufferView()
3133 ? &(documentBufferView()->buffer()) : 0;
3135 switch (cmd.action()) {
3136 case LFUN_VC_REGISTER:
3137 if (!buffer || !ensureBufferClean(buffer))
3139 if (!buffer->lyxvc().inUse()) {
3140 if (buffer->lyxvc().registrer()) {
3141 reloadBuffer(*buffer);
3142 dr.clearMessageUpdate();
3147 case LFUN_VC_RENAME:
3148 case LFUN_VC_COPY: {
3149 if (!buffer || !ensureBufferClean(buffer))
3151 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3152 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3153 // Some changes are not yet committed.
3154 // We test here and not in getStatus(), since
3155 // this test is expensive.
3157 LyXVC::CommandResult ret =
3158 buffer->lyxvc().checkIn(log);
3160 if (ret == LyXVC::ErrorCommand ||
3161 ret == LyXVC::VCSuccess)
3162 reloadBuffer(*buffer);
3163 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3164 frontend::Alert::error(
3165 _("Revision control error."),
3166 _("Document could not be checked in."));
3170 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3171 LV_VC_RENAME : LV_VC_COPY;
3172 renameBuffer(*buffer, cmd.argument(), kind);
3177 case LFUN_VC_CHECK_IN:
3178 if (!buffer || !ensureBufferClean(buffer))
3180 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3182 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3184 // Only skip reloading if the checkin was cancelled or
3185 // an error occurred before the real checkin VCS command
3186 // was executed, since the VCS might have changed the
3187 // file even if it could not checkin successfully.
3188 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3189 reloadBuffer(*buffer);
3193 case LFUN_VC_CHECK_OUT:
3194 if (!buffer || !ensureBufferClean(buffer))
3196 if (buffer->lyxvc().inUse()) {
3197 dr.setMessage(buffer->lyxvc().checkOut());
3198 reloadBuffer(*buffer);
3202 case LFUN_VC_LOCKING_TOGGLE:
3203 LASSERT(buffer, return);
3204 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3206 if (buffer->lyxvc().inUse()) {
3207 string res = buffer->lyxvc().lockingToggle();
3209 frontend::Alert::error(_("Revision control error."),
3210 _("Error when setting the locking property."));
3213 reloadBuffer(*buffer);
3218 case LFUN_VC_REVERT:
3219 LASSERT(buffer, return);
3220 if (buffer->lyxvc().revert()) {
3221 reloadBuffer(*buffer);
3222 dr.clearMessageUpdate();
3226 case LFUN_VC_UNDO_LAST:
3227 LASSERT(buffer, return);
3228 buffer->lyxvc().undoLast();
3229 reloadBuffer(*buffer);
3230 dr.clearMessageUpdate();
3233 case LFUN_VC_REPO_UPDATE:
3234 LASSERT(buffer, return);
3235 if (ensureBufferClean(buffer)) {
3236 dr.setMessage(buffer->lyxvc().repoUpdate());
3237 checkExternallyModifiedBuffers();
3241 case LFUN_VC_COMMAND: {
3242 string flag = cmd.getArg(0);
3243 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3246 if (contains(flag, 'M')) {
3247 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3250 string path = cmd.getArg(1);
3251 if (contains(path, "$$p") && buffer)
3252 path = subst(path, "$$p", buffer->filePath());
3253 LYXERR(Debug::LYXVC, "Directory: " << path);
3255 if (!pp.isReadableDirectory()) {
3256 lyxerr << _("Directory is not accessible.") << endl;
3259 support::PathChanger p(pp);
3261 string command = cmd.getArg(2);
3262 if (command.empty())
3265 command = subst(command, "$$i", buffer->absFileName());
3266 command = subst(command, "$$p", buffer->filePath());
3268 command = subst(command, "$$m", to_utf8(message));
3269 LYXERR(Debug::LYXVC, "Command: " << command);
3271 one.startscript(Systemcall::Wait, command);
3275 if (contains(flag, 'I'))
3276 buffer->markDirty();
3277 if (contains(flag, 'R'))
3278 reloadBuffer(*buffer);
3283 case LFUN_VC_COMPARE: {
3284 if (cmd.argument().empty()) {
3285 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3289 string rev1 = cmd.getArg(0);
3293 // it seems safe to assume we have a buffer
3294 // coverity[FORWARD_NULL]
3295 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3298 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3299 f2 = buffer->absFileName();
3301 string rev2 = cmd.getArg(1);
3305 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3309 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3310 f1 << "\n" << f2 << "\n" );
3311 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3312 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3322 void GuiView::openChildDocument(string const & fname)
3324 LASSERT(documentBufferView(), return);
3325 Buffer & buffer = documentBufferView()->buffer();
3326 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3327 documentBufferView()->saveBookmark(false);
3329 if (theBufferList().exists(filename)) {
3330 child = theBufferList().getBuffer(filename);
3333 message(bformat(_("Opening child document %1$s..."),
3334 makeDisplayPath(filename.absFileName())));
3335 child = loadDocument(filename, false);
3337 // Set the parent name of the child document.
3338 // This makes insertion of citations and references in the child work,
3339 // when the target is in the parent or another child document.
3341 child->setParent(&buffer);
3345 bool GuiView::goToFileRow(string const & argument)
3349 size_t i = argument.find_last_of(' ');
3350 if (i != string::npos) {
3351 file_name = os::internal_path(trim(argument.substr(0, i)));
3352 istringstream is(argument.substr(i + 1));
3357 if (i == string::npos) {
3358 LYXERR0("Wrong argument: " << argument);
3362 string const abstmp = package().temp_dir().absFileName();
3363 string const realtmp = package().temp_dir().realPath();
3364 // We have to use os::path_prefix_is() here, instead of
3365 // simply prefixIs(), because the file name comes from
3366 // an external application and may need case adjustment.
3367 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3368 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3369 // Needed by inverse dvi search. If it is a file
3370 // in tmpdir, call the apropriated function.
3371 // If tmpdir is a symlink, we may have the real
3372 // path passed back, so we correct for that.
3373 if (!prefixIs(file_name, abstmp))
3374 file_name = subst(file_name, realtmp, abstmp);
3375 buf = theBufferList().getBufferFromTmp(file_name);
3377 // Must replace extension of the file to be .lyx
3378 // and get full path
3379 FileName const s = fileSearch(string(),
3380 support::changeExtension(file_name, ".lyx"), "lyx");
3381 // Either change buffer or load the file
3382 if (theBufferList().exists(s))
3383 buf = theBufferList().getBuffer(s);
3384 else if (s.exists()) {
3385 buf = loadDocument(s);
3390 _("File does not exist: %1$s"),
3391 makeDisplayPath(file_name)));
3397 _("No buffer for file: %1$s."),
3398 makeDisplayPath(file_name))
3403 bool success = documentBufferView()->setCursorFromRow(row);
3405 LYXERR(Debug::LATEX,
3406 "setCursorFromRow: invalid position for row " << row);
3407 frontend::Alert::error(_("Inverse Search Failed"),
3408 _("Invalid position requested by inverse search.\n"
3409 "You may need to update the viewed document."));
3416 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3418 Buffer::ExportStatus const status = func(format);
3420 // the cloning operation will have produced a clone of the entire set of
3421 // documents, starting from the master. so we must delete those.
3422 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3424 busyBuffers.remove(orig);
3429 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3431 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3432 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3436 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3438 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3439 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3443 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3445 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3446 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3450 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3451 string const & argument,
3452 Buffer const * used_buffer,
3453 docstring const & msg,
3454 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3455 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3456 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3461 string format = argument;
3463 format = used_buffer->params().getDefaultOutputFormat();
3464 processing_format = format;
3466 progress_->clearMessages();
3469 #if EXPORT_in_THREAD
3470 GuiViewPrivate::busyBuffers.insert(used_buffer);
3471 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3472 if (!cloned_buffer) {
3473 Alert::error(_("Export Error"),
3474 _("Error cloning the Buffer."));
3477 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3482 setPreviewFuture(f);
3483 last_export_format = used_buffer->params().bufferFormat();
3486 // We are asynchronous, so we don't know here anything about the success
3489 Buffer::ExportStatus status;
3491 status = (used_buffer->*syncFunc)(format, true);
3492 } else if (previewFunc) {
3493 status = (used_buffer->*previewFunc)(format);
3496 handleExportStatus(gv_, status, format);
3498 return (status == Buffer::ExportSuccess
3499 || status == Buffer::PreviewSuccess);
3503 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3505 BufferView * bv = currentBufferView();
3506 LASSERT(bv, return);
3508 // Let the current BufferView dispatch its own actions.
3509 bv->dispatch(cmd, dr);
3510 if (dr.dispatched())
3513 // Try with the document BufferView dispatch if any.
3514 BufferView * doc_bv = documentBufferView();
3515 if (doc_bv && doc_bv != bv) {
3516 doc_bv->dispatch(cmd, dr);
3517 if (dr.dispatched())
3521 // Then let the current Cursor dispatch its own actions.
3522 bv->cursor().dispatch(cmd);
3524 // update completion. We do it here and not in
3525 // processKeySym to avoid another redraw just for a
3526 // changed inline completion
3527 if (cmd.origin() == FuncRequest::KEYBOARD) {
3528 if (cmd.action() == LFUN_SELF_INSERT
3529 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3530 updateCompletion(bv->cursor(), true, true);
3531 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3532 updateCompletion(bv->cursor(), false, true);
3534 updateCompletion(bv->cursor(), false, false);
3537 dr = bv->cursor().result();
3541 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3543 BufferView * bv = currentBufferView();
3544 // By default we won't need any update.
3545 dr.screenUpdate(Update::None);
3546 // assume cmd will be dispatched
3547 dr.dispatched(true);
3549 Buffer * doc_buffer = documentBufferView()
3550 ? &(documentBufferView()->buffer()) : 0;
3552 if (cmd.origin() == FuncRequest::TOC) {
3553 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3554 // FIXME: do we need to pass a DispatchResult object here?
3555 toc->doDispatch(bv->cursor(), cmd);
3559 string const argument = to_utf8(cmd.argument());
3561 switch(cmd.action()) {
3562 case LFUN_BUFFER_CHILD_OPEN:
3563 openChildDocument(to_utf8(cmd.argument()));
3566 case LFUN_BUFFER_IMPORT:
3567 importDocument(to_utf8(cmd.argument()));
3570 case LFUN_BUFFER_EXPORT: {
3573 // GCC only sees strfwd.h when building merged
3574 if (::lyx::operator==(cmd.argument(), "custom")) {
3575 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3579 string const dest = cmd.getArg(1);
3580 FileName target_dir;
3581 if (!dest.empty() && FileName::isAbsolute(dest))
3582 target_dir = FileName(support::onlyPath(dest));
3584 target_dir = doc_buffer->fileName().onlyPath();
3586 if ((dest.empty() && doc_buffer->isUnnamed())
3587 || !target_dir.isDirWritable()) {
3588 exportBufferAs(*doc_buffer, cmd.argument());
3591 /* TODO/Review: Is it a problem to also export the children?
3592 See the update_unincluded flag */
3593 d.asyncBufferProcessing(argument,
3596 &GuiViewPrivate::exportAndDestroy,
3599 // TODO Inform user about success
3603 case LFUN_BUFFER_EXPORT_AS: {
3604 LASSERT(doc_buffer, break);
3605 docstring f = cmd.argument();
3607 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3608 exportBufferAs(*doc_buffer, f);
3612 case LFUN_BUFFER_UPDATE: {
3613 d.asyncBufferProcessing(argument,
3616 &GuiViewPrivate::compileAndDestroy,
3621 case LFUN_BUFFER_VIEW: {
3622 d.asyncBufferProcessing(argument,
3624 _("Previewing ..."),
3625 &GuiViewPrivate::previewAndDestroy,
3630 case LFUN_MASTER_BUFFER_UPDATE: {
3631 d.asyncBufferProcessing(argument,
3632 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3634 &GuiViewPrivate::compileAndDestroy,
3639 case LFUN_MASTER_BUFFER_VIEW: {
3640 d.asyncBufferProcessing(argument,
3641 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3643 &GuiViewPrivate::previewAndDestroy,
3644 0, &Buffer::preview);
3647 case LFUN_BUFFER_SWITCH: {
3648 string const file_name = to_utf8(cmd.argument());
3649 if (!FileName::isAbsolute(file_name)) {
3651 dr.setMessage(_("Absolute filename expected."));
3655 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3658 dr.setMessage(_("Document not loaded"));
3662 // Do we open or switch to the buffer in this view ?
3663 if (workArea(*buffer)
3664 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3669 // Look for the buffer in other views
3670 QList<int> const ids = guiApp->viewIds();
3672 for (; i != ids.size(); ++i) {
3673 GuiView & gv = guiApp->view(ids[i]);
3674 if (gv.workArea(*buffer)) {
3676 gv.activateWindow();
3678 gv.setBuffer(buffer);
3683 // If necessary, open a new window as a last resort
3684 if (i == ids.size()) {
3685 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3691 case LFUN_BUFFER_NEXT:
3692 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3695 case LFUN_BUFFER_MOVE_NEXT:
3696 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3699 case LFUN_BUFFER_PREVIOUS:
3700 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3703 case LFUN_BUFFER_MOVE_PREVIOUS:
3704 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3707 case LFUN_COMMAND_EXECUTE: {
3708 command_execute_ = true;
3709 minibuffer_focus_ = true;
3712 case LFUN_DROP_LAYOUTS_CHOICE:
3713 d.layout_->showPopup();
3716 case LFUN_MENU_OPEN:
3717 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3718 menu->exec(QCursor::pos());
3721 case LFUN_FILE_INSERT:
3722 insertLyXFile(cmd.argument());
3725 case LFUN_FILE_INSERT_PLAINTEXT:
3726 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3727 string const fname = to_utf8(cmd.argument());
3728 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3729 dr.setMessage(_("Absolute filename expected."));
3733 FileName filename(fname);
3734 if (fname.empty()) {
3735 FileDialog dlg(qt_("Select file to insert"));
3737 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3738 QStringList(qt_("All Files (*)")));
3740 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3741 dr.setMessage(_("Canceled."));
3745 filename.set(fromqstr(result.second));
3749 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3750 bv->dispatch(new_cmd, dr);
3755 case LFUN_BUFFER_RELOAD: {
3756 LASSERT(doc_buffer, break);
3759 if (!doc_buffer->isClean()) {
3760 docstring const file =
3761 makeDisplayPath(doc_buffer->absFileName(), 20);
3762 docstring text = bformat(_("Any changes will be lost. "
3763 "Are you sure you want to revert to the saved version "
3764 "of the document %1$s?"), file);
3765 ret = Alert::prompt(_("Revert to saved document?"),
3766 text, 1, 1, _("&Revert"), _("&Cancel"));
3770 doc_buffer->markClean();
3771 reloadBuffer(*doc_buffer);
3772 dr.forceBufferUpdate();
3777 case LFUN_BUFFER_WRITE:
3778 LASSERT(doc_buffer, break);
3779 saveBuffer(*doc_buffer);
3782 case LFUN_BUFFER_WRITE_AS:
3783 LASSERT(doc_buffer, break);
3784 renameBuffer(*doc_buffer, cmd.argument());
3787 case LFUN_BUFFER_WRITE_ALL: {
3788 Buffer * first = theBufferList().first();
3791 message(_("Saving all documents..."));
3792 // We cannot use a for loop as the buffer list cycles.
3795 if (!b->isClean()) {
3797 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3799 b = theBufferList().next(b);
3800 } while (b != first);
3801 dr.setMessage(_("All documents saved."));
3805 case LFUN_BUFFER_CLOSE:
3809 case LFUN_BUFFER_CLOSE_ALL:
3813 case LFUN_TOOLBAR_TOGGLE: {
3814 string const name = cmd.getArg(0);
3815 if (GuiToolbar * t = toolbar(name))
3820 case LFUN_DIALOG_UPDATE: {
3821 string const name = to_utf8(cmd.argument());
3822 if (name == "prefs" || name == "document")
3823 updateDialog(name, string());
3824 else if (name == "paragraph")
3825 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3826 else if (currentBufferView()) {
3827 Inset * inset = currentBufferView()->editedInset(name);
3828 // Can only update a dialog connected to an existing inset
3830 // FIXME: get rid of this indirection; GuiView ask the inset
3831 // if he is kind enough to update itself...
3832 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3833 //FIXME: pass DispatchResult here?
3834 inset->dispatch(currentBufferView()->cursor(), fr);
3840 case LFUN_DIALOG_TOGGLE: {
3841 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3842 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3843 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3847 case LFUN_DIALOG_DISCONNECT_INSET:
3848 disconnectDialog(to_utf8(cmd.argument()));
3851 case LFUN_DIALOG_HIDE: {
3852 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3856 case LFUN_DIALOG_SHOW: {
3857 string const name = cmd.getArg(0);
3858 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3860 if (name == "character") {
3861 data = freefont2string();
3863 showDialog("character", data);
3864 } else if (name == "latexlog") {
3865 Buffer::LogType type;
3866 string const logfile = doc_buffer->logName(&type);
3868 case Buffer::latexlog:
3871 case Buffer::buildlog:
3875 data += Lexer::quoteString(logfile);
3876 showDialog("log", data);
3877 } else if (name == "vclog") {
3878 string const data = "vc " +
3879 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3880 showDialog("log", data);
3881 } else if (name == "symbols") {
3882 data = bv->cursor().getEncoding()->name();
3884 showDialog("symbols", data);
3886 } else if (name == "prefs" && isFullScreen()) {
3887 lfunUiToggle("fullscreen");
3888 showDialog("prefs", data);
3890 showDialog(name, data);
3895 dr.setMessage(cmd.argument());
3898 case LFUN_UI_TOGGLE: {
3899 string arg = cmd.getArg(0);
3900 if (!lfunUiToggle(arg)) {
3901 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3902 dr.setMessage(bformat(msg, from_utf8(arg)));
3904 // Make sure the keyboard focus stays in the work area.
3909 case LFUN_VIEW_SPLIT: {
3910 LASSERT(doc_buffer, break);
3911 string const orientation = cmd.getArg(0);
3912 d.splitter_->setOrientation(orientation == "vertical"
3913 ? Qt::Vertical : Qt::Horizontal);
3914 TabWorkArea * twa = addTabWorkArea();
3915 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3916 setCurrentWorkArea(wa);
3919 case LFUN_TAB_GROUP_CLOSE:
3920 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3921 closeTabWorkArea(twa);
3922 d.current_work_area_ = 0;
3923 twa = d.currentTabWorkArea();
3924 // Switch to the next GuiWorkArea in the found TabWorkArea.
3926 // Make sure the work area is up to date.
3927 setCurrentWorkArea(twa->currentWorkArea());
3929 setCurrentWorkArea(0);
3934 case LFUN_VIEW_CLOSE:
3935 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3936 closeWorkArea(twa->currentWorkArea());
3937 d.current_work_area_ = 0;
3938 twa = d.currentTabWorkArea();
3939 // Switch to the next GuiWorkArea in the found TabWorkArea.
3941 // Make sure the work area is up to date.
3942 setCurrentWorkArea(twa->currentWorkArea());
3944 setCurrentWorkArea(0);
3949 case LFUN_COMPLETION_INLINE:
3950 if (d.current_work_area_)
3951 d.current_work_area_->completer().showInline();
3954 case LFUN_COMPLETION_POPUP:
3955 if (d.current_work_area_)
3956 d.current_work_area_->completer().showPopup();
3961 if (d.current_work_area_)
3962 d.current_work_area_->completer().tab();
3965 case LFUN_COMPLETION_CANCEL:
3966 if (d.current_work_area_) {
3967 if (d.current_work_area_->completer().popupVisible())
3968 d.current_work_area_->completer().hidePopup();
3970 d.current_work_area_->completer().hideInline();
3974 case LFUN_COMPLETION_ACCEPT:
3975 if (d.current_work_area_)
3976 d.current_work_area_->completer().activate();
3979 case LFUN_BUFFER_ZOOM_IN:
3980 case LFUN_BUFFER_ZOOM_OUT: {
3981 // use a signed temp to avoid overflow
3982 int zoom = lyxrc.zoom;
3983 if (cmd.argument().empty()) {
3984 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3989 zoom += convert<int>(cmd.argument());
3991 if (zoom < static_cast<int>(zoom_min_))
3995 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3997 // The global QPixmapCache is used in GuiPainter to cache text
3998 // painting so we must reset it.
3999 QPixmapCache::clear();
4000 guiApp->fontLoader().update();
4001 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4005 case LFUN_VC_REGISTER:
4006 case LFUN_VC_RENAME:
4008 case LFUN_VC_CHECK_IN:
4009 case LFUN_VC_CHECK_OUT:
4010 case LFUN_VC_REPO_UPDATE:
4011 case LFUN_VC_LOCKING_TOGGLE:
4012 case LFUN_VC_REVERT:
4013 case LFUN_VC_UNDO_LAST:
4014 case LFUN_VC_COMMAND:
4015 case LFUN_VC_COMPARE:
4016 dispatchVC(cmd, dr);
4019 case LFUN_SERVER_GOTO_FILE_ROW:
4020 goToFileRow(to_utf8(cmd.argument()));
4023 case LFUN_LYX_ACTIVATE:
4027 case LFUN_FORWARD_SEARCH: {
4028 // it seems safe to assume we have a document buffer, since
4029 // getStatus wants one.
4030 // coverity[FORWARD_NULL]
4031 Buffer const * doc_master = doc_buffer->masterBuffer();
4032 FileName const path(doc_master->temppath());
4033 string const texname = doc_master->isChild(doc_buffer)
4034 ? DocFileName(changeExtension(
4035 doc_buffer->absFileName(),
4036 "tex")).mangledFileName()
4037 : doc_buffer->latexName();
4038 string const fulltexname =
4039 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4040 string const mastername =
4041 removeExtension(doc_master->latexName());
4042 FileName const dviname(addName(path.absFileName(),
4043 addExtension(mastername, "dvi")));
4044 FileName const pdfname(addName(path.absFileName(),
4045 addExtension(mastername, "pdf")));
4046 bool const have_dvi = dviname.exists();
4047 bool const have_pdf = pdfname.exists();
4048 if (!have_dvi && !have_pdf) {
4049 dr.setMessage(_("Please, preview the document first."));
4052 string outname = dviname.onlyFileName();
4053 string command = lyxrc.forward_search_dvi;
4054 if (!have_dvi || (have_pdf &&
4055 pdfname.lastModified() > dviname.lastModified())) {
4056 outname = pdfname.onlyFileName();
4057 command = lyxrc.forward_search_pdf;
4060 DocIterator cur = bv->cursor();
4061 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4062 LYXERR(Debug::ACTION, "Forward search: row:" << row
4064 if (row == -1 || command.empty()) {
4065 dr.setMessage(_("Couldn't proceed."));
4068 string texrow = convert<string>(row);
4070 command = subst(command, "$$n", texrow);
4071 command = subst(command, "$$f", fulltexname);
4072 command = subst(command, "$$t", texname);
4073 command = subst(command, "$$o", outname);
4075 PathChanger p(path);
4077 one.startscript(Systemcall::DontWait, command);
4081 case LFUN_SPELLING_CONTINUOUSLY:
4082 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4083 dr.screenUpdate(Update::Force | Update::FitCursor);
4087 // The LFUN must be for one of BufferView, Buffer or Cursor;
4089 dispatchToBufferView(cmd, dr);
4093 // Part of automatic menu appearance feature.
4094 if (isFullScreen()) {
4095 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4099 // Need to update bv because many LFUNs here might have destroyed it
4100 bv = currentBufferView();
4102 // Clear non-empty selections
4103 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4105 Cursor & cur = bv->cursor();
4106 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4107 cur.clearSelection();
4113 bool GuiView::lfunUiToggle(string const & ui_component)
4115 if (ui_component == "scrollbar") {
4116 // hide() is of no help
4117 if (d.current_work_area_->verticalScrollBarPolicy() ==
4118 Qt::ScrollBarAlwaysOff)
4120 d.current_work_area_->setVerticalScrollBarPolicy(
4121 Qt::ScrollBarAsNeeded);
4123 d.current_work_area_->setVerticalScrollBarPolicy(
4124 Qt::ScrollBarAlwaysOff);
4125 } else if (ui_component == "statusbar") {
4126 statusBar()->setVisible(!statusBar()->isVisible());
4127 } else if (ui_component == "menubar") {
4128 menuBar()->setVisible(!menuBar()->isVisible());
4130 if (ui_component == "frame") {
4132 getContentsMargins(&l, &t, &r, &b);
4133 //are the frames in default state?
4134 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4136 setContentsMargins(-2, -2, -2, -2);
4138 setContentsMargins(0, 0, 0, 0);
4141 if (ui_component == "fullscreen") {
4149 void GuiView::toggleFullScreen()
4151 if (isFullScreen()) {
4152 for (int i = 0; i != d.splitter_->count(); ++i)
4153 d.tabWorkArea(i)->setFullScreen(false);
4154 setContentsMargins(0, 0, 0, 0);
4155 setWindowState(windowState() ^ Qt::WindowFullScreen);
4158 statusBar()->show();
4161 hideDialogs("prefs", 0);
4162 for (int i = 0; i != d.splitter_->count(); ++i)
4163 d.tabWorkArea(i)->setFullScreen(true);
4164 setContentsMargins(-2, -2, -2, -2);
4166 setWindowState(windowState() ^ Qt::WindowFullScreen);
4167 if (lyxrc.full_screen_statusbar)
4168 statusBar()->hide();
4169 if (lyxrc.full_screen_menubar)
4171 if (lyxrc.full_screen_toolbars) {
4172 ToolbarMap::iterator end = d.toolbars_.end();
4173 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4178 // give dialogs like the TOC a chance to adapt
4183 Buffer const * GuiView::updateInset(Inset const * inset)
4188 Buffer const * inset_buffer = &(inset->buffer());
4190 for (int i = 0; i != d.splitter_->count(); ++i) {
4191 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4194 Buffer const * buffer = &(wa->bufferView().buffer());
4195 if (inset_buffer == buffer)
4196 wa->scheduleRedraw();
4198 return inset_buffer;
4202 void GuiView::restartCursor()
4204 /* When we move around, or type, it's nice to be able to see
4205 * the cursor immediately after the keypress.
4207 if (d.current_work_area_)
4208 d.current_work_area_->startBlinkingCursor();
4210 // Take this occasion to update the other GUI elements.
4216 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4218 if (d.current_work_area_)
4219 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4224 // This list should be kept in sync with the list of insets in
4225 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4226 // dialog should have the same name as the inset.
4227 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4228 // docs in LyXAction.cpp.
4230 char const * const dialognames[] = {
4232 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4233 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4234 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4235 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4236 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4237 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4238 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4239 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4241 char const * const * const end_dialognames =
4242 dialognames + (sizeof(dialognames) / sizeof(char *));
4246 cmpCStr(char const * name) : name_(name) {}
4247 bool operator()(char const * other) {
4248 return strcmp(other, name_) == 0;
4255 bool isValidName(string const & name)
4257 return find_if(dialognames, end_dialognames,
4258 cmpCStr(name.c_str())) != end_dialognames;
4264 void GuiView::resetDialogs()
4266 // Make sure that no LFUN uses any GuiView.
4267 guiApp->setCurrentView(0);
4271 constructToolbars();
4272 guiApp->menus().fillMenuBar(menuBar(), this, false);
4273 d.layout_->updateContents(true);
4274 // Now update controls with current buffer.
4275 guiApp->setCurrentView(this);
4281 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4283 if (!isValidName(name))
4286 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4288 if (it != d.dialogs_.end()) {
4290 it->second->hideView();
4291 return it->second.get();
4294 Dialog * dialog = build(name);
4295 d.dialogs_[name].reset(dialog);
4296 if (lyxrc.allow_geometry_session)
4297 dialog->restoreSession();
4304 void GuiView::showDialog(string const & name, string const & data,
4307 triggerShowDialog(toqstr(name), toqstr(data), inset);
4311 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4317 const string name = fromqstr(qname);
4318 const string data = fromqstr(qdata);
4322 Dialog * dialog = findOrBuild(name, false);
4324 bool const visible = dialog->isVisibleView();
4325 dialog->showData(data);
4326 if (inset && currentBufferView())
4327 currentBufferView()->editInset(name, inset);
4328 // We only set the focus to the new dialog if it was not yet
4329 // visible in order not to change the existing previous behaviour
4331 // activateWindow is needed for floating dockviews
4332 dialog->asQWidget()->raise();
4333 dialog->asQWidget()->activateWindow();
4334 dialog->asQWidget()->setFocus();
4338 catch (ExceptionMessage const & ex) {
4346 bool GuiView::isDialogVisible(string const & name) const
4348 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4349 if (it == d.dialogs_.end())
4351 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4355 void GuiView::hideDialog(string const & name, Inset * inset)
4357 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4358 if (it == d.dialogs_.end())
4362 if (!currentBufferView())
4364 if (inset != currentBufferView()->editedInset(name))
4368 Dialog * const dialog = it->second.get();
4369 if (dialog->isVisibleView())
4371 if (currentBufferView())
4372 currentBufferView()->editInset(name, 0);
4376 void GuiView::disconnectDialog(string const & name)
4378 if (!isValidName(name))
4380 if (currentBufferView())
4381 currentBufferView()->editInset(name, 0);
4385 void GuiView::hideAll() const
4387 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4388 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4390 for(; it != end; ++it)
4391 it->second->hideView();
4395 void GuiView::updateDialogs()
4397 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4398 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4400 for(; it != end; ++it) {
4401 Dialog * dialog = it->second.get();
4403 if (dialog->needBufferOpen() && !documentBufferView())
4404 hideDialog(fromqstr(dialog->name()), 0);
4405 else if (dialog->isVisibleView())
4406 dialog->checkStatus();
4413 Dialog * createDialog(GuiView & lv, string const & name);
4415 // will be replaced by a proper factory...
4416 Dialog * createGuiAbout(GuiView & lv);
4417 Dialog * createGuiBibtex(GuiView & lv);
4418 Dialog * createGuiChanges(GuiView & lv);
4419 Dialog * createGuiCharacter(GuiView & lv);
4420 Dialog * createGuiCitation(GuiView & lv);
4421 Dialog * createGuiCompare(GuiView & lv);
4422 Dialog * createGuiCompareHistory(GuiView & lv);
4423 Dialog * createGuiDelimiter(GuiView & lv);
4424 Dialog * createGuiDocument(GuiView & lv);
4425 Dialog * createGuiErrorList(GuiView & lv);
4426 Dialog * createGuiExternal(GuiView & lv);
4427 Dialog * createGuiGraphics(GuiView & lv);
4428 Dialog * createGuiInclude(GuiView & lv);
4429 Dialog * createGuiIndex(GuiView & lv);
4430 Dialog * createGuiListings(GuiView & lv);
4431 Dialog * createGuiLog(GuiView & lv);
4432 Dialog * createGuiMathMatrix(GuiView & lv);
4433 Dialog * createGuiNote(GuiView & lv);
4434 Dialog * createGuiParagraph(GuiView & lv);
4435 Dialog * createGuiPhantom(GuiView & lv);
4436 Dialog * createGuiPreferences(GuiView & lv);
4437 Dialog * createGuiPrint(GuiView & lv);
4438 Dialog * createGuiPrintindex(GuiView & lv);
4439 Dialog * createGuiRef(GuiView & lv);
4440 Dialog * createGuiSearch(GuiView & lv);
4441 Dialog * createGuiSearchAdv(GuiView & lv);
4442 Dialog * createGuiSendTo(GuiView & lv);
4443 Dialog * createGuiShowFile(GuiView & lv);
4444 Dialog * createGuiSpellchecker(GuiView & lv);
4445 Dialog * createGuiSymbols(GuiView & lv);
4446 Dialog * createGuiTabularCreate(GuiView & lv);
4447 Dialog * createGuiTexInfo(GuiView & lv);
4448 Dialog * createGuiToc(GuiView & lv);
4449 Dialog * createGuiThesaurus(GuiView & lv);
4450 Dialog * createGuiViewSource(GuiView & lv);
4451 Dialog * createGuiWrap(GuiView & lv);
4452 Dialog * createGuiProgressView(GuiView & lv);
4456 Dialog * GuiView::build(string const & name)
4458 LASSERT(isValidName(name), return 0);
4460 Dialog * dialog = createDialog(*this, name);
4464 if (name == "aboutlyx")
4465 return createGuiAbout(*this);
4466 if (name == "bibtex")
4467 return createGuiBibtex(*this);
4468 if (name == "changes")
4469 return createGuiChanges(*this);
4470 if (name == "character")
4471 return createGuiCharacter(*this);
4472 if (name == "citation")
4473 return createGuiCitation(*this);
4474 if (name == "compare")
4475 return createGuiCompare(*this);
4476 if (name == "comparehistory")
4477 return createGuiCompareHistory(*this);
4478 if (name == "document")
4479 return createGuiDocument(*this);
4480 if (name == "errorlist")
4481 return createGuiErrorList(*this);
4482 if (name == "external")
4483 return createGuiExternal(*this);
4485 return createGuiShowFile(*this);
4486 if (name == "findreplace")
4487 return createGuiSearch(*this);
4488 if (name == "findreplaceadv")
4489 return createGuiSearchAdv(*this);
4490 if (name == "graphics")
4491 return createGuiGraphics(*this);
4492 if (name == "include")
4493 return createGuiInclude(*this);
4494 if (name == "index")
4495 return createGuiIndex(*this);
4496 if (name == "index_print")
4497 return createGuiPrintindex(*this);
4498 if (name == "listings")
4499 return createGuiListings(*this);
4501 return createGuiLog(*this);
4502 if (name == "mathdelimiter")
4503 return createGuiDelimiter(*this);
4504 if (name == "mathmatrix")
4505 return createGuiMathMatrix(*this);
4507 return createGuiNote(*this);
4508 if (name == "paragraph")
4509 return createGuiParagraph(*this);
4510 if (name == "phantom")
4511 return createGuiPhantom(*this);
4512 if (name == "prefs")
4513 return createGuiPreferences(*this);
4515 return createGuiRef(*this);
4516 if (name == "sendto")
4517 return createGuiSendTo(*this);
4518 if (name == "spellchecker")
4519 return createGuiSpellchecker(*this);
4520 if (name == "symbols")
4521 return createGuiSymbols(*this);
4522 if (name == "tabularcreate")
4523 return createGuiTabularCreate(*this);
4524 if (name == "texinfo")
4525 return createGuiTexInfo(*this);
4526 if (name == "thesaurus")
4527 return createGuiThesaurus(*this);
4529 return createGuiToc(*this);
4530 if (name == "view-source")
4531 return createGuiViewSource(*this);
4533 return createGuiWrap(*this);
4534 if (name == "progress")
4535 return createGuiProgressView(*this);
4541 } // namespace frontend
4544 #include "moc_GuiView.cpp"