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 // Set the windows title
1174 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1176 title += from_ascii(" - LyX");
1178 setWindowTitle(toqstr(title));
1179 // Sets the path for the window: this is used by OSX to
1180 // allow a context click on the title bar showing a menu
1181 // with the path up to the file
1182 setWindowFilePath(toqstr(buf.absFileName()));
1183 // Tell Qt whether the current document is changed
1184 setWindowModified(!buf.isClean());
1186 if (buf.isReadonly())
1191 if (buf.lyxvc().inUse()) {
1192 version_control_->show();
1193 if (buf.lyxvc().locking())
1194 version_control_->setText(
1195 toqstr(bformat(_("%1$s lock"),
1196 from_ascii(buf.lyxvc().vcname()))));
1198 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1200 version_control_->hide();
1204 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1206 if (d.current_work_area_)
1207 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1208 this, SLOT(setBusy(bool)));
1210 disconnectBufferView();
1211 connectBufferView(wa->bufferView());
1212 connectBuffer(wa->bufferView().buffer());
1213 d.current_work_area_ = wa;
1214 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1215 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1216 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1217 updateWindowTitle(wa);
1221 // The document settings needs to be reinitialised.
1222 updateDialog("document", "");
1224 // Buffer-dependent dialogs must be updated. This is done here because
1225 // some dialogs require buffer()->text.
1230 void GuiView::on_lastWorkAreaRemoved()
1233 // We already are in a close event. Nothing more to do.
1236 if (d.splitter_->count() > 1)
1237 // We have a splitter so don't close anything.
1240 // Reset and updates the dialogs.
1241 d.toc_models_.reset(0);
1242 updateDialog("document", "");
1248 if (lyxrc.open_buffers_in_tabs)
1249 // Nothing more to do, the window should stay open.
1252 if (guiApp->viewIds().size() > 1) {
1258 // On Mac we also close the last window because the application stay
1259 // resident in memory. On other platforms we don't close the last
1260 // window because this would quit the application.
1266 void GuiView::updateStatusBar()
1268 // let the user see the explicit message
1269 if (d.statusbar_timer_.isActive())
1276 void GuiView::showMessage()
1280 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1281 if (msg.isEmpty()) {
1282 BufferView const * bv = currentBufferView();
1284 msg = toqstr(bv->cursor().currentState());
1286 msg = qt_("Welcome to LyX!");
1288 statusBar()->showMessage(msg);
1292 bool GuiView::event(QEvent * e)
1296 // Useful debug code:
1297 //case QEvent::ActivationChange:
1298 //case QEvent::WindowDeactivate:
1299 //case QEvent::Paint:
1300 //case QEvent::Enter:
1301 //case QEvent::Leave:
1302 //case QEvent::HoverEnter:
1303 //case QEvent::HoverLeave:
1304 //case QEvent::HoverMove:
1305 //case QEvent::StatusTip:
1306 //case QEvent::DragEnter:
1307 //case QEvent::DragLeave:
1308 //case QEvent::Drop:
1311 case QEvent::WindowActivate: {
1312 GuiView * old_view = guiApp->currentView();
1313 if (this == old_view) {
1315 return QMainWindow::event(e);
1317 if (old_view && old_view->currentBufferView()) {
1318 // save current selection to the selection buffer to allow
1319 // middle-button paste in this window.
1320 cap::saveSelection(old_view->currentBufferView()->cursor());
1322 guiApp->setCurrentView(this);
1323 if (d.current_work_area_) {
1324 BufferView & bv = d.current_work_area_->bufferView();
1325 connectBufferView(bv);
1326 connectBuffer(bv.buffer());
1327 // The document structure, name and dialogs might have
1328 // changed in another view.
1330 // The document settings needs to be reinitialised.
1331 updateDialog("document", "");
1337 return QMainWindow::event(e);
1340 case QEvent::ShortcutOverride: {
1342 if (isFullScreen() && menuBar()->isHidden()) {
1343 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1344 // FIXME: we should also try to detect special LyX shortcut such as
1345 // Alt-P and Alt-M. Right now there is a hack in
1346 // GuiWorkArea::processKeySym() that hides again the menubar for
1348 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1350 return QMainWindow::event(e);
1353 return QMainWindow::event(e);
1357 return QMainWindow::event(e);
1361 void GuiView::resetWindowTitle()
1363 setWindowTitle(qt_("LyX"));
1366 bool GuiView::focusNextPrevChild(bool /*next*/)
1373 bool GuiView::busy() const
1379 void GuiView::setBusy(bool busy)
1381 bool const busy_before = busy_ > 0;
1382 busy ? ++busy_ : --busy_;
1383 if ((busy_ > 0) == busy_before)
1384 // busy state didn't change
1388 QApplication::setOverrideCursor(Qt::WaitCursor);
1391 QApplication::restoreOverrideCursor();
1396 void GuiView::resetCommandExecute()
1398 command_execute_ = false;
1403 double GuiView::pixelRatio() const
1405 #if QT_VERSION >= 0x050000
1406 return devicePixelRatio();
1413 GuiWorkArea * GuiView::workArea(int index)
1415 if (TabWorkArea * twa = d.currentTabWorkArea())
1416 if (index < twa->count())
1417 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1422 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1424 if (currentWorkArea()
1425 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1426 return currentWorkArea();
1427 if (TabWorkArea * twa = d.currentTabWorkArea())
1428 return twa->workArea(buffer);
1433 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1435 // Automatically create a TabWorkArea if there are none yet.
1436 TabWorkArea * tab_widget = d.splitter_->count()
1437 ? d.currentTabWorkArea() : addTabWorkArea();
1438 return tab_widget->addWorkArea(buffer, *this);
1442 TabWorkArea * GuiView::addTabWorkArea()
1444 TabWorkArea * twa = new TabWorkArea;
1445 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1446 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1447 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1448 this, SLOT(on_lastWorkAreaRemoved()));
1450 d.splitter_->addWidget(twa);
1451 d.stack_widget_->setCurrentWidget(d.splitter_);
1456 GuiWorkArea const * GuiView::currentWorkArea() const
1458 return d.current_work_area_;
1462 GuiWorkArea * GuiView::currentWorkArea()
1464 return d.current_work_area_;
1468 GuiWorkArea const * GuiView::currentMainWorkArea() const
1470 if (!d.currentTabWorkArea())
1472 return d.currentTabWorkArea()->currentWorkArea();
1476 GuiWorkArea * GuiView::currentMainWorkArea()
1478 if (!d.currentTabWorkArea())
1480 return d.currentTabWorkArea()->currentWorkArea();
1484 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1486 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1488 d.current_work_area_ = 0;
1493 // FIXME: I've no clue why this is here and why it accesses
1494 // theGuiApp()->currentView, which might be 0 (bug 6464).
1495 // See also 27525 (vfr).
1496 if (theGuiApp()->currentView() == this
1497 && theGuiApp()->currentView()->currentWorkArea() == wa)
1500 if (currentBufferView())
1501 cap::saveSelection(currentBufferView()->cursor());
1503 theGuiApp()->setCurrentView(this);
1504 d.current_work_area_ = wa;
1506 // We need to reset this now, because it will need to be
1507 // right if the tabWorkArea gets reset in the for loop. We
1508 // will change it back if we aren't in that case.
1509 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1510 d.current_main_work_area_ = wa;
1512 for (int i = 0; i != d.splitter_->count(); ++i) {
1513 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1514 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1515 << ", Current main wa: " << currentMainWorkArea());
1520 d.current_main_work_area_ = old_cmwa;
1522 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1523 on_currentWorkAreaChanged(wa);
1524 BufferView & bv = wa->bufferView();
1525 bv.cursor().fixIfBroken();
1527 wa->setUpdatesEnabled(true);
1528 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1532 void GuiView::removeWorkArea(GuiWorkArea * wa)
1534 LASSERT(wa, return);
1535 if (wa == d.current_work_area_) {
1537 disconnectBufferView();
1538 d.current_work_area_ = 0;
1539 d.current_main_work_area_ = 0;
1542 bool found_twa = false;
1543 for (int i = 0; i != d.splitter_->count(); ++i) {
1544 TabWorkArea * twa = d.tabWorkArea(i);
1545 if (twa->removeWorkArea(wa)) {
1546 // Found in this tab group, and deleted the GuiWorkArea.
1548 if (twa->count() != 0) {
1549 if (d.current_work_area_ == 0)
1550 // This means that we are closing the current GuiWorkArea, so
1551 // switch to the next GuiWorkArea in the found TabWorkArea.
1552 setCurrentWorkArea(twa->currentWorkArea());
1554 // No more WorkAreas in this tab group, so delete it.
1561 // It is not a tabbed work area (i.e., the search work area), so it
1562 // should be deleted by other means.
1563 LASSERT(found_twa, return);
1565 if (d.current_work_area_ == 0) {
1566 if (d.splitter_->count() != 0) {
1567 TabWorkArea * twa = d.currentTabWorkArea();
1568 setCurrentWorkArea(twa->currentWorkArea());
1570 // No more work areas, switch to the background widget.
1571 setCurrentWorkArea(0);
1577 LayoutBox * GuiView::getLayoutDialog() const
1583 void GuiView::updateLayoutList()
1586 d.layout_->updateContents(false);
1590 void GuiView::updateToolbars()
1592 ToolbarMap::iterator end = d.toolbars_.end();
1593 if (d.current_work_area_) {
1595 if (d.current_work_area_->bufferView().cursor().inMathed()
1596 && !d.current_work_area_->bufferView().cursor().inRegexped())
1597 context |= Toolbars::MATH;
1598 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1599 context |= Toolbars::TABLE;
1600 if (currentBufferView()->buffer().areChangesPresent()
1601 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1602 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1603 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1604 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1605 context |= Toolbars::REVIEW;
1606 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1607 context |= Toolbars::MATHMACROTEMPLATE;
1608 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1609 context |= Toolbars::IPA;
1610 if (command_execute_)
1611 context |= Toolbars::MINIBUFFER;
1612 if (minibuffer_focus_) {
1613 context |= Toolbars::MINIBUFFER_FOCUS;
1614 minibuffer_focus_ = false;
1617 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1618 it->second->update(context);
1620 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1621 it->second->update();
1625 void GuiView::setBuffer(Buffer * newBuffer)
1627 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1628 LASSERT(newBuffer, return);
1630 GuiWorkArea * wa = workArea(*newBuffer);
1633 newBuffer->masterBuffer()->updateBuffer();
1635 wa = addWorkArea(*newBuffer);
1636 // scroll to the position when the BufferView was last closed
1637 if (lyxrc.use_lastfilepos) {
1638 LastFilePosSection::FilePos filepos =
1639 theSession().lastFilePos().load(newBuffer->fileName());
1640 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1643 //Disconnect the old buffer...there's no new one.
1646 connectBuffer(*newBuffer);
1647 connectBufferView(wa->bufferView());
1648 setCurrentWorkArea(wa);
1652 void GuiView::connectBuffer(Buffer & buf)
1654 buf.setGuiDelegate(this);
1658 void GuiView::disconnectBuffer()
1660 if (d.current_work_area_)
1661 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1665 void GuiView::connectBufferView(BufferView & bv)
1667 bv.setGuiDelegate(this);
1671 void GuiView::disconnectBufferView()
1673 if (d.current_work_area_)
1674 d.current_work_area_->bufferView().setGuiDelegate(0);
1678 void GuiView::errors(string const & error_type, bool from_master)
1680 BufferView const * const bv = currentBufferView();
1684 #if EXPORT_in_THREAD
1685 // We are called with from_master == false by default, so we
1686 // have to figure out whether that is the case or not.
1687 ErrorList & el = bv->buffer().errorList(error_type);
1689 el = bv->buffer().masterBuffer()->errorList(error_type);
1693 ErrorList const & el = from_master ?
1694 bv->buffer().masterBuffer()->errorList(error_type) :
1695 bv->buffer().errorList(error_type);
1701 string data = error_type;
1703 data = "from_master|" + error_type;
1704 showDialog("errorlist", data);
1708 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1710 d.toc_models_.updateItem(toqstr(type), dit);
1714 void GuiView::structureChanged()
1716 // FIXME: This is slightly expensive, though less than the tocBackend update
1717 // (#9880). This also resets the view in the Toc Widget (#6675).
1718 d.toc_models_.reset(documentBufferView());
1719 // Navigator needs more than a simple update in this case. It needs to be
1721 updateDialog("toc", "");
1725 void GuiView::updateDialog(string const & name, string const & data)
1727 if (!isDialogVisible(name))
1730 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1731 if (it == d.dialogs_.end())
1734 Dialog * const dialog = it->second.get();
1735 if (dialog->isVisibleView())
1736 dialog->initialiseParams(data);
1740 BufferView * GuiView::documentBufferView()
1742 return currentMainWorkArea()
1743 ? ¤tMainWorkArea()->bufferView()
1748 BufferView const * GuiView::documentBufferView() const
1750 return currentMainWorkArea()
1751 ? ¤tMainWorkArea()->bufferView()
1756 BufferView * GuiView::currentBufferView()
1758 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1762 BufferView const * GuiView::currentBufferView() const
1764 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1768 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1769 Buffer const * orig, Buffer * clone)
1771 bool const success = clone->autoSave();
1773 busyBuffers.remove(orig);
1775 ? _("Automatic save done.")
1776 : _("Automatic save failed!");
1780 void GuiView::autoSave()
1782 LYXERR(Debug::INFO, "Running autoSave()");
1784 Buffer * buffer = documentBufferView()
1785 ? &documentBufferView()->buffer() : 0;
1787 resetAutosaveTimers();
1791 GuiViewPrivate::busyBuffers.insert(buffer);
1792 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1793 buffer, buffer->cloneBufferOnly());
1794 d.autosave_watcher_.setFuture(f);
1795 resetAutosaveTimers();
1799 void GuiView::resetAutosaveTimers()
1802 d.autosave_timeout_.restart();
1806 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1809 Buffer * buf = currentBufferView()
1810 ? ¤tBufferView()->buffer() : 0;
1811 Buffer * doc_buffer = documentBufferView()
1812 ? &(documentBufferView()->buffer()) : 0;
1815 /* In LyX/Mac, when a dialog is open, the menus of the
1816 application can still be accessed without giving focus to
1817 the main window. In this case, we want to disable the menu
1818 entries that are buffer-related.
1819 This code must not be used on Linux and Windows, since it
1820 would disable buffer-related entries when hovering over the
1821 menu (see bug #9574).
1823 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1829 // Check whether we need a buffer
1830 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1831 // no, exit directly
1832 flag.message(from_utf8(N_("Command not allowed with"
1833 "out any document open")));
1834 flag.setEnabled(false);
1838 if (cmd.origin() == FuncRequest::TOC) {
1839 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1840 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1841 flag.setEnabled(false);
1845 switch(cmd.action()) {
1846 case LFUN_BUFFER_IMPORT:
1849 case LFUN_MASTER_BUFFER_UPDATE:
1850 case LFUN_MASTER_BUFFER_VIEW:
1852 && (doc_buffer->parent() != 0
1853 || doc_buffer->hasChildren())
1854 && !d.processing_thread_watcher_.isRunning();
1857 case LFUN_BUFFER_UPDATE:
1858 case LFUN_BUFFER_VIEW: {
1859 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1863 string format = to_utf8(cmd.argument());
1864 if (cmd.argument().empty())
1865 format = doc_buffer->params().getDefaultOutputFormat();
1866 enable = doc_buffer->params().isExportableFormat(format);
1870 case LFUN_BUFFER_RELOAD:
1871 enable = doc_buffer && !doc_buffer->isUnnamed()
1872 && doc_buffer->fileName().exists()
1873 && (!doc_buffer->isClean()
1874 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1877 case LFUN_BUFFER_CHILD_OPEN:
1878 enable = doc_buffer != 0;
1881 case LFUN_BUFFER_WRITE:
1882 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1885 //FIXME: This LFUN should be moved to GuiApplication.
1886 case LFUN_BUFFER_WRITE_ALL: {
1887 // We enable the command only if there are some modified buffers
1888 Buffer * first = theBufferList().first();
1893 // We cannot use a for loop as the buffer list is a cycle.
1895 if (!b->isClean()) {
1899 b = theBufferList().next(b);
1900 } while (b != first);
1904 case LFUN_BUFFER_WRITE_AS:
1905 case LFUN_BUFFER_EXPORT_AS:
1906 enable = doc_buffer != 0;
1909 case LFUN_BUFFER_CLOSE:
1910 case LFUN_VIEW_CLOSE:
1911 enable = doc_buffer != 0;
1914 case LFUN_BUFFER_CLOSE_ALL:
1915 enable = theBufferList().last() != theBufferList().first();
1918 case LFUN_VIEW_SPLIT:
1919 if (cmd.getArg(0) == "vertical")
1920 enable = doc_buffer && (d.splitter_->count() == 1 ||
1921 d.splitter_->orientation() == Qt::Vertical);
1923 enable = doc_buffer && (d.splitter_->count() == 1 ||
1924 d.splitter_->orientation() == Qt::Horizontal);
1927 case LFUN_TAB_GROUP_CLOSE:
1928 enable = d.tabWorkAreaCount() > 1;
1931 case LFUN_TOOLBAR_TOGGLE: {
1932 string const name = cmd.getArg(0);
1933 if (GuiToolbar * t = toolbar(name))
1934 flag.setOnOff(t->isVisible());
1937 docstring const msg =
1938 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1944 case LFUN_DROP_LAYOUTS_CHOICE:
1948 case LFUN_UI_TOGGLE:
1949 flag.setOnOff(isFullScreen());
1952 case LFUN_DIALOG_DISCONNECT_INSET:
1955 case LFUN_DIALOG_HIDE:
1956 // FIXME: should we check if the dialog is shown?
1959 case LFUN_DIALOG_TOGGLE:
1960 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1961 // fall through to set "enable"
1962 case LFUN_DIALOG_SHOW: {
1963 string const name = cmd.getArg(0);
1965 enable = name == "aboutlyx"
1966 || name == "file" //FIXME: should be removed.
1968 || name == "texinfo"
1969 || name == "progress"
1970 || name == "compare";
1971 else if (name == "character" || name == "symbols"
1972 || name == "mathdelimiter" || name == "mathmatrix") {
1973 if (!buf || buf->isReadonly())
1976 Cursor const & cur = currentBufferView()->cursor();
1977 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1980 else if (name == "latexlog")
1981 enable = FileName(doc_buffer->logName()).isReadableFile();
1982 else if (name == "spellchecker")
1983 enable = theSpellChecker()
1984 && !doc_buffer->isReadonly()
1985 && !doc_buffer->text().empty();
1986 else if (name == "vclog")
1987 enable = doc_buffer->lyxvc().inUse();
1991 case LFUN_DIALOG_UPDATE: {
1992 string const name = cmd.getArg(0);
1994 enable = name == "prefs";
1998 case LFUN_COMMAND_EXECUTE:
2000 case LFUN_MENU_OPEN:
2001 // Nothing to check.
2004 case LFUN_COMPLETION_INLINE:
2005 if (!d.current_work_area_
2006 || !d.current_work_area_->completer().inlinePossible(
2007 currentBufferView()->cursor()))
2011 case LFUN_COMPLETION_POPUP:
2012 if (!d.current_work_area_
2013 || !d.current_work_area_->completer().popupPossible(
2014 currentBufferView()->cursor()))
2019 if (!d.current_work_area_
2020 || !d.current_work_area_->completer().inlinePossible(
2021 currentBufferView()->cursor()))
2025 case LFUN_COMPLETION_ACCEPT:
2026 if (!d.current_work_area_
2027 || (!d.current_work_area_->completer().popupVisible()
2028 && !d.current_work_area_->completer().inlineVisible()
2029 && !d.current_work_area_->completer().completionAvailable()))
2033 case LFUN_COMPLETION_CANCEL:
2034 if (!d.current_work_area_
2035 || (!d.current_work_area_->completer().popupVisible()
2036 && !d.current_work_area_->completer().inlineVisible()))
2040 case LFUN_BUFFER_ZOOM_OUT:
2041 case LFUN_BUFFER_ZOOM_IN: {
2042 // only diff between these two is that the default for ZOOM_OUT
2044 bool const neg_zoom =
2045 convert<int>(cmd.argument()) < 0 ||
2046 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2047 if (lyxrc.zoom <= 10 && neg_zoom) {
2048 flag.message(_("Zoom level cannot be less than 10%."));
2051 enable = doc_buffer;
2054 case LFUN_BUFFER_MOVE_NEXT:
2055 case LFUN_BUFFER_MOVE_PREVIOUS:
2056 // we do not cycle when moving
2057 case LFUN_BUFFER_NEXT:
2058 case LFUN_BUFFER_PREVIOUS:
2059 // because we cycle, it doesn't matter whether on first or last
2060 enable = (d.currentTabWorkArea()->count() > 1);
2062 case LFUN_BUFFER_SWITCH:
2063 // toggle on the current buffer, but do not toggle off
2064 // the other ones (is that a good idea?)
2066 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2067 flag.setOnOff(true);
2070 case LFUN_VC_REGISTER:
2071 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2073 case LFUN_VC_RENAME:
2074 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2077 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2079 case LFUN_VC_CHECK_IN:
2080 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2082 case LFUN_VC_CHECK_OUT:
2083 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2085 case LFUN_VC_LOCKING_TOGGLE:
2086 enable = doc_buffer && !doc_buffer->isReadonly()
2087 && doc_buffer->lyxvc().lockingToggleEnabled();
2088 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2090 case LFUN_VC_REVERT:
2091 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2093 case LFUN_VC_UNDO_LAST:
2094 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2096 case LFUN_VC_REPO_UPDATE:
2097 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2099 case LFUN_VC_COMMAND: {
2100 if (cmd.argument().empty())
2102 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2106 case LFUN_VC_COMPARE:
2107 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2110 case LFUN_SERVER_GOTO_FILE_ROW:
2111 case LFUN_LYX_ACTIVATE:
2113 case LFUN_FORWARD_SEARCH:
2114 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2117 case LFUN_FILE_INSERT_PLAINTEXT:
2118 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2119 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2122 case LFUN_SPELLING_CONTINUOUSLY:
2123 flag.setOnOff(lyxrc.spellcheck_continuously);
2131 flag.setEnabled(false);
2137 static FileName selectTemplateFile()
2139 FileDialog dlg(qt_("Select template file"));
2140 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2141 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2143 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2144 QStringList(qt_("LyX Documents (*.lyx)")));
2146 if (result.first == FileDialog::Later)
2148 if (result.second.isEmpty())
2150 return FileName(fromqstr(result.second));
2154 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2158 Buffer * newBuffer = 0;
2160 newBuffer = checkAndLoadLyXFile(filename);
2161 } catch (ExceptionMessage const & e) {
2168 message(_("Document not loaded."));
2172 setBuffer(newBuffer);
2173 newBuffer->errors("Parse");
2176 theSession().lastFiles().add(filename);
2182 void GuiView::openDocument(string const & fname)
2184 string initpath = lyxrc.document_path;
2186 if (documentBufferView()) {
2187 string const trypath = documentBufferView()->buffer().filePath();
2188 // If directory is writeable, use this as default.
2189 if (FileName(trypath).isDirWritable())
2195 if (fname.empty()) {
2196 FileDialog dlg(qt_("Select document to open"));
2197 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2198 dlg.setButton2(qt_("Examples|#E#e"),
2199 toqstr(addPath(package().system_support().absFileName(), "examples")));
2201 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2202 FileDialog::Result result =
2203 dlg.open(toqstr(initpath), filter);
2205 if (result.first == FileDialog::Later)
2208 filename = fromqstr(result.second);
2210 // check selected filename
2211 if (filename.empty()) {
2212 message(_("Canceled."));
2218 // get absolute path of file and add ".lyx" to the filename if
2220 FileName const fullname =
2221 fileSearch(string(), filename, "lyx", support::may_not_exist);
2222 if (!fullname.empty())
2223 filename = fullname.absFileName();
2225 if (!fullname.onlyPath().isDirectory()) {
2226 Alert::warning(_("Invalid filename"),
2227 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2228 from_utf8(fullname.absFileName())));
2232 // if the file doesn't exist and isn't already open (bug 6645),
2233 // let the user create one
2234 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2235 !LyXVC::file_not_found_hook(fullname)) {
2236 // the user specifically chose this name. Believe him.
2237 Buffer * const b = newFile(filename, string(), true);
2243 docstring const disp_fn = makeDisplayPath(filename);
2244 message(bformat(_("Opening document %1$s..."), disp_fn));
2247 Buffer * buf = loadDocument(fullname);
2249 str2 = bformat(_("Document %1$s opened."), disp_fn);
2250 if (buf->lyxvc().inUse())
2251 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2252 " " + _("Version control detected.");
2254 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2259 // FIXME: clean that
2260 static bool import(GuiView * lv, FileName const & filename,
2261 string const & format, ErrorList & errorList)
2263 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2265 string loader_format;
2266 vector<string> loaders = theConverters().loaders();
2267 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2268 vector<string>::const_iterator it = loaders.begin();
2269 vector<string>::const_iterator en = loaders.end();
2270 for (; it != en; ++it) {
2271 if (!theConverters().isReachable(format, *it))
2274 string const tofile =
2275 support::changeExtension(filename.absFileName(),
2276 formats.extension(*it));
2277 if (!theConverters().convert(0, filename, FileName(tofile),
2278 filename, format, *it, errorList))
2280 loader_format = *it;
2283 if (loader_format.empty()) {
2284 frontend::Alert::error(_("Couldn't import file"),
2285 bformat(_("No information for importing the format %1$s."),
2286 formats.prettyName(format)));
2290 loader_format = format;
2292 if (loader_format == "lyx") {
2293 Buffer * buf = lv->loadDocument(lyxfile);
2297 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2301 bool as_paragraphs = loader_format == "textparagraph";
2302 string filename2 = (loader_format == format) ? filename.absFileName()
2303 : support::changeExtension(filename.absFileName(),
2304 formats.extension(loader_format));
2305 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2307 guiApp->setCurrentView(lv);
2308 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2315 void GuiView::importDocument(string const & argument)
2318 string filename = split(argument, format, ' ');
2320 LYXERR(Debug::INFO, format << " file: " << filename);
2322 // need user interaction
2323 if (filename.empty()) {
2324 string initpath = lyxrc.document_path;
2325 if (documentBufferView()) {
2326 string const trypath = documentBufferView()->buffer().filePath();
2327 // If directory is writeable, use this as default.
2328 if (FileName(trypath).isDirWritable())
2332 docstring const text = bformat(_("Select %1$s file to import"),
2333 formats.prettyName(format));
2335 FileDialog dlg(toqstr(text));
2336 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2337 dlg.setButton2(qt_("Examples|#E#e"),
2338 toqstr(addPath(package().system_support().absFileName(), "examples")));
2340 docstring filter = formats.prettyName(format);
2343 filter += from_utf8(formats.extensions(format));
2346 FileDialog::Result result =
2347 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2349 if (result.first == FileDialog::Later)
2352 filename = fromqstr(result.second);
2354 // check selected filename
2355 if (filename.empty())
2356 message(_("Canceled."));
2359 if (filename.empty())
2362 // get absolute path of file
2363 FileName const fullname(support::makeAbsPath(filename));
2365 // Can happen if the user entered a path into the dialog
2367 if (fullname.onlyFileName().empty()) {
2368 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2369 "Aborting import."),
2370 from_utf8(fullname.absFileName()));
2371 frontend::Alert::error(_("File name error"), msg);
2372 message(_("Canceled."));
2377 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2379 // Check if the document already is open
2380 Buffer * buf = theBufferList().getBuffer(lyxfile);
2383 if (!closeBuffer()) {
2384 message(_("Canceled."));
2389 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2391 // if the file exists already, and we didn't do
2392 // -i lyx thefile.lyx, warn
2393 if (lyxfile.exists() && fullname != lyxfile) {
2395 docstring text = bformat(_("The document %1$s already exists.\n\n"
2396 "Do you want to overwrite that document?"), displaypath);
2397 int const ret = Alert::prompt(_("Overwrite document?"),
2398 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2401 message(_("Canceled."));
2406 message(bformat(_("Importing %1$s..."), displaypath));
2407 ErrorList errorList;
2408 if (import(this, fullname, format, errorList))
2409 message(_("imported."));
2411 message(_("file not imported!"));
2413 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2417 void GuiView::newDocument(string const & filename, bool from_template)
2419 FileName initpath(lyxrc.document_path);
2420 if (documentBufferView()) {
2421 FileName const trypath(documentBufferView()->buffer().filePath());
2422 // If directory is writeable, use this as default.
2423 if (trypath.isDirWritable())
2427 string templatefile;
2428 if (from_template) {
2429 templatefile = selectTemplateFile().absFileName();
2430 if (templatefile.empty())
2435 if (filename.empty())
2436 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2438 b = newFile(filename, templatefile, true);
2443 // If no new document could be created, it is unsure
2444 // whether there is a valid BufferView.
2445 if (currentBufferView())
2446 // Ensure the cursor is correctly positioned on screen.
2447 currentBufferView()->showCursor();
2451 void GuiView::insertLyXFile(docstring const & fname)
2453 BufferView * bv = documentBufferView();
2458 FileName filename(to_utf8(fname));
2459 if (filename.empty()) {
2460 // Launch a file browser
2462 string initpath = lyxrc.document_path;
2463 string const trypath = bv->buffer().filePath();
2464 // If directory is writeable, use this as default.
2465 if (FileName(trypath).isDirWritable())
2469 FileDialog dlg(qt_("Select LyX document to insert"));
2470 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2471 dlg.setButton2(qt_("Examples|#E#e"),
2472 toqstr(addPath(package().system_support().absFileName(),
2475 FileDialog::Result result = dlg.open(toqstr(initpath),
2476 QStringList(qt_("LyX Documents (*.lyx)")));
2478 if (result.first == FileDialog::Later)
2482 filename.set(fromqstr(result.second));
2484 // check selected filename
2485 if (filename.empty()) {
2486 // emit message signal.
2487 message(_("Canceled."));
2492 bv->insertLyXFile(filename);
2493 bv->buffer().errors("Parse");
2497 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2499 FileName fname = b.fileName();
2500 FileName const oldname = fname;
2502 if (!newname.empty()) {
2504 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2506 // Switch to this Buffer.
2509 // No argument? Ask user through dialog.
2511 FileDialog dlg(qt_("Choose a filename to save document as"));
2512 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2513 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2515 if (!isLyXFileName(fname.absFileName()))
2516 fname.changeExtension(".lyx");
2518 FileDialog::Result result =
2519 dlg.save(toqstr(fname.onlyPath().absFileName()),
2520 QStringList(qt_("LyX Documents (*.lyx)")),
2521 toqstr(fname.onlyFileName()));
2523 if (result.first == FileDialog::Later)
2526 fname.set(fromqstr(result.second));
2531 if (!isLyXFileName(fname.absFileName()))
2532 fname.changeExtension(".lyx");
2535 // fname is now the new Buffer location.
2537 // if there is already a Buffer open with this name, we do not want
2538 // to have another one. (the second test makes sure we're not just
2539 // trying to overwrite ourselves, which is fine.)
2540 if (theBufferList().exists(fname) && fname != oldname
2541 && theBufferList().getBuffer(fname) != &b) {
2542 docstring const text =
2543 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2544 "Please close it before attempting to overwrite it.\n"
2545 "Do you want to choose a new filename?"),
2546 from_utf8(fname.absFileName()));
2547 int const ret = Alert::prompt(_("Chosen File Already Open"),
2548 text, 0, 1, _("&Rename"), _("&Cancel"));
2550 case 0: return renameBuffer(b, docstring(), kind);
2551 case 1: return false;
2556 bool const existsLocal = fname.exists();
2557 bool const existsInVC = LyXVC::fileInVC(fname);
2558 if (existsLocal || existsInVC) {
2559 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2560 if (kind != LV_WRITE_AS && existsInVC) {
2561 // renaming to a name that is already in VC
2563 docstring text = bformat(_("The document %1$s "
2564 "is already registered.\n\n"
2565 "Do you want to choose a new name?"),
2567 docstring const title = (kind == LV_VC_RENAME) ?
2568 _("Rename document?") : _("Copy document?");
2569 docstring const button = (kind == LV_VC_RENAME) ?
2570 _("&Rename") : _("&Copy");
2571 int const ret = Alert::prompt(title, text, 0, 1,
2572 button, _("&Cancel"));
2574 case 0: return renameBuffer(b, docstring(), kind);
2575 case 1: return false;
2580 docstring text = bformat(_("The document %1$s "
2581 "already exists.\n\n"
2582 "Do you want to overwrite that document?"),
2584 int const ret = Alert::prompt(_("Overwrite document?"),
2585 text, 0, 2, _("&Overwrite"),
2586 _("&Rename"), _("&Cancel"));
2589 case 1: return renameBuffer(b, docstring(), kind);
2590 case 2: return false;
2596 case LV_VC_RENAME: {
2597 string msg = b.lyxvc().rename(fname);
2600 message(from_utf8(msg));
2604 string msg = b.lyxvc().copy(fname);
2607 message(from_utf8(msg));
2613 // LyXVC created the file already in case of LV_VC_RENAME or
2614 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2615 // relative paths of included stuff right if we moved e.g. from
2616 // /a/b.lyx to /a/c/b.lyx.
2618 bool const saved = saveBuffer(b, fname);
2625 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2627 FileName fname = b.fileName();
2629 FileDialog dlg(qt_("Choose a filename to export the document as"));
2630 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2633 QString const anyformat = qt_("Guess from extension (*.*)");
2636 vector<Format const *> export_formats;
2637 for (Format const & f : formats)
2638 if (f.documentFormat())
2639 export_formats.push_back(&f);
2640 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2641 map<QString, string> fmap;
2644 for (Format const * f : export_formats) {
2645 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2646 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2648 from_ascii(f->extension())));
2649 types << loc_filter;
2650 fmap[loc_filter] = f->name();
2651 if (from_ascii(f->name()) == iformat) {
2652 filter = loc_filter;
2653 ext = f->extension();
2656 string ofname = fname.onlyFileName();
2658 ofname = support::changeExtension(ofname, ext);
2659 FileDialog::Result result =
2660 dlg.save(toqstr(fname.onlyPath().absFileName()),
2664 if (result.first != FileDialog::Chosen)
2668 fname.set(fromqstr(result.second));
2669 if (filter == anyformat)
2670 fmt_name = formats.getFormatFromExtension(fname.extension());
2672 fmt_name = fmap[filter];
2673 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2674 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2676 if (fmt_name.empty() || fname.empty())
2679 // fname is now the new Buffer location.
2680 if (FileName(fname).exists()) {
2681 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2682 docstring text = bformat(_("The document %1$s already "
2683 "exists.\n\nDo you want to "
2684 "overwrite that document?"),
2686 int const ret = Alert::prompt(_("Overwrite document?"),
2687 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2690 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2691 case 2: return false;
2695 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2698 return dr.dispatched();
2702 bool GuiView::saveBuffer(Buffer & b)
2704 return saveBuffer(b, FileName());
2708 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2710 if (workArea(b) && workArea(b)->inDialogMode())
2713 if (fn.empty() && b.isUnnamed())
2714 return renameBuffer(b, docstring());
2716 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2718 theSession().lastFiles().add(b.fileName());
2722 // Switch to this Buffer.
2725 // FIXME: we don't tell the user *WHY* the save failed !!
2726 docstring const file = makeDisplayPath(b.absFileName(), 30);
2727 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2728 "Do you want to rename the document and "
2729 "try again?"), file);
2730 int const ret = Alert::prompt(_("Rename and save?"),
2731 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2734 if (!renameBuffer(b, docstring()))
2743 return saveBuffer(b, fn);
2747 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2749 return closeWorkArea(wa, false);
2753 // We only want to close the buffer if it is not visible in other workareas
2754 // of the same view, nor in other views, and if this is not a child
2755 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2757 Buffer & buf = wa->bufferView().buffer();
2759 bool last_wa = d.countWorkAreasOf(buf) == 1
2760 && !inOtherView(buf) && !buf.parent();
2762 bool close_buffer = last_wa;
2765 if (lyxrc.close_buffer_with_last_view == "yes")
2767 else if (lyxrc.close_buffer_with_last_view == "no")
2768 close_buffer = false;
2771 if (buf.isUnnamed())
2772 file = from_utf8(buf.fileName().onlyFileName());
2774 file = buf.fileName().displayName(30);
2775 docstring const text = bformat(
2776 _("Last view on document %1$s is being closed.\n"
2777 "Would you like to close or hide the document?\n"
2779 "Hidden documents can be displayed back through\n"
2780 "the menu: View->Hidden->...\n"
2782 "To remove this question, set your preference in:\n"
2783 " Tools->Preferences->Look&Feel->UserInterface\n"
2785 int ret = Alert::prompt(_("Close or hide document?"),
2786 text, 0, 1, _("&Close"), _("&Hide"));
2787 close_buffer = (ret == 0);
2791 return closeWorkArea(wa, close_buffer);
2795 bool GuiView::closeBuffer()
2797 GuiWorkArea * wa = currentMainWorkArea();
2798 // coverity complained about this
2799 // it seems unnecessary, but perhaps is worth the check
2800 LASSERT(wa, return false);
2802 setCurrentWorkArea(wa);
2803 Buffer & buf = wa->bufferView().buffer();
2804 return closeWorkArea(wa, !buf.parent());
2808 void GuiView::writeSession() const {
2809 GuiWorkArea const * active_wa = currentMainWorkArea();
2810 for (int i = 0; i < d.splitter_->count(); ++i) {
2811 TabWorkArea * twa = d.tabWorkArea(i);
2812 for (int j = 0; j < twa->count(); ++j) {
2813 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2814 Buffer & buf = wa->bufferView().buffer();
2815 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2821 bool GuiView::closeBufferAll()
2823 // Close the workareas in all other views
2824 QList<int> const ids = guiApp->viewIds();
2825 for (int i = 0; i != ids.size(); ++i) {
2826 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2830 // Close our own workareas
2831 if (!closeWorkAreaAll())
2834 // Now close the hidden buffers. We prevent hidden buffers from being
2835 // dirty, so we can just close them.
2836 theBufferList().closeAll();
2841 bool GuiView::closeWorkAreaAll()
2843 setCurrentWorkArea(currentMainWorkArea());
2845 // We might be in a situation that there is still a tabWorkArea, but
2846 // there are no tabs anymore. This can happen when we get here after a
2847 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2848 // many TabWorkArea's have no documents anymore.
2851 // We have to call count() each time, because it can happen that
2852 // more than one splitter will disappear in one iteration (bug 5998).
2853 while (d.splitter_->count() > empty_twa) {
2854 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2856 if (twa->count() == 0)
2859 setCurrentWorkArea(twa->currentWorkArea());
2860 if (!closeTabWorkArea(twa))
2868 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2873 Buffer & buf = wa->bufferView().buffer();
2875 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2876 Alert::warning(_("Close document"),
2877 _("Document could not be closed because it is being processed by LyX."));
2882 return closeBuffer(buf);
2884 if (!inMultiTabs(wa))
2885 if (!saveBufferIfNeeded(buf, true))
2893 bool GuiView::closeBuffer(Buffer & buf)
2895 // If we are in a close_event all children will be closed in some time,
2896 // so no need to do it here. This will ensure that the children end up
2897 // in the session file in the correct order. If we close the master
2898 // buffer, we can close or release the child buffers here too.
2899 bool success = true;
2901 ListOfBuffers clist = buf.getChildren();
2902 ListOfBuffers::const_iterator it = clist.begin();
2903 ListOfBuffers::const_iterator const bend = clist.end();
2904 for (; it != bend; ++it) {
2905 Buffer * child_buf = *it;
2906 if (theBufferList().isOthersChild(&buf, child_buf)) {
2907 child_buf->setParent(0);
2911 // FIXME: should we look in other tabworkareas?
2912 // ANSWER: I don't think so. I've tested, and if the child is
2913 // open in some other window, it closes without a problem.
2914 GuiWorkArea * child_wa = workArea(*child_buf);
2916 success = closeWorkArea(child_wa, true);
2920 // In this case the child buffer is open but hidden.
2921 // It therefore should not (MUST NOT) be dirty!
2922 LATTEST(child_buf->isClean());
2923 theBufferList().release(child_buf);
2928 // goto bookmark to update bookmark pit.
2929 // FIXME: we should update only the bookmarks related to this buffer!
2930 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2931 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2932 guiApp->gotoBookmark(i+1, false, false);
2934 if (saveBufferIfNeeded(buf, false)) {
2935 buf.removeAutosaveFile();
2936 theBufferList().release(&buf);
2940 // open all children again to avoid a crash because of dangling
2941 // pointers (bug 6603)
2947 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2949 while (twa == d.currentTabWorkArea()) {
2950 twa->setCurrentIndex(twa->count() - 1);
2952 GuiWorkArea * wa = twa->currentWorkArea();
2953 Buffer & b = wa->bufferView().buffer();
2955 // We only want to close the buffer if the same buffer is not visible
2956 // in another view, and if this is not a child and if we are closing
2957 // a view (not a tabgroup).
2958 bool const close_buffer =
2959 !inOtherView(b) && !b.parent() && closing_;
2961 if (!closeWorkArea(wa, close_buffer))
2968 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2970 if (buf.isClean() || buf.paragraphs().empty())
2973 // Switch to this Buffer.
2978 if (buf.isUnnamed())
2979 file = from_utf8(buf.fileName().onlyFileName());
2981 file = buf.fileName().displayName(30);
2983 // Bring this window to top before asking questions.
2988 if (hiding && buf.isUnnamed()) {
2989 docstring const text = bformat(_("The document %1$s has not been "
2990 "saved yet.\n\nDo you want to save "
2991 "the document?"), file);
2992 ret = Alert::prompt(_("Save new document?"),
2993 text, 0, 1, _("&Save"), _("&Cancel"));
2997 docstring const text = bformat(_("The document %1$s has unsaved changes."
2998 "\n\nDo you want to save the document or discard the changes?"), file);
2999 ret = Alert::prompt(_("Save changed document?"),
3000 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
3005 if (!saveBuffer(buf))
3009 // If we crash after this we could have no autosave file
3010 // but I guess this is really improbable (Jug).
3011 // Sometimes improbable things happen:
3012 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3013 // buf.removeAutosaveFile();
3015 // revert all changes
3026 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3028 Buffer & buf = wa->bufferView().buffer();
3030 for (int i = 0; i != d.splitter_->count(); ++i) {
3031 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3032 if (wa_ && wa_ != wa)
3035 return inOtherView(buf);
3039 bool GuiView::inOtherView(Buffer & buf)
3041 QList<int> const ids = guiApp->viewIds();
3043 for (int i = 0; i != ids.size(); ++i) {
3047 if (guiApp->view(ids[i]).workArea(buf))
3054 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3056 if (!documentBufferView())
3059 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3060 Buffer * const curbuf = &documentBufferView()->buffer();
3061 int nwa = twa->count();
3062 for (int i = 0; i < nwa; ++i) {
3063 if (&workArea(i)->bufferView().buffer() == curbuf) {
3065 if (np == NEXTBUFFER)
3066 next_index = (i == nwa - 1 ? 0 : i + 1);
3068 next_index = (i == 0 ? nwa - 1 : i - 1);
3070 twa->moveTab(i, next_index);
3072 setBuffer(&workArea(next_index)->bufferView().buffer());
3080 /// make sure the document is saved
3081 static bool ensureBufferClean(Buffer * buffer)
3083 LASSERT(buffer, return false);
3084 if (buffer->isClean() && !buffer->isUnnamed())
3087 docstring const file = buffer->fileName().displayName(30);
3090 if (!buffer->isUnnamed()) {
3091 text = bformat(_("The document %1$s has unsaved "
3092 "changes.\n\nDo you want to save "
3093 "the document?"), file);
3094 title = _("Save changed document?");
3097 text = bformat(_("The document %1$s has not been "
3098 "saved yet.\n\nDo you want to save "
3099 "the document?"), file);
3100 title = _("Save new document?");
3102 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3105 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3107 return buffer->isClean() && !buffer->isUnnamed();
3111 bool GuiView::reloadBuffer(Buffer & buf)
3113 Buffer::ReadStatus status = buf.reload();
3114 return status == Buffer::ReadSuccess;
3118 void GuiView::checkExternallyModifiedBuffers()
3120 BufferList::iterator bit = theBufferList().begin();
3121 BufferList::iterator const bend = theBufferList().end();
3122 for (; bit != bend; ++bit) {
3123 Buffer * buf = *bit;
3124 if (buf->fileName().exists()
3125 && buf->isExternallyModified(Buffer::checksum_method)) {
3126 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3127 " Reload now? Any local changes will be lost."),
3128 from_utf8(buf->absFileName()));
3129 int const ret = Alert::prompt(_("Reload externally changed document?"),
3130 text, 0, 1, _("&Reload"), _("&Cancel"));
3138 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3140 Buffer * buffer = documentBufferView()
3141 ? &(documentBufferView()->buffer()) : 0;
3143 switch (cmd.action()) {
3144 case LFUN_VC_REGISTER:
3145 if (!buffer || !ensureBufferClean(buffer))
3147 if (!buffer->lyxvc().inUse()) {
3148 if (buffer->lyxvc().registrer()) {
3149 reloadBuffer(*buffer);
3150 dr.clearMessageUpdate();
3155 case LFUN_VC_RENAME:
3156 case LFUN_VC_COPY: {
3157 if (!buffer || !ensureBufferClean(buffer))
3159 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3160 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3161 // Some changes are not yet committed.
3162 // We test here and not in getStatus(), since
3163 // this test is expensive.
3165 LyXVC::CommandResult ret =
3166 buffer->lyxvc().checkIn(log);
3168 if (ret == LyXVC::ErrorCommand ||
3169 ret == LyXVC::VCSuccess)
3170 reloadBuffer(*buffer);
3171 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3172 frontend::Alert::error(
3173 _("Revision control error."),
3174 _("Document could not be checked in."));
3178 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3179 LV_VC_RENAME : LV_VC_COPY;
3180 renameBuffer(*buffer, cmd.argument(), kind);
3185 case LFUN_VC_CHECK_IN:
3186 if (!buffer || !ensureBufferClean(buffer))
3188 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3190 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3192 // Only skip reloading if the checkin was cancelled or
3193 // an error occurred before the real checkin VCS command
3194 // was executed, since the VCS might have changed the
3195 // file even if it could not checkin successfully.
3196 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3197 reloadBuffer(*buffer);
3201 case LFUN_VC_CHECK_OUT:
3202 if (!buffer || !ensureBufferClean(buffer))
3204 if (buffer->lyxvc().inUse()) {
3205 dr.setMessage(buffer->lyxvc().checkOut());
3206 reloadBuffer(*buffer);
3210 case LFUN_VC_LOCKING_TOGGLE:
3211 LASSERT(buffer, return);
3212 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3214 if (buffer->lyxvc().inUse()) {
3215 string res = buffer->lyxvc().lockingToggle();
3217 frontend::Alert::error(_("Revision control error."),
3218 _("Error when setting the locking property."));
3221 reloadBuffer(*buffer);
3226 case LFUN_VC_REVERT:
3227 LASSERT(buffer, return);
3228 if (buffer->lyxvc().revert()) {
3229 reloadBuffer(*buffer);
3230 dr.clearMessageUpdate();
3234 case LFUN_VC_UNDO_LAST:
3235 LASSERT(buffer, return);
3236 buffer->lyxvc().undoLast();
3237 reloadBuffer(*buffer);
3238 dr.clearMessageUpdate();
3241 case LFUN_VC_REPO_UPDATE:
3242 LASSERT(buffer, return);
3243 if (ensureBufferClean(buffer)) {
3244 dr.setMessage(buffer->lyxvc().repoUpdate());
3245 checkExternallyModifiedBuffers();
3249 case LFUN_VC_COMMAND: {
3250 string flag = cmd.getArg(0);
3251 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3254 if (contains(flag, 'M')) {
3255 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3258 string path = cmd.getArg(1);
3259 if (contains(path, "$$p") && buffer)
3260 path = subst(path, "$$p", buffer->filePath());
3261 LYXERR(Debug::LYXVC, "Directory: " << path);
3263 if (!pp.isReadableDirectory()) {
3264 lyxerr << _("Directory is not accessible.") << endl;
3267 support::PathChanger p(pp);
3269 string command = cmd.getArg(2);
3270 if (command.empty())
3273 command = subst(command, "$$i", buffer->absFileName());
3274 command = subst(command, "$$p", buffer->filePath());
3276 command = subst(command, "$$m", to_utf8(message));
3277 LYXERR(Debug::LYXVC, "Command: " << command);
3279 one.startscript(Systemcall::Wait, command);
3283 if (contains(flag, 'I'))
3284 buffer->markDirty();
3285 if (contains(flag, 'R'))
3286 reloadBuffer(*buffer);
3291 case LFUN_VC_COMPARE: {
3292 if (cmd.argument().empty()) {
3293 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3297 string rev1 = cmd.getArg(0);
3301 // it seems safe to assume we have a buffer
3302 // coverity[FORWARD_NULL]
3303 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3306 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3307 f2 = buffer->absFileName();
3309 string rev2 = cmd.getArg(1);
3313 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3317 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3318 f1 << "\n" << f2 << "\n" );
3319 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3320 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3330 void GuiView::openChildDocument(string const & fname)
3332 LASSERT(documentBufferView(), return);
3333 Buffer & buffer = documentBufferView()->buffer();
3334 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3335 documentBufferView()->saveBookmark(false);
3337 if (theBufferList().exists(filename)) {
3338 child = theBufferList().getBuffer(filename);
3341 message(bformat(_("Opening child document %1$s..."),
3342 makeDisplayPath(filename.absFileName())));
3343 child = loadDocument(filename, false);
3345 // Set the parent name of the child document.
3346 // This makes insertion of citations and references in the child work,
3347 // when the target is in the parent or another child document.
3349 child->setParent(&buffer);
3353 bool GuiView::goToFileRow(string const & argument)
3357 size_t i = argument.find_last_of(' ');
3358 if (i != string::npos) {
3359 file_name = os::internal_path(trim(argument.substr(0, i)));
3360 istringstream is(argument.substr(i + 1));
3365 if (i == string::npos) {
3366 LYXERR0("Wrong argument: " << argument);
3370 string const abstmp = package().temp_dir().absFileName();
3371 string const realtmp = package().temp_dir().realPath();
3372 // We have to use os::path_prefix_is() here, instead of
3373 // simply prefixIs(), because the file name comes from
3374 // an external application and may need case adjustment.
3375 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3376 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3377 // Needed by inverse dvi search. If it is a file
3378 // in tmpdir, call the apropriated function.
3379 // If tmpdir is a symlink, we may have the real
3380 // path passed back, so we correct for that.
3381 if (!prefixIs(file_name, abstmp))
3382 file_name = subst(file_name, realtmp, abstmp);
3383 buf = theBufferList().getBufferFromTmp(file_name);
3385 // Must replace extension of the file to be .lyx
3386 // and get full path
3387 FileName const s = fileSearch(string(),
3388 support::changeExtension(file_name, ".lyx"), "lyx");
3389 // Either change buffer or load the file
3390 if (theBufferList().exists(s))
3391 buf = theBufferList().getBuffer(s);
3392 else if (s.exists()) {
3393 buf = loadDocument(s);
3398 _("File does not exist: %1$s"),
3399 makeDisplayPath(file_name)));
3405 _("No buffer for file: %1$s."),
3406 makeDisplayPath(file_name))
3411 documentBufferView()->setCursorFromRow(row);
3417 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3419 Buffer::ExportStatus const status = func(format);
3421 // the cloning operation will have produced a clone of the entire set of
3422 // documents, starting from the master. so we must delete those.
3423 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3425 busyBuffers.remove(orig);
3430 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3432 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3433 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3437 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3439 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3440 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3444 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3446 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3447 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3451 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3452 string const & argument,
3453 Buffer const * used_buffer,
3454 docstring const & msg,
3455 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3456 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3457 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3462 string format = argument;
3464 format = used_buffer->params().getDefaultOutputFormat();
3465 processing_format = format;
3467 progress_->clearMessages();
3470 #if EXPORT_in_THREAD
3471 GuiViewPrivate::busyBuffers.insert(used_buffer);
3472 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3473 if (!cloned_buffer) {
3474 Alert::error(_("Export Error"),
3475 _("Error cloning the Buffer."));
3478 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3483 setPreviewFuture(f);
3484 last_export_format = used_buffer->params().bufferFormat();
3487 // We are asynchronous, so we don't know here anything about the success
3490 Buffer::ExportStatus status;
3492 status = (used_buffer->*syncFunc)(format, true);
3493 } else if (previewFunc) {
3494 status = (used_buffer->*previewFunc)(format);
3497 handleExportStatus(gv_, status, format);
3499 return (status == Buffer::ExportSuccess
3500 || status == Buffer::PreviewSuccess);
3504 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3506 BufferView * bv = currentBufferView();
3507 LASSERT(bv, return);
3509 // Let the current BufferView dispatch its own actions.
3510 bv->dispatch(cmd, dr);
3511 if (dr.dispatched())
3514 // Try with the document BufferView dispatch if any.
3515 BufferView * doc_bv = documentBufferView();
3516 if (doc_bv && doc_bv != bv) {
3517 doc_bv->dispatch(cmd, dr);
3518 if (dr.dispatched())
3522 // Then let the current Cursor dispatch its own actions.
3523 bv->cursor().dispatch(cmd);
3525 // update completion. We do it here and not in
3526 // processKeySym to avoid another redraw just for a
3527 // changed inline completion
3528 if (cmd.origin() == FuncRequest::KEYBOARD) {
3529 if (cmd.action() == LFUN_SELF_INSERT
3530 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3531 updateCompletion(bv->cursor(), true, true);
3532 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3533 updateCompletion(bv->cursor(), false, true);
3535 updateCompletion(bv->cursor(), false, false);
3538 dr = bv->cursor().result();
3542 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3544 BufferView * bv = currentBufferView();
3545 // By default we won't need any update.
3546 dr.screenUpdate(Update::None);
3547 // assume cmd will be dispatched
3548 dr.dispatched(true);
3550 Buffer * doc_buffer = documentBufferView()
3551 ? &(documentBufferView()->buffer()) : 0;
3553 if (cmd.origin() == FuncRequest::TOC) {
3554 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3555 // FIXME: do we need to pass a DispatchResult object here?
3556 toc->doDispatch(bv->cursor(), cmd);
3560 string const argument = to_utf8(cmd.argument());
3562 switch(cmd.action()) {
3563 case LFUN_BUFFER_CHILD_OPEN:
3564 openChildDocument(to_utf8(cmd.argument()));
3567 case LFUN_BUFFER_IMPORT:
3568 importDocument(to_utf8(cmd.argument()));
3571 case LFUN_BUFFER_EXPORT: {
3574 // GCC only sees strfwd.h when building merged
3575 if (::lyx::operator==(cmd.argument(), "custom")) {
3576 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3580 string const dest = cmd.getArg(1);
3581 FileName target_dir;
3582 if (!dest.empty() && FileName::isAbsolute(dest))
3583 target_dir = FileName(support::onlyPath(dest));
3585 target_dir = doc_buffer->fileName().onlyPath();
3587 if ((dest.empty() && doc_buffer->isUnnamed())
3588 || !target_dir.isDirWritable()) {
3589 exportBufferAs(*doc_buffer, cmd.argument());
3592 /* TODO/Review: Is it a problem to also export the children?
3593 See the update_unincluded flag */
3594 d.asyncBufferProcessing(argument,
3597 &GuiViewPrivate::exportAndDestroy,
3600 // TODO Inform user about success
3604 case LFUN_BUFFER_EXPORT_AS: {
3605 LASSERT(doc_buffer, break);
3606 docstring f = cmd.argument();
3608 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3609 exportBufferAs(*doc_buffer, f);
3613 case LFUN_BUFFER_UPDATE: {
3614 d.asyncBufferProcessing(argument,
3617 &GuiViewPrivate::compileAndDestroy,
3622 case LFUN_BUFFER_VIEW: {
3623 d.asyncBufferProcessing(argument,
3625 _("Previewing ..."),
3626 &GuiViewPrivate::previewAndDestroy,
3631 case LFUN_MASTER_BUFFER_UPDATE: {
3632 d.asyncBufferProcessing(argument,
3633 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3635 &GuiViewPrivate::compileAndDestroy,
3640 case LFUN_MASTER_BUFFER_VIEW: {
3641 d.asyncBufferProcessing(argument,
3642 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3644 &GuiViewPrivate::previewAndDestroy,
3645 0, &Buffer::preview);
3648 case LFUN_BUFFER_SWITCH: {
3649 string const file_name = to_utf8(cmd.argument());
3650 if (!FileName::isAbsolute(file_name)) {
3652 dr.setMessage(_("Absolute filename expected."));
3656 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3659 dr.setMessage(_("Document not loaded"));
3663 // Do we open or switch to the buffer in this view ?
3664 if (workArea(*buffer)
3665 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3670 // Look for the buffer in other views
3671 QList<int> const ids = guiApp->viewIds();
3673 for (; i != ids.size(); ++i) {
3674 GuiView & gv = guiApp->view(ids[i]);
3675 if (gv.workArea(*buffer)) {
3677 gv.activateWindow();
3679 gv.setBuffer(buffer);
3684 // If necessary, open a new window as a last resort
3685 if (i == ids.size()) {
3686 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3692 case LFUN_BUFFER_NEXT:
3693 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3696 case LFUN_BUFFER_MOVE_NEXT:
3697 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3700 case LFUN_BUFFER_PREVIOUS:
3701 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3704 case LFUN_BUFFER_MOVE_PREVIOUS:
3705 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3708 case LFUN_COMMAND_EXECUTE: {
3709 command_execute_ = true;
3710 minibuffer_focus_ = true;
3713 case LFUN_DROP_LAYOUTS_CHOICE:
3714 d.layout_->showPopup();
3717 case LFUN_MENU_OPEN:
3718 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3719 menu->exec(QCursor::pos());
3722 case LFUN_FILE_INSERT:
3723 insertLyXFile(cmd.argument());
3726 case LFUN_FILE_INSERT_PLAINTEXT:
3727 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3728 string const fname = to_utf8(cmd.argument());
3729 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3730 dr.setMessage(_("Absolute filename expected."));
3734 FileName filename(fname);
3735 if (fname.empty()) {
3736 FileDialog dlg(qt_("Select file to insert"));
3738 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3739 QStringList(qt_("All Files (*)")));
3741 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3742 dr.setMessage(_("Canceled."));
3746 filename.set(fromqstr(result.second));
3750 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3751 bv->dispatch(new_cmd, dr);
3756 case LFUN_BUFFER_RELOAD: {
3757 LASSERT(doc_buffer, break);
3760 if (!doc_buffer->isClean()) {
3761 docstring const file =
3762 makeDisplayPath(doc_buffer->absFileName(), 20);
3763 docstring text = bformat(_("Any changes will be lost. "
3764 "Are you sure you want to revert to the saved version "
3765 "of the document %1$s?"), file);
3766 ret = Alert::prompt(_("Revert to saved document?"),
3767 text, 1, 1, _("&Revert"), _("&Cancel"));
3771 doc_buffer->markClean();
3772 reloadBuffer(*doc_buffer);
3773 dr.forceBufferUpdate();
3778 case LFUN_BUFFER_WRITE:
3779 LASSERT(doc_buffer, break);
3780 saveBuffer(*doc_buffer);
3783 case LFUN_BUFFER_WRITE_AS:
3784 LASSERT(doc_buffer, break);
3785 renameBuffer(*doc_buffer, cmd.argument());
3788 case LFUN_BUFFER_WRITE_ALL: {
3789 Buffer * first = theBufferList().first();
3792 message(_("Saving all documents..."));
3793 // We cannot use a for loop as the buffer list cycles.
3796 if (!b->isClean()) {
3798 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3800 b = theBufferList().next(b);
3801 } while (b != first);
3802 dr.setMessage(_("All documents saved."));
3806 case LFUN_BUFFER_CLOSE:
3810 case LFUN_BUFFER_CLOSE_ALL:
3814 case LFUN_TOOLBAR_TOGGLE: {
3815 string const name = cmd.getArg(0);
3816 if (GuiToolbar * t = toolbar(name))
3821 case LFUN_DIALOG_UPDATE: {
3822 string const name = to_utf8(cmd.argument());
3823 if (name == "prefs" || name == "document")
3824 updateDialog(name, string());
3825 else if (name == "paragraph")
3826 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3827 else if (currentBufferView()) {
3828 Inset * inset = currentBufferView()->editedInset(name);
3829 // Can only update a dialog connected to an existing inset
3831 // FIXME: get rid of this indirection; GuiView ask the inset
3832 // if he is kind enough to update itself...
3833 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3834 //FIXME: pass DispatchResult here?
3835 inset->dispatch(currentBufferView()->cursor(), fr);
3841 case LFUN_DIALOG_TOGGLE: {
3842 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3843 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3844 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3848 case LFUN_DIALOG_DISCONNECT_INSET:
3849 disconnectDialog(to_utf8(cmd.argument()));
3852 case LFUN_DIALOG_HIDE: {
3853 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3857 case LFUN_DIALOG_SHOW: {
3858 string const name = cmd.getArg(0);
3859 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3861 if (name == "character") {
3862 data = freefont2string();
3864 showDialog("character", data);
3865 } else if (name == "latexlog") {
3866 Buffer::LogType type;
3867 string const logfile = doc_buffer->logName(&type);
3869 case Buffer::latexlog:
3872 case Buffer::buildlog:
3876 data += Lexer::quoteString(logfile);
3877 showDialog("log", data);
3878 } else if (name == "vclog") {
3879 string const data = "vc " +
3880 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3881 showDialog("log", data);
3882 } else if (name == "symbols") {
3883 data = bv->cursor().getEncoding()->name();
3885 showDialog("symbols", data);
3887 } else if (name == "prefs" && isFullScreen()) {
3888 lfunUiToggle("fullscreen");
3889 showDialog("prefs", data);
3891 showDialog(name, data);
3896 dr.setMessage(cmd.argument());
3899 case LFUN_UI_TOGGLE: {
3900 string arg = cmd.getArg(0);
3901 if (!lfunUiToggle(arg)) {
3902 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3903 dr.setMessage(bformat(msg, from_utf8(arg)));
3905 // Make sure the keyboard focus stays in the work area.
3910 case LFUN_VIEW_SPLIT: {
3911 LASSERT(doc_buffer, break);
3912 string const orientation = cmd.getArg(0);
3913 d.splitter_->setOrientation(orientation == "vertical"
3914 ? Qt::Vertical : Qt::Horizontal);
3915 TabWorkArea * twa = addTabWorkArea();
3916 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3917 setCurrentWorkArea(wa);
3920 case LFUN_TAB_GROUP_CLOSE:
3921 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3922 closeTabWorkArea(twa);
3923 d.current_work_area_ = 0;
3924 twa = d.currentTabWorkArea();
3925 // Switch to the next GuiWorkArea in the found TabWorkArea.
3927 // Make sure the work area is up to date.
3928 setCurrentWorkArea(twa->currentWorkArea());
3930 setCurrentWorkArea(0);
3935 case LFUN_VIEW_CLOSE:
3936 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3937 closeWorkArea(twa->currentWorkArea());
3938 d.current_work_area_ = 0;
3939 twa = d.currentTabWorkArea();
3940 // Switch to the next GuiWorkArea in the found TabWorkArea.
3942 // Make sure the work area is up to date.
3943 setCurrentWorkArea(twa->currentWorkArea());
3945 setCurrentWorkArea(0);
3950 case LFUN_COMPLETION_INLINE:
3951 if (d.current_work_area_)
3952 d.current_work_area_->completer().showInline();
3955 case LFUN_COMPLETION_POPUP:
3956 if (d.current_work_area_)
3957 d.current_work_area_->completer().showPopup();
3962 if (d.current_work_area_)
3963 d.current_work_area_->completer().tab();
3966 case LFUN_COMPLETION_CANCEL:
3967 if (d.current_work_area_) {
3968 if (d.current_work_area_->completer().popupVisible())
3969 d.current_work_area_->completer().hidePopup();
3971 d.current_work_area_->completer().hideInline();
3975 case LFUN_COMPLETION_ACCEPT:
3976 if (d.current_work_area_)
3977 d.current_work_area_->completer().activate();
3980 case LFUN_BUFFER_ZOOM_IN:
3981 case LFUN_BUFFER_ZOOM_OUT: {
3982 // use a signed temp to avoid overflow
3983 int zoom = lyxrc.zoom;
3984 if (cmd.argument().empty()) {
3985 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3990 zoom += convert<int>(cmd.argument());
3996 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3998 // The global QPixmapCache is used in GuiPainter to cache text
3999 // painting so we must reset it.
4000 QPixmapCache::clear();
4001 guiApp->fontLoader().update();
4002 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4006 case LFUN_VC_REGISTER:
4007 case LFUN_VC_RENAME:
4009 case LFUN_VC_CHECK_IN:
4010 case LFUN_VC_CHECK_OUT:
4011 case LFUN_VC_REPO_UPDATE:
4012 case LFUN_VC_LOCKING_TOGGLE:
4013 case LFUN_VC_REVERT:
4014 case LFUN_VC_UNDO_LAST:
4015 case LFUN_VC_COMMAND:
4016 case LFUN_VC_COMPARE:
4017 dispatchVC(cmd, dr);
4020 case LFUN_SERVER_GOTO_FILE_ROW:
4021 goToFileRow(to_utf8(cmd.argument()));
4024 case LFUN_LYX_ACTIVATE:
4028 case LFUN_FORWARD_SEARCH: {
4029 // it seems safe to assume we have a document buffer, since
4030 // getStatus wants one.
4031 // coverity[FORWARD_NULL]
4032 Buffer const * doc_master = doc_buffer->masterBuffer();
4033 FileName const path(doc_master->temppath());
4034 string const texname = doc_master->isChild(doc_buffer)
4035 ? DocFileName(changeExtension(
4036 doc_buffer->absFileName(),
4037 "tex")).mangledFileName()
4038 : doc_buffer->latexName();
4039 string const fulltexname =
4040 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4041 string const mastername =
4042 removeExtension(doc_master->latexName());
4043 FileName const dviname(addName(path.absFileName(),
4044 addExtension(mastername, "dvi")));
4045 FileName const pdfname(addName(path.absFileName(),
4046 addExtension(mastername, "pdf")));
4047 bool const have_dvi = dviname.exists();
4048 bool const have_pdf = pdfname.exists();
4049 if (!have_dvi && !have_pdf) {
4050 dr.setMessage(_("Please, preview the document first."));
4053 string outname = dviname.onlyFileName();
4054 string command = lyxrc.forward_search_dvi;
4055 if (!have_dvi || (have_pdf &&
4056 pdfname.lastModified() > dviname.lastModified())) {
4057 outname = pdfname.onlyFileName();
4058 command = lyxrc.forward_search_pdf;
4061 DocIterator cur = bv->cursor();
4062 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4063 LYXERR(Debug::ACTION, "Forward search: row:" << row
4065 if (row == -1 || command.empty()) {
4066 dr.setMessage(_("Couldn't proceed."));
4069 string texrow = convert<string>(row);
4071 command = subst(command, "$$n", texrow);
4072 command = subst(command, "$$f", fulltexname);
4073 command = subst(command, "$$t", texname);
4074 command = subst(command, "$$o", outname);
4076 PathChanger p(path);
4078 one.startscript(Systemcall::DontWait, command);
4082 case LFUN_SPELLING_CONTINUOUSLY:
4083 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4084 dr.screenUpdate(Update::Force | Update::FitCursor);
4088 // The LFUN must be for one of BufferView, Buffer or Cursor;
4090 dispatchToBufferView(cmd, dr);
4094 // Part of automatic menu appearance feature.
4095 if (isFullScreen()) {
4096 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4100 // Need to update bv because many LFUNs here might have destroyed it
4101 bv = currentBufferView();
4103 // Clear non-empty selections
4104 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4106 Cursor & cur = bv->cursor();
4107 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4108 cur.clearSelection();
4114 bool GuiView::lfunUiToggle(string const & ui_component)
4116 if (ui_component == "scrollbar") {
4117 // hide() is of no help
4118 if (d.current_work_area_->verticalScrollBarPolicy() ==
4119 Qt::ScrollBarAlwaysOff)
4121 d.current_work_area_->setVerticalScrollBarPolicy(
4122 Qt::ScrollBarAsNeeded);
4124 d.current_work_area_->setVerticalScrollBarPolicy(
4125 Qt::ScrollBarAlwaysOff);
4126 } else if (ui_component == "statusbar") {
4127 statusBar()->setVisible(!statusBar()->isVisible());
4128 } else if (ui_component == "menubar") {
4129 menuBar()->setVisible(!menuBar()->isVisible());
4131 if (ui_component == "frame") {
4133 getContentsMargins(&l, &t, &r, &b);
4134 //are the frames in default state?
4135 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4137 setContentsMargins(-2, -2, -2, -2);
4139 setContentsMargins(0, 0, 0, 0);
4142 if (ui_component == "fullscreen") {
4150 void GuiView::toggleFullScreen()
4152 if (isFullScreen()) {
4153 for (int i = 0; i != d.splitter_->count(); ++i)
4154 d.tabWorkArea(i)->setFullScreen(false);
4155 setContentsMargins(0, 0, 0, 0);
4156 setWindowState(windowState() ^ Qt::WindowFullScreen);
4159 statusBar()->show();
4162 hideDialogs("prefs", 0);
4163 for (int i = 0; i != d.splitter_->count(); ++i)
4164 d.tabWorkArea(i)->setFullScreen(true);
4165 setContentsMargins(-2, -2, -2, -2);
4167 setWindowState(windowState() ^ Qt::WindowFullScreen);
4168 if (lyxrc.full_screen_statusbar)
4169 statusBar()->hide();
4170 if (lyxrc.full_screen_menubar)
4172 if (lyxrc.full_screen_toolbars) {
4173 ToolbarMap::iterator end = d.toolbars_.end();
4174 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4179 // give dialogs like the TOC a chance to adapt
4184 Buffer const * GuiView::updateInset(Inset const * inset)
4189 Buffer const * inset_buffer = &(inset->buffer());
4191 for (int i = 0; i != d.splitter_->count(); ++i) {
4192 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4195 Buffer const * buffer = &(wa->bufferView().buffer());
4196 if (inset_buffer == buffer)
4197 wa->scheduleRedraw();
4199 return inset_buffer;
4203 void GuiView::restartCursor()
4205 /* When we move around, or type, it's nice to be able to see
4206 * the cursor immediately after the keypress.
4208 if (d.current_work_area_)
4209 d.current_work_area_->startBlinkingCursor();
4211 // Take this occasion to update the other GUI elements.
4217 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4219 if (d.current_work_area_)
4220 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4225 // This list should be kept in sync with the list of insets in
4226 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4227 // dialog should have the same name as the inset.
4228 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4229 // docs in LyXAction.cpp.
4231 char const * const dialognames[] = {
4233 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4234 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4235 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4236 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4237 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4238 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4239 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4240 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4242 char const * const * const end_dialognames =
4243 dialognames + (sizeof(dialognames) / sizeof(char *));
4247 cmpCStr(char const * name) : name_(name) {}
4248 bool operator()(char const * other) {
4249 return strcmp(other, name_) == 0;
4256 bool isValidName(string const & name)
4258 return find_if(dialognames, end_dialognames,
4259 cmpCStr(name.c_str())) != end_dialognames;
4265 void GuiView::resetDialogs()
4267 // Make sure that no LFUN uses any GuiView.
4268 guiApp->setCurrentView(0);
4272 constructToolbars();
4273 guiApp->menus().fillMenuBar(menuBar(), this, false);
4274 d.layout_->updateContents(true);
4275 // Now update controls with current buffer.
4276 guiApp->setCurrentView(this);
4282 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4284 if (!isValidName(name))
4287 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4289 if (it != d.dialogs_.end()) {
4291 it->second->hideView();
4292 return it->second.get();
4295 Dialog * dialog = build(name);
4296 d.dialogs_[name].reset(dialog);
4297 if (lyxrc.allow_geometry_session)
4298 dialog->restoreSession();
4305 void GuiView::showDialog(string const & name, string const & data,
4308 triggerShowDialog(toqstr(name), toqstr(data), inset);
4312 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4318 const string name = fromqstr(qname);
4319 const string data = fromqstr(qdata);
4323 Dialog * dialog = findOrBuild(name, false);
4325 bool const visible = dialog->isVisibleView();
4326 dialog->showData(data);
4327 if (inset && currentBufferView())
4328 currentBufferView()->editInset(name, inset);
4329 // We only set the focus to the new dialog if it was not yet
4330 // visible in order not to change the existing previous behaviour
4332 // activateWindow is needed for floating dockviews
4333 dialog->asQWidget()->raise();
4334 dialog->asQWidget()->activateWindow();
4335 dialog->asQWidget()->setFocus();
4339 catch (ExceptionMessage const & ex) {
4347 bool GuiView::isDialogVisible(string const & name) const
4349 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4350 if (it == d.dialogs_.end())
4352 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4356 void GuiView::hideDialog(string const & name, Inset * inset)
4358 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4359 if (it == d.dialogs_.end())
4363 if (!currentBufferView())
4365 if (inset != currentBufferView()->editedInset(name))
4369 Dialog * const dialog = it->second.get();
4370 if (dialog->isVisibleView())
4372 if (currentBufferView())
4373 currentBufferView()->editInset(name, 0);
4377 void GuiView::disconnectDialog(string const & name)
4379 if (!isValidName(name))
4381 if (currentBufferView())
4382 currentBufferView()->editInset(name, 0);
4386 void GuiView::hideAll() const
4388 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4389 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4391 for(; it != end; ++it)
4392 it->second->hideView();
4396 void GuiView::updateDialogs()
4398 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4399 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4401 for(; it != end; ++it) {
4402 Dialog * dialog = it->second.get();
4404 if (dialog->needBufferOpen() && !documentBufferView())
4405 hideDialog(fromqstr(dialog->name()), 0);
4406 else if (dialog->isVisibleView())
4407 dialog->checkStatus();
4414 Dialog * createDialog(GuiView & lv, string const & name);
4416 // will be replaced by a proper factory...
4417 Dialog * createGuiAbout(GuiView & lv);
4418 Dialog * createGuiBibtex(GuiView & lv);
4419 Dialog * createGuiChanges(GuiView & lv);
4420 Dialog * createGuiCharacter(GuiView & lv);
4421 Dialog * createGuiCitation(GuiView & lv);
4422 Dialog * createGuiCompare(GuiView & lv);
4423 Dialog * createGuiCompareHistory(GuiView & lv);
4424 Dialog * createGuiDelimiter(GuiView & lv);
4425 Dialog * createGuiDocument(GuiView & lv);
4426 Dialog * createGuiErrorList(GuiView & lv);
4427 Dialog * createGuiExternal(GuiView & lv);
4428 Dialog * createGuiGraphics(GuiView & lv);
4429 Dialog * createGuiInclude(GuiView & lv);
4430 Dialog * createGuiIndex(GuiView & lv);
4431 Dialog * createGuiListings(GuiView & lv);
4432 Dialog * createGuiLog(GuiView & lv);
4433 Dialog * createGuiMathMatrix(GuiView & lv);
4434 Dialog * createGuiNote(GuiView & lv);
4435 Dialog * createGuiParagraph(GuiView & lv);
4436 Dialog * createGuiPhantom(GuiView & lv);
4437 Dialog * createGuiPreferences(GuiView & lv);
4438 Dialog * createGuiPrint(GuiView & lv);
4439 Dialog * createGuiPrintindex(GuiView & lv);
4440 Dialog * createGuiRef(GuiView & lv);
4441 Dialog * createGuiSearch(GuiView & lv);
4442 Dialog * createGuiSearchAdv(GuiView & lv);
4443 Dialog * createGuiSendTo(GuiView & lv);
4444 Dialog * createGuiShowFile(GuiView & lv);
4445 Dialog * createGuiSpellchecker(GuiView & lv);
4446 Dialog * createGuiSymbols(GuiView & lv);
4447 Dialog * createGuiTabularCreate(GuiView & lv);
4448 Dialog * createGuiTexInfo(GuiView & lv);
4449 Dialog * createGuiToc(GuiView & lv);
4450 Dialog * createGuiThesaurus(GuiView & lv);
4451 Dialog * createGuiViewSource(GuiView & lv);
4452 Dialog * createGuiWrap(GuiView & lv);
4453 Dialog * createGuiProgressView(GuiView & lv);
4457 Dialog * GuiView::build(string const & name)
4459 LASSERT(isValidName(name), return 0);
4461 Dialog * dialog = createDialog(*this, name);
4465 if (name == "aboutlyx")
4466 return createGuiAbout(*this);
4467 if (name == "bibtex")
4468 return createGuiBibtex(*this);
4469 if (name == "changes")
4470 return createGuiChanges(*this);
4471 if (name == "character")
4472 return createGuiCharacter(*this);
4473 if (name == "citation")
4474 return createGuiCitation(*this);
4475 if (name == "compare")
4476 return createGuiCompare(*this);
4477 if (name == "comparehistory")
4478 return createGuiCompareHistory(*this);
4479 if (name == "document")
4480 return createGuiDocument(*this);
4481 if (name == "errorlist")
4482 return createGuiErrorList(*this);
4483 if (name == "external")
4484 return createGuiExternal(*this);
4486 return createGuiShowFile(*this);
4487 if (name == "findreplace")
4488 return createGuiSearch(*this);
4489 if (name == "findreplaceadv")
4490 return createGuiSearchAdv(*this);
4491 if (name == "graphics")
4492 return createGuiGraphics(*this);
4493 if (name == "include")
4494 return createGuiInclude(*this);
4495 if (name == "index")
4496 return createGuiIndex(*this);
4497 if (name == "index_print")
4498 return createGuiPrintindex(*this);
4499 if (name == "listings")
4500 return createGuiListings(*this);
4502 return createGuiLog(*this);
4503 if (name == "mathdelimiter")
4504 return createGuiDelimiter(*this);
4505 if (name == "mathmatrix")
4506 return createGuiMathMatrix(*this);
4508 return createGuiNote(*this);
4509 if (name == "paragraph")
4510 return createGuiParagraph(*this);
4511 if (name == "phantom")
4512 return createGuiPhantom(*this);
4513 if (name == "prefs")
4514 return createGuiPreferences(*this);
4516 return createGuiRef(*this);
4517 if (name == "sendto")
4518 return createGuiSendTo(*this);
4519 if (name == "spellchecker")
4520 return createGuiSpellchecker(*this);
4521 if (name == "symbols")
4522 return createGuiSymbols(*this);
4523 if (name == "tabularcreate")
4524 return createGuiTabularCreate(*this);
4525 if (name == "texinfo")
4526 return createGuiTexInfo(*this);
4527 if (name == "thesaurus")
4528 return createGuiThesaurus(*this);
4530 return createGuiToc(*this);
4531 if (name == "view-source")
4532 return createGuiViewSource(*this);
4534 return createGuiWrap(*this);
4535 if (name == "progress")
4536 return createGuiProgressView(*this);
4542 } // namespace frontend
4545 #include "moc_GuiView.cpp"