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 = QPixmap(rosize);
582 QString imagedir = "images/";
583 FileName fname = imageLibFileSearch(imagedir, "emblem-readonly", "svgz");
584 QSvgRenderer renderer(toqstr(fname.absFileName()));
585 if (renderer.isValid()) {
586 readonly.fill(statusBar()->palette().color(QWidget::backgroundRole()));
587 QPainter painter(&readonly);
588 renderer.render(&painter);
590 readonly = getPixmap("images/", "emblem-readonly", "png").scaled(rosize, Qt::KeepAspectRatio);
592 read_only_ = new QLabel(statusBar());
593 read_only_->setPixmap(readonly);
594 read_only_->setScaledContents(true);
595 read_only_->setAlignment(Qt::AlignCenter);
597 statusBar()->addPermanentWidget(read_only_);
599 version_control_ = new QLabel(statusBar());
600 version_control_->setAlignment(Qt::AlignCenter);
601 version_control_->setFrameStyle(QFrame::StyledPanel);
602 version_control_->hide();
603 statusBar()->addPermanentWidget(version_control_);
605 statusBar()->setSizeGripEnabled(true);
608 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
609 SLOT(autoSaveThreadFinished()));
611 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
612 SLOT(processingThreadStarted()));
613 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
614 SLOT(processingThreadFinished()));
616 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
617 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
619 // Forbid too small unresizable window because it can happen
620 // with some window manager under X11.
621 setMinimumSize(300, 200);
623 if (lyxrc.allow_geometry_session) {
624 // Now take care of session management.
629 // no session handling, default to a sane size.
630 setGeometry(50, 50, 690, 510);
633 // clear session data if any.
635 settings.remove("views");
645 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
647 QVector<GuiWorkArea*> areas;
648 for (int i = 0; i < tabWorkAreaCount(); i++) {
649 TabWorkArea* ta = tabWorkArea(i);
650 for (int u = 0; u < ta->count(); u++) {
651 areas << ta->workArea(u);
657 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
658 string const & format)
660 docstring const fmt = formats.prettyName(format);
663 case Buffer::ExportSuccess:
664 msg = bformat(_("Successful export to format: %1$s"), fmt);
666 case Buffer::ExportCancel:
667 msg = _("Document export cancelled.");
669 case Buffer::ExportError:
670 case Buffer::ExportNoPathToFormat:
671 case Buffer::ExportTexPathHasSpaces:
672 case Buffer::ExportConverterError:
673 msg = bformat(_("Error while exporting format: %1$s"), fmt);
675 case Buffer::PreviewSuccess:
676 msg = bformat(_("Successful preview of format: %1$s"), fmt);
678 case Buffer::PreviewError:
679 msg = bformat(_("Error while previewing format: %1$s"), fmt);
686 void GuiView::processingThreadStarted()
691 void GuiView::processingThreadFinished()
693 QFutureWatcher<Buffer::ExportStatus> const * watcher =
694 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
696 Buffer::ExportStatus const status = watcher->result();
697 handleExportStatus(this, status, d.processing_format);
700 BufferView const * const bv = currentBufferView();
701 if (bv && !bv->buffer().errorList("Export").empty()) {
705 errors(d.last_export_format);
709 void GuiView::autoSaveThreadFinished()
711 QFutureWatcher<docstring> const * watcher =
712 static_cast<QFutureWatcher<docstring> const *>(sender());
713 message(watcher->result());
718 void GuiView::saveLayout() const
721 settings.beginGroup("views");
722 settings.beginGroup(QString::number(id_));
723 #if defined(Q_WS_X11) || defined(QPA_XCB)
724 settings.setValue("pos", pos());
725 settings.setValue("size", size());
727 settings.setValue("geometry", saveGeometry());
729 settings.setValue("layout", saveState(0));
730 settings.setValue("icon_size", iconSize());
734 void GuiView::saveUISettings() const
736 // Save the toolbar private states
737 ToolbarMap::iterator end = d.toolbars_.end();
738 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
739 it->second->saveSession();
740 // Now take care of all other dialogs
741 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
742 for (; it!= d.dialogs_.end(); ++it)
743 it->second->saveSession();
747 bool GuiView::restoreLayout()
750 settings.beginGroup("views");
751 settings.beginGroup(QString::number(id_));
752 QString const icon_key = "icon_size";
753 if (!settings.contains(icon_key))
756 //code below is skipped when when ~/.config/LyX is (re)created
757 QSize icon_size = settings.value(icon_key).toSize();
758 // Check whether session size changed.
759 if (icon_size.width() != int(d.smallIconSize) &&
760 icon_size.width() != int(d.normalIconSize) &&
761 icon_size.width() != int(d.bigIconSize) &&
762 icon_size.width() != int(d.hugeIconSize) &&
763 icon_size.width() != int(d.giantIconSize)) {
764 icon_size.setWidth(d.normalIconSize);
765 icon_size.setHeight(d.normalIconSize);
767 setIconSize(icon_size);
769 #if defined(Q_WS_X11) || defined(QPA_XCB)
770 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
771 QSize size = settings.value("size", QSize(690, 510)).toSize();
775 // Work-around for bug #6034: the window ends up in an undetermined
776 // state when trying to restore a maximized window when it is
777 // already maximized.
778 if (!(windowState() & Qt::WindowMaximized))
779 if (!restoreGeometry(settings.value("geometry").toByteArray()))
780 setGeometry(50, 50, 690, 510);
782 // Make sure layout is correctly oriented.
783 setLayoutDirection(qApp->layoutDirection());
785 // Allow the toc and view-source dock widget to be restored if needed.
787 if ((dialog = findOrBuild("toc", true)))
788 // see bug 5082. At least setup title and enabled state.
789 // Visibility will be adjusted by restoreState below.
790 dialog->prepareView();
791 if ((dialog = findOrBuild("view-source", true)))
792 dialog->prepareView();
793 if ((dialog = findOrBuild("progress", true)))
794 dialog->prepareView();
796 if (!restoreState(settings.value("layout").toByteArray(), 0))
799 // init the toolbars that have not been restored
800 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
801 Toolbars::Infos::iterator end = guiApp->toolbars().end();
802 for (; cit != end; ++cit) {
803 GuiToolbar * tb = toolbar(cit->name);
804 if (tb && !tb->isRestored())
805 initToolbar(cit->name);
813 GuiToolbar * GuiView::toolbar(string const & name)
815 ToolbarMap::iterator it = d.toolbars_.find(name);
816 if (it != d.toolbars_.end())
819 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
824 void GuiView::constructToolbars()
826 ToolbarMap::iterator it = d.toolbars_.begin();
827 for (; it != d.toolbars_.end(); ++it)
831 // I don't like doing this here, but the standard toolbar
832 // destroys this object when it's destroyed itself (vfr)
833 d.layout_ = new LayoutBox(*this);
834 d.stack_widget_->addWidget(d.layout_);
835 d.layout_->move(0,0);
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 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
845 void GuiView::initToolbars()
847 // extracts the toolbars from the backend
848 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
849 Toolbars::Infos::iterator end = guiApp->toolbars().end();
850 for (; cit != end; ++cit)
851 initToolbar(cit->name);
855 void GuiView::initToolbar(string const & name)
857 GuiToolbar * tb = toolbar(name);
860 int const visibility = guiApp->toolbars().defaultVisibility(name);
861 bool newline = !(visibility & Toolbars::SAMEROW);
862 tb->setVisible(false);
863 tb->setVisibility(visibility);
865 if (visibility & Toolbars::TOP) {
867 addToolBarBreak(Qt::TopToolBarArea);
868 addToolBar(Qt::TopToolBarArea, tb);
871 if (visibility & Toolbars::BOTTOM) {
873 addToolBarBreak(Qt::BottomToolBarArea);
874 addToolBar(Qt::BottomToolBarArea, tb);
877 if (visibility & Toolbars::LEFT) {
879 addToolBarBreak(Qt::LeftToolBarArea);
880 addToolBar(Qt::LeftToolBarArea, tb);
883 if (visibility & Toolbars::RIGHT) {
885 addToolBarBreak(Qt::RightToolBarArea);
886 addToolBar(Qt::RightToolBarArea, tb);
889 if (visibility & Toolbars::ON)
890 tb->setVisible(true);
894 TocModels & GuiView::tocModels()
896 return d.toc_models_;
900 void GuiView::setFocus()
902 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
903 QMainWindow::setFocus();
907 bool GuiView::hasFocus() const
909 if (currentWorkArea())
910 return currentWorkArea()->hasFocus();
911 if (currentMainWorkArea())
912 return currentMainWorkArea()->hasFocus();
913 return d.bg_widget_->hasFocus();
917 void GuiView::focusInEvent(QFocusEvent * e)
919 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
920 QMainWindow::focusInEvent(e);
921 // Make sure guiApp points to the correct view.
922 guiApp->setCurrentView(this);
923 if (currentWorkArea())
924 currentWorkArea()->setFocus();
925 else if (currentMainWorkArea())
926 currentMainWorkArea()->setFocus();
928 d.bg_widget_->setFocus();
932 QMenu * GuiView::createPopupMenu()
934 return d.toolBarPopup(this);
938 void GuiView::showEvent(QShowEvent * e)
940 LYXERR(Debug::GUI, "Passed Geometry "
941 << size().height() << "x" << size().width()
942 << "+" << pos().x() << "+" << pos().y());
944 if (d.splitter_->count() == 0)
945 // No work area, switch to the background widget.
949 QMainWindow::showEvent(e);
953 bool GuiView::closeScheduled()
960 bool GuiView::prepareAllBuffersForLogout()
962 Buffer * first = theBufferList().first();
966 // First, iterate over all buffers and ask the users if unsaved
967 // changes should be saved.
968 // We cannot use a for loop as the buffer list cycles.
971 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
973 b = theBufferList().next(b);
974 } while (b != first);
976 // Next, save session state
977 // When a view/window was closed before without quitting LyX, there
978 // are already entries in the lastOpened list.
979 theSession().lastOpened().clear();
986 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
987 ** is responsibility of the container (e.g., dialog)
989 void GuiView::closeEvent(QCloseEvent * close_event)
991 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
993 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
994 Alert::warning(_("Exit LyX"),
995 _("LyX could not be closed because documents are being processed by LyX."));
996 close_event->setAccepted(false);
1000 // If the user pressed the x (so we didn't call closeView
1001 // programmatically), we want to clear all existing entries.
1003 theSession().lastOpened().clear();
1008 // it can happen that this event arrives without selecting the view,
1009 // e.g. when clicking the close button on a background window.
1011 if (!closeWorkAreaAll()) {
1013 close_event->ignore();
1017 // Make sure that nothing will use this to be closed View.
1018 guiApp->unregisterView(this);
1020 if (isFullScreen()) {
1021 // Switch off fullscreen before closing.
1026 // Make sure the timer time out will not trigger a statusbar update.
1027 d.statusbar_timer_.stop();
1029 // Saving fullscreen requires additional tweaks in the toolbar code.
1030 // It wouldn't also work under linux natively.
1031 if (lyxrc.allow_geometry_session) {
1036 close_event->accept();
1040 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1042 if (event->mimeData()->hasUrls())
1044 /// \todo Ask lyx-devel is this is enough:
1045 /// if (event->mimeData()->hasFormat("text/plain"))
1046 /// event->acceptProposedAction();
1050 void GuiView::dropEvent(QDropEvent * event)
1052 QList<QUrl> files = event->mimeData()->urls();
1053 if (files.isEmpty())
1056 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1057 for (int i = 0; i != files.size(); ++i) {
1058 string const file = os::internal_path(fromqstr(
1059 files.at(i).toLocalFile()));
1063 string const ext = support::getExtension(file);
1064 vector<const Format *> found_formats;
1066 // Find all formats that have the correct extension.
1067 vector<const Format *> const & import_formats
1068 = theConverters().importableFormats();
1069 vector<const Format *>::const_iterator it = import_formats.begin();
1070 for (; it != import_formats.end(); ++it)
1071 if ((*it)->hasExtension(ext))
1072 found_formats.push_back(*it);
1075 if (found_formats.size() >= 1) {
1076 if (found_formats.size() > 1) {
1077 //FIXME: show a dialog to choose the correct importable format
1078 LYXERR(Debug::FILES,
1079 "Multiple importable formats found, selecting first");
1081 string const arg = found_formats[0]->name() + " " + file;
1082 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1085 //FIXME: do we have to explicitly check whether it's a lyx file?
1086 LYXERR(Debug::FILES,
1087 "No formats found, trying to open it as a lyx file");
1088 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1090 // add the functions to the queue
1091 guiApp->addToFuncRequestQueue(cmd);
1094 // now process the collected functions. We perform the events
1095 // asynchronously. This prevents potential problems in case the
1096 // BufferView is closed within an event.
1097 guiApp->processFuncRequestQueueAsync();
1101 void GuiView::message(docstring const & str)
1103 if (ForkedProcess::iAmAChild())
1106 // call is moved to GUI-thread by GuiProgress
1107 d.progress_->appendMessage(toqstr(str));
1111 void GuiView::clearMessageText()
1113 message(docstring());
1117 void GuiView::updateStatusBarMessage(QString const & str)
1119 statusBar()->showMessage(str);
1120 d.statusbar_timer_.stop();
1121 d.statusbar_timer_.start(3000);
1125 void GuiView::smallSizedIcons()
1127 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1131 void GuiView::normalSizedIcons()
1133 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1137 void GuiView::bigSizedIcons()
1139 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1143 void GuiView::hugeSizedIcons()
1145 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1149 void GuiView::giantSizedIcons()
1151 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1155 void GuiView::clearMessage()
1157 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1158 // the hasFocus function mostly returns false, even if the focus is on
1159 // a workarea in this view.
1163 d.statusbar_timer_.stop();
1167 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1169 if (wa != d.current_work_area_
1170 || wa->bufferView().buffer().isInternal())
1172 Buffer const & buf = wa->bufferView().buffer();
1173 // Sets the path for the window: this is used by OSX to
1174 // allow a context click on the title bar showing a menu
1175 // with the path up to the file
1176 setWindowFilePath(toqstr(buf.absFileName()));
1177 // Tell Qt whether the current document is changed
1178 setWindowModified(!buf.isClean());
1179 // Set the windows title
1180 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1183 title += from_ascii(" ") + char_type(0x2014) + from_ascii(" LyX");
1185 setWindowTitle(toqstr(title));
1187 if (buf.isReadonly())
1192 if (buf.lyxvc().inUse()) {
1193 version_control_->show();
1194 if (buf.lyxvc().locking())
1195 version_control_->setText(
1196 toqstr(bformat(_("%1$s lock"),
1197 from_ascii(buf.lyxvc().vcname()))));
1199 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1201 version_control_->hide();
1205 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1207 if (d.current_work_area_)
1208 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1209 this, SLOT(setBusy(bool)));
1211 disconnectBufferView();
1212 connectBufferView(wa->bufferView());
1213 connectBuffer(wa->bufferView().buffer());
1214 d.current_work_area_ = wa;
1215 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1216 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1217 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1218 updateWindowTitle(wa);
1222 // The document settings needs to be reinitialised.
1223 updateDialog("document", "");
1225 // Buffer-dependent dialogs must be updated. This is done here because
1226 // some dialogs require buffer()->text.
1231 void GuiView::on_lastWorkAreaRemoved()
1234 // We already are in a close event. Nothing more to do.
1237 if (d.splitter_->count() > 1)
1238 // We have a splitter so don't close anything.
1241 // Reset and updates the dialogs.
1242 d.toc_models_.reset(0);
1243 updateDialog("document", "");
1249 if (lyxrc.open_buffers_in_tabs)
1250 // Nothing more to do, the window should stay open.
1253 if (guiApp->viewIds().size() > 1) {
1259 // On Mac we also close the last window because the application stay
1260 // resident in memory. On other platforms we don't close the last
1261 // window because this would quit the application.
1267 void GuiView::updateStatusBar()
1269 // let the user see the explicit message
1270 if (d.statusbar_timer_.isActive())
1277 void GuiView::showMessage()
1281 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1282 if (msg.isEmpty()) {
1283 BufferView const * bv = currentBufferView();
1285 msg = toqstr(bv->cursor().currentState());
1287 msg = qt_("Welcome to LyX!");
1289 statusBar()->showMessage(msg);
1293 bool GuiView::event(QEvent * e)
1297 // Useful debug code:
1298 //case QEvent::ActivationChange:
1299 //case QEvent::WindowDeactivate:
1300 //case QEvent::Paint:
1301 //case QEvent::Enter:
1302 //case QEvent::Leave:
1303 //case QEvent::HoverEnter:
1304 //case QEvent::HoverLeave:
1305 //case QEvent::HoverMove:
1306 //case QEvent::StatusTip:
1307 //case QEvent::DragEnter:
1308 //case QEvent::DragLeave:
1309 //case QEvent::Drop:
1312 case QEvent::WindowActivate: {
1313 GuiView * old_view = guiApp->currentView();
1314 if (this == old_view) {
1316 return QMainWindow::event(e);
1318 if (old_view && old_view->currentBufferView()) {
1319 // save current selection to the selection buffer to allow
1320 // middle-button paste in this window.
1321 cap::saveSelection(old_view->currentBufferView()->cursor());
1323 guiApp->setCurrentView(this);
1324 if (d.current_work_area_) {
1325 BufferView & bv = d.current_work_area_->bufferView();
1326 connectBufferView(bv);
1327 connectBuffer(bv.buffer());
1328 // The document structure, name and dialogs might have
1329 // changed in another view.
1331 // The document settings needs to be reinitialised.
1332 updateDialog("document", "");
1338 return QMainWindow::event(e);
1341 case QEvent::ShortcutOverride: {
1343 if (isFullScreen() && menuBar()->isHidden()) {
1344 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1345 // FIXME: we should also try to detect special LyX shortcut such as
1346 // Alt-P and Alt-M. Right now there is a hack in
1347 // GuiWorkArea::processKeySym() that hides again the menubar for
1349 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1351 return QMainWindow::event(e);
1354 return QMainWindow::event(e);
1358 return QMainWindow::event(e);
1362 void GuiView::resetWindowTitle()
1364 setWindowTitle(qt_("LyX"));
1367 bool GuiView::focusNextPrevChild(bool /*next*/)
1374 bool GuiView::busy() const
1380 void GuiView::setBusy(bool busy)
1382 bool const busy_before = busy_ > 0;
1383 busy ? ++busy_ : --busy_;
1384 if ((busy_ > 0) == busy_before)
1385 // busy state didn't change
1389 QApplication::setOverrideCursor(Qt::WaitCursor);
1392 QApplication::restoreOverrideCursor();
1397 void GuiView::resetCommandExecute()
1399 command_execute_ = false;
1404 double GuiView::pixelRatio() const
1406 #if QT_VERSION >= 0x050000
1407 return devicePixelRatio();
1414 GuiWorkArea * GuiView::workArea(int index)
1416 if (TabWorkArea * twa = d.currentTabWorkArea())
1417 if (index < twa->count())
1418 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1423 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1425 if (currentWorkArea()
1426 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1427 return currentWorkArea();
1428 if (TabWorkArea * twa = d.currentTabWorkArea())
1429 return twa->workArea(buffer);
1434 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1436 // Automatically create a TabWorkArea if there are none yet.
1437 TabWorkArea * tab_widget = d.splitter_->count()
1438 ? d.currentTabWorkArea() : addTabWorkArea();
1439 return tab_widget->addWorkArea(buffer, *this);
1443 TabWorkArea * GuiView::addTabWorkArea()
1445 TabWorkArea * twa = new TabWorkArea;
1446 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1447 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1448 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1449 this, SLOT(on_lastWorkAreaRemoved()));
1451 d.splitter_->addWidget(twa);
1452 d.stack_widget_->setCurrentWidget(d.splitter_);
1457 GuiWorkArea const * GuiView::currentWorkArea() const
1459 return d.current_work_area_;
1463 GuiWorkArea * GuiView::currentWorkArea()
1465 return d.current_work_area_;
1469 GuiWorkArea const * GuiView::currentMainWorkArea() const
1471 if (!d.currentTabWorkArea())
1473 return d.currentTabWorkArea()->currentWorkArea();
1477 GuiWorkArea * GuiView::currentMainWorkArea()
1479 if (!d.currentTabWorkArea())
1481 return d.currentTabWorkArea()->currentWorkArea();
1485 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1487 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1489 d.current_work_area_ = 0;
1494 // FIXME: I've no clue why this is here and why it accesses
1495 // theGuiApp()->currentView, which might be 0 (bug 6464).
1496 // See also 27525 (vfr).
1497 if (theGuiApp()->currentView() == this
1498 && theGuiApp()->currentView()->currentWorkArea() == wa)
1501 if (currentBufferView())
1502 cap::saveSelection(currentBufferView()->cursor());
1504 theGuiApp()->setCurrentView(this);
1505 d.current_work_area_ = wa;
1507 // We need to reset this now, because it will need to be
1508 // right if the tabWorkArea gets reset in the for loop. We
1509 // will change it back if we aren't in that case.
1510 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1511 d.current_main_work_area_ = wa;
1513 for (int i = 0; i != d.splitter_->count(); ++i) {
1514 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1515 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1516 << ", Current main wa: " << currentMainWorkArea());
1521 d.current_main_work_area_ = old_cmwa;
1523 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1524 on_currentWorkAreaChanged(wa);
1525 BufferView & bv = wa->bufferView();
1526 bv.cursor().fixIfBroken();
1528 wa->setUpdatesEnabled(true);
1529 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1533 void GuiView::removeWorkArea(GuiWorkArea * wa)
1535 LASSERT(wa, return);
1536 if (wa == d.current_work_area_) {
1538 disconnectBufferView();
1539 d.current_work_area_ = 0;
1540 d.current_main_work_area_ = 0;
1543 bool found_twa = false;
1544 for (int i = 0; i != d.splitter_->count(); ++i) {
1545 TabWorkArea * twa = d.tabWorkArea(i);
1546 if (twa->removeWorkArea(wa)) {
1547 // Found in this tab group, and deleted the GuiWorkArea.
1549 if (twa->count() != 0) {
1550 if (d.current_work_area_ == 0)
1551 // This means that we are closing the current GuiWorkArea, so
1552 // switch to the next GuiWorkArea in the found TabWorkArea.
1553 setCurrentWorkArea(twa->currentWorkArea());
1555 // No more WorkAreas in this tab group, so delete it.
1562 // It is not a tabbed work area (i.e., the search work area), so it
1563 // should be deleted by other means.
1564 LASSERT(found_twa, return);
1566 if (d.current_work_area_ == 0) {
1567 if (d.splitter_->count() != 0) {
1568 TabWorkArea * twa = d.currentTabWorkArea();
1569 setCurrentWorkArea(twa->currentWorkArea());
1571 // No more work areas, switch to the background widget.
1572 setCurrentWorkArea(0);
1578 LayoutBox * GuiView::getLayoutDialog() const
1584 void GuiView::updateLayoutList()
1587 d.layout_->updateContents(false);
1591 void GuiView::updateToolbars()
1593 ToolbarMap::iterator end = d.toolbars_.end();
1594 if (d.current_work_area_) {
1596 if (d.current_work_area_->bufferView().cursor().inMathed()
1597 && !d.current_work_area_->bufferView().cursor().inRegexped())
1598 context |= Toolbars::MATH;
1599 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1600 context |= Toolbars::TABLE;
1601 if (currentBufferView()->buffer().areChangesPresent()
1602 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1603 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1604 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1605 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1606 context |= Toolbars::REVIEW;
1607 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1608 context |= Toolbars::MATHMACROTEMPLATE;
1609 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1610 context |= Toolbars::IPA;
1611 if (command_execute_)
1612 context |= Toolbars::MINIBUFFER;
1613 if (minibuffer_focus_) {
1614 context |= Toolbars::MINIBUFFER_FOCUS;
1615 minibuffer_focus_ = false;
1618 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1619 it->second->update(context);
1621 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1622 it->second->update();
1626 void GuiView::setBuffer(Buffer * newBuffer)
1628 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1629 LASSERT(newBuffer, return);
1631 GuiWorkArea * wa = workArea(*newBuffer);
1634 newBuffer->masterBuffer()->updateBuffer();
1636 wa = addWorkArea(*newBuffer);
1637 // scroll to the position when the BufferView was last closed
1638 if (lyxrc.use_lastfilepos) {
1639 LastFilePosSection::FilePos filepos =
1640 theSession().lastFilePos().load(newBuffer->fileName());
1641 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1644 //Disconnect the old buffer...there's no new one.
1647 connectBuffer(*newBuffer);
1648 connectBufferView(wa->bufferView());
1649 setCurrentWorkArea(wa);
1653 void GuiView::connectBuffer(Buffer & buf)
1655 buf.setGuiDelegate(this);
1659 void GuiView::disconnectBuffer()
1661 if (d.current_work_area_)
1662 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1666 void GuiView::connectBufferView(BufferView & bv)
1668 bv.setGuiDelegate(this);
1672 void GuiView::disconnectBufferView()
1674 if (d.current_work_area_)
1675 d.current_work_area_->bufferView().setGuiDelegate(0);
1679 void GuiView::errors(string const & error_type, bool from_master)
1681 BufferView const * const bv = currentBufferView();
1685 #if EXPORT_in_THREAD
1686 // We are called with from_master == false by default, so we
1687 // have to figure out whether that is the case or not.
1688 ErrorList & el = bv->buffer().errorList(error_type);
1690 el = bv->buffer().masterBuffer()->errorList(error_type);
1694 ErrorList const & el = from_master ?
1695 bv->buffer().masterBuffer()->errorList(error_type) :
1696 bv->buffer().errorList(error_type);
1702 string data = error_type;
1704 data = "from_master|" + error_type;
1705 showDialog("errorlist", data);
1709 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1711 d.toc_models_.updateItem(toqstr(type), dit);
1715 void GuiView::structureChanged()
1717 // FIXME: This is slightly expensive, though less than the tocBackend update
1718 // (#9880). This also resets the view in the Toc Widget (#6675).
1719 d.toc_models_.reset(documentBufferView());
1720 // Navigator needs more than a simple update in this case. It needs to be
1722 updateDialog("toc", "");
1726 void GuiView::updateDialog(string const & name, string const & data)
1728 if (!isDialogVisible(name))
1731 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1732 if (it == d.dialogs_.end())
1735 Dialog * const dialog = it->second.get();
1736 if (dialog->isVisibleView())
1737 dialog->initialiseParams(data);
1741 BufferView * GuiView::documentBufferView()
1743 return currentMainWorkArea()
1744 ? ¤tMainWorkArea()->bufferView()
1749 BufferView const * GuiView::documentBufferView() const
1751 return currentMainWorkArea()
1752 ? ¤tMainWorkArea()->bufferView()
1757 BufferView * GuiView::currentBufferView()
1759 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1763 BufferView const * GuiView::currentBufferView() const
1765 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1769 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1770 Buffer const * orig, Buffer * clone)
1772 bool const success = clone->autoSave();
1774 busyBuffers.remove(orig);
1776 ? _("Automatic save done.")
1777 : _("Automatic save failed!");
1781 void GuiView::autoSave()
1783 LYXERR(Debug::INFO, "Running autoSave()");
1785 Buffer * buffer = documentBufferView()
1786 ? &documentBufferView()->buffer() : 0;
1788 resetAutosaveTimers();
1792 GuiViewPrivate::busyBuffers.insert(buffer);
1793 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1794 buffer, buffer->cloneBufferOnly());
1795 d.autosave_watcher_.setFuture(f);
1796 resetAutosaveTimers();
1800 void GuiView::resetAutosaveTimers()
1803 d.autosave_timeout_.restart();
1807 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1810 Buffer * buf = currentBufferView()
1811 ? ¤tBufferView()->buffer() : 0;
1812 Buffer * doc_buffer = documentBufferView()
1813 ? &(documentBufferView()->buffer()) : 0;
1816 /* In LyX/Mac, when a dialog is open, the menus of the
1817 application can still be accessed without giving focus to
1818 the main window. In this case, we want to disable the menu
1819 entries that are buffer-related.
1820 This code must not be used on Linux and Windows, since it
1821 would disable buffer-related entries when hovering over the
1822 menu (see bug #9574).
1824 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1830 // Check whether we need a buffer
1831 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1832 // no, exit directly
1833 flag.message(from_utf8(N_("Command not allowed with"
1834 "out any document open")));
1835 flag.setEnabled(false);
1839 if (cmd.origin() == FuncRequest::TOC) {
1840 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1841 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1842 flag.setEnabled(false);
1846 switch(cmd.action()) {
1847 case LFUN_BUFFER_IMPORT:
1850 case LFUN_MASTER_BUFFER_UPDATE:
1851 case LFUN_MASTER_BUFFER_VIEW:
1853 && (doc_buffer->parent() != 0
1854 || doc_buffer->hasChildren())
1855 && !d.processing_thread_watcher_.isRunning();
1858 case LFUN_BUFFER_UPDATE:
1859 case LFUN_BUFFER_VIEW: {
1860 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1864 string format = to_utf8(cmd.argument());
1865 if (cmd.argument().empty())
1866 format = doc_buffer->params().getDefaultOutputFormat();
1867 enable = doc_buffer->params().isExportableFormat(format);
1871 case LFUN_BUFFER_RELOAD:
1872 enable = doc_buffer && !doc_buffer->isUnnamed()
1873 && doc_buffer->fileName().exists()
1874 && (!doc_buffer->isClean()
1875 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1878 case LFUN_BUFFER_CHILD_OPEN:
1879 enable = doc_buffer != 0;
1882 case LFUN_BUFFER_WRITE:
1883 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1886 //FIXME: This LFUN should be moved to GuiApplication.
1887 case LFUN_BUFFER_WRITE_ALL: {
1888 // We enable the command only if there are some modified buffers
1889 Buffer * first = theBufferList().first();
1894 // We cannot use a for loop as the buffer list is a cycle.
1896 if (!b->isClean()) {
1900 b = theBufferList().next(b);
1901 } while (b != first);
1905 case LFUN_BUFFER_WRITE_AS:
1906 case LFUN_BUFFER_EXPORT_AS:
1907 enable = doc_buffer != 0;
1910 case LFUN_BUFFER_CLOSE:
1911 case LFUN_VIEW_CLOSE:
1912 enable = doc_buffer != 0;
1915 case LFUN_BUFFER_CLOSE_ALL:
1916 enable = theBufferList().last() != theBufferList().first();
1919 case LFUN_VIEW_SPLIT:
1920 if (cmd.getArg(0) == "vertical")
1921 enable = doc_buffer && (d.splitter_->count() == 1 ||
1922 d.splitter_->orientation() == Qt::Vertical);
1924 enable = doc_buffer && (d.splitter_->count() == 1 ||
1925 d.splitter_->orientation() == Qt::Horizontal);
1928 case LFUN_TAB_GROUP_CLOSE:
1929 enable = d.tabWorkAreaCount() > 1;
1932 case LFUN_TOOLBAR_TOGGLE: {
1933 string const name = cmd.getArg(0);
1934 if (GuiToolbar * t = toolbar(name))
1935 flag.setOnOff(t->isVisible());
1938 docstring const msg =
1939 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1945 case LFUN_DROP_LAYOUTS_CHOICE:
1949 case LFUN_UI_TOGGLE:
1950 flag.setOnOff(isFullScreen());
1953 case LFUN_DIALOG_DISCONNECT_INSET:
1956 case LFUN_DIALOG_HIDE:
1957 // FIXME: should we check if the dialog is shown?
1960 case LFUN_DIALOG_TOGGLE:
1961 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1962 // fall through to set "enable"
1963 case LFUN_DIALOG_SHOW: {
1964 string const name = cmd.getArg(0);
1966 enable = name == "aboutlyx"
1967 || name == "file" //FIXME: should be removed.
1969 || name == "texinfo"
1970 || name == "progress"
1971 || name == "compare";
1972 else if (name == "character" || name == "symbols"
1973 || name == "mathdelimiter" || name == "mathmatrix") {
1974 if (!buf || buf->isReadonly())
1977 Cursor const & cur = currentBufferView()->cursor();
1978 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1981 else if (name == "latexlog")
1982 enable = FileName(doc_buffer->logName()).isReadableFile();
1983 else if (name == "spellchecker")
1984 enable = theSpellChecker()
1985 && !doc_buffer->isReadonly()
1986 && !doc_buffer->text().empty();
1987 else if (name == "vclog")
1988 enable = doc_buffer->lyxvc().inUse();
1992 case LFUN_DIALOG_UPDATE: {
1993 string const name = cmd.getArg(0);
1995 enable = name == "prefs";
1999 case LFUN_COMMAND_EXECUTE:
2001 case LFUN_MENU_OPEN:
2002 // Nothing to check.
2005 case LFUN_COMPLETION_INLINE:
2006 if (!d.current_work_area_
2007 || !d.current_work_area_->completer().inlinePossible(
2008 currentBufferView()->cursor()))
2012 case LFUN_COMPLETION_POPUP:
2013 if (!d.current_work_area_
2014 || !d.current_work_area_->completer().popupPossible(
2015 currentBufferView()->cursor()))
2020 if (!d.current_work_area_
2021 || !d.current_work_area_->completer().inlinePossible(
2022 currentBufferView()->cursor()))
2026 case LFUN_COMPLETION_ACCEPT:
2027 if (!d.current_work_area_
2028 || (!d.current_work_area_->completer().popupVisible()
2029 && !d.current_work_area_->completer().inlineVisible()
2030 && !d.current_work_area_->completer().completionAvailable()))
2034 case LFUN_COMPLETION_CANCEL:
2035 if (!d.current_work_area_
2036 || (!d.current_work_area_->completer().popupVisible()
2037 && !d.current_work_area_->completer().inlineVisible()))
2041 case LFUN_BUFFER_ZOOM_OUT:
2042 case LFUN_BUFFER_ZOOM_IN: {
2043 // only diff between these two is that the default for ZOOM_OUT
2045 bool const neg_zoom =
2046 convert<int>(cmd.argument()) < 0 ||
2047 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2048 if (lyxrc.zoom <= 10 && neg_zoom) {
2049 flag.message(_("Zoom level cannot be less than 10%."));
2052 enable = doc_buffer;
2055 case LFUN_BUFFER_MOVE_NEXT:
2056 case LFUN_BUFFER_MOVE_PREVIOUS:
2057 // we do not cycle when moving
2058 case LFUN_BUFFER_NEXT:
2059 case LFUN_BUFFER_PREVIOUS:
2060 // because we cycle, it doesn't matter whether on first or last
2061 enable = (d.currentTabWorkArea()->count() > 1);
2063 case LFUN_BUFFER_SWITCH:
2064 // toggle on the current buffer, but do not toggle off
2065 // the other ones (is that a good idea?)
2067 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2068 flag.setOnOff(true);
2071 case LFUN_VC_REGISTER:
2072 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2074 case LFUN_VC_RENAME:
2075 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2078 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2080 case LFUN_VC_CHECK_IN:
2081 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2083 case LFUN_VC_CHECK_OUT:
2084 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2086 case LFUN_VC_LOCKING_TOGGLE:
2087 enable = doc_buffer && !doc_buffer->isReadonly()
2088 && doc_buffer->lyxvc().lockingToggleEnabled();
2089 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2091 case LFUN_VC_REVERT:
2092 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2094 case LFUN_VC_UNDO_LAST:
2095 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2097 case LFUN_VC_REPO_UPDATE:
2098 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2100 case LFUN_VC_COMMAND: {
2101 if (cmd.argument().empty())
2103 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2107 case LFUN_VC_COMPARE:
2108 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2111 case LFUN_SERVER_GOTO_FILE_ROW:
2112 case LFUN_LYX_ACTIVATE:
2114 case LFUN_FORWARD_SEARCH:
2115 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2118 case LFUN_FILE_INSERT_PLAINTEXT:
2119 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2120 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2123 case LFUN_SPELLING_CONTINUOUSLY:
2124 flag.setOnOff(lyxrc.spellcheck_continuously);
2132 flag.setEnabled(false);
2138 static FileName selectTemplateFile()
2140 FileDialog dlg(qt_("Select template file"));
2141 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2142 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2144 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2145 QStringList(qt_("LyX Documents (*.lyx)")));
2147 if (result.first == FileDialog::Later)
2149 if (result.second.isEmpty())
2151 return FileName(fromqstr(result.second));
2155 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2159 Buffer * newBuffer = 0;
2161 newBuffer = checkAndLoadLyXFile(filename);
2162 } catch (ExceptionMessage const & e) {
2169 message(_("Document not loaded."));
2173 setBuffer(newBuffer);
2174 newBuffer->errors("Parse");
2177 theSession().lastFiles().add(filename);
2183 void GuiView::openDocument(string const & fname)
2185 string initpath = lyxrc.document_path;
2187 if (documentBufferView()) {
2188 string const trypath = documentBufferView()->buffer().filePath();
2189 // If directory is writeable, use this as default.
2190 if (FileName(trypath).isDirWritable())
2196 if (fname.empty()) {
2197 FileDialog dlg(qt_("Select document to open"));
2198 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2199 dlg.setButton2(qt_("Examples|#E#e"),
2200 toqstr(addPath(package().system_support().absFileName(), "examples")));
2202 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2203 FileDialog::Result result =
2204 dlg.open(toqstr(initpath), filter);
2206 if (result.first == FileDialog::Later)
2209 filename = fromqstr(result.second);
2211 // check selected filename
2212 if (filename.empty()) {
2213 message(_("Canceled."));
2219 // get absolute path of file and add ".lyx" to the filename if
2221 FileName const fullname =
2222 fileSearch(string(), filename, "lyx", support::may_not_exist);
2223 if (!fullname.empty())
2224 filename = fullname.absFileName();
2226 if (!fullname.onlyPath().isDirectory()) {
2227 Alert::warning(_("Invalid filename"),
2228 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2229 from_utf8(fullname.absFileName())));
2233 // if the file doesn't exist and isn't already open (bug 6645),
2234 // let the user create one
2235 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2236 !LyXVC::file_not_found_hook(fullname)) {
2237 // the user specifically chose this name. Believe him.
2238 Buffer * const b = newFile(filename, string(), true);
2244 docstring const disp_fn = makeDisplayPath(filename);
2245 message(bformat(_("Opening document %1$s..."), disp_fn));
2248 Buffer * buf = loadDocument(fullname);
2250 str2 = bformat(_("Document %1$s opened."), disp_fn);
2251 if (buf->lyxvc().inUse())
2252 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2253 " " + _("Version control detected.");
2255 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2260 // FIXME: clean that
2261 static bool import(GuiView * lv, FileName const & filename,
2262 string const & format, ErrorList & errorList)
2264 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2266 string loader_format;
2267 vector<string> loaders = theConverters().loaders();
2268 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2269 vector<string>::const_iterator it = loaders.begin();
2270 vector<string>::const_iterator en = loaders.end();
2271 for (; it != en; ++it) {
2272 if (!theConverters().isReachable(format, *it))
2275 string const tofile =
2276 support::changeExtension(filename.absFileName(),
2277 formats.extension(*it));
2278 if (!theConverters().convert(0, filename, FileName(tofile),
2279 filename, format, *it, errorList))
2281 loader_format = *it;
2284 if (loader_format.empty()) {
2285 frontend::Alert::error(_("Couldn't import file"),
2286 bformat(_("No information for importing the format %1$s."),
2287 formats.prettyName(format)));
2291 loader_format = format;
2293 if (loader_format == "lyx") {
2294 Buffer * buf = lv->loadDocument(lyxfile);
2298 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2302 bool as_paragraphs = loader_format == "textparagraph";
2303 string filename2 = (loader_format == format) ? filename.absFileName()
2304 : support::changeExtension(filename.absFileName(),
2305 formats.extension(loader_format));
2306 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2308 guiApp->setCurrentView(lv);
2309 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2316 void GuiView::importDocument(string const & argument)
2319 string filename = split(argument, format, ' ');
2321 LYXERR(Debug::INFO, format << " file: " << filename);
2323 // need user interaction
2324 if (filename.empty()) {
2325 string initpath = lyxrc.document_path;
2326 if (documentBufferView()) {
2327 string const trypath = documentBufferView()->buffer().filePath();
2328 // If directory is writeable, use this as default.
2329 if (FileName(trypath).isDirWritable())
2333 docstring const text = bformat(_("Select %1$s file to import"),
2334 formats.prettyName(format));
2336 FileDialog dlg(toqstr(text));
2337 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2338 dlg.setButton2(qt_("Examples|#E#e"),
2339 toqstr(addPath(package().system_support().absFileName(), "examples")));
2341 docstring filter = formats.prettyName(format);
2344 filter += from_utf8(formats.extensions(format));
2347 FileDialog::Result result =
2348 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2350 if (result.first == FileDialog::Later)
2353 filename = fromqstr(result.second);
2355 // check selected filename
2356 if (filename.empty())
2357 message(_("Canceled."));
2360 if (filename.empty())
2363 // get absolute path of file
2364 FileName const fullname(support::makeAbsPath(filename));
2366 // Can happen if the user entered a path into the dialog
2368 if (fullname.onlyFileName().empty()) {
2369 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2370 "Aborting import."),
2371 from_utf8(fullname.absFileName()));
2372 frontend::Alert::error(_("File name error"), msg);
2373 message(_("Canceled."));
2378 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2380 // Check if the document already is open
2381 Buffer * buf = theBufferList().getBuffer(lyxfile);
2384 if (!closeBuffer()) {
2385 message(_("Canceled."));
2390 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2392 // if the file exists already, and we didn't do
2393 // -i lyx thefile.lyx, warn
2394 if (lyxfile.exists() && fullname != lyxfile) {
2396 docstring text = bformat(_("The document %1$s already exists.\n\n"
2397 "Do you want to overwrite that document?"), displaypath);
2398 int const ret = Alert::prompt(_("Overwrite document?"),
2399 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2402 message(_("Canceled."));
2407 message(bformat(_("Importing %1$s..."), displaypath));
2408 ErrorList errorList;
2409 if (import(this, fullname, format, errorList))
2410 message(_("imported."));
2412 message(_("file not imported!"));
2414 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2418 void GuiView::newDocument(string const & filename, bool from_template)
2420 FileName initpath(lyxrc.document_path);
2421 if (documentBufferView()) {
2422 FileName const trypath(documentBufferView()->buffer().filePath());
2423 // If directory is writeable, use this as default.
2424 if (trypath.isDirWritable())
2428 string templatefile;
2429 if (from_template) {
2430 templatefile = selectTemplateFile().absFileName();
2431 if (templatefile.empty())
2436 if (filename.empty())
2437 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2439 b = newFile(filename, templatefile, true);
2444 // If no new document could be created, it is unsure
2445 // whether there is a valid BufferView.
2446 if (currentBufferView())
2447 // Ensure the cursor is correctly positioned on screen.
2448 currentBufferView()->showCursor();
2452 void GuiView::insertLyXFile(docstring const & fname)
2454 BufferView * bv = documentBufferView();
2459 FileName filename(to_utf8(fname));
2460 if (filename.empty()) {
2461 // Launch a file browser
2463 string initpath = lyxrc.document_path;
2464 string const trypath = bv->buffer().filePath();
2465 // If directory is writeable, use this as default.
2466 if (FileName(trypath).isDirWritable())
2470 FileDialog dlg(qt_("Select LyX document to insert"));
2471 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2472 dlg.setButton2(qt_("Examples|#E#e"),
2473 toqstr(addPath(package().system_support().absFileName(),
2476 FileDialog::Result result = dlg.open(toqstr(initpath),
2477 QStringList(qt_("LyX Documents (*.lyx)")));
2479 if (result.first == FileDialog::Later)
2483 filename.set(fromqstr(result.second));
2485 // check selected filename
2486 if (filename.empty()) {
2487 // emit message signal.
2488 message(_("Canceled."));
2493 bv->insertLyXFile(filename);
2494 bv->buffer().errors("Parse");
2498 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2500 FileName fname = b.fileName();
2501 FileName const oldname = fname;
2503 if (!newname.empty()) {
2505 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2507 // Switch to this Buffer.
2510 // No argument? Ask user through dialog.
2512 FileDialog dlg(qt_("Choose a filename to save document as"));
2513 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2514 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2516 if (!isLyXFileName(fname.absFileName()))
2517 fname.changeExtension(".lyx");
2519 FileDialog::Result result =
2520 dlg.save(toqstr(fname.onlyPath().absFileName()),
2521 QStringList(qt_("LyX Documents (*.lyx)")),
2522 toqstr(fname.onlyFileName()));
2524 if (result.first == FileDialog::Later)
2527 fname.set(fromqstr(result.second));
2532 if (!isLyXFileName(fname.absFileName()))
2533 fname.changeExtension(".lyx");
2536 // fname is now the new Buffer location.
2538 // if there is already a Buffer open with this name, we do not want
2539 // to have another one. (the second test makes sure we're not just
2540 // trying to overwrite ourselves, which is fine.)
2541 if (theBufferList().exists(fname) && fname != oldname
2542 && theBufferList().getBuffer(fname) != &b) {
2543 docstring const text =
2544 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2545 "Please close it before attempting to overwrite it.\n"
2546 "Do you want to choose a new filename?"),
2547 from_utf8(fname.absFileName()));
2548 int const ret = Alert::prompt(_("Chosen File Already Open"),
2549 text, 0, 1, _("&Rename"), _("&Cancel"));
2551 case 0: return renameBuffer(b, docstring(), kind);
2552 case 1: return false;
2557 bool const existsLocal = fname.exists();
2558 bool const existsInVC = LyXVC::fileInVC(fname);
2559 if (existsLocal || existsInVC) {
2560 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2561 if (kind != LV_WRITE_AS && existsInVC) {
2562 // renaming to a name that is already in VC
2564 docstring text = bformat(_("The document %1$s "
2565 "is already registered.\n\n"
2566 "Do you want to choose a new name?"),
2568 docstring const title = (kind == LV_VC_RENAME) ?
2569 _("Rename document?") : _("Copy document?");
2570 docstring const button = (kind == LV_VC_RENAME) ?
2571 _("&Rename") : _("&Copy");
2572 int const ret = Alert::prompt(title, text, 0, 1,
2573 button, _("&Cancel"));
2575 case 0: return renameBuffer(b, docstring(), kind);
2576 case 1: return false;
2581 docstring text = bformat(_("The document %1$s "
2582 "already exists.\n\n"
2583 "Do you want to overwrite that document?"),
2585 int const ret = Alert::prompt(_("Overwrite document?"),
2586 text, 0, 2, _("&Overwrite"),
2587 _("&Rename"), _("&Cancel"));
2590 case 1: return renameBuffer(b, docstring(), kind);
2591 case 2: return false;
2597 case LV_VC_RENAME: {
2598 string msg = b.lyxvc().rename(fname);
2601 message(from_utf8(msg));
2605 string msg = b.lyxvc().copy(fname);
2608 message(from_utf8(msg));
2614 // LyXVC created the file already in case of LV_VC_RENAME or
2615 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2616 // relative paths of included stuff right if we moved e.g. from
2617 // /a/b.lyx to /a/c/b.lyx.
2619 bool const saved = saveBuffer(b, fname);
2626 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2628 FileName fname = b.fileName();
2630 FileDialog dlg(qt_("Choose a filename to export the document as"));
2631 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2634 QString const anyformat = qt_("Guess from extension (*.*)");
2637 vector<Format const *> export_formats;
2638 for (Format const & f : formats)
2639 if (f.documentFormat())
2640 export_formats.push_back(&f);
2641 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2642 map<QString, string> fmap;
2645 for (Format const * f : export_formats) {
2646 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2647 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2649 from_ascii(f->extension())));
2650 types << loc_filter;
2651 fmap[loc_filter] = f->name();
2652 if (from_ascii(f->name()) == iformat) {
2653 filter = loc_filter;
2654 ext = f->extension();
2657 string ofname = fname.onlyFileName();
2659 ofname = support::changeExtension(ofname, ext);
2660 FileDialog::Result result =
2661 dlg.save(toqstr(fname.onlyPath().absFileName()),
2665 if (result.first != FileDialog::Chosen)
2669 fname.set(fromqstr(result.second));
2670 if (filter == anyformat)
2671 fmt_name = formats.getFormatFromExtension(fname.extension());
2673 fmt_name = fmap[filter];
2674 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2675 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2677 if (fmt_name.empty() || fname.empty())
2680 // fname is now the new Buffer location.
2681 if (FileName(fname).exists()) {
2682 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2683 docstring text = bformat(_("The document %1$s already "
2684 "exists.\n\nDo you want to "
2685 "overwrite that document?"),
2687 int const ret = Alert::prompt(_("Overwrite document?"),
2688 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2691 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2692 case 2: return false;
2696 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2699 return dr.dispatched();
2703 bool GuiView::saveBuffer(Buffer & b)
2705 return saveBuffer(b, FileName());
2709 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2711 if (workArea(b) && workArea(b)->inDialogMode())
2714 if (fn.empty() && b.isUnnamed())
2715 return renameBuffer(b, docstring());
2717 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2719 theSession().lastFiles().add(b.fileName());
2723 // Switch to this Buffer.
2726 // FIXME: we don't tell the user *WHY* the save failed !!
2727 docstring const file = makeDisplayPath(b.absFileName(), 30);
2728 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2729 "Do you want to rename the document and "
2730 "try again?"), file);
2731 int const ret = Alert::prompt(_("Rename and save?"),
2732 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2735 if (!renameBuffer(b, docstring()))
2744 return saveBuffer(b, fn);
2748 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2750 return closeWorkArea(wa, false);
2754 // We only want to close the buffer if it is not visible in other workareas
2755 // of the same view, nor in other views, and if this is not a child
2756 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2758 Buffer & buf = wa->bufferView().buffer();
2760 bool last_wa = d.countWorkAreasOf(buf) == 1
2761 && !inOtherView(buf) && !buf.parent();
2763 bool close_buffer = last_wa;
2766 if (lyxrc.close_buffer_with_last_view == "yes")
2768 else if (lyxrc.close_buffer_with_last_view == "no")
2769 close_buffer = false;
2772 if (buf.isUnnamed())
2773 file = from_utf8(buf.fileName().onlyFileName());
2775 file = buf.fileName().displayName(30);
2776 docstring const text = bformat(
2777 _("Last view on document %1$s is being closed.\n"
2778 "Would you like to close or hide the document?\n"
2780 "Hidden documents can be displayed back through\n"
2781 "the menu: View->Hidden->...\n"
2783 "To remove this question, set your preference in:\n"
2784 " Tools->Preferences->Look&Feel->UserInterface\n"
2786 int ret = Alert::prompt(_("Close or hide document?"),
2787 text, 0, 1, _("&Close"), _("&Hide"));
2788 close_buffer = (ret == 0);
2792 return closeWorkArea(wa, close_buffer);
2796 bool GuiView::closeBuffer()
2798 GuiWorkArea * wa = currentMainWorkArea();
2799 // coverity complained about this
2800 // it seems unnecessary, but perhaps is worth the check
2801 LASSERT(wa, return false);
2803 setCurrentWorkArea(wa);
2804 Buffer & buf = wa->bufferView().buffer();
2805 return closeWorkArea(wa, !buf.parent());
2809 void GuiView::writeSession() const {
2810 GuiWorkArea const * active_wa = currentMainWorkArea();
2811 for (int i = 0; i < d.splitter_->count(); ++i) {
2812 TabWorkArea * twa = d.tabWorkArea(i);
2813 for (int j = 0; j < twa->count(); ++j) {
2814 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2815 Buffer & buf = wa->bufferView().buffer();
2816 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2822 bool GuiView::closeBufferAll()
2824 // Close the workareas in all other views
2825 QList<int> const ids = guiApp->viewIds();
2826 for (int i = 0; i != ids.size(); ++i) {
2827 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2831 // Close our own workareas
2832 if (!closeWorkAreaAll())
2835 // Now close the hidden buffers. We prevent hidden buffers from being
2836 // dirty, so we can just close them.
2837 theBufferList().closeAll();
2842 bool GuiView::closeWorkAreaAll()
2844 setCurrentWorkArea(currentMainWorkArea());
2846 // We might be in a situation that there is still a tabWorkArea, but
2847 // there are no tabs anymore. This can happen when we get here after a
2848 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2849 // many TabWorkArea's have no documents anymore.
2852 // We have to call count() each time, because it can happen that
2853 // more than one splitter will disappear in one iteration (bug 5998).
2854 while (d.splitter_->count() > empty_twa) {
2855 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2857 if (twa->count() == 0)
2860 setCurrentWorkArea(twa->currentWorkArea());
2861 if (!closeTabWorkArea(twa))
2869 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2874 Buffer & buf = wa->bufferView().buffer();
2876 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2877 Alert::warning(_("Close document"),
2878 _("Document could not be closed because it is being processed by LyX."));
2883 return closeBuffer(buf);
2885 if (!inMultiTabs(wa))
2886 if (!saveBufferIfNeeded(buf, true))
2894 bool GuiView::closeBuffer(Buffer & buf)
2896 // If we are in a close_event all children will be closed in some time,
2897 // so no need to do it here. This will ensure that the children end up
2898 // in the session file in the correct order. If we close the master
2899 // buffer, we can close or release the child buffers here too.
2900 bool success = true;
2902 ListOfBuffers clist = buf.getChildren();
2903 ListOfBuffers::const_iterator it = clist.begin();
2904 ListOfBuffers::const_iterator const bend = clist.end();
2905 for (; it != bend; ++it) {
2906 Buffer * child_buf = *it;
2907 if (theBufferList().isOthersChild(&buf, child_buf)) {
2908 child_buf->setParent(0);
2912 // FIXME: should we look in other tabworkareas?
2913 // ANSWER: I don't think so. I've tested, and if the child is
2914 // open in some other window, it closes without a problem.
2915 GuiWorkArea * child_wa = workArea(*child_buf);
2917 success = closeWorkArea(child_wa, true);
2921 // In this case the child buffer is open but hidden.
2922 // It therefore should not (MUST NOT) be dirty!
2923 LATTEST(child_buf->isClean());
2924 theBufferList().release(child_buf);
2929 // goto bookmark to update bookmark pit.
2930 // FIXME: we should update only the bookmarks related to this buffer!
2931 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2932 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2933 guiApp->gotoBookmark(i+1, false, false);
2935 if (saveBufferIfNeeded(buf, false)) {
2936 buf.removeAutosaveFile();
2937 theBufferList().release(&buf);
2941 // open all children again to avoid a crash because of dangling
2942 // pointers (bug 6603)
2948 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2950 while (twa == d.currentTabWorkArea()) {
2951 twa->setCurrentIndex(twa->count() - 1);
2953 GuiWorkArea * wa = twa->currentWorkArea();
2954 Buffer & b = wa->bufferView().buffer();
2956 // We only want to close the buffer if the same buffer is not visible
2957 // in another view, and if this is not a child and if we are closing
2958 // a view (not a tabgroup).
2959 bool const close_buffer =
2960 !inOtherView(b) && !b.parent() && closing_;
2962 if (!closeWorkArea(wa, close_buffer))
2969 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2971 if (buf.isClean() || buf.paragraphs().empty())
2974 // Switch to this Buffer.
2979 if (buf.isUnnamed())
2980 file = from_utf8(buf.fileName().onlyFileName());
2982 file = buf.fileName().displayName(30);
2984 // Bring this window to top before asking questions.
2989 if (hiding && buf.isUnnamed()) {
2990 docstring const text = bformat(_("The document %1$s has not been "
2991 "saved yet.\n\nDo you want to save "
2992 "the document?"), file);
2993 ret = Alert::prompt(_("Save new document?"),
2994 text, 0, 1, _("&Save"), _("&Cancel"));
2998 docstring const text = bformat(_("The document %1$s has unsaved changes."
2999 "\n\nDo you want to save the document or discard the changes?"), file);
3000 ret = Alert::prompt(_("Save changed document?"),
3001 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
3006 if (!saveBuffer(buf))
3010 // If we crash after this we could have no autosave file
3011 // but I guess this is really improbable (Jug).
3012 // Sometimes improbable things happen:
3013 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3014 // buf.removeAutosaveFile();
3016 // revert all changes
3027 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3029 Buffer & buf = wa->bufferView().buffer();
3031 for (int i = 0; i != d.splitter_->count(); ++i) {
3032 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3033 if (wa_ && wa_ != wa)
3036 return inOtherView(buf);
3040 bool GuiView::inOtherView(Buffer & buf)
3042 QList<int> const ids = guiApp->viewIds();
3044 for (int i = 0; i != ids.size(); ++i) {
3048 if (guiApp->view(ids[i]).workArea(buf))
3055 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3057 if (!documentBufferView())
3060 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3061 Buffer * const curbuf = &documentBufferView()->buffer();
3062 int nwa = twa->count();
3063 for (int i = 0; i < nwa; ++i) {
3064 if (&workArea(i)->bufferView().buffer() == curbuf) {
3066 if (np == NEXTBUFFER)
3067 next_index = (i == nwa - 1 ? 0 : i + 1);
3069 next_index = (i == 0 ? nwa - 1 : i - 1);
3071 twa->moveTab(i, next_index);
3073 setBuffer(&workArea(next_index)->bufferView().buffer());
3081 /// make sure the document is saved
3082 static bool ensureBufferClean(Buffer * buffer)
3084 LASSERT(buffer, return false);
3085 if (buffer->isClean() && !buffer->isUnnamed())
3088 docstring const file = buffer->fileName().displayName(30);
3091 if (!buffer->isUnnamed()) {
3092 text = bformat(_("The document %1$s has unsaved "
3093 "changes.\n\nDo you want to save "
3094 "the document?"), file);
3095 title = _("Save changed document?");
3098 text = bformat(_("The document %1$s has not been "
3099 "saved yet.\n\nDo you want to save "
3100 "the document?"), file);
3101 title = _("Save new document?");
3103 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3106 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3108 return buffer->isClean() && !buffer->isUnnamed();
3112 bool GuiView::reloadBuffer(Buffer & buf)
3114 Buffer::ReadStatus status = buf.reload();
3115 return status == Buffer::ReadSuccess;
3119 void GuiView::checkExternallyModifiedBuffers()
3121 BufferList::iterator bit = theBufferList().begin();
3122 BufferList::iterator const bend = theBufferList().end();
3123 for (; bit != bend; ++bit) {
3124 Buffer * buf = *bit;
3125 if (buf->fileName().exists()
3126 && buf->isExternallyModified(Buffer::checksum_method)) {
3127 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3128 " Reload now? Any local changes will be lost."),
3129 from_utf8(buf->absFileName()));
3130 int const ret = Alert::prompt(_("Reload externally changed document?"),
3131 text, 0, 1, _("&Reload"), _("&Cancel"));
3139 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3141 Buffer * buffer = documentBufferView()
3142 ? &(documentBufferView()->buffer()) : 0;
3144 switch (cmd.action()) {
3145 case LFUN_VC_REGISTER:
3146 if (!buffer || !ensureBufferClean(buffer))
3148 if (!buffer->lyxvc().inUse()) {
3149 if (buffer->lyxvc().registrer()) {
3150 reloadBuffer(*buffer);
3151 dr.clearMessageUpdate();
3156 case LFUN_VC_RENAME:
3157 case LFUN_VC_COPY: {
3158 if (!buffer || !ensureBufferClean(buffer))
3160 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3161 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3162 // Some changes are not yet committed.
3163 // We test here and not in getStatus(), since
3164 // this test is expensive.
3166 LyXVC::CommandResult ret =
3167 buffer->lyxvc().checkIn(log);
3169 if (ret == LyXVC::ErrorCommand ||
3170 ret == LyXVC::VCSuccess)
3171 reloadBuffer(*buffer);
3172 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3173 frontend::Alert::error(
3174 _("Revision control error."),
3175 _("Document could not be checked in."));
3179 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3180 LV_VC_RENAME : LV_VC_COPY;
3181 renameBuffer(*buffer, cmd.argument(), kind);
3186 case LFUN_VC_CHECK_IN:
3187 if (!buffer || !ensureBufferClean(buffer))
3189 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3191 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3193 // Only skip reloading if the checkin was cancelled or
3194 // an error occurred before the real checkin VCS command
3195 // was executed, since the VCS might have changed the
3196 // file even if it could not checkin successfully.
3197 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3198 reloadBuffer(*buffer);
3202 case LFUN_VC_CHECK_OUT:
3203 if (!buffer || !ensureBufferClean(buffer))
3205 if (buffer->lyxvc().inUse()) {
3206 dr.setMessage(buffer->lyxvc().checkOut());
3207 reloadBuffer(*buffer);
3211 case LFUN_VC_LOCKING_TOGGLE:
3212 LASSERT(buffer, return);
3213 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3215 if (buffer->lyxvc().inUse()) {
3216 string res = buffer->lyxvc().lockingToggle();
3218 frontend::Alert::error(_("Revision control error."),
3219 _("Error when setting the locking property."));
3222 reloadBuffer(*buffer);
3227 case LFUN_VC_REVERT:
3228 LASSERT(buffer, return);
3229 if (buffer->lyxvc().revert()) {
3230 reloadBuffer(*buffer);
3231 dr.clearMessageUpdate();
3235 case LFUN_VC_UNDO_LAST:
3236 LASSERT(buffer, return);
3237 buffer->lyxvc().undoLast();
3238 reloadBuffer(*buffer);
3239 dr.clearMessageUpdate();
3242 case LFUN_VC_REPO_UPDATE:
3243 LASSERT(buffer, return);
3244 if (ensureBufferClean(buffer)) {
3245 dr.setMessage(buffer->lyxvc().repoUpdate());
3246 checkExternallyModifiedBuffers();
3250 case LFUN_VC_COMMAND: {
3251 string flag = cmd.getArg(0);
3252 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3255 if (contains(flag, 'M')) {
3256 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3259 string path = cmd.getArg(1);
3260 if (contains(path, "$$p") && buffer)
3261 path = subst(path, "$$p", buffer->filePath());
3262 LYXERR(Debug::LYXVC, "Directory: " << path);
3264 if (!pp.isReadableDirectory()) {
3265 lyxerr << _("Directory is not accessible.") << endl;
3268 support::PathChanger p(pp);
3270 string command = cmd.getArg(2);
3271 if (command.empty())
3274 command = subst(command, "$$i", buffer->absFileName());
3275 command = subst(command, "$$p", buffer->filePath());
3277 command = subst(command, "$$m", to_utf8(message));
3278 LYXERR(Debug::LYXVC, "Command: " << command);
3280 one.startscript(Systemcall::Wait, command);
3284 if (contains(flag, 'I'))
3285 buffer->markDirty();
3286 if (contains(flag, 'R'))
3287 reloadBuffer(*buffer);
3292 case LFUN_VC_COMPARE: {
3293 if (cmd.argument().empty()) {
3294 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3298 string rev1 = cmd.getArg(0);
3302 // it seems safe to assume we have a buffer
3303 // coverity[FORWARD_NULL]
3304 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3307 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3308 f2 = buffer->absFileName();
3310 string rev2 = cmd.getArg(1);
3314 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3318 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3319 f1 << "\n" << f2 << "\n" );
3320 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3321 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3331 void GuiView::openChildDocument(string const & fname)
3333 LASSERT(documentBufferView(), return);
3334 Buffer & buffer = documentBufferView()->buffer();
3335 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3336 documentBufferView()->saveBookmark(false);
3338 if (theBufferList().exists(filename)) {
3339 child = theBufferList().getBuffer(filename);
3342 message(bformat(_("Opening child document %1$s..."),
3343 makeDisplayPath(filename.absFileName())));
3344 child = loadDocument(filename, false);
3346 // Set the parent name of the child document.
3347 // This makes insertion of citations and references in the child work,
3348 // when the target is in the parent or another child document.
3350 child->setParent(&buffer);
3354 bool GuiView::goToFileRow(string const & argument)
3358 size_t i = argument.find_last_of(' ');
3359 if (i != string::npos) {
3360 file_name = os::internal_path(trim(argument.substr(0, i)));
3361 istringstream is(argument.substr(i + 1));
3366 if (i == string::npos) {
3367 LYXERR0("Wrong argument: " << argument);
3371 string const abstmp = package().temp_dir().absFileName();
3372 string const realtmp = package().temp_dir().realPath();
3373 // We have to use os::path_prefix_is() here, instead of
3374 // simply prefixIs(), because the file name comes from
3375 // an external application and may need case adjustment.
3376 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3377 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3378 // Needed by inverse dvi search. If it is a file
3379 // in tmpdir, call the apropriated function.
3380 // If tmpdir is a symlink, we may have the real
3381 // path passed back, so we correct for that.
3382 if (!prefixIs(file_name, abstmp))
3383 file_name = subst(file_name, realtmp, abstmp);
3384 buf = theBufferList().getBufferFromTmp(file_name);
3386 // Must replace extension of the file to be .lyx
3387 // and get full path
3388 FileName const s = fileSearch(string(),
3389 support::changeExtension(file_name, ".lyx"), "lyx");
3390 // Either change buffer or load the file
3391 if (theBufferList().exists(s))
3392 buf = theBufferList().getBuffer(s);
3393 else if (s.exists()) {
3394 buf = loadDocument(s);
3399 _("File does not exist: %1$s"),
3400 makeDisplayPath(file_name)));
3406 _("No buffer for file: %1$s."),
3407 makeDisplayPath(file_name))
3412 documentBufferView()->setCursorFromRow(row);
3418 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3420 Buffer::ExportStatus const status = func(format);
3422 // the cloning operation will have produced a clone of the entire set of
3423 // documents, starting from the master. so we must delete those.
3424 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3426 busyBuffers.remove(orig);
3431 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3433 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3434 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3438 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3440 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3441 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3445 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3447 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3448 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3452 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3453 string const & argument,
3454 Buffer const * used_buffer,
3455 docstring const & msg,
3456 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3457 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3458 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3463 string format = argument;
3465 format = used_buffer->params().getDefaultOutputFormat();
3466 processing_format = format;
3468 progress_->clearMessages();
3471 #if EXPORT_in_THREAD
3472 GuiViewPrivate::busyBuffers.insert(used_buffer);
3473 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3474 if (!cloned_buffer) {
3475 Alert::error(_("Export Error"),
3476 _("Error cloning the Buffer."));
3479 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3484 setPreviewFuture(f);
3485 last_export_format = used_buffer->params().bufferFormat();
3488 // We are asynchronous, so we don't know here anything about the success
3491 Buffer::ExportStatus status;
3493 status = (used_buffer->*syncFunc)(format, true);
3494 } else if (previewFunc) {
3495 status = (used_buffer->*previewFunc)(format);
3498 handleExportStatus(gv_, status, format);
3500 return (status == Buffer::ExportSuccess
3501 || status == Buffer::PreviewSuccess);
3505 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3507 BufferView * bv = currentBufferView();
3508 LASSERT(bv, return);
3510 // Let the current BufferView dispatch its own actions.
3511 bv->dispatch(cmd, dr);
3512 if (dr.dispatched())
3515 // Try with the document BufferView dispatch if any.
3516 BufferView * doc_bv = documentBufferView();
3517 if (doc_bv && doc_bv != bv) {
3518 doc_bv->dispatch(cmd, dr);
3519 if (dr.dispatched())
3523 // Then let the current Cursor dispatch its own actions.
3524 bv->cursor().dispatch(cmd);
3526 // update completion. We do it here and not in
3527 // processKeySym to avoid another redraw just for a
3528 // changed inline completion
3529 if (cmd.origin() == FuncRequest::KEYBOARD) {
3530 if (cmd.action() == LFUN_SELF_INSERT
3531 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3532 updateCompletion(bv->cursor(), true, true);
3533 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3534 updateCompletion(bv->cursor(), false, true);
3536 updateCompletion(bv->cursor(), false, false);
3539 dr = bv->cursor().result();
3543 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3545 BufferView * bv = currentBufferView();
3546 // By default we won't need any update.
3547 dr.screenUpdate(Update::None);
3548 // assume cmd will be dispatched
3549 dr.dispatched(true);
3551 Buffer * doc_buffer = documentBufferView()
3552 ? &(documentBufferView()->buffer()) : 0;
3554 if (cmd.origin() == FuncRequest::TOC) {
3555 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3556 // FIXME: do we need to pass a DispatchResult object here?
3557 toc->doDispatch(bv->cursor(), cmd);
3561 string const argument = to_utf8(cmd.argument());
3563 switch(cmd.action()) {
3564 case LFUN_BUFFER_CHILD_OPEN:
3565 openChildDocument(to_utf8(cmd.argument()));
3568 case LFUN_BUFFER_IMPORT:
3569 importDocument(to_utf8(cmd.argument()));
3572 case LFUN_BUFFER_EXPORT: {
3575 // GCC only sees strfwd.h when building merged
3576 if (::lyx::operator==(cmd.argument(), "custom")) {
3577 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3581 string const dest = cmd.getArg(1);
3582 FileName target_dir;
3583 if (!dest.empty() && FileName::isAbsolute(dest))
3584 target_dir = FileName(support::onlyPath(dest));
3586 target_dir = doc_buffer->fileName().onlyPath();
3588 if ((dest.empty() && doc_buffer->isUnnamed())
3589 || !target_dir.isDirWritable()) {
3590 exportBufferAs(*doc_buffer, cmd.argument());
3593 /* TODO/Review: Is it a problem to also export the children?
3594 See the update_unincluded flag */
3595 d.asyncBufferProcessing(argument,
3598 &GuiViewPrivate::exportAndDestroy,
3601 // TODO Inform user about success
3605 case LFUN_BUFFER_EXPORT_AS: {
3606 LASSERT(doc_buffer, break);
3607 docstring f = cmd.argument();
3609 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3610 exportBufferAs(*doc_buffer, f);
3614 case LFUN_BUFFER_UPDATE: {
3615 d.asyncBufferProcessing(argument,
3618 &GuiViewPrivate::compileAndDestroy,
3623 case LFUN_BUFFER_VIEW: {
3624 d.asyncBufferProcessing(argument,
3626 _("Previewing ..."),
3627 &GuiViewPrivate::previewAndDestroy,
3632 case LFUN_MASTER_BUFFER_UPDATE: {
3633 d.asyncBufferProcessing(argument,
3634 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3636 &GuiViewPrivate::compileAndDestroy,
3641 case LFUN_MASTER_BUFFER_VIEW: {
3642 d.asyncBufferProcessing(argument,
3643 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3645 &GuiViewPrivate::previewAndDestroy,
3646 0, &Buffer::preview);
3649 case LFUN_BUFFER_SWITCH: {
3650 string const file_name = to_utf8(cmd.argument());
3651 if (!FileName::isAbsolute(file_name)) {
3653 dr.setMessage(_("Absolute filename expected."));
3657 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3660 dr.setMessage(_("Document not loaded"));
3664 // Do we open or switch to the buffer in this view ?
3665 if (workArea(*buffer)
3666 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3671 // Look for the buffer in other views
3672 QList<int> const ids = guiApp->viewIds();
3674 for (; i != ids.size(); ++i) {
3675 GuiView & gv = guiApp->view(ids[i]);
3676 if (gv.workArea(*buffer)) {
3678 gv.activateWindow();
3680 gv.setBuffer(buffer);
3685 // If necessary, open a new window as a last resort
3686 if (i == ids.size()) {
3687 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3693 case LFUN_BUFFER_NEXT:
3694 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3697 case LFUN_BUFFER_MOVE_NEXT:
3698 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3701 case LFUN_BUFFER_PREVIOUS:
3702 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3705 case LFUN_BUFFER_MOVE_PREVIOUS:
3706 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3709 case LFUN_COMMAND_EXECUTE: {
3710 command_execute_ = true;
3711 minibuffer_focus_ = true;
3714 case LFUN_DROP_LAYOUTS_CHOICE:
3715 d.layout_->showPopup();
3718 case LFUN_MENU_OPEN:
3719 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3720 menu->exec(QCursor::pos());
3723 case LFUN_FILE_INSERT:
3724 insertLyXFile(cmd.argument());
3727 case LFUN_FILE_INSERT_PLAINTEXT:
3728 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3729 string const fname = to_utf8(cmd.argument());
3730 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3731 dr.setMessage(_("Absolute filename expected."));
3735 FileName filename(fname);
3736 if (fname.empty()) {
3737 FileDialog dlg(qt_("Select file to insert"));
3739 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3740 QStringList(qt_("All Files (*)")));
3742 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3743 dr.setMessage(_("Canceled."));
3747 filename.set(fromqstr(result.second));
3751 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3752 bv->dispatch(new_cmd, dr);
3757 case LFUN_BUFFER_RELOAD: {
3758 LASSERT(doc_buffer, break);
3761 if (!doc_buffer->isClean()) {
3762 docstring const file =
3763 makeDisplayPath(doc_buffer->absFileName(), 20);
3764 docstring text = bformat(_("Any changes will be lost. "
3765 "Are you sure you want to revert to the saved version "
3766 "of the document %1$s?"), file);
3767 ret = Alert::prompt(_("Revert to saved document?"),
3768 text, 1, 1, _("&Revert"), _("&Cancel"));
3772 doc_buffer->markClean();
3773 reloadBuffer(*doc_buffer);
3774 dr.forceBufferUpdate();
3779 case LFUN_BUFFER_WRITE:
3780 LASSERT(doc_buffer, break);
3781 saveBuffer(*doc_buffer);
3784 case LFUN_BUFFER_WRITE_AS:
3785 LASSERT(doc_buffer, break);
3786 renameBuffer(*doc_buffer, cmd.argument());
3789 case LFUN_BUFFER_WRITE_ALL: {
3790 Buffer * first = theBufferList().first();
3793 message(_("Saving all documents..."));
3794 // We cannot use a for loop as the buffer list cycles.
3797 if (!b->isClean()) {
3799 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3801 b = theBufferList().next(b);
3802 } while (b != first);
3803 dr.setMessage(_("All documents saved."));
3807 case LFUN_BUFFER_CLOSE:
3811 case LFUN_BUFFER_CLOSE_ALL:
3815 case LFUN_TOOLBAR_TOGGLE: {
3816 string const name = cmd.getArg(0);
3817 if (GuiToolbar * t = toolbar(name))
3822 case LFUN_DIALOG_UPDATE: {
3823 string const name = to_utf8(cmd.argument());
3824 if (name == "prefs" || name == "document")
3825 updateDialog(name, string());
3826 else if (name == "paragraph")
3827 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3828 else if (currentBufferView()) {
3829 Inset * inset = currentBufferView()->editedInset(name);
3830 // Can only update a dialog connected to an existing inset
3832 // FIXME: get rid of this indirection; GuiView ask the inset
3833 // if he is kind enough to update itself...
3834 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3835 //FIXME: pass DispatchResult here?
3836 inset->dispatch(currentBufferView()->cursor(), fr);
3842 case LFUN_DIALOG_TOGGLE: {
3843 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3844 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3845 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3849 case LFUN_DIALOG_DISCONNECT_INSET:
3850 disconnectDialog(to_utf8(cmd.argument()));
3853 case LFUN_DIALOG_HIDE: {
3854 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3858 case LFUN_DIALOG_SHOW: {
3859 string const name = cmd.getArg(0);
3860 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3862 if (name == "character") {
3863 data = freefont2string();
3865 showDialog("character", data);
3866 } else if (name == "latexlog") {
3867 Buffer::LogType type;
3868 string const logfile = doc_buffer->logName(&type);
3870 case Buffer::latexlog:
3873 case Buffer::buildlog:
3877 data += Lexer::quoteString(logfile);
3878 showDialog("log", data);
3879 } else if (name == "vclog") {
3880 string const data = "vc " +
3881 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3882 showDialog("log", data);
3883 } else if (name == "symbols") {
3884 data = bv->cursor().getEncoding()->name();
3886 showDialog("symbols", data);
3888 } else if (name == "prefs" && isFullScreen()) {
3889 lfunUiToggle("fullscreen");
3890 showDialog("prefs", data);
3892 showDialog(name, data);
3897 dr.setMessage(cmd.argument());
3900 case LFUN_UI_TOGGLE: {
3901 string arg = cmd.getArg(0);
3902 if (!lfunUiToggle(arg)) {
3903 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3904 dr.setMessage(bformat(msg, from_utf8(arg)));
3906 // Make sure the keyboard focus stays in the work area.
3911 case LFUN_VIEW_SPLIT: {
3912 LASSERT(doc_buffer, break);
3913 string const orientation = cmd.getArg(0);
3914 d.splitter_->setOrientation(orientation == "vertical"
3915 ? Qt::Vertical : Qt::Horizontal);
3916 TabWorkArea * twa = addTabWorkArea();
3917 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3918 setCurrentWorkArea(wa);
3921 case LFUN_TAB_GROUP_CLOSE:
3922 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3923 closeTabWorkArea(twa);
3924 d.current_work_area_ = 0;
3925 twa = d.currentTabWorkArea();
3926 // Switch to the next GuiWorkArea in the found TabWorkArea.
3928 // Make sure the work area is up to date.
3929 setCurrentWorkArea(twa->currentWorkArea());
3931 setCurrentWorkArea(0);
3936 case LFUN_VIEW_CLOSE:
3937 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3938 closeWorkArea(twa->currentWorkArea());
3939 d.current_work_area_ = 0;
3940 twa = d.currentTabWorkArea();
3941 // Switch to the next GuiWorkArea in the found TabWorkArea.
3943 // Make sure the work area is up to date.
3944 setCurrentWorkArea(twa->currentWorkArea());
3946 setCurrentWorkArea(0);
3951 case LFUN_COMPLETION_INLINE:
3952 if (d.current_work_area_)
3953 d.current_work_area_->completer().showInline();
3956 case LFUN_COMPLETION_POPUP:
3957 if (d.current_work_area_)
3958 d.current_work_area_->completer().showPopup();
3963 if (d.current_work_area_)
3964 d.current_work_area_->completer().tab();
3967 case LFUN_COMPLETION_CANCEL:
3968 if (d.current_work_area_) {
3969 if (d.current_work_area_->completer().popupVisible())
3970 d.current_work_area_->completer().hidePopup();
3972 d.current_work_area_->completer().hideInline();
3976 case LFUN_COMPLETION_ACCEPT:
3977 if (d.current_work_area_)
3978 d.current_work_area_->completer().activate();
3981 case LFUN_BUFFER_ZOOM_IN:
3982 case LFUN_BUFFER_ZOOM_OUT: {
3983 // use a signed temp to avoid overflow
3984 int zoom = lyxrc.zoom;
3985 if (cmd.argument().empty()) {
3986 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3991 zoom += convert<int>(cmd.argument());
3997 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3999 // The global QPixmapCache is used in GuiPainter to cache text
4000 // painting so we must reset it.
4001 QPixmapCache::clear();
4002 guiApp->fontLoader().update();
4003 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4007 case LFUN_VC_REGISTER:
4008 case LFUN_VC_RENAME:
4010 case LFUN_VC_CHECK_IN:
4011 case LFUN_VC_CHECK_OUT:
4012 case LFUN_VC_REPO_UPDATE:
4013 case LFUN_VC_LOCKING_TOGGLE:
4014 case LFUN_VC_REVERT:
4015 case LFUN_VC_UNDO_LAST:
4016 case LFUN_VC_COMMAND:
4017 case LFUN_VC_COMPARE:
4018 dispatchVC(cmd, dr);
4021 case LFUN_SERVER_GOTO_FILE_ROW:
4022 goToFileRow(to_utf8(cmd.argument()));
4025 case LFUN_LYX_ACTIVATE:
4029 case LFUN_FORWARD_SEARCH: {
4030 // it seems safe to assume we have a document buffer, since
4031 // getStatus wants one.
4032 // coverity[FORWARD_NULL]
4033 Buffer const * doc_master = doc_buffer->masterBuffer();
4034 FileName const path(doc_master->temppath());
4035 string const texname = doc_master->isChild(doc_buffer)
4036 ? DocFileName(changeExtension(
4037 doc_buffer->absFileName(),
4038 "tex")).mangledFileName()
4039 : doc_buffer->latexName();
4040 string const fulltexname =
4041 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4042 string const mastername =
4043 removeExtension(doc_master->latexName());
4044 FileName const dviname(addName(path.absFileName(),
4045 addExtension(mastername, "dvi")));
4046 FileName const pdfname(addName(path.absFileName(),
4047 addExtension(mastername, "pdf")));
4048 bool const have_dvi = dviname.exists();
4049 bool const have_pdf = pdfname.exists();
4050 if (!have_dvi && !have_pdf) {
4051 dr.setMessage(_("Please, preview the document first."));
4054 string outname = dviname.onlyFileName();
4055 string command = lyxrc.forward_search_dvi;
4056 if (!have_dvi || (have_pdf &&
4057 pdfname.lastModified() > dviname.lastModified())) {
4058 outname = pdfname.onlyFileName();
4059 command = lyxrc.forward_search_pdf;
4062 DocIterator cur = bv->cursor();
4063 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4064 LYXERR(Debug::ACTION, "Forward search: row:" << row
4066 if (row == -1 || command.empty()) {
4067 dr.setMessage(_("Couldn't proceed."));
4070 string texrow = convert<string>(row);
4072 command = subst(command, "$$n", texrow);
4073 command = subst(command, "$$f", fulltexname);
4074 command = subst(command, "$$t", texname);
4075 command = subst(command, "$$o", outname);
4077 PathChanger p(path);
4079 one.startscript(Systemcall::DontWait, command);
4083 case LFUN_SPELLING_CONTINUOUSLY:
4084 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4085 dr.screenUpdate(Update::Force | Update::FitCursor);
4089 // The LFUN must be for one of BufferView, Buffer or Cursor;
4091 dispatchToBufferView(cmd, dr);
4095 // Part of automatic menu appearance feature.
4096 if (isFullScreen()) {
4097 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4101 // Need to update bv because many LFUNs here might have destroyed it
4102 bv = currentBufferView();
4104 // Clear non-empty selections
4105 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4107 Cursor & cur = bv->cursor();
4108 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4109 cur.clearSelection();
4115 bool GuiView::lfunUiToggle(string const & ui_component)
4117 if (ui_component == "scrollbar") {
4118 // hide() is of no help
4119 if (d.current_work_area_->verticalScrollBarPolicy() ==
4120 Qt::ScrollBarAlwaysOff)
4122 d.current_work_area_->setVerticalScrollBarPolicy(
4123 Qt::ScrollBarAsNeeded);
4125 d.current_work_area_->setVerticalScrollBarPolicy(
4126 Qt::ScrollBarAlwaysOff);
4127 } else if (ui_component == "statusbar") {
4128 statusBar()->setVisible(!statusBar()->isVisible());
4129 } else if (ui_component == "menubar") {
4130 menuBar()->setVisible(!menuBar()->isVisible());
4132 if (ui_component == "frame") {
4134 getContentsMargins(&l, &t, &r, &b);
4135 //are the frames in default state?
4136 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4138 setContentsMargins(-2, -2, -2, -2);
4140 setContentsMargins(0, 0, 0, 0);
4143 if (ui_component == "fullscreen") {
4151 void GuiView::toggleFullScreen()
4153 if (isFullScreen()) {
4154 for (int i = 0; i != d.splitter_->count(); ++i)
4155 d.tabWorkArea(i)->setFullScreen(false);
4156 setContentsMargins(0, 0, 0, 0);
4157 setWindowState(windowState() ^ Qt::WindowFullScreen);
4160 statusBar()->show();
4163 hideDialogs("prefs", 0);
4164 for (int i = 0; i != d.splitter_->count(); ++i)
4165 d.tabWorkArea(i)->setFullScreen(true);
4166 setContentsMargins(-2, -2, -2, -2);
4168 setWindowState(windowState() ^ Qt::WindowFullScreen);
4169 if (lyxrc.full_screen_statusbar)
4170 statusBar()->hide();
4171 if (lyxrc.full_screen_menubar)
4173 if (lyxrc.full_screen_toolbars) {
4174 ToolbarMap::iterator end = d.toolbars_.end();
4175 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4180 // give dialogs like the TOC a chance to adapt
4185 Buffer const * GuiView::updateInset(Inset const * inset)
4190 Buffer const * inset_buffer = &(inset->buffer());
4192 for (int i = 0; i != d.splitter_->count(); ++i) {
4193 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4196 Buffer const * buffer = &(wa->bufferView().buffer());
4197 if (inset_buffer == buffer)
4198 wa->scheduleRedraw();
4200 return inset_buffer;
4204 void GuiView::restartCursor()
4206 /* When we move around, or type, it's nice to be able to see
4207 * the cursor immediately after the keypress.
4209 if (d.current_work_area_)
4210 d.current_work_area_->startBlinkingCursor();
4212 // Take this occasion to update the other GUI elements.
4218 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4220 if (d.current_work_area_)
4221 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4226 // This list should be kept in sync with the list of insets in
4227 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4228 // dialog should have the same name as the inset.
4229 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4230 // docs in LyXAction.cpp.
4232 char const * const dialognames[] = {
4234 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4235 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4236 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4237 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4238 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4239 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4240 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4241 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4243 char const * const * const end_dialognames =
4244 dialognames + (sizeof(dialognames) / sizeof(char *));
4248 cmpCStr(char const * name) : name_(name) {}
4249 bool operator()(char const * other) {
4250 return strcmp(other, name_) == 0;
4257 bool isValidName(string const & name)
4259 return find_if(dialognames, end_dialognames,
4260 cmpCStr(name.c_str())) != end_dialognames;
4266 void GuiView::resetDialogs()
4268 // Make sure that no LFUN uses any GuiView.
4269 guiApp->setCurrentView(0);
4273 constructToolbars();
4274 guiApp->menus().fillMenuBar(menuBar(), this, false);
4275 d.layout_->updateContents(true);
4276 // Now update controls with current buffer.
4277 guiApp->setCurrentView(this);
4283 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4285 if (!isValidName(name))
4288 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4290 if (it != d.dialogs_.end()) {
4292 it->second->hideView();
4293 return it->second.get();
4296 Dialog * dialog = build(name);
4297 d.dialogs_[name].reset(dialog);
4298 if (lyxrc.allow_geometry_session)
4299 dialog->restoreSession();
4306 void GuiView::showDialog(string const & name, string const & data,
4309 triggerShowDialog(toqstr(name), toqstr(data), inset);
4313 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4319 const string name = fromqstr(qname);
4320 const string data = fromqstr(qdata);
4324 Dialog * dialog = findOrBuild(name, false);
4326 bool const visible = dialog->isVisibleView();
4327 dialog->showData(data);
4328 if (inset && currentBufferView())
4329 currentBufferView()->editInset(name, inset);
4330 // We only set the focus to the new dialog if it was not yet
4331 // visible in order not to change the existing previous behaviour
4333 // activateWindow is needed for floating dockviews
4334 dialog->asQWidget()->raise();
4335 dialog->asQWidget()->activateWindow();
4336 dialog->asQWidget()->setFocus();
4340 catch (ExceptionMessage const & ex) {
4348 bool GuiView::isDialogVisible(string const & name) const
4350 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4351 if (it == d.dialogs_.end())
4353 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4357 void GuiView::hideDialog(string const & name, Inset * inset)
4359 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4360 if (it == d.dialogs_.end())
4364 if (!currentBufferView())
4366 if (inset != currentBufferView()->editedInset(name))
4370 Dialog * const dialog = it->second.get();
4371 if (dialog->isVisibleView())
4373 if (currentBufferView())
4374 currentBufferView()->editInset(name, 0);
4378 void GuiView::disconnectDialog(string const & name)
4380 if (!isValidName(name))
4382 if (currentBufferView())
4383 currentBufferView()->editInset(name, 0);
4387 void GuiView::hideAll() const
4389 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4390 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4392 for(; it != end; ++it)
4393 it->second->hideView();
4397 void GuiView::updateDialogs()
4399 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4400 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4402 for(; it != end; ++it) {
4403 Dialog * dialog = it->second.get();
4405 if (dialog->needBufferOpen() && !documentBufferView())
4406 hideDialog(fromqstr(dialog->name()), 0);
4407 else if (dialog->isVisibleView())
4408 dialog->checkStatus();
4415 Dialog * createDialog(GuiView & lv, string const & name);
4417 // will be replaced by a proper factory...
4418 Dialog * createGuiAbout(GuiView & lv);
4419 Dialog * createGuiBibtex(GuiView & lv);
4420 Dialog * createGuiChanges(GuiView & lv);
4421 Dialog * createGuiCharacter(GuiView & lv);
4422 Dialog * createGuiCitation(GuiView & lv);
4423 Dialog * createGuiCompare(GuiView & lv);
4424 Dialog * createGuiCompareHistory(GuiView & lv);
4425 Dialog * createGuiDelimiter(GuiView & lv);
4426 Dialog * createGuiDocument(GuiView & lv);
4427 Dialog * createGuiErrorList(GuiView & lv);
4428 Dialog * createGuiExternal(GuiView & lv);
4429 Dialog * createGuiGraphics(GuiView & lv);
4430 Dialog * createGuiInclude(GuiView & lv);
4431 Dialog * createGuiIndex(GuiView & lv);
4432 Dialog * createGuiListings(GuiView & lv);
4433 Dialog * createGuiLog(GuiView & lv);
4434 Dialog * createGuiMathMatrix(GuiView & lv);
4435 Dialog * createGuiNote(GuiView & lv);
4436 Dialog * createGuiParagraph(GuiView & lv);
4437 Dialog * createGuiPhantom(GuiView & lv);
4438 Dialog * createGuiPreferences(GuiView & lv);
4439 Dialog * createGuiPrint(GuiView & lv);
4440 Dialog * createGuiPrintindex(GuiView & lv);
4441 Dialog * createGuiRef(GuiView & lv);
4442 Dialog * createGuiSearch(GuiView & lv);
4443 Dialog * createGuiSearchAdv(GuiView & lv);
4444 Dialog * createGuiSendTo(GuiView & lv);
4445 Dialog * createGuiShowFile(GuiView & lv);
4446 Dialog * createGuiSpellchecker(GuiView & lv);
4447 Dialog * createGuiSymbols(GuiView & lv);
4448 Dialog * createGuiTabularCreate(GuiView & lv);
4449 Dialog * createGuiTexInfo(GuiView & lv);
4450 Dialog * createGuiToc(GuiView & lv);
4451 Dialog * createGuiThesaurus(GuiView & lv);
4452 Dialog * createGuiViewSource(GuiView & lv);
4453 Dialog * createGuiWrap(GuiView & lv);
4454 Dialog * createGuiProgressView(GuiView & lv);
4458 Dialog * GuiView::build(string const & name)
4460 LASSERT(isValidName(name), return 0);
4462 Dialog * dialog = createDialog(*this, name);
4466 if (name == "aboutlyx")
4467 return createGuiAbout(*this);
4468 if (name == "bibtex")
4469 return createGuiBibtex(*this);
4470 if (name == "changes")
4471 return createGuiChanges(*this);
4472 if (name == "character")
4473 return createGuiCharacter(*this);
4474 if (name == "citation")
4475 return createGuiCitation(*this);
4476 if (name == "compare")
4477 return createGuiCompare(*this);
4478 if (name == "comparehistory")
4479 return createGuiCompareHistory(*this);
4480 if (name == "document")
4481 return createGuiDocument(*this);
4482 if (name == "errorlist")
4483 return createGuiErrorList(*this);
4484 if (name == "external")
4485 return createGuiExternal(*this);
4487 return createGuiShowFile(*this);
4488 if (name == "findreplace")
4489 return createGuiSearch(*this);
4490 if (name == "findreplaceadv")
4491 return createGuiSearchAdv(*this);
4492 if (name == "graphics")
4493 return createGuiGraphics(*this);
4494 if (name == "include")
4495 return createGuiInclude(*this);
4496 if (name == "index")
4497 return createGuiIndex(*this);
4498 if (name == "index_print")
4499 return createGuiPrintindex(*this);
4500 if (name == "listings")
4501 return createGuiListings(*this);
4503 return createGuiLog(*this);
4504 if (name == "mathdelimiter")
4505 return createGuiDelimiter(*this);
4506 if (name == "mathmatrix")
4507 return createGuiMathMatrix(*this);
4509 return createGuiNote(*this);
4510 if (name == "paragraph")
4511 return createGuiParagraph(*this);
4512 if (name == "phantom")
4513 return createGuiPhantom(*this);
4514 if (name == "prefs")
4515 return createGuiPreferences(*this);
4517 return createGuiRef(*this);
4518 if (name == "sendto")
4519 return createGuiSendTo(*this);
4520 if (name == "spellchecker")
4521 return createGuiSpellchecker(*this);
4522 if (name == "symbols")
4523 return createGuiSymbols(*this);
4524 if (name == "tabularcreate")
4525 return createGuiTabularCreate(*this);
4526 if (name == "texinfo")
4527 return createGuiTexInfo(*this);
4528 if (name == "thesaurus")
4529 return createGuiThesaurus(*this);
4531 return createGuiToc(*this);
4532 if (name == "view-source")
4533 return createGuiViewSource(*this);
4535 return createGuiWrap(*this);
4536 if (name == "progress")
4537 return createGuiProgressView(*this);
4543 } // namespace frontend
4546 #include "moc_GuiView.cpp"