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 #if QT_VERSION >= 0x050000
114 #include <QSvgRenderer>
116 #include <QtConcurrentRun>
124 // sync with GuiAlert.cpp
125 #define EXPORT_in_THREAD 1
128 #include "support/bind.h"
132 #ifdef HAVE_SYS_TIME_H
133 # include <sys/time.h>
141 using namespace lyx::support;
145 using support::addExtension;
146 using support::changeExtension;
147 using support::removeExtension;
153 class BackgroundWidget : public QWidget
156 BackgroundWidget(int width, int height)
157 : width_(width), height_(height)
159 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
160 if (!lyxrc.show_banner)
162 /// The text to be written on top of the pixmap
163 QString const text = lyx_version ?
164 qt_("version ") + lyx_version : qt_("unknown version");
165 #if QT_VERSION >= 0x050000
166 QString imagedir = "images/";
167 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
168 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
169 if (svgRenderer.isValid()) {
170 splash_ = QPixmap(splashSize());
171 QPainter painter(&splash_);
172 svgRenderer.render(&painter);
173 splash_.setDevicePixelRatio(pixelRatio());
175 splash_ = getPixmap("images/", "banner", "png");
178 splash_ = getPixmap("images/", "banner", "svgz,png");
181 QPainter pain(&splash_);
182 pain.setPen(QColor(0, 0, 0));
183 qreal const fsize = fontSize();
184 QPointF const position = textPosition();
186 "widget pixel ratio: " << pixelRatio() <<
187 " splash pixel ratio: " << splashPixelRatio() <<
188 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
190 // The font used to display the version info
191 font.setStyleHint(QFont::SansSerif);
192 font.setWeight(QFont::Bold);
193 font.setPointSizeF(fsize);
195 pain.drawText(position, text);
196 setFocusPolicy(Qt::StrongFocus);
199 void paintEvent(QPaintEvent *)
201 int const w = width_;
202 int const h = height_;
203 int const x = (width() - w) / 2;
204 int const y = (height() - h) / 2;
206 "widget pixel ratio: " << pixelRatio() <<
207 " splash pixel ratio: " << splashPixelRatio() <<
208 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
210 pain.drawPixmap(x, y, w, h, splash_);
213 void keyPressEvent(QKeyEvent * ev)
216 setKeySymbol(&sym, ev);
218 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
230 /// Current ratio between physical pixels and device-independent pixels
231 double pixelRatio() const {
232 #if QT_VERSION >= 0x050000
233 return devicePixelRatio();
239 qreal fontSize() const {
240 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
243 QPointF textPosition() const {
244 return QPointF(width_/2 - 18, height_/2 + 45);
247 QSize splashSize() const {
249 static_cast<unsigned int>(width_ * pixelRatio()),
250 static_cast<unsigned int>(height_ * pixelRatio()));
253 /// Ratio between physical pixels and device-independent pixels of splash image
254 double splashPixelRatio() const {
255 #if QT_VERSION >= 0x050000
256 return splash_.devicePixelRatio();
264 /// Toolbar store providing access to individual toolbars by name.
265 typedef map<string, GuiToolbar *> ToolbarMap;
267 typedef shared_ptr<Dialog> DialogPtr;
272 class GuiView::GuiViewPrivate
275 GuiViewPrivate(GuiViewPrivate const &);
276 void operator=(GuiViewPrivate const &);
278 GuiViewPrivate(GuiView * gv)
279 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
280 layout_(0), autosave_timeout_(5000),
283 // hardcode here the platform specific icon size
284 smallIconSize = 16; // scaling problems
285 normalIconSize = 20; // ok, default if iconsize.png is missing
286 bigIconSize = 26; // better for some math icons
287 hugeIconSize = 32; // better for hires displays
290 // if it exists, use width of iconsize.png as normal size
291 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
292 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
294 QImage image(toqstr(fn.absFileName()));
295 if (image.width() < int(smallIconSize))
296 normalIconSize = smallIconSize;
297 else if (image.width() > int(giantIconSize))
298 normalIconSize = giantIconSize;
300 normalIconSize = image.width();
303 splitter_ = new QSplitter;
304 bg_widget_ = new BackgroundWidget(400, 250);
305 stack_widget_ = new QStackedWidget;
306 stack_widget_->addWidget(bg_widget_);
307 stack_widget_->addWidget(splitter_);
310 // TODO cleanup, remove the singleton, handle multiple Windows?
311 progress_ = ProgressInterface::instance();
312 if (!dynamic_cast<GuiProgress*>(progress_)) {
313 progress_ = new GuiProgress; // TODO who deletes it
314 ProgressInterface::setInstance(progress_);
317 dynamic_cast<GuiProgress*>(progress_),
318 SIGNAL(updateStatusBarMessage(QString const&)),
319 gv, SLOT(updateStatusBarMessage(QString const&)));
321 dynamic_cast<GuiProgress*>(progress_),
322 SIGNAL(clearMessageText()),
323 gv, SLOT(clearMessageText()));
330 delete stack_widget_;
333 QMenu * toolBarPopup(GuiView * parent)
335 // FIXME: translation
336 QMenu * menu = new QMenu(parent);
337 QActionGroup * iconSizeGroup = new QActionGroup(parent);
339 QAction * smallIcons = new QAction(iconSizeGroup);
340 smallIcons->setText(qt_("Small-sized icons"));
341 smallIcons->setCheckable(true);
342 QObject::connect(smallIcons, SIGNAL(triggered()),
343 parent, SLOT(smallSizedIcons()));
344 menu->addAction(smallIcons);
346 QAction * normalIcons = new QAction(iconSizeGroup);
347 normalIcons->setText(qt_("Normal-sized icons"));
348 normalIcons->setCheckable(true);
349 QObject::connect(normalIcons, SIGNAL(triggered()),
350 parent, SLOT(normalSizedIcons()));
351 menu->addAction(normalIcons);
353 QAction * bigIcons = new QAction(iconSizeGroup);
354 bigIcons->setText(qt_("Big-sized icons"));
355 bigIcons->setCheckable(true);
356 QObject::connect(bigIcons, SIGNAL(triggered()),
357 parent, SLOT(bigSizedIcons()));
358 menu->addAction(bigIcons);
360 QAction * hugeIcons = new QAction(iconSizeGroup);
361 hugeIcons->setText(qt_("Huge-sized icons"));
362 hugeIcons->setCheckable(true);
363 QObject::connect(hugeIcons, SIGNAL(triggered()),
364 parent, SLOT(hugeSizedIcons()));
365 menu->addAction(hugeIcons);
367 QAction * giantIcons = new QAction(iconSizeGroup);
368 giantIcons->setText(qt_("Giant-sized icons"));
369 giantIcons->setCheckable(true);
370 QObject::connect(giantIcons, SIGNAL(triggered()),
371 parent, SLOT(giantSizedIcons()));
372 menu->addAction(giantIcons);
374 unsigned int cur = parent->iconSize().width();
375 if ( cur == parent->d.smallIconSize)
376 smallIcons->setChecked(true);
377 else if (cur == parent->d.normalIconSize)
378 normalIcons->setChecked(true);
379 else if (cur == parent->d.bigIconSize)
380 bigIcons->setChecked(true);
381 else if (cur == parent->d.hugeIconSize)
382 hugeIcons->setChecked(true);
383 else if (cur == parent->d.giantIconSize)
384 giantIcons->setChecked(true);
391 stack_widget_->setCurrentWidget(bg_widget_);
392 bg_widget_->setUpdatesEnabled(true);
393 bg_widget_->setFocus();
396 int tabWorkAreaCount()
398 return splitter_->count();
401 TabWorkArea * tabWorkArea(int i)
403 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
406 TabWorkArea * currentTabWorkArea()
408 int areas = tabWorkAreaCount();
410 // The first TabWorkArea is always the first one, if any.
411 return tabWorkArea(0);
413 for (int i = 0; i != areas; ++i) {
414 TabWorkArea * twa = tabWorkArea(i);
415 if (current_main_work_area_ == twa->currentWorkArea())
419 // None has the focus so we just take the first one.
420 return tabWorkArea(0);
423 int countWorkAreasOf(Buffer & buf)
425 int areas = tabWorkAreaCount();
427 for (int i = 0; i != areas; ++i) {
428 TabWorkArea * twa = tabWorkArea(i);
429 if (twa->workArea(buf))
435 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
437 if (processing_thread_watcher_.isRunning()) {
438 // we prefer to cancel this preview in order to keep a snappy
442 processing_thread_watcher_.setFuture(f);
447 GuiWorkArea * current_work_area_;
448 GuiWorkArea * current_main_work_area_;
449 QSplitter * splitter_;
450 QStackedWidget * stack_widget_;
451 BackgroundWidget * bg_widget_;
453 ToolbarMap toolbars_;
454 ProgressInterface* progress_;
455 /// The main layout box.
457 * \warning Don't Delete! The layout box is actually owned by
458 * whichever toolbar contains it. All the GuiView class needs is a
459 * means of accessing it.
461 * FIXME: replace that with a proper model so that we are not limited
462 * to only one dialog.
467 map<string, DialogPtr> dialogs_;
469 unsigned int smallIconSize;
470 unsigned int normalIconSize;
471 unsigned int bigIconSize;
472 unsigned int hugeIconSize;
473 unsigned int giantIconSize;
475 QTimer statusbar_timer_;
476 /// auto-saving of buffers
477 Timeout autosave_timeout_;
478 /// flag against a race condition due to multiclicks, see bug #1119
482 TocModels toc_models_;
485 QFutureWatcher<docstring> autosave_watcher_;
486 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
488 string last_export_format;
489 string processing_format;
491 static QSet<Buffer const *> busyBuffers;
492 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
493 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
494 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
495 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
498 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
500 // TODO syncFunc/previewFunc: use bind
501 bool asyncBufferProcessing(string const & argument,
502 Buffer const * used_buffer,
503 docstring const & msg,
504 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
505 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
506 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
508 QVector<GuiWorkArea*> guiWorkAreas();
511 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
514 GuiView::GuiView(int id)
515 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
516 command_execute_(false)
518 // GuiToolbars *must* be initialised before the menu bar.
519 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
522 // set ourself as the current view. This is needed for the menu bar
523 // filling, at least for the static special menu item on Mac. Otherwise
524 // they are greyed out.
525 guiApp->setCurrentView(this);
527 // Fill up the menu bar.
528 guiApp->menus().fillMenuBar(menuBar(), this, true);
530 setCentralWidget(d.stack_widget_);
532 // Start autosave timer
533 if (lyxrc.autosave) {
534 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
535 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
536 d.autosave_timeout_.start();
538 connect(&d.statusbar_timer_, SIGNAL(timeout()),
539 this, SLOT(clearMessage()));
541 // We don't want to keep the window in memory if it is closed.
542 setAttribute(Qt::WA_DeleteOnClose, true);
544 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
545 // QIcon::fromTheme was introduced in Qt 4.6
546 #if (QT_VERSION >= 0x040600)
547 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
548 // since the icon is provided in the application bundle. We use a themed
549 // version when available and use the bundled one as fallback.
550 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
552 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
556 resetWindowTitleAndIconText();
558 // use tabbed dock area for multiple docks
559 // (such as "source" and "messages")
560 setDockOptions(QMainWindow::ForceTabbedDocks);
563 setAcceptDrops(true);
565 // add busy indicator to statusbar
566 QLabel * busylabel = new QLabel(statusBar());
567 statusBar()->addPermanentWidget(busylabel);
568 search_mode mode = theGuiApp()->imageSearchMode();
569 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
570 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
571 busylabel->setMovie(busyanim);
575 connect(&d.processing_thread_watcher_, SIGNAL(started()),
576 busylabel, SLOT(show()));
577 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
578 busylabel, SLOT(hide()));
580 statusBar()->setSizeGripEnabled(true);
583 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
584 SLOT(autoSaveThreadFinished()));
586 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
587 SLOT(processingThreadStarted()));
588 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
589 SLOT(processingThreadFinished()));
591 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
592 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
594 // Forbid too small unresizable window because it can happen
595 // with some window manager under X11.
596 setMinimumSize(300, 200);
598 if (lyxrc.allow_geometry_session) {
599 // Now take care of session management.
604 // no session handling, default to a sane size.
605 setGeometry(50, 50, 690, 510);
608 // clear session data if any.
610 settings.remove("views");
620 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
622 QVector<GuiWorkArea*> areas;
623 for (int i = 0; i < tabWorkAreaCount(); i++) {
624 TabWorkArea* ta = tabWorkArea(i);
625 for (int u = 0; u < ta->count(); u++) {
626 areas << ta->workArea(u);
632 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
633 string const & format)
635 docstring const fmt = formats.prettyName(format);
638 case Buffer::ExportSuccess:
639 msg = bformat(_("Successful export to format: %1$s"), fmt);
641 case Buffer::ExportCancel:
642 msg = _("Document export cancelled.");
644 case Buffer::ExportError:
645 case Buffer::ExportNoPathToFormat:
646 case Buffer::ExportTexPathHasSpaces:
647 case Buffer::ExportConverterError:
648 msg = bformat(_("Error while exporting format: %1$s"), fmt);
650 case Buffer::PreviewSuccess:
651 msg = bformat(_("Successful preview of format: %1$s"), fmt);
653 case Buffer::PreviewError:
654 msg = bformat(_("Error while previewing format: %1$s"), fmt);
661 void GuiView::processingThreadStarted()
666 void GuiView::processingThreadFinished()
668 QFutureWatcher<Buffer::ExportStatus> const * watcher =
669 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
671 Buffer::ExportStatus const status = watcher->result();
672 handleExportStatus(this, status, d.processing_format);
675 BufferView const * const bv = currentBufferView();
676 if (bv && !bv->buffer().errorList("Export").empty()) {
680 errors(d.last_export_format);
684 void GuiView::autoSaveThreadFinished()
686 QFutureWatcher<docstring> const * watcher =
687 static_cast<QFutureWatcher<docstring> const *>(sender());
688 message(watcher->result());
693 void GuiView::saveLayout() const
696 settings.beginGroup("views");
697 settings.beginGroup(QString::number(id_));
698 #if defined(Q_WS_X11) || defined(QPA_XCB)
699 settings.setValue("pos", pos());
700 settings.setValue("size", size());
702 settings.setValue("geometry", saveGeometry());
704 settings.setValue("layout", saveState(0));
705 settings.setValue("icon_size", iconSize());
709 void GuiView::saveUISettings() const
711 // Save the toolbar private states
712 ToolbarMap::iterator end = d.toolbars_.end();
713 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
714 it->second->saveSession();
715 // Now take care of all other dialogs
716 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
717 for (; it!= d.dialogs_.end(); ++it)
718 it->second->saveSession();
722 bool GuiView::restoreLayout()
725 settings.beginGroup("views");
726 settings.beginGroup(QString::number(id_));
727 QString const icon_key = "icon_size";
728 if (!settings.contains(icon_key))
731 //code below is skipped when when ~/.config/LyX is (re)created
732 QSize icon_size = settings.value(icon_key).toSize();
733 // Check whether session size changed.
734 if (icon_size.width() != int(d.smallIconSize) &&
735 icon_size.width() != int(d.normalIconSize) &&
736 icon_size.width() != int(d.bigIconSize) &&
737 icon_size.width() != int(d.hugeIconSize) &&
738 icon_size.width() != int(d.giantIconSize)) {
739 icon_size.setWidth(d.normalIconSize);
740 icon_size.setHeight(d.normalIconSize);
742 setIconSize(icon_size);
744 #if defined(Q_WS_X11) || defined(QPA_XCB)
745 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
746 QSize size = settings.value("size", QSize(690, 510)).toSize();
750 // Work-around for bug #6034: the window ends up in an undetermined
751 // state when trying to restore a maximized window when it is
752 // already maximized.
753 if (!(windowState() & Qt::WindowMaximized))
754 if (!restoreGeometry(settings.value("geometry").toByteArray()))
755 setGeometry(50, 50, 690, 510);
757 // Make sure layout is correctly oriented.
758 setLayoutDirection(qApp->layoutDirection());
760 // Allow the toc and view-source dock widget to be restored if needed.
762 if ((dialog = findOrBuild("toc", true)))
763 // see bug 5082. At least setup title and enabled state.
764 // Visibility will be adjusted by restoreState below.
765 dialog->prepareView();
766 if ((dialog = findOrBuild("view-source", true)))
767 dialog->prepareView();
768 if ((dialog = findOrBuild("progress", true)))
769 dialog->prepareView();
771 if (!restoreState(settings.value("layout").toByteArray(), 0))
774 // init the toolbars that have not been restored
775 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
776 Toolbars::Infos::iterator end = guiApp->toolbars().end();
777 for (; cit != end; ++cit) {
778 GuiToolbar * tb = toolbar(cit->name);
779 if (tb && !tb->isRestored())
780 initToolbar(cit->name);
788 GuiToolbar * GuiView::toolbar(string const & name)
790 ToolbarMap::iterator it = d.toolbars_.find(name);
791 if (it != d.toolbars_.end())
794 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
799 void GuiView::constructToolbars()
801 ToolbarMap::iterator it = d.toolbars_.begin();
802 for (; it != d.toolbars_.end(); ++it)
806 // I don't like doing this here, but the standard toolbar
807 // destroys this object when it's destroyed itself (vfr)
808 d.layout_ = new LayoutBox(*this);
809 d.stack_widget_->addWidget(d.layout_);
810 d.layout_->move(0,0);
812 // extracts the toolbars from the backend
813 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
814 Toolbars::Infos::iterator end = guiApp->toolbars().end();
815 for (; cit != end; ++cit)
816 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
820 void GuiView::initToolbars()
822 // extracts the toolbars from the backend
823 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
824 Toolbars::Infos::iterator end = guiApp->toolbars().end();
825 for (; cit != end; ++cit)
826 initToolbar(cit->name);
830 void GuiView::initToolbar(string const & name)
832 GuiToolbar * tb = toolbar(name);
835 int const visibility = guiApp->toolbars().defaultVisibility(name);
836 bool newline = !(visibility & Toolbars::SAMEROW);
837 tb->setVisible(false);
838 tb->setVisibility(visibility);
840 if (visibility & Toolbars::TOP) {
842 addToolBarBreak(Qt::TopToolBarArea);
843 addToolBar(Qt::TopToolBarArea, tb);
846 if (visibility & Toolbars::BOTTOM) {
848 addToolBarBreak(Qt::BottomToolBarArea);
849 addToolBar(Qt::BottomToolBarArea, tb);
852 if (visibility & Toolbars::LEFT) {
854 addToolBarBreak(Qt::LeftToolBarArea);
855 addToolBar(Qt::LeftToolBarArea, tb);
858 if (visibility & Toolbars::RIGHT) {
860 addToolBarBreak(Qt::RightToolBarArea);
861 addToolBar(Qt::RightToolBarArea, tb);
864 if (visibility & Toolbars::ON)
865 tb->setVisible(true);
869 TocModels & GuiView::tocModels()
871 return d.toc_models_;
875 void GuiView::setFocus()
877 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
878 QMainWindow::setFocus();
882 bool GuiView::hasFocus() const
884 if (currentWorkArea())
885 return currentWorkArea()->hasFocus();
886 if (currentMainWorkArea())
887 return currentMainWorkArea()->hasFocus();
888 return d.bg_widget_->hasFocus();
892 void GuiView::focusInEvent(QFocusEvent * e)
894 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
895 QMainWindow::focusInEvent(e);
896 // Make sure guiApp points to the correct view.
897 guiApp->setCurrentView(this);
898 if (currentWorkArea())
899 currentWorkArea()->setFocus();
900 else if (currentMainWorkArea())
901 currentMainWorkArea()->setFocus();
903 d.bg_widget_->setFocus();
907 QMenu * GuiView::createPopupMenu()
909 return d.toolBarPopup(this);
913 void GuiView::showEvent(QShowEvent * e)
915 LYXERR(Debug::GUI, "Passed Geometry "
916 << size().height() << "x" << size().width()
917 << "+" << pos().x() << "+" << pos().y());
919 if (d.splitter_->count() == 0)
920 // No work area, switch to the background widget.
924 QMainWindow::showEvent(e);
928 bool GuiView::closeScheduled()
935 bool GuiView::prepareAllBuffersForLogout()
937 Buffer * first = theBufferList().first();
941 // First, iterate over all buffers and ask the users if unsaved
942 // changes should be saved.
943 // We cannot use a for loop as the buffer list cycles.
946 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
948 b = theBufferList().next(b);
949 } while (b != first);
951 // Next, save session state
952 // When a view/window was closed before without quitting LyX, there
953 // are already entries in the lastOpened list.
954 theSession().lastOpened().clear();
961 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
962 ** is responsibility of the container (e.g., dialog)
964 void GuiView::closeEvent(QCloseEvent * close_event)
966 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
968 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
969 Alert::warning(_("Exit LyX"),
970 _("LyX could not be closed because documents are being processed by LyX."));
971 close_event->setAccepted(false);
975 // If the user pressed the x (so we didn't call closeView
976 // programmatically), we want to clear all existing entries.
978 theSession().lastOpened().clear();
983 // it can happen that this event arrives without selecting the view,
984 // e.g. when clicking the close button on a background window.
986 if (!closeWorkAreaAll()) {
988 close_event->ignore();
992 // Make sure that nothing will use this to be closed View.
993 guiApp->unregisterView(this);
995 if (isFullScreen()) {
996 // Switch off fullscreen before closing.
1001 // Make sure the timer time out will not trigger a statusbar update.
1002 d.statusbar_timer_.stop();
1004 // Saving fullscreen requires additional tweaks in the toolbar code.
1005 // It wouldn't also work under linux natively.
1006 if (lyxrc.allow_geometry_session) {
1011 close_event->accept();
1015 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1017 if (event->mimeData()->hasUrls())
1019 /// \todo Ask lyx-devel is this is enough:
1020 /// if (event->mimeData()->hasFormat("text/plain"))
1021 /// event->acceptProposedAction();
1025 void GuiView::dropEvent(QDropEvent * event)
1027 QList<QUrl> files = event->mimeData()->urls();
1028 if (files.isEmpty())
1031 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1032 for (int i = 0; i != files.size(); ++i) {
1033 string const file = os::internal_path(fromqstr(
1034 files.at(i).toLocalFile()));
1038 string const ext = support::getExtension(file);
1039 vector<const Format *> found_formats;
1041 // Find all formats that have the correct extension.
1042 vector<const Format *> const & import_formats
1043 = theConverters().importableFormats();
1044 vector<const Format *>::const_iterator it = import_formats.begin();
1045 for (; it != import_formats.end(); ++it)
1046 if ((*it)->hasExtension(ext))
1047 found_formats.push_back(*it);
1050 if (found_formats.size() >= 1) {
1051 if (found_formats.size() > 1) {
1052 //FIXME: show a dialog to choose the correct importable format
1053 LYXERR(Debug::FILES,
1054 "Multiple importable formats found, selecting first");
1056 string const arg = found_formats[0]->name() + " " + file;
1057 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1060 //FIXME: do we have to explicitly check whether it's a lyx file?
1061 LYXERR(Debug::FILES,
1062 "No formats found, trying to open it as a lyx file");
1063 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1065 // add the functions to the queue
1066 guiApp->addToFuncRequestQueue(cmd);
1069 // now process the collected functions. We perform the events
1070 // asynchronously. This prevents potential problems in case the
1071 // BufferView is closed within an event.
1072 guiApp->processFuncRequestQueueAsync();
1076 void GuiView::message(docstring const & str)
1078 if (ForkedProcess::iAmAChild())
1081 // call is moved to GUI-thread by GuiProgress
1082 d.progress_->appendMessage(toqstr(str));
1086 void GuiView::clearMessageText()
1088 message(docstring());
1092 void GuiView::updateStatusBarMessage(QString const & str)
1094 statusBar()->showMessage(str);
1095 d.statusbar_timer_.stop();
1096 d.statusbar_timer_.start(3000);
1100 void GuiView::smallSizedIcons()
1102 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1106 void GuiView::normalSizedIcons()
1108 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1112 void GuiView::bigSizedIcons()
1114 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1118 void GuiView::hugeSizedIcons()
1120 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1124 void GuiView::giantSizedIcons()
1126 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1130 void GuiView::clearMessage()
1132 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1133 // the hasFocus function mostly returns false, even if the focus is on
1134 // a workarea in this view.
1138 d.statusbar_timer_.stop();
1142 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1144 if (wa != d.current_work_area_
1145 || wa->bufferView().buffer().isInternal())
1147 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1148 setWindowIconText(wa->windowIconText());
1149 #if (QT_VERSION >= 0x040400)
1150 // Sets the path for the window: this is used by OSX to
1151 // allow a context click on the title bar showing a menu
1152 // with the path up to the file
1153 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1158 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1160 if (d.current_work_area_)
1161 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1162 this, SLOT(setBusy(bool)));
1164 disconnectBufferView();
1165 connectBufferView(wa->bufferView());
1166 connectBuffer(wa->bufferView().buffer());
1167 d.current_work_area_ = wa;
1168 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1169 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1170 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1171 updateWindowTitle(wa);
1175 // The document settings needs to be reinitialised.
1176 updateDialog("document", "");
1178 // Buffer-dependent dialogs must be updated. This is done here because
1179 // some dialogs require buffer()->text.
1184 void GuiView::on_lastWorkAreaRemoved()
1187 // We already are in a close event. Nothing more to do.
1190 if (d.splitter_->count() > 1)
1191 // We have a splitter so don't close anything.
1194 // Reset and updates the dialogs.
1195 d.toc_models_.reset(0);
1196 updateDialog("document", "");
1199 resetWindowTitleAndIconText();
1202 if (lyxrc.open_buffers_in_tabs)
1203 // Nothing more to do, the window should stay open.
1206 if (guiApp->viewIds().size() > 1) {
1212 // On Mac we also close the last window because the application stay
1213 // resident in memory. On other platforms we don't close the last
1214 // window because this would quit the application.
1220 void GuiView::updateStatusBar()
1222 // let the user see the explicit message
1223 if (d.statusbar_timer_.isActive())
1230 void GuiView::showMessage()
1234 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1235 if (msg.isEmpty()) {
1236 BufferView const * bv = currentBufferView();
1238 msg = toqstr(bv->cursor().currentState());
1240 msg = qt_("Welcome to LyX!");
1242 statusBar()->showMessage(msg);
1246 bool GuiView::event(QEvent * e)
1250 // Useful debug code:
1251 //case QEvent::ActivationChange:
1252 //case QEvent::WindowDeactivate:
1253 //case QEvent::Paint:
1254 //case QEvent::Enter:
1255 //case QEvent::Leave:
1256 //case QEvent::HoverEnter:
1257 //case QEvent::HoverLeave:
1258 //case QEvent::HoverMove:
1259 //case QEvent::StatusTip:
1260 //case QEvent::DragEnter:
1261 //case QEvent::DragLeave:
1262 //case QEvent::Drop:
1265 case QEvent::WindowActivate: {
1266 GuiView * old_view = guiApp->currentView();
1267 if (this == old_view) {
1269 return QMainWindow::event(e);
1271 if (old_view && old_view->currentBufferView()) {
1272 // save current selection to the selection buffer to allow
1273 // middle-button paste in this window.
1274 cap::saveSelection(old_view->currentBufferView()->cursor());
1276 guiApp->setCurrentView(this);
1277 if (d.current_work_area_) {
1278 BufferView & bv = d.current_work_area_->bufferView();
1279 connectBufferView(bv);
1280 connectBuffer(bv.buffer());
1281 // The document structure, name and dialogs might have
1282 // changed in another view.
1284 // The document settings needs to be reinitialised.
1285 updateDialog("document", "");
1288 resetWindowTitleAndIconText();
1291 return QMainWindow::event(e);
1294 case QEvent::ShortcutOverride: {
1296 if (isFullScreen() && menuBar()->isHidden()) {
1297 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1298 // FIXME: we should also try to detect special LyX shortcut such as
1299 // Alt-P and Alt-M. Right now there is a hack in
1300 // GuiWorkArea::processKeySym() that hides again the menubar for
1302 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1304 return QMainWindow::event(e);
1307 return QMainWindow::event(e);
1311 return QMainWindow::event(e);
1315 void GuiView::resetWindowTitleAndIconText()
1317 setWindowTitle(qt_("LyX"));
1318 setWindowIconText(qt_("LyX"));
1321 bool GuiView::focusNextPrevChild(bool /*next*/)
1328 bool GuiView::busy() const
1334 void GuiView::setBusy(bool busy)
1336 bool const busy_before = busy_ > 0;
1337 busy ? ++busy_ : --busy_;
1338 if ((busy_ > 0) == busy_before)
1339 // busy state didn't change
1343 QApplication::setOverrideCursor(Qt::WaitCursor);
1346 QApplication::restoreOverrideCursor();
1351 void GuiView::resetCommandExecute()
1353 command_execute_ = false;
1358 double GuiView::pixelRatio() const
1360 #if QT_VERSION >= 0x050000
1361 return devicePixelRatio();
1368 GuiWorkArea * GuiView::workArea(int index)
1370 if (TabWorkArea * twa = d.currentTabWorkArea())
1371 if (index < twa->count())
1372 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1377 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1379 if (currentWorkArea()
1380 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1381 return currentWorkArea();
1382 if (TabWorkArea * twa = d.currentTabWorkArea())
1383 return twa->workArea(buffer);
1388 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1390 // Automatically create a TabWorkArea if there are none yet.
1391 TabWorkArea * tab_widget = d.splitter_->count()
1392 ? d.currentTabWorkArea() : addTabWorkArea();
1393 return tab_widget->addWorkArea(buffer, *this);
1397 TabWorkArea * GuiView::addTabWorkArea()
1399 TabWorkArea * twa = new TabWorkArea;
1400 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1401 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1402 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1403 this, SLOT(on_lastWorkAreaRemoved()));
1405 d.splitter_->addWidget(twa);
1406 d.stack_widget_->setCurrentWidget(d.splitter_);
1411 GuiWorkArea const * GuiView::currentWorkArea() const
1413 return d.current_work_area_;
1417 GuiWorkArea * GuiView::currentWorkArea()
1419 return d.current_work_area_;
1423 GuiWorkArea const * GuiView::currentMainWorkArea() const
1425 if (!d.currentTabWorkArea())
1427 return d.currentTabWorkArea()->currentWorkArea();
1431 GuiWorkArea * GuiView::currentMainWorkArea()
1433 if (!d.currentTabWorkArea())
1435 return d.currentTabWorkArea()->currentWorkArea();
1439 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1441 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1443 d.current_work_area_ = 0;
1448 // FIXME: I've no clue why this is here and why it accesses
1449 // theGuiApp()->currentView, which might be 0 (bug 6464).
1450 // See also 27525 (vfr).
1451 if (theGuiApp()->currentView() == this
1452 && theGuiApp()->currentView()->currentWorkArea() == wa)
1455 if (currentBufferView())
1456 cap::saveSelection(currentBufferView()->cursor());
1458 theGuiApp()->setCurrentView(this);
1459 d.current_work_area_ = wa;
1461 // We need to reset this now, because it will need to be
1462 // right if the tabWorkArea gets reset in the for loop. We
1463 // will change it back if we aren't in that case.
1464 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1465 d.current_main_work_area_ = wa;
1467 for (int i = 0; i != d.splitter_->count(); ++i) {
1468 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1469 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1470 << ", Current main wa: " << currentMainWorkArea());
1475 d.current_main_work_area_ = old_cmwa;
1477 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1478 on_currentWorkAreaChanged(wa);
1479 BufferView & bv = wa->bufferView();
1480 bv.cursor().fixIfBroken();
1482 wa->setUpdatesEnabled(true);
1483 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1487 void GuiView::removeWorkArea(GuiWorkArea * wa)
1489 LASSERT(wa, return);
1490 if (wa == d.current_work_area_) {
1492 disconnectBufferView();
1493 d.current_work_area_ = 0;
1494 d.current_main_work_area_ = 0;
1497 bool found_twa = false;
1498 for (int i = 0; i != d.splitter_->count(); ++i) {
1499 TabWorkArea * twa = d.tabWorkArea(i);
1500 if (twa->removeWorkArea(wa)) {
1501 // Found in this tab group, and deleted the GuiWorkArea.
1503 if (twa->count() != 0) {
1504 if (d.current_work_area_ == 0)
1505 // This means that we are closing the current GuiWorkArea, so
1506 // switch to the next GuiWorkArea in the found TabWorkArea.
1507 setCurrentWorkArea(twa->currentWorkArea());
1509 // No more WorkAreas in this tab group, so delete it.
1516 // It is not a tabbed work area (i.e., the search work area), so it
1517 // should be deleted by other means.
1518 LASSERT(found_twa, return);
1520 if (d.current_work_area_ == 0) {
1521 if (d.splitter_->count() != 0) {
1522 TabWorkArea * twa = d.currentTabWorkArea();
1523 setCurrentWorkArea(twa->currentWorkArea());
1525 // No more work areas, switch to the background widget.
1526 setCurrentWorkArea(0);
1532 LayoutBox * GuiView::getLayoutDialog() const
1538 void GuiView::updateLayoutList()
1541 d.layout_->updateContents(false);
1545 void GuiView::updateToolbars()
1547 ToolbarMap::iterator end = d.toolbars_.end();
1548 if (d.current_work_area_) {
1550 if (d.current_work_area_->bufferView().cursor().inMathed()
1551 && !d.current_work_area_->bufferView().cursor().inRegexped())
1552 context |= Toolbars::MATH;
1553 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1554 context |= Toolbars::TABLE;
1555 if (currentBufferView()->buffer().areChangesPresent()
1556 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1557 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1558 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1559 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1560 context |= Toolbars::REVIEW;
1561 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1562 context |= Toolbars::MATHMACROTEMPLATE;
1563 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1564 context |= Toolbars::IPA;
1565 if (command_execute_)
1566 context |= Toolbars::MINIBUFFER;
1568 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1569 it->second->update(context);
1571 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1572 it->second->update();
1576 void GuiView::setBuffer(Buffer * newBuffer)
1578 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1579 LASSERT(newBuffer, return);
1581 GuiWorkArea * wa = workArea(*newBuffer);
1584 newBuffer->masterBuffer()->updateBuffer();
1586 wa = addWorkArea(*newBuffer);
1587 // scroll to the position when the BufferView was last closed
1588 if (lyxrc.use_lastfilepos) {
1589 LastFilePosSection::FilePos filepos =
1590 theSession().lastFilePos().load(newBuffer->fileName());
1591 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1594 //Disconnect the old buffer...there's no new one.
1597 connectBuffer(*newBuffer);
1598 connectBufferView(wa->bufferView());
1599 setCurrentWorkArea(wa);
1603 void GuiView::connectBuffer(Buffer & buf)
1605 buf.setGuiDelegate(this);
1609 void GuiView::disconnectBuffer()
1611 if (d.current_work_area_)
1612 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1616 void GuiView::connectBufferView(BufferView & bv)
1618 bv.setGuiDelegate(this);
1622 void GuiView::disconnectBufferView()
1624 if (d.current_work_area_)
1625 d.current_work_area_->bufferView().setGuiDelegate(0);
1629 void GuiView::errors(string const & error_type, bool from_master)
1631 BufferView const * const bv = currentBufferView();
1635 #if EXPORT_in_THREAD
1636 // We are called with from_master == false by default, so we
1637 // have to figure out whether that is the case or not.
1638 ErrorList & el = bv->buffer().errorList(error_type);
1640 el = bv->buffer().masterBuffer()->errorList(error_type);
1644 ErrorList const & el = from_master ?
1645 bv->buffer().masterBuffer()->errorList(error_type) :
1646 bv->buffer().errorList(error_type);
1652 string data = error_type;
1654 data = "from_master|" + error_type;
1655 showDialog("errorlist", data);
1659 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1661 d.toc_models_.updateItem(toqstr(type), dit);
1665 void GuiView::structureChanged()
1667 // FIXME: This is slightly expensive, though less than the tocBackend update
1668 // (#9880). This also resets the view in the Toc Widget (#6675).
1669 d.toc_models_.reset(documentBufferView());
1670 // Navigator needs more than a simple update in this case. It needs to be
1672 updateDialog("toc", "");
1676 void GuiView::updateDialog(string const & name, string const & data)
1678 if (!isDialogVisible(name))
1681 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1682 if (it == d.dialogs_.end())
1685 Dialog * const dialog = it->second.get();
1686 if (dialog->isVisibleView())
1687 dialog->initialiseParams(data);
1691 BufferView * GuiView::documentBufferView()
1693 return currentMainWorkArea()
1694 ? ¤tMainWorkArea()->bufferView()
1699 BufferView const * GuiView::documentBufferView() const
1701 return currentMainWorkArea()
1702 ? ¤tMainWorkArea()->bufferView()
1707 BufferView * GuiView::currentBufferView()
1709 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1713 BufferView const * GuiView::currentBufferView() const
1715 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1719 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1720 Buffer const * orig, Buffer * clone)
1722 bool const success = clone->autoSave();
1724 busyBuffers.remove(orig);
1726 ? _("Automatic save done.")
1727 : _("Automatic save failed!");
1731 void GuiView::autoSave()
1733 LYXERR(Debug::INFO, "Running autoSave()");
1735 Buffer * buffer = documentBufferView()
1736 ? &documentBufferView()->buffer() : 0;
1738 resetAutosaveTimers();
1742 GuiViewPrivate::busyBuffers.insert(buffer);
1743 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1744 buffer, buffer->cloneBufferOnly());
1745 d.autosave_watcher_.setFuture(f);
1746 resetAutosaveTimers();
1750 void GuiView::resetAutosaveTimers()
1753 d.autosave_timeout_.restart();
1757 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1760 Buffer * buf = currentBufferView()
1761 ? ¤tBufferView()->buffer() : 0;
1762 Buffer * doc_buffer = documentBufferView()
1763 ? &(documentBufferView()->buffer()) : 0;
1766 /* In LyX/Mac, when a dialog is open, the menus of the
1767 application can still be accessed without giving focus to
1768 the main window. In this case, we want to disable the menu
1769 entries that are buffer-related.
1770 This code must not be used on Linux and Windows, since it
1771 would disable buffer-related entries when hovering over the
1772 menu (see bug #9574).
1774 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1780 // Check whether we need a buffer
1781 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1782 // no, exit directly
1783 flag.message(from_utf8(N_("Command not allowed with"
1784 "out any document open")));
1785 flag.setEnabled(false);
1789 if (cmd.origin() == FuncRequest::TOC) {
1790 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1791 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1792 flag.setEnabled(false);
1796 switch(cmd.action()) {
1797 case LFUN_BUFFER_IMPORT:
1800 case LFUN_MASTER_BUFFER_UPDATE:
1801 case LFUN_MASTER_BUFFER_VIEW:
1803 && (doc_buffer->parent() != 0
1804 || doc_buffer->hasChildren())
1805 && !d.processing_thread_watcher_.isRunning();
1808 case LFUN_BUFFER_UPDATE:
1809 case LFUN_BUFFER_VIEW: {
1810 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1814 string format = to_utf8(cmd.argument());
1815 if (cmd.argument().empty())
1816 format = doc_buffer->params().getDefaultOutputFormat();
1817 enable = doc_buffer->params().isExportableFormat(format);
1821 case LFUN_BUFFER_RELOAD:
1822 enable = doc_buffer && !doc_buffer->isUnnamed()
1823 && doc_buffer->fileName().exists()
1824 && (!doc_buffer->isClean()
1825 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1828 case LFUN_BUFFER_CHILD_OPEN:
1829 enable = doc_buffer != 0;
1832 case LFUN_BUFFER_WRITE:
1833 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1836 //FIXME: This LFUN should be moved to GuiApplication.
1837 case LFUN_BUFFER_WRITE_ALL: {
1838 // We enable the command only if there are some modified buffers
1839 Buffer * first = theBufferList().first();
1844 // We cannot use a for loop as the buffer list is a cycle.
1846 if (!b->isClean()) {
1850 b = theBufferList().next(b);
1851 } while (b != first);
1855 case LFUN_BUFFER_WRITE_AS:
1856 case LFUN_BUFFER_EXPORT_AS:
1857 enable = doc_buffer != 0;
1860 case LFUN_BUFFER_CLOSE:
1861 case LFUN_VIEW_CLOSE:
1862 enable = doc_buffer != 0;
1865 case LFUN_BUFFER_CLOSE_ALL:
1866 enable = theBufferList().last() != theBufferList().first();
1869 case LFUN_VIEW_SPLIT:
1870 if (cmd.getArg(0) == "vertical")
1871 enable = doc_buffer && (d.splitter_->count() == 1 ||
1872 d.splitter_->orientation() == Qt::Vertical);
1874 enable = doc_buffer && (d.splitter_->count() == 1 ||
1875 d.splitter_->orientation() == Qt::Horizontal);
1878 case LFUN_TAB_GROUP_CLOSE:
1879 enable = d.tabWorkAreaCount() > 1;
1882 case LFUN_TOOLBAR_TOGGLE: {
1883 string const name = cmd.getArg(0);
1884 if (GuiToolbar * t = toolbar(name))
1885 flag.setOnOff(t->isVisible());
1888 docstring const msg =
1889 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1895 case LFUN_DROP_LAYOUTS_CHOICE:
1899 case LFUN_UI_TOGGLE:
1900 flag.setOnOff(isFullScreen());
1903 case LFUN_DIALOG_DISCONNECT_INSET:
1906 case LFUN_DIALOG_HIDE:
1907 // FIXME: should we check if the dialog is shown?
1910 case LFUN_DIALOG_TOGGLE:
1911 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1912 // fall through to set "enable"
1913 case LFUN_DIALOG_SHOW: {
1914 string const name = cmd.getArg(0);
1916 enable = name == "aboutlyx"
1917 || name == "file" //FIXME: should be removed.
1919 || name == "texinfo"
1920 || name == "progress"
1921 || name == "compare";
1922 else if (name == "character" || name == "symbols"
1923 || name == "mathdelimiter" || name == "mathmatrix") {
1924 if (!buf || buf->isReadonly())
1927 Cursor const & cur = currentBufferView()->cursor();
1928 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1931 else if (name == "latexlog")
1932 enable = FileName(doc_buffer->logName()).isReadableFile();
1933 else if (name == "spellchecker")
1934 enable = theSpellChecker()
1935 && !doc_buffer->isReadonly()
1936 && !doc_buffer->text().empty();
1937 else if (name == "vclog")
1938 enable = doc_buffer->lyxvc().inUse();
1942 case LFUN_DIALOG_UPDATE: {
1943 string const name = cmd.getArg(0);
1945 enable = name == "prefs";
1949 case LFUN_COMMAND_EXECUTE:
1951 case LFUN_MENU_OPEN:
1952 // Nothing to check.
1955 case LFUN_COMPLETION_INLINE:
1956 if (!d.current_work_area_
1957 || !d.current_work_area_->completer().inlinePossible(
1958 currentBufferView()->cursor()))
1962 case LFUN_COMPLETION_POPUP:
1963 if (!d.current_work_area_
1964 || !d.current_work_area_->completer().popupPossible(
1965 currentBufferView()->cursor()))
1970 if (!d.current_work_area_
1971 || !d.current_work_area_->completer().inlinePossible(
1972 currentBufferView()->cursor()))
1976 case LFUN_COMPLETION_ACCEPT:
1977 if (!d.current_work_area_
1978 || (!d.current_work_area_->completer().popupVisible()
1979 && !d.current_work_area_->completer().inlineVisible()
1980 && !d.current_work_area_->completer().completionAvailable()))
1984 case LFUN_COMPLETION_CANCEL:
1985 if (!d.current_work_area_
1986 || (!d.current_work_area_->completer().popupVisible()
1987 && !d.current_work_area_->completer().inlineVisible()))
1991 case LFUN_BUFFER_ZOOM_OUT:
1992 enable = doc_buffer && lyxrc.zoom > 10;
1993 if (lyxrc.zoom <= 10)
1994 flag.message(_("Zoom level cannot be less than 10%."));
1997 case LFUN_BUFFER_ZOOM_IN:
1998 enable = doc_buffer != 0;
2001 case LFUN_BUFFER_MOVE_NEXT:
2002 case LFUN_BUFFER_MOVE_PREVIOUS:
2003 // we do not cycle when moving
2004 case LFUN_BUFFER_NEXT:
2005 case LFUN_BUFFER_PREVIOUS:
2006 // because we cycle, it doesn't matter whether on first or last
2007 enable = (d.currentTabWorkArea()->count() > 1);
2009 case LFUN_BUFFER_SWITCH:
2010 // toggle on the current buffer, but do not toggle off
2011 // the other ones (is that a good idea?)
2013 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2014 flag.setOnOff(true);
2017 case LFUN_VC_REGISTER:
2018 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2020 case LFUN_VC_RENAME:
2021 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2024 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2026 case LFUN_VC_CHECK_IN:
2027 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2029 case LFUN_VC_CHECK_OUT:
2030 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2032 case LFUN_VC_LOCKING_TOGGLE:
2033 enable = doc_buffer && !doc_buffer->isReadonly()
2034 && doc_buffer->lyxvc().lockingToggleEnabled();
2035 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2037 case LFUN_VC_REVERT:
2038 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2040 case LFUN_VC_UNDO_LAST:
2041 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2043 case LFUN_VC_REPO_UPDATE:
2044 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2046 case LFUN_VC_COMMAND: {
2047 if (cmd.argument().empty())
2049 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2053 case LFUN_VC_COMPARE:
2054 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2057 case LFUN_SERVER_GOTO_FILE_ROW:
2058 case LFUN_LYX_ACTIVATE:
2060 case LFUN_FORWARD_SEARCH:
2061 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2064 case LFUN_FILE_INSERT_PLAINTEXT:
2065 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2066 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2069 case LFUN_SPELLING_CONTINUOUSLY:
2070 flag.setOnOff(lyxrc.spellcheck_continuously);
2078 flag.setEnabled(false);
2084 static FileName selectTemplateFile()
2086 FileDialog dlg(qt_("Select template file"));
2087 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2088 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2090 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2091 QStringList(qt_("LyX Documents (*.lyx)")));
2093 if (result.first == FileDialog::Later)
2095 if (result.second.isEmpty())
2097 return FileName(fromqstr(result.second));
2101 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2105 Buffer * newBuffer = 0;
2107 newBuffer = checkAndLoadLyXFile(filename);
2108 } catch (ExceptionMessage const & e) {
2115 message(_("Document not loaded."));
2119 setBuffer(newBuffer);
2120 newBuffer->errors("Parse");
2123 theSession().lastFiles().add(filename);
2129 void GuiView::openDocument(string const & fname)
2131 string initpath = lyxrc.document_path;
2133 if (documentBufferView()) {
2134 string const trypath = documentBufferView()->buffer().filePath();
2135 // If directory is writeable, use this as default.
2136 if (FileName(trypath).isDirWritable())
2142 if (fname.empty()) {
2143 FileDialog dlg(qt_("Select document to open"));
2144 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2145 dlg.setButton2(qt_("Examples|#E#e"),
2146 toqstr(addPath(package().system_support().absFileName(), "examples")));
2148 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2149 FileDialog::Result result =
2150 dlg.open(toqstr(initpath), filter);
2152 if (result.first == FileDialog::Later)
2155 filename = fromqstr(result.second);
2157 // check selected filename
2158 if (filename.empty()) {
2159 message(_("Canceled."));
2165 // get absolute path of file and add ".lyx" to the filename if
2167 FileName const fullname =
2168 fileSearch(string(), filename, "lyx", support::may_not_exist);
2169 if (!fullname.empty())
2170 filename = fullname.absFileName();
2172 if (!fullname.onlyPath().isDirectory()) {
2173 Alert::warning(_("Invalid filename"),
2174 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2175 from_utf8(fullname.absFileName())));
2179 // if the file doesn't exist and isn't already open (bug 6645),
2180 // let the user create one
2181 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2182 !LyXVC::file_not_found_hook(fullname)) {
2183 // the user specifically chose this name. Believe him.
2184 Buffer * const b = newFile(filename, string(), true);
2190 docstring const disp_fn = makeDisplayPath(filename);
2191 message(bformat(_("Opening document %1$s..."), disp_fn));
2194 Buffer * buf = loadDocument(fullname);
2196 str2 = bformat(_("Document %1$s opened."), disp_fn);
2197 if (buf->lyxvc().inUse())
2198 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2199 " " + _("Version control detected.");
2201 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2206 // FIXME: clean that
2207 static bool import(GuiView * lv, FileName const & filename,
2208 string const & format, ErrorList & errorList)
2210 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2212 string loader_format;
2213 vector<string> loaders = theConverters().loaders();
2214 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2215 vector<string>::const_iterator it = loaders.begin();
2216 vector<string>::const_iterator en = loaders.end();
2217 for (; it != en; ++it) {
2218 if (!theConverters().isReachable(format, *it))
2221 string const tofile =
2222 support::changeExtension(filename.absFileName(),
2223 formats.extension(*it));
2224 if (!theConverters().convert(0, filename, FileName(tofile),
2225 filename, format, *it, errorList))
2227 loader_format = *it;
2230 if (loader_format.empty()) {
2231 frontend::Alert::error(_("Couldn't import file"),
2232 bformat(_("No information for importing the format %1$s."),
2233 formats.prettyName(format)));
2237 loader_format = format;
2239 if (loader_format == "lyx") {
2240 Buffer * buf = lv->loadDocument(lyxfile);
2244 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2248 bool as_paragraphs = loader_format == "textparagraph";
2249 string filename2 = (loader_format == format) ? filename.absFileName()
2250 : support::changeExtension(filename.absFileName(),
2251 formats.extension(loader_format));
2252 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2254 guiApp->setCurrentView(lv);
2255 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2262 void GuiView::importDocument(string const & argument)
2265 string filename = split(argument, format, ' ');
2267 LYXERR(Debug::INFO, format << " file: " << filename);
2269 // need user interaction
2270 if (filename.empty()) {
2271 string initpath = lyxrc.document_path;
2272 if (documentBufferView()) {
2273 string const trypath = documentBufferView()->buffer().filePath();
2274 // If directory is writeable, use this as default.
2275 if (FileName(trypath).isDirWritable())
2279 docstring const text = bformat(_("Select %1$s file to import"),
2280 formats.prettyName(format));
2282 FileDialog dlg(toqstr(text));
2283 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2284 dlg.setButton2(qt_("Examples|#E#e"),
2285 toqstr(addPath(package().system_support().absFileName(), "examples")));
2287 docstring filter = formats.prettyName(format);
2290 filter += from_utf8(formats.extensions(format));
2293 FileDialog::Result result =
2294 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2296 if (result.first == FileDialog::Later)
2299 filename = fromqstr(result.second);
2301 // check selected filename
2302 if (filename.empty())
2303 message(_("Canceled."));
2306 if (filename.empty())
2309 // get absolute path of file
2310 FileName const fullname(support::makeAbsPath(filename));
2312 // Can happen if the user entered a path into the dialog
2314 if (fullname.onlyFileName().empty()) {
2315 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2316 "Aborting import."),
2317 from_utf8(fullname.absFileName()));
2318 frontend::Alert::error(_("File name error"), msg);
2319 message(_("Canceled."));
2324 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2326 // Check if the document already is open
2327 Buffer * buf = theBufferList().getBuffer(lyxfile);
2330 if (!closeBuffer()) {
2331 message(_("Canceled."));
2336 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2338 // if the file exists already, and we didn't do
2339 // -i lyx thefile.lyx, warn
2340 if (lyxfile.exists() && fullname != lyxfile) {
2342 docstring text = bformat(_("The document %1$s already exists.\n\n"
2343 "Do you want to overwrite that document?"), displaypath);
2344 int const ret = Alert::prompt(_("Overwrite document?"),
2345 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2348 message(_("Canceled."));
2353 message(bformat(_("Importing %1$s..."), displaypath));
2354 ErrorList errorList;
2355 if (import(this, fullname, format, errorList))
2356 message(_("imported."));
2358 message(_("file not imported!"));
2360 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2364 void GuiView::newDocument(string const & filename, bool from_template)
2366 FileName initpath(lyxrc.document_path);
2367 if (documentBufferView()) {
2368 FileName const trypath(documentBufferView()->buffer().filePath());
2369 // If directory is writeable, use this as default.
2370 if (trypath.isDirWritable())
2374 string templatefile;
2375 if (from_template) {
2376 templatefile = selectTemplateFile().absFileName();
2377 if (templatefile.empty())
2382 if (filename.empty())
2383 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2385 b = newFile(filename, templatefile, true);
2390 // If no new document could be created, it is unsure
2391 // whether there is a valid BufferView.
2392 if (currentBufferView())
2393 // Ensure the cursor is correctly positioned on screen.
2394 currentBufferView()->showCursor();
2398 void GuiView::insertLyXFile(docstring const & fname)
2400 BufferView * bv = documentBufferView();
2405 FileName filename(to_utf8(fname));
2406 if (filename.empty()) {
2407 // Launch a file browser
2409 string initpath = lyxrc.document_path;
2410 string const trypath = bv->buffer().filePath();
2411 // If directory is writeable, use this as default.
2412 if (FileName(trypath).isDirWritable())
2416 FileDialog dlg(qt_("Select LyX document to insert"));
2417 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2418 dlg.setButton2(qt_("Examples|#E#e"),
2419 toqstr(addPath(package().system_support().absFileName(),
2422 FileDialog::Result result = dlg.open(toqstr(initpath),
2423 QStringList(qt_("LyX Documents (*.lyx)")));
2425 if (result.first == FileDialog::Later)
2429 filename.set(fromqstr(result.second));
2431 // check selected filename
2432 if (filename.empty()) {
2433 // emit message signal.
2434 message(_("Canceled."));
2439 bv->insertLyXFile(filename);
2440 bv->buffer().errors("Parse");
2444 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2446 FileName fname = b.fileName();
2447 FileName const oldname = fname;
2449 if (!newname.empty()) {
2451 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2453 // Switch to this Buffer.
2456 // No argument? Ask user through dialog.
2458 FileDialog dlg(qt_("Choose a filename to save document as"));
2459 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2460 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2462 if (!isLyXFileName(fname.absFileName()))
2463 fname.changeExtension(".lyx");
2465 FileDialog::Result result =
2466 dlg.save(toqstr(fname.onlyPath().absFileName()),
2467 QStringList(qt_("LyX Documents (*.lyx)")),
2468 toqstr(fname.onlyFileName()));
2470 if (result.first == FileDialog::Later)
2473 fname.set(fromqstr(result.second));
2478 if (!isLyXFileName(fname.absFileName()))
2479 fname.changeExtension(".lyx");
2482 // fname is now the new Buffer location.
2484 // if there is already a Buffer open with this name, we do not want
2485 // to have another one. (the second test makes sure we're not just
2486 // trying to overwrite ourselves, which is fine.)
2487 if (theBufferList().exists(fname) && fname != oldname
2488 && theBufferList().getBuffer(fname) != &b) {
2489 docstring const text =
2490 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2491 "Please close it before attempting to overwrite it.\n"
2492 "Do you want to choose a new filename?"),
2493 from_utf8(fname.absFileName()));
2494 int const ret = Alert::prompt(_("Chosen File Already Open"),
2495 text, 0, 1, _("&Rename"), _("&Cancel"));
2497 case 0: return renameBuffer(b, docstring(), kind);
2498 case 1: return false;
2503 bool const existsLocal = fname.exists();
2504 bool const existsInVC = LyXVC::fileInVC(fname);
2505 if (existsLocal || existsInVC) {
2506 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2507 if (kind != LV_WRITE_AS && existsInVC) {
2508 // renaming to a name that is already in VC
2510 docstring text = bformat(_("The document %1$s "
2511 "is already registered.\n\n"
2512 "Do you want to choose a new name?"),
2514 docstring const title = (kind == LV_VC_RENAME) ?
2515 _("Rename document?") : _("Copy document?");
2516 docstring const button = (kind == LV_VC_RENAME) ?
2517 _("&Rename") : _("&Copy");
2518 int const ret = Alert::prompt(title, text, 0, 1,
2519 button, _("&Cancel"));
2521 case 0: return renameBuffer(b, docstring(), kind);
2522 case 1: return false;
2527 docstring text = bformat(_("The document %1$s "
2528 "already exists.\n\n"
2529 "Do you want to overwrite that document?"),
2531 int const ret = Alert::prompt(_("Overwrite document?"),
2532 text, 0, 2, _("&Overwrite"),
2533 _("&Rename"), _("&Cancel"));
2536 case 1: return renameBuffer(b, docstring(), kind);
2537 case 2: return false;
2543 case LV_VC_RENAME: {
2544 string msg = b.lyxvc().rename(fname);
2547 message(from_utf8(msg));
2551 string msg = b.lyxvc().copy(fname);
2554 message(from_utf8(msg));
2560 // LyXVC created the file already in case of LV_VC_RENAME or
2561 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2562 // relative paths of included stuff right if we moved e.g. from
2563 // /a/b.lyx to /a/c/b.lyx.
2565 bool const saved = saveBuffer(b, fname);
2572 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2574 FileName fname = b.fileName();
2576 FileDialog dlg(qt_("Choose a filename to export the document as"));
2577 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2580 QString const anyformat = qt_("Guess from extension (*.*)");
2582 Formats::const_iterator it = formats.begin();
2583 vector<Format const *> export_formats;
2584 for (; it != formats.end(); ++it)
2585 if (it->documentFormat())
2586 export_formats.push_back(&(*it));
2587 sort(export_formats.begin(), export_formats.end(),
2588 [](Format const *first, Format const *second) {
2589 QString name1 = qt_(first->prettyname());
2590 QString name2 = qt_(second->prettyname());
2591 return 0 < name2.localeAwareCompare(name1);
2593 vector<Format const *>::const_iterator fit = export_formats.begin();
2594 map<QString, string> fmap;
2597 for (; fit != export_formats.end(); ++fit) {
2598 docstring const loc_prettyname =
2599 translateIfPossible(from_utf8((*fit)->prettyname()));
2600 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2602 from_ascii((*fit)->extension())));
2603 types << loc_filter;
2604 fmap[loc_filter] = (*fit)->name();
2605 if (from_ascii((*fit)->name()) == iformat) {
2606 filter = loc_filter;
2607 ext = (*fit)->extension();
2610 string ofname = fname.onlyFileName();
2612 ofname = support::changeExtension(ofname, ext);
2613 FileDialog::Result result =
2614 dlg.save(toqstr(fname.onlyPath().absFileName()),
2618 if (result.first != FileDialog::Chosen)
2622 fname.set(fromqstr(result.second));
2623 if (filter == anyformat)
2624 fmt_name = formats.getFormatFromExtension(fname.extension());
2626 fmt_name = fmap[filter];
2627 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2628 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2630 if (fmt_name.empty() || fname.empty())
2633 // fname is now the new Buffer location.
2634 if (FileName(fname).exists()) {
2635 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2636 docstring text = bformat(_("The document %1$s already "
2637 "exists.\n\nDo you want to "
2638 "overwrite that document?"),
2640 int const ret = Alert::prompt(_("Overwrite document?"),
2641 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2644 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2645 case 2: return false;
2649 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2652 return dr.dispatched();
2656 bool GuiView::saveBuffer(Buffer & b)
2658 return saveBuffer(b, FileName());
2662 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2664 if (workArea(b) && workArea(b)->inDialogMode())
2667 if (fn.empty() && b.isUnnamed())
2668 return renameBuffer(b, docstring());
2670 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2672 theSession().lastFiles().add(b.fileName());
2676 // Switch to this Buffer.
2679 // FIXME: we don't tell the user *WHY* the save failed !!
2680 docstring const file = makeDisplayPath(b.absFileName(), 30);
2681 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2682 "Do you want to rename the document and "
2683 "try again?"), file);
2684 int const ret = Alert::prompt(_("Rename and save?"),
2685 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2688 if (!renameBuffer(b, docstring()))
2697 return saveBuffer(b, fn);
2701 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2703 return closeWorkArea(wa, false);
2707 // We only want to close the buffer if it is not visible in other workareas
2708 // of the same view, nor in other views, and if this is not a child
2709 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2711 Buffer & buf = wa->bufferView().buffer();
2713 bool last_wa = d.countWorkAreasOf(buf) == 1
2714 && !inOtherView(buf) && !buf.parent();
2716 bool close_buffer = last_wa;
2719 if (lyxrc.close_buffer_with_last_view == "yes")
2721 else if (lyxrc.close_buffer_with_last_view == "no")
2722 close_buffer = false;
2725 if (buf.isUnnamed())
2726 file = from_utf8(buf.fileName().onlyFileName());
2728 file = buf.fileName().displayName(30);
2729 docstring const text = bformat(
2730 _("Last view on document %1$s is being closed.\n"
2731 "Would you like to close or hide the document?\n"
2733 "Hidden documents can be displayed back through\n"
2734 "the menu: View->Hidden->...\n"
2736 "To remove this question, set your preference in:\n"
2737 " Tools->Preferences->Look&Feel->UserInterface\n"
2739 int ret = Alert::prompt(_("Close or hide document?"),
2740 text, 0, 1, _("&Close"), _("&Hide"));
2741 close_buffer = (ret == 0);
2745 return closeWorkArea(wa, close_buffer);
2749 bool GuiView::closeBuffer()
2751 GuiWorkArea * wa = currentMainWorkArea();
2752 // coverity complained about this
2753 // it seems unnecessary, but perhaps is worth the check
2754 LASSERT(wa, return false);
2756 setCurrentWorkArea(wa);
2757 Buffer & buf = wa->bufferView().buffer();
2758 return closeWorkArea(wa, !buf.parent());
2762 void GuiView::writeSession() const {
2763 GuiWorkArea const * active_wa = currentMainWorkArea();
2764 for (int i = 0; i < d.splitter_->count(); ++i) {
2765 TabWorkArea * twa = d.tabWorkArea(i);
2766 for (int j = 0; j < twa->count(); ++j) {
2767 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2768 Buffer & buf = wa->bufferView().buffer();
2769 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2775 bool GuiView::closeBufferAll()
2777 // Close the workareas in all other views
2778 QList<int> const ids = guiApp->viewIds();
2779 for (int i = 0; i != ids.size(); ++i) {
2780 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2784 // Close our own workareas
2785 if (!closeWorkAreaAll())
2788 // Now close the hidden buffers. We prevent hidden buffers from being
2789 // dirty, so we can just close them.
2790 theBufferList().closeAll();
2795 bool GuiView::closeWorkAreaAll()
2797 setCurrentWorkArea(currentMainWorkArea());
2799 // We might be in a situation that there is still a tabWorkArea, but
2800 // there are no tabs anymore. This can happen when we get here after a
2801 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2802 // many TabWorkArea's have no documents anymore.
2805 // We have to call count() each time, because it can happen that
2806 // more than one splitter will disappear in one iteration (bug 5998).
2807 while (d.splitter_->count() > empty_twa) {
2808 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2810 if (twa->count() == 0)
2813 setCurrentWorkArea(twa->currentWorkArea());
2814 if (!closeTabWorkArea(twa))
2822 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2827 Buffer & buf = wa->bufferView().buffer();
2829 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2830 Alert::warning(_("Close document"),
2831 _("Document could not be closed because it is being processed by LyX."));
2836 return closeBuffer(buf);
2838 if (!inMultiTabs(wa))
2839 if (!saveBufferIfNeeded(buf, true))
2847 bool GuiView::closeBuffer(Buffer & buf)
2849 // If we are in a close_event all children will be closed in some time,
2850 // so no need to do it here. This will ensure that the children end up
2851 // in the session file in the correct order. If we close the master
2852 // buffer, we can close or release the child buffers here too.
2853 bool success = true;
2855 ListOfBuffers clist = buf.getChildren();
2856 ListOfBuffers::const_iterator it = clist.begin();
2857 ListOfBuffers::const_iterator const bend = clist.end();
2858 for (; it != bend; ++it) {
2859 Buffer * child_buf = *it;
2860 if (theBufferList().isOthersChild(&buf, child_buf)) {
2861 child_buf->setParent(0);
2865 // FIXME: should we look in other tabworkareas?
2866 // ANSWER: I don't think so. I've tested, and if the child is
2867 // open in some other window, it closes without a problem.
2868 GuiWorkArea * child_wa = workArea(*child_buf);
2870 success = closeWorkArea(child_wa, true);
2874 // In this case the child buffer is open but hidden.
2875 // It therefore should not (MUST NOT) be dirty!
2876 LATTEST(child_buf->isClean());
2877 theBufferList().release(child_buf);
2882 // goto bookmark to update bookmark pit.
2883 // FIXME: we should update only the bookmarks related to this buffer!
2884 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2885 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2886 guiApp->gotoBookmark(i+1, false, false);
2888 if (saveBufferIfNeeded(buf, false)) {
2889 buf.removeAutosaveFile();
2890 theBufferList().release(&buf);
2894 // open all children again to avoid a crash because of dangling
2895 // pointers (bug 6603)
2901 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2903 while (twa == d.currentTabWorkArea()) {
2904 twa->setCurrentIndex(twa->count() - 1);
2906 GuiWorkArea * wa = twa->currentWorkArea();
2907 Buffer & b = wa->bufferView().buffer();
2909 // We only want to close the buffer if the same buffer is not visible
2910 // in another view, and if this is not a child and if we are closing
2911 // a view (not a tabgroup).
2912 bool const close_buffer =
2913 !inOtherView(b) && !b.parent() && closing_;
2915 if (!closeWorkArea(wa, close_buffer))
2922 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2924 if (buf.isClean() || buf.paragraphs().empty())
2927 // Switch to this Buffer.
2932 if (buf.isUnnamed())
2933 file = from_utf8(buf.fileName().onlyFileName());
2935 file = buf.fileName().displayName(30);
2937 // Bring this window to top before asking questions.
2942 if (hiding && buf.isUnnamed()) {
2943 docstring const text = bformat(_("The document %1$s has not been "
2944 "saved yet.\n\nDo you want to save "
2945 "the document?"), file);
2946 ret = Alert::prompt(_("Save new document?"),
2947 text, 0, 1, _("&Save"), _("&Cancel"));
2951 docstring const text = bformat(_("The document %1$s has unsaved changes."
2952 "\n\nDo you want to save the document or discard the changes?"), file);
2953 ret = Alert::prompt(_("Save changed document?"),
2954 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2959 if (!saveBuffer(buf))
2963 // If we crash after this we could have no autosave file
2964 // but I guess this is really improbable (Jug).
2965 // Sometimes improbable things happen:
2966 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2967 // buf.removeAutosaveFile();
2969 // revert all changes
2980 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2982 Buffer & buf = wa->bufferView().buffer();
2984 for (int i = 0; i != d.splitter_->count(); ++i) {
2985 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2986 if (wa_ && wa_ != wa)
2989 return inOtherView(buf);
2993 bool GuiView::inOtherView(Buffer & buf)
2995 QList<int> const ids = guiApp->viewIds();
2997 for (int i = 0; i != ids.size(); ++i) {
3001 if (guiApp->view(ids[i]).workArea(buf))
3008 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3010 if (!documentBufferView())
3013 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3014 Buffer * const curbuf = &documentBufferView()->buffer();
3015 int nwa = twa->count();
3016 for (int i = 0; i < nwa; ++i) {
3017 if (&workArea(i)->bufferView().buffer() == curbuf) {
3019 if (np == NEXTBUFFER)
3020 next_index = (i == nwa - 1 ? 0 : i + 1);
3022 next_index = (i == 0 ? nwa - 1 : i - 1);
3024 twa->moveTab(i, next_index);
3026 setBuffer(&workArea(next_index)->bufferView().buffer());
3034 /// make sure the document is saved
3035 static bool ensureBufferClean(Buffer * buffer)
3037 LASSERT(buffer, return false);
3038 if (buffer->isClean() && !buffer->isUnnamed())
3041 docstring const file = buffer->fileName().displayName(30);
3044 if (!buffer->isUnnamed()) {
3045 text = bformat(_("The document %1$s has unsaved "
3046 "changes.\n\nDo you want to save "
3047 "the document?"), file);
3048 title = _("Save changed document?");
3051 text = bformat(_("The document %1$s has not been "
3052 "saved yet.\n\nDo you want to save "
3053 "the document?"), file);
3054 title = _("Save new document?");
3056 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3059 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3061 return buffer->isClean() && !buffer->isUnnamed();
3065 bool GuiView::reloadBuffer(Buffer & buf)
3067 Buffer::ReadStatus status = buf.reload();
3068 return status == Buffer::ReadSuccess;
3072 void GuiView::checkExternallyModifiedBuffers()
3074 BufferList::iterator bit = theBufferList().begin();
3075 BufferList::iterator const bend = theBufferList().end();
3076 for (; bit != bend; ++bit) {
3077 Buffer * buf = *bit;
3078 if (buf->fileName().exists()
3079 && buf->isExternallyModified(Buffer::checksum_method)) {
3080 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3081 " Reload now? Any local changes will be lost."),
3082 from_utf8(buf->absFileName()));
3083 int const ret = Alert::prompt(_("Reload externally changed document?"),
3084 text, 0, 1, _("&Reload"), _("&Cancel"));
3092 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3094 Buffer * buffer = documentBufferView()
3095 ? &(documentBufferView()->buffer()) : 0;
3097 switch (cmd.action()) {
3098 case LFUN_VC_REGISTER:
3099 if (!buffer || !ensureBufferClean(buffer))
3101 if (!buffer->lyxvc().inUse()) {
3102 if (buffer->lyxvc().registrer()) {
3103 reloadBuffer(*buffer);
3104 dr.clearMessageUpdate();
3109 case LFUN_VC_RENAME:
3110 case LFUN_VC_COPY: {
3111 if (!buffer || !ensureBufferClean(buffer))
3113 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3114 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3115 // Some changes are not yet committed.
3116 // We test here and not in getStatus(), since
3117 // this test is expensive.
3119 LyXVC::CommandResult ret =
3120 buffer->lyxvc().checkIn(log);
3122 if (ret == LyXVC::ErrorCommand ||
3123 ret == LyXVC::VCSuccess)
3124 reloadBuffer(*buffer);
3125 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3126 frontend::Alert::error(
3127 _("Revision control error."),
3128 _("Document could not be checked in."));
3132 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3133 LV_VC_RENAME : LV_VC_COPY;
3134 renameBuffer(*buffer, cmd.argument(), kind);
3139 case LFUN_VC_CHECK_IN:
3140 if (!buffer || !ensureBufferClean(buffer))
3142 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3144 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3146 // Only skip reloading if the checkin was cancelled or
3147 // an error occurred before the real checkin VCS command
3148 // was executed, since the VCS might have changed the
3149 // file even if it could not checkin successfully.
3150 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3151 reloadBuffer(*buffer);
3155 case LFUN_VC_CHECK_OUT:
3156 if (!buffer || !ensureBufferClean(buffer))
3158 if (buffer->lyxvc().inUse()) {
3159 dr.setMessage(buffer->lyxvc().checkOut());
3160 reloadBuffer(*buffer);
3164 case LFUN_VC_LOCKING_TOGGLE:
3165 LASSERT(buffer, return);
3166 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3168 if (buffer->lyxvc().inUse()) {
3169 string res = buffer->lyxvc().lockingToggle();
3171 frontend::Alert::error(_("Revision control error."),
3172 _("Error when setting the locking property."));
3175 reloadBuffer(*buffer);
3180 case LFUN_VC_REVERT:
3181 LASSERT(buffer, return);
3182 if (buffer->lyxvc().revert()) {
3183 reloadBuffer(*buffer);
3184 dr.clearMessageUpdate();
3188 case LFUN_VC_UNDO_LAST:
3189 LASSERT(buffer, return);
3190 buffer->lyxvc().undoLast();
3191 reloadBuffer(*buffer);
3192 dr.clearMessageUpdate();
3195 case LFUN_VC_REPO_UPDATE:
3196 LASSERT(buffer, return);
3197 if (ensureBufferClean(buffer)) {
3198 dr.setMessage(buffer->lyxvc().repoUpdate());
3199 checkExternallyModifiedBuffers();
3203 case LFUN_VC_COMMAND: {
3204 string flag = cmd.getArg(0);
3205 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3208 if (contains(flag, 'M')) {
3209 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3212 string path = cmd.getArg(1);
3213 if (contains(path, "$$p") && buffer)
3214 path = subst(path, "$$p", buffer->filePath());
3215 LYXERR(Debug::LYXVC, "Directory: " << path);
3217 if (!pp.isReadableDirectory()) {
3218 lyxerr << _("Directory is not accessible.") << endl;
3221 support::PathChanger p(pp);
3223 string command = cmd.getArg(2);
3224 if (command.empty())
3227 command = subst(command, "$$i", buffer->absFileName());
3228 command = subst(command, "$$p", buffer->filePath());
3230 command = subst(command, "$$m", to_utf8(message));
3231 LYXERR(Debug::LYXVC, "Command: " << command);
3233 one.startscript(Systemcall::Wait, command);
3237 if (contains(flag, 'I'))
3238 buffer->markDirty();
3239 if (contains(flag, 'R'))
3240 reloadBuffer(*buffer);
3245 case LFUN_VC_COMPARE: {
3246 if (cmd.argument().empty()) {
3247 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3251 string rev1 = cmd.getArg(0);
3255 // it seems safe to assume we have a buffer
3256 // coverity[FORWARD_NULL]
3257 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3260 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3261 f2 = buffer->absFileName();
3263 string rev2 = cmd.getArg(1);
3267 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3271 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3272 f1 << "\n" << f2 << "\n" );
3273 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3274 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3284 void GuiView::openChildDocument(string const & fname)
3286 LASSERT(documentBufferView(), return);
3287 Buffer & buffer = documentBufferView()->buffer();
3288 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3289 documentBufferView()->saveBookmark(false);
3291 if (theBufferList().exists(filename)) {
3292 child = theBufferList().getBuffer(filename);
3295 message(bformat(_("Opening child document %1$s..."),
3296 makeDisplayPath(filename.absFileName())));
3297 child = loadDocument(filename, false);
3299 // Set the parent name of the child document.
3300 // This makes insertion of citations and references in the child work,
3301 // when the target is in the parent or another child document.
3303 child->setParent(&buffer);
3307 bool GuiView::goToFileRow(string const & argument)
3311 size_t i = argument.find_last_of(' ');
3312 if (i != string::npos) {
3313 file_name = os::internal_path(trim(argument.substr(0, i)));
3314 istringstream is(argument.substr(i + 1));
3319 if (i == string::npos) {
3320 LYXERR0("Wrong argument: " << argument);
3324 string const abstmp = package().temp_dir().absFileName();
3325 string const realtmp = package().temp_dir().realPath();
3326 // We have to use os::path_prefix_is() here, instead of
3327 // simply prefixIs(), because the file name comes from
3328 // an external application and may need case adjustment.
3329 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3330 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3331 // Needed by inverse dvi search. If it is a file
3332 // in tmpdir, call the apropriated function.
3333 // If tmpdir is a symlink, we may have the real
3334 // path passed back, so we correct for that.
3335 if (!prefixIs(file_name, abstmp))
3336 file_name = subst(file_name, realtmp, abstmp);
3337 buf = theBufferList().getBufferFromTmp(file_name);
3339 // Must replace extension of the file to be .lyx
3340 // and get full path
3341 FileName const s = fileSearch(string(),
3342 support::changeExtension(file_name, ".lyx"), "lyx");
3343 // Either change buffer or load the file
3344 if (theBufferList().exists(s))
3345 buf = theBufferList().getBuffer(s);
3346 else if (s.exists()) {
3347 buf = loadDocument(s);
3352 _("File does not exist: %1$s"),
3353 makeDisplayPath(file_name)));
3359 _("No buffer for file: %1$s."),
3360 makeDisplayPath(file_name))
3365 documentBufferView()->setCursorFromRow(row);
3371 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3373 Buffer::ExportStatus const status = func(format);
3375 // the cloning operation will have produced a clone of the entire set of
3376 // documents, starting from the master. so we must delete those.
3377 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3379 busyBuffers.remove(orig);
3384 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3386 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3387 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3391 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3393 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3394 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3398 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3400 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3401 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3405 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3406 string const & argument,
3407 Buffer const * used_buffer,
3408 docstring const & msg,
3409 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3410 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3411 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3416 string format = argument;
3418 format = used_buffer->params().getDefaultOutputFormat();
3419 processing_format = format;
3421 progress_->clearMessages();
3424 #if EXPORT_in_THREAD
3425 GuiViewPrivate::busyBuffers.insert(used_buffer);
3426 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3427 if (!cloned_buffer) {
3428 Alert::error(_("Export Error"),
3429 _("Error cloning the Buffer."));
3432 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3437 setPreviewFuture(f);
3438 last_export_format = used_buffer->params().bufferFormat();
3441 // We are asynchronous, so we don't know here anything about the success
3444 Buffer::ExportStatus status;
3446 status = (used_buffer->*syncFunc)(format, true);
3447 } else if (previewFunc) {
3448 status = (used_buffer->*previewFunc)(format);
3451 handleExportStatus(gv_, status, format);
3453 return (status == Buffer::ExportSuccess
3454 || status == Buffer::PreviewSuccess);
3458 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3460 BufferView * bv = currentBufferView();
3461 LASSERT(bv, return);
3463 // Let the current BufferView dispatch its own actions.
3464 bv->dispatch(cmd, dr);
3465 if (dr.dispatched())
3468 // Try with the document BufferView dispatch if any.
3469 BufferView * doc_bv = documentBufferView();
3470 if (doc_bv && doc_bv != bv) {
3471 doc_bv->dispatch(cmd, dr);
3472 if (dr.dispatched())
3476 // Then let the current Cursor dispatch its own actions.
3477 bv->cursor().dispatch(cmd);
3479 // update completion. We do it here and not in
3480 // processKeySym to avoid another redraw just for a
3481 // changed inline completion
3482 if (cmd.origin() == FuncRequest::KEYBOARD) {
3483 if (cmd.action() == LFUN_SELF_INSERT
3484 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3485 updateCompletion(bv->cursor(), true, true);
3486 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3487 updateCompletion(bv->cursor(), false, true);
3489 updateCompletion(bv->cursor(), false, false);
3492 dr = bv->cursor().result();
3496 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3498 BufferView * bv = currentBufferView();
3499 // By default we won't need any update.
3500 dr.screenUpdate(Update::None);
3501 // assume cmd will be dispatched
3502 dr.dispatched(true);
3504 Buffer * doc_buffer = documentBufferView()
3505 ? &(documentBufferView()->buffer()) : 0;
3507 if (cmd.origin() == FuncRequest::TOC) {
3508 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3509 // FIXME: do we need to pass a DispatchResult object here?
3510 toc->doDispatch(bv->cursor(), cmd);
3514 string const argument = to_utf8(cmd.argument());
3516 switch(cmd.action()) {
3517 case LFUN_BUFFER_CHILD_OPEN:
3518 openChildDocument(to_utf8(cmd.argument()));
3521 case LFUN_BUFFER_IMPORT:
3522 importDocument(to_utf8(cmd.argument()));
3525 case LFUN_BUFFER_EXPORT: {
3528 // GCC only sees strfwd.h when building merged
3529 if (::lyx::operator==(cmd.argument(), "custom")) {
3530 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3534 string const dest = cmd.getArg(1);
3535 FileName target_dir;
3536 if (!dest.empty() && FileName::isAbsolute(dest))
3537 target_dir = FileName(support::onlyPath(dest));
3539 target_dir = doc_buffer->fileName().onlyPath();
3541 if ((dest.empty() && doc_buffer->isUnnamed())
3542 || !target_dir.isDirWritable()) {
3543 exportBufferAs(*doc_buffer, cmd.argument());
3546 /* TODO/Review: Is it a problem to also export the children?
3547 See the update_unincluded flag */
3548 d.asyncBufferProcessing(argument,
3551 &GuiViewPrivate::exportAndDestroy,
3554 // TODO Inform user about success
3558 case LFUN_BUFFER_EXPORT_AS: {
3559 LASSERT(doc_buffer, break);
3560 docstring f = cmd.argument();
3562 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3563 exportBufferAs(*doc_buffer, f);
3567 case LFUN_BUFFER_UPDATE: {
3568 d.asyncBufferProcessing(argument,
3571 &GuiViewPrivate::compileAndDestroy,
3576 case LFUN_BUFFER_VIEW: {
3577 d.asyncBufferProcessing(argument,
3579 _("Previewing ..."),
3580 &GuiViewPrivate::previewAndDestroy,
3585 case LFUN_MASTER_BUFFER_UPDATE: {
3586 d.asyncBufferProcessing(argument,
3587 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3589 &GuiViewPrivate::compileAndDestroy,
3594 case LFUN_MASTER_BUFFER_VIEW: {
3595 d.asyncBufferProcessing(argument,
3596 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3598 &GuiViewPrivate::previewAndDestroy,
3599 0, &Buffer::preview);
3602 case LFUN_BUFFER_SWITCH: {
3603 string const file_name = to_utf8(cmd.argument());
3604 if (!FileName::isAbsolute(file_name)) {
3606 dr.setMessage(_("Absolute filename expected."));
3610 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3613 dr.setMessage(_("Document not loaded"));
3617 // Do we open or switch to the buffer in this view ?
3618 if (workArea(*buffer)
3619 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3624 // Look for the buffer in other views
3625 QList<int> const ids = guiApp->viewIds();
3627 for (; i != ids.size(); ++i) {
3628 GuiView & gv = guiApp->view(ids[i]);
3629 if (gv.workArea(*buffer)) {
3631 gv.activateWindow();
3633 gv.setBuffer(buffer);
3638 // If necessary, open a new window as a last resort
3639 if (i == ids.size()) {
3640 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3646 case LFUN_BUFFER_NEXT:
3647 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3650 case LFUN_BUFFER_MOVE_NEXT:
3651 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3654 case LFUN_BUFFER_PREVIOUS:
3655 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3658 case LFUN_BUFFER_MOVE_PREVIOUS:
3659 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3662 case LFUN_COMMAND_EXECUTE: {
3663 command_execute_ = true;
3666 case LFUN_DROP_LAYOUTS_CHOICE:
3667 d.layout_->showPopup();
3670 case LFUN_MENU_OPEN:
3671 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3672 menu->exec(QCursor::pos());
3675 case LFUN_FILE_INSERT:
3676 insertLyXFile(cmd.argument());
3679 case LFUN_FILE_INSERT_PLAINTEXT:
3680 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3681 string const fname = to_utf8(cmd.argument());
3682 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3683 dr.setMessage(_("Absolute filename expected."));
3687 FileName filename(fname);
3688 if (fname.empty()) {
3689 FileDialog dlg(qt_("Select file to insert"));
3691 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3692 QStringList(qt_("All Files (*)")));
3694 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3695 dr.setMessage(_("Canceled."));
3699 filename.set(fromqstr(result.second));
3703 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3704 bv->dispatch(new_cmd, dr);
3709 case LFUN_BUFFER_RELOAD: {
3710 LASSERT(doc_buffer, break);
3713 if (!doc_buffer->isClean()) {
3714 docstring const file =
3715 makeDisplayPath(doc_buffer->absFileName(), 20);
3716 docstring text = bformat(_("Any changes will be lost. "
3717 "Are you sure you want to revert to the saved version "
3718 "of the document %1$s?"), file);
3719 ret = Alert::prompt(_("Revert to saved document?"),
3720 text, 1, 1, _("&Revert"), _("&Cancel"));
3724 doc_buffer->markClean();
3725 reloadBuffer(*doc_buffer);
3726 dr.forceBufferUpdate();
3731 case LFUN_BUFFER_WRITE:
3732 LASSERT(doc_buffer, break);
3733 saveBuffer(*doc_buffer);
3736 case LFUN_BUFFER_WRITE_AS:
3737 LASSERT(doc_buffer, break);
3738 renameBuffer(*doc_buffer, cmd.argument());
3741 case LFUN_BUFFER_WRITE_ALL: {
3742 Buffer * first = theBufferList().first();
3745 message(_("Saving all documents..."));
3746 // We cannot use a for loop as the buffer list cycles.
3749 if (!b->isClean()) {
3751 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3753 b = theBufferList().next(b);
3754 } while (b != first);
3755 dr.setMessage(_("All documents saved."));
3759 case LFUN_BUFFER_CLOSE:
3763 case LFUN_BUFFER_CLOSE_ALL:
3767 case LFUN_TOOLBAR_TOGGLE: {
3768 string const name = cmd.getArg(0);
3769 if (GuiToolbar * t = toolbar(name))
3774 case LFUN_DIALOG_UPDATE: {
3775 string const name = to_utf8(cmd.argument());
3776 if (name == "prefs" || name == "document")
3777 updateDialog(name, string());
3778 else if (name == "paragraph")
3779 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3780 else if (currentBufferView()) {
3781 Inset * inset = currentBufferView()->editedInset(name);
3782 // Can only update a dialog connected to an existing inset
3784 // FIXME: get rid of this indirection; GuiView ask the inset
3785 // if he is kind enough to update itself...
3786 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3787 //FIXME: pass DispatchResult here?
3788 inset->dispatch(currentBufferView()->cursor(), fr);
3794 case LFUN_DIALOG_TOGGLE: {
3795 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3796 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3797 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3801 case LFUN_DIALOG_DISCONNECT_INSET:
3802 disconnectDialog(to_utf8(cmd.argument()));
3805 case LFUN_DIALOG_HIDE: {
3806 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3810 case LFUN_DIALOG_SHOW: {
3811 string const name = cmd.getArg(0);
3812 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3814 if (name == "character") {
3815 data = freefont2string();
3817 showDialog("character", data);
3818 } else if (name == "latexlog") {
3819 Buffer::LogType type;
3820 string const logfile = doc_buffer->logName(&type);
3822 case Buffer::latexlog:
3825 case Buffer::buildlog:
3829 data += Lexer::quoteString(logfile);
3830 showDialog("log", data);
3831 } else if (name == "vclog") {
3832 string const data = "vc " +
3833 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3834 showDialog("log", data);
3835 } else if (name == "symbols") {
3836 data = bv->cursor().getEncoding()->name();
3838 showDialog("symbols", data);
3840 } else if (name == "prefs" && isFullScreen()) {
3841 lfunUiToggle("fullscreen");
3842 showDialog("prefs", data);
3844 showDialog(name, data);
3849 dr.setMessage(cmd.argument());
3852 case LFUN_UI_TOGGLE: {
3853 string arg = cmd.getArg(0);
3854 if (!lfunUiToggle(arg)) {
3855 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3856 dr.setMessage(bformat(msg, from_utf8(arg)));
3858 // Make sure the keyboard focus stays in the work area.
3863 case LFUN_VIEW_SPLIT: {
3864 LASSERT(doc_buffer, break);
3865 string const orientation = cmd.getArg(0);
3866 d.splitter_->setOrientation(orientation == "vertical"
3867 ? Qt::Vertical : Qt::Horizontal);
3868 TabWorkArea * twa = addTabWorkArea();
3869 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3870 setCurrentWorkArea(wa);
3873 case LFUN_TAB_GROUP_CLOSE:
3874 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3875 closeTabWorkArea(twa);
3876 d.current_work_area_ = 0;
3877 twa = d.currentTabWorkArea();
3878 // Switch to the next GuiWorkArea in the found TabWorkArea.
3880 // Make sure the work area is up to date.
3881 setCurrentWorkArea(twa->currentWorkArea());
3883 setCurrentWorkArea(0);
3888 case LFUN_VIEW_CLOSE:
3889 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3890 closeWorkArea(twa->currentWorkArea());
3891 d.current_work_area_ = 0;
3892 twa = d.currentTabWorkArea();
3893 // Switch to the next GuiWorkArea in the found TabWorkArea.
3895 // Make sure the work area is up to date.
3896 setCurrentWorkArea(twa->currentWorkArea());
3898 setCurrentWorkArea(0);
3903 case LFUN_COMPLETION_INLINE:
3904 if (d.current_work_area_)
3905 d.current_work_area_->completer().showInline();
3908 case LFUN_COMPLETION_POPUP:
3909 if (d.current_work_area_)
3910 d.current_work_area_->completer().showPopup();
3915 if (d.current_work_area_)
3916 d.current_work_area_->completer().tab();
3919 case LFUN_COMPLETION_CANCEL:
3920 if (d.current_work_area_) {
3921 if (d.current_work_area_->completer().popupVisible())
3922 d.current_work_area_->completer().hidePopup();
3924 d.current_work_area_->completer().hideInline();
3928 case LFUN_COMPLETION_ACCEPT:
3929 if (d.current_work_area_)
3930 d.current_work_area_->completer().activate();
3933 case LFUN_BUFFER_ZOOM_IN:
3934 case LFUN_BUFFER_ZOOM_OUT: {
3935 // use a signed temp to avoid overflow
3936 int zoom = lyxrc.zoom;
3937 if (cmd.argument().empty()) {
3938 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3943 zoom += convert<int>(cmd.argument());
3949 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3951 // The global QPixmapCache is used in GuiPainter to cache text
3952 // painting so we must reset it.
3953 QPixmapCache::clear();
3954 guiApp->fontLoader().update();
3955 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3959 case LFUN_VC_REGISTER:
3960 case LFUN_VC_RENAME:
3962 case LFUN_VC_CHECK_IN:
3963 case LFUN_VC_CHECK_OUT:
3964 case LFUN_VC_REPO_UPDATE:
3965 case LFUN_VC_LOCKING_TOGGLE:
3966 case LFUN_VC_REVERT:
3967 case LFUN_VC_UNDO_LAST:
3968 case LFUN_VC_COMMAND:
3969 case LFUN_VC_COMPARE:
3970 dispatchVC(cmd, dr);
3973 case LFUN_SERVER_GOTO_FILE_ROW:
3974 goToFileRow(to_utf8(cmd.argument()));
3977 case LFUN_LYX_ACTIVATE:
3981 case LFUN_FORWARD_SEARCH: {
3982 // it seems safe to assume we have a document buffer, since
3983 // getStatus wants one.
3984 // coverity[FORWARD_NULL]
3985 Buffer const * doc_master = doc_buffer->masterBuffer();
3986 FileName const path(doc_master->temppath());
3987 string const texname = doc_master->isChild(doc_buffer)
3988 ? DocFileName(changeExtension(
3989 doc_buffer->absFileName(),
3990 "tex")).mangledFileName()
3991 : doc_buffer->latexName();
3992 string const fulltexname =
3993 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3994 string const mastername =
3995 removeExtension(doc_master->latexName());
3996 FileName const dviname(addName(path.absFileName(),
3997 addExtension(mastername, "dvi")));
3998 FileName const pdfname(addName(path.absFileName(),
3999 addExtension(mastername, "pdf")));
4000 bool const have_dvi = dviname.exists();
4001 bool const have_pdf = pdfname.exists();
4002 if (!have_dvi && !have_pdf) {
4003 dr.setMessage(_("Please, preview the document first."));
4006 string outname = dviname.onlyFileName();
4007 string command = lyxrc.forward_search_dvi;
4008 if (!have_dvi || (have_pdf &&
4009 pdfname.lastModified() > dviname.lastModified())) {
4010 outname = pdfname.onlyFileName();
4011 command = lyxrc.forward_search_pdf;
4014 DocIterator cur = bv->cursor();
4015 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4016 LYXERR(Debug::ACTION, "Forward search: row:" << row
4018 if (row == -1 || command.empty()) {
4019 dr.setMessage(_("Couldn't proceed."));
4022 string texrow = convert<string>(row);
4024 command = subst(command, "$$n", texrow);
4025 command = subst(command, "$$f", fulltexname);
4026 command = subst(command, "$$t", texname);
4027 command = subst(command, "$$o", outname);
4029 PathChanger p(path);
4031 one.startscript(Systemcall::DontWait, command);
4035 case LFUN_SPELLING_CONTINUOUSLY:
4036 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4037 dr.screenUpdate(Update::Force | Update::FitCursor);
4041 // The LFUN must be for one of BufferView, Buffer or Cursor;
4043 dispatchToBufferView(cmd, dr);
4047 // Part of automatic menu appearance feature.
4048 if (isFullScreen()) {
4049 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4053 // Need to update bv because many LFUNs here might have destroyed it
4054 bv = currentBufferView();
4056 // Clear non-empty selections
4057 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4059 Cursor & cur = bv->cursor();
4060 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4061 cur.clearSelection();
4067 bool GuiView::lfunUiToggle(string const & ui_component)
4069 if (ui_component == "scrollbar") {
4070 // hide() is of no help
4071 if (d.current_work_area_->verticalScrollBarPolicy() ==
4072 Qt::ScrollBarAlwaysOff)
4074 d.current_work_area_->setVerticalScrollBarPolicy(
4075 Qt::ScrollBarAsNeeded);
4077 d.current_work_area_->setVerticalScrollBarPolicy(
4078 Qt::ScrollBarAlwaysOff);
4079 } else if (ui_component == "statusbar") {
4080 statusBar()->setVisible(!statusBar()->isVisible());
4081 } else if (ui_component == "menubar") {
4082 menuBar()->setVisible(!menuBar()->isVisible());
4084 if (ui_component == "frame") {
4086 getContentsMargins(&l, &t, &r, &b);
4087 //are the frames in default state?
4088 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4090 setContentsMargins(-2, -2, -2, -2);
4092 setContentsMargins(0, 0, 0, 0);
4095 if (ui_component == "fullscreen") {
4103 void GuiView::toggleFullScreen()
4105 if (isFullScreen()) {
4106 for (int i = 0; i != d.splitter_->count(); ++i)
4107 d.tabWorkArea(i)->setFullScreen(false);
4108 setContentsMargins(0, 0, 0, 0);
4109 setWindowState(windowState() ^ Qt::WindowFullScreen);
4112 statusBar()->show();
4115 hideDialogs("prefs", 0);
4116 for (int i = 0; i != d.splitter_->count(); ++i)
4117 d.tabWorkArea(i)->setFullScreen(true);
4118 setContentsMargins(-2, -2, -2, -2);
4120 setWindowState(windowState() ^ Qt::WindowFullScreen);
4121 if (lyxrc.full_screen_statusbar)
4122 statusBar()->hide();
4123 if (lyxrc.full_screen_menubar)
4125 if (lyxrc.full_screen_toolbars) {
4126 ToolbarMap::iterator end = d.toolbars_.end();
4127 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4132 // give dialogs like the TOC a chance to adapt
4137 Buffer const * GuiView::updateInset(Inset const * inset)
4142 Buffer const * inset_buffer = &(inset->buffer());
4144 for (int i = 0; i != d.splitter_->count(); ++i) {
4145 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4148 Buffer const * buffer = &(wa->bufferView().buffer());
4149 if (inset_buffer == buffer)
4150 wa->scheduleRedraw();
4152 return inset_buffer;
4156 void GuiView::restartCursor()
4158 /* When we move around, or type, it's nice to be able to see
4159 * the cursor immediately after the keypress.
4161 if (d.current_work_area_)
4162 d.current_work_area_->startBlinkingCursor();
4164 // Take this occasion to update the other GUI elements.
4170 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4172 if (d.current_work_area_)
4173 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4178 // This list should be kept in sync with the list of insets in
4179 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4180 // dialog should have the same name as the inset.
4181 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4182 // docs in LyXAction.cpp.
4184 char const * const dialognames[] = {
4186 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4187 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4188 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4189 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4190 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4191 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4192 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4193 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4195 char const * const * const end_dialognames =
4196 dialognames + (sizeof(dialognames) / sizeof(char *));
4200 cmpCStr(char const * name) : name_(name) {}
4201 bool operator()(char const * other) {
4202 return strcmp(other, name_) == 0;
4209 bool isValidName(string const & name)
4211 return find_if(dialognames, end_dialognames,
4212 cmpCStr(name.c_str())) != end_dialognames;
4218 void GuiView::resetDialogs()
4220 // Make sure that no LFUN uses any GuiView.
4221 guiApp->setCurrentView(0);
4225 constructToolbars();
4226 guiApp->menus().fillMenuBar(menuBar(), this, false);
4227 d.layout_->updateContents(true);
4228 // Now update controls with current buffer.
4229 guiApp->setCurrentView(this);
4235 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4237 if (!isValidName(name))
4240 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4242 if (it != d.dialogs_.end()) {
4244 it->second->hideView();
4245 return it->second.get();
4248 Dialog * dialog = build(name);
4249 d.dialogs_[name].reset(dialog);
4250 if (lyxrc.allow_geometry_session)
4251 dialog->restoreSession();
4258 void GuiView::showDialog(string const & name, string const & data,
4261 triggerShowDialog(toqstr(name), toqstr(data), inset);
4265 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4271 const string name = fromqstr(qname);
4272 const string data = fromqstr(qdata);
4276 Dialog * dialog = findOrBuild(name, false);
4278 bool const visible = dialog->isVisibleView();
4279 dialog->showData(data);
4280 if (inset && currentBufferView())
4281 currentBufferView()->editInset(name, inset);
4282 // We only set the focus to the new dialog if it was not yet
4283 // visible in order not to change the existing previous behaviour
4285 // activateWindow is needed for floating dockviews
4286 dialog->asQWidget()->raise();
4287 dialog->asQWidget()->activateWindow();
4288 dialog->asQWidget()->setFocus();
4292 catch (ExceptionMessage const & ex) {
4300 bool GuiView::isDialogVisible(string const & name) const
4302 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4303 if (it == d.dialogs_.end())
4305 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4309 void GuiView::hideDialog(string const & name, Inset * inset)
4311 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4312 if (it == d.dialogs_.end())
4316 if (!currentBufferView())
4318 if (inset != currentBufferView()->editedInset(name))
4322 Dialog * const dialog = it->second.get();
4323 if (dialog->isVisibleView())
4325 if (currentBufferView())
4326 currentBufferView()->editInset(name, 0);
4330 void GuiView::disconnectDialog(string const & name)
4332 if (!isValidName(name))
4334 if (currentBufferView())
4335 currentBufferView()->editInset(name, 0);
4339 void GuiView::hideAll() const
4341 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4342 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4344 for(; it != end; ++it)
4345 it->second->hideView();
4349 void GuiView::updateDialogs()
4351 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4352 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4354 for(; it != end; ++it) {
4355 Dialog * dialog = it->second.get();
4357 if (dialog->needBufferOpen() && !documentBufferView())
4358 hideDialog(fromqstr(dialog->name()), 0);
4359 else if (dialog->isVisibleView())
4360 dialog->checkStatus();
4367 Dialog * createDialog(GuiView & lv, string const & name);
4369 // will be replaced by a proper factory...
4370 Dialog * createGuiAbout(GuiView & lv);
4371 Dialog * createGuiBibtex(GuiView & lv);
4372 Dialog * createGuiChanges(GuiView & lv);
4373 Dialog * createGuiCharacter(GuiView & lv);
4374 Dialog * createGuiCitation(GuiView & lv);
4375 Dialog * createGuiCompare(GuiView & lv);
4376 Dialog * createGuiCompareHistory(GuiView & lv);
4377 Dialog * createGuiDelimiter(GuiView & lv);
4378 Dialog * createGuiDocument(GuiView & lv);
4379 Dialog * createGuiErrorList(GuiView & lv);
4380 Dialog * createGuiExternal(GuiView & lv);
4381 Dialog * createGuiGraphics(GuiView & lv);
4382 Dialog * createGuiInclude(GuiView & lv);
4383 Dialog * createGuiIndex(GuiView & lv);
4384 Dialog * createGuiListings(GuiView & lv);
4385 Dialog * createGuiLog(GuiView & lv);
4386 Dialog * createGuiMathMatrix(GuiView & lv);
4387 Dialog * createGuiNote(GuiView & lv);
4388 Dialog * createGuiParagraph(GuiView & lv);
4389 Dialog * createGuiPhantom(GuiView & lv);
4390 Dialog * createGuiPreferences(GuiView & lv);
4391 Dialog * createGuiPrint(GuiView & lv);
4392 Dialog * createGuiPrintindex(GuiView & lv);
4393 Dialog * createGuiRef(GuiView & lv);
4394 Dialog * createGuiSearch(GuiView & lv);
4395 Dialog * createGuiSearchAdv(GuiView & lv);
4396 Dialog * createGuiSendTo(GuiView & lv);
4397 Dialog * createGuiShowFile(GuiView & lv);
4398 Dialog * createGuiSpellchecker(GuiView & lv);
4399 Dialog * createGuiSymbols(GuiView & lv);
4400 Dialog * createGuiTabularCreate(GuiView & lv);
4401 Dialog * createGuiTexInfo(GuiView & lv);
4402 Dialog * createGuiToc(GuiView & lv);
4403 Dialog * createGuiThesaurus(GuiView & lv);
4404 Dialog * createGuiViewSource(GuiView & lv);
4405 Dialog * createGuiWrap(GuiView & lv);
4406 Dialog * createGuiProgressView(GuiView & lv);
4410 Dialog * GuiView::build(string const & name)
4412 LASSERT(isValidName(name), return 0);
4414 Dialog * dialog = createDialog(*this, name);
4418 if (name == "aboutlyx")
4419 return createGuiAbout(*this);
4420 if (name == "bibtex")
4421 return createGuiBibtex(*this);
4422 if (name == "changes")
4423 return createGuiChanges(*this);
4424 if (name == "character")
4425 return createGuiCharacter(*this);
4426 if (name == "citation")
4427 return createGuiCitation(*this);
4428 if (name == "compare")
4429 return createGuiCompare(*this);
4430 if (name == "comparehistory")
4431 return createGuiCompareHistory(*this);
4432 if (name == "document")
4433 return createGuiDocument(*this);
4434 if (name == "errorlist")
4435 return createGuiErrorList(*this);
4436 if (name == "external")
4437 return createGuiExternal(*this);
4439 return createGuiShowFile(*this);
4440 if (name == "findreplace")
4441 return createGuiSearch(*this);
4442 if (name == "findreplaceadv")
4443 return createGuiSearchAdv(*this);
4444 if (name == "graphics")
4445 return createGuiGraphics(*this);
4446 if (name == "include")
4447 return createGuiInclude(*this);
4448 if (name == "index")
4449 return createGuiIndex(*this);
4450 if (name == "index_print")
4451 return createGuiPrintindex(*this);
4452 if (name == "listings")
4453 return createGuiListings(*this);
4455 return createGuiLog(*this);
4456 if (name == "mathdelimiter")
4457 return createGuiDelimiter(*this);
4458 if (name == "mathmatrix")
4459 return createGuiMathMatrix(*this);
4461 return createGuiNote(*this);
4462 if (name == "paragraph")
4463 return createGuiParagraph(*this);
4464 if (name == "phantom")
4465 return createGuiPhantom(*this);
4466 if (name == "prefs")
4467 return createGuiPreferences(*this);
4469 return createGuiRef(*this);
4470 if (name == "sendto")
4471 return createGuiSendTo(*this);
4472 if (name == "spellchecker")
4473 return createGuiSpellchecker(*this);
4474 if (name == "symbols")
4475 return createGuiSymbols(*this);
4476 if (name == "tabularcreate")
4477 return createGuiTabularCreate(*this);
4478 if (name == "texinfo")
4479 return createGuiTexInfo(*this);
4480 if (name == "thesaurus")
4481 return createGuiThesaurus(*this);
4483 return createGuiToc(*this);
4484 if (name == "view-source")
4485 return createGuiViewSource(*this);
4487 return createGuiWrap(*this);
4488 if (name == "progress")
4489 return createGuiProgressView(*this);
4495 } // namespace frontend
4498 #include "moc_GuiView.cpp"