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), minibuffer_focus_(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;
1567 if (minibuffer_focus_) {
1568 context |= Toolbars::MINIBUFFER_FOCUS;
1569 minibuffer_focus_ = false;
1572 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1573 it->second->update(context);
1575 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1576 it->second->update();
1580 void GuiView::setBuffer(Buffer * newBuffer)
1582 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1583 LASSERT(newBuffer, return);
1585 GuiWorkArea * wa = workArea(*newBuffer);
1588 newBuffer->masterBuffer()->updateBuffer();
1590 wa = addWorkArea(*newBuffer);
1591 // scroll to the position when the BufferView was last closed
1592 if (lyxrc.use_lastfilepos) {
1593 LastFilePosSection::FilePos filepos =
1594 theSession().lastFilePos().load(newBuffer->fileName());
1595 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1598 //Disconnect the old buffer...there's no new one.
1601 connectBuffer(*newBuffer);
1602 connectBufferView(wa->bufferView());
1603 setCurrentWorkArea(wa);
1607 void GuiView::connectBuffer(Buffer & buf)
1609 buf.setGuiDelegate(this);
1613 void GuiView::disconnectBuffer()
1615 if (d.current_work_area_)
1616 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1620 void GuiView::connectBufferView(BufferView & bv)
1622 bv.setGuiDelegate(this);
1626 void GuiView::disconnectBufferView()
1628 if (d.current_work_area_)
1629 d.current_work_area_->bufferView().setGuiDelegate(0);
1633 void GuiView::errors(string const & error_type, bool from_master)
1635 BufferView const * const bv = currentBufferView();
1639 #if EXPORT_in_THREAD
1640 // We are called with from_master == false by default, so we
1641 // have to figure out whether that is the case or not.
1642 ErrorList & el = bv->buffer().errorList(error_type);
1644 el = bv->buffer().masterBuffer()->errorList(error_type);
1648 ErrorList const & el = from_master ?
1649 bv->buffer().masterBuffer()->errorList(error_type) :
1650 bv->buffer().errorList(error_type);
1656 string data = error_type;
1658 data = "from_master|" + error_type;
1659 showDialog("errorlist", data);
1663 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1665 d.toc_models_.updateItem(toqstr(type), dit);
1669 void GuiView::structureChanged()
1671 // FIXME: This is slightly expensive, though less than the tocBackend update
1672 // (#9880). This also resets the view in the Toc Widget (#6675).
1673 d.toc_models_.reset(documentBufferView());
1674 // Navigator needs more than a simple update in this case. It needs to be
1676 updateDialog("toc", "");
1680 void GuiView::updateDialog(string const & name, string const & data)
1682 if (!isDialogVisible(name))
1685 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1686 if (it == d.dialogs_.end())
1689 Dialog * const dialog = it->second.get();
1690 if (dialog->isVisibleView())
1691 dialog->initialiseParams(data);
1695 BufferView * GuiView::documentBufferView()
1697 return currentMainWorkArea()
1698 ? ¤tMainWorkArea()->bufferView()
1703 BufferView const * GuiView::documentBufferView() const
1705 return currentMainWorkArea()
1706 ? ¤tMainWorkArea()->bufferView()
1711 BufferView * GuiView::currentBufferView()
1713 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1717 BufferView const * GuiView::currentBufferView() const
1719 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1723 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1724 Buffer const * orig, Buffer * clone)
1726 bool const success = clone->autoSave();
1728 busyBuffers.remove(orig);
1730 ? _("Automatic save done.")
1731 : _("Automatic save failed!");
1735 void GuiView::autoSave()
1737 LYXERR(Debug::INFO, "Running autoSave()");
1739 Buffer * buffer = documentBufferView()
1740 ? &documentBufferView()->buffer() : 0;
1742 resetAutosaveTimers();
1746 GuiViewPrivate::busyBuffers.insert(buffer);
1747 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1748 buffer, buffer->cloneBufferOnly());
1749 d.autosave_watcher_.setFuture(f);
1750 resetAutosaveTimers();
1754 void GuiView::resetAutosaveTimers()
1757 d.autosave_timeout_.restart();
1761 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1764 Buffer * buf = currentBufferView()
1765 ? ¤tBufferView()->buffer() : 0;
1766 Buffer * doc_buffer = documentBufferView()
1767 ? &(documentBufferView()->buffer()) : 0;
1770 /* In LyX/Mac, when a dialog is open, the menus of the
1771 application can still be accessed without giving focus to
1772 the main window. In this case, we want to disable the menu
1773 entries that are buffer-related.
1774 This code must not be used on Linux and Windows, since it
1775 would disable buffer-related entries when hovering over the
1776 menu (see bug #9574).
1778 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1784 // Check whether we need a buffer
1785 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1786 // no, exit directly
1787 flag.message(from_utf8(N_("Command not allowed with"
1788 "out any document open")));
1789 flag.setEnabled(false);
1793 if (cmd.origin() == FuncRequest::TOC) {
1794 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1795 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1796 flag.setEnabled(false);
1800 switch(cmd.action()) {
1801 case LFUN_BUFFER_IMPORT:
1804 case LFUN_MASTER_BUFFER_UPDATE:
1805 case LFUN_MASTER_BUFFER_VIEW:
1807 && (doc_buffer->parent() != 0
1808 || doc_buffer->hasChildren())
1809 && !d.processing_thread_watcher_.isRunning();
1812 case LFUN_BUFFER_UPDATE:
1813 case LFUN_BUFFER_VIEW: {
1814 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1818 string format = to_utf8(cmd.argument());
1819 if (cmd.argument().empty())
1820 format = doc_buffer->params().getDefaultOutputFormat();
1821 enable = doc_buffer->params().isExportableFormat(format);
1825 case LFUN_BUFFER_RELOAD:
1826 enable = doc_buffer && !doc_buffer->isUnnamed()
1827 && doc_buffer->fileName().exists()
1828 && (!doc_buffer->isClean()
1829 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1832 case LFUN_BUFFER_CHILD_OPEN:
1833 enable = doc_buffer != 0;
1836 case LFUN_BUFFER_WRITE:
1837 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1840 //FIXME: This LFUN should be moved to GuiApplication.
1841 case LFUN_BUFFER_WRITE_ALL: {
1842 // We enable the command only if there are some modified buffers
1843 Buffer * first = theBufferList().first();
1848 // We cannot use a for loop as the buffer list is a cycle.
1850 if (!b->isClean()) {
1854 b = theBufferList().next(b);
1855 } while (b != first);
1859 case LFUN_BUFFER_WRITE_AS:
1860 case LFUN_BUFFER_EXPORT_AS:
1861 enable = doc_buffer != 0;
1864 case LFUN_BUFFER_CLOSE:
1865 case LFUN_VIEW_CLOSE:
1866 enable = doc_buffer != 0;
1869 case LFUN_BUFFER_CLOSE_ALL:
1870 enable = theBufferList().last() != theBufferList().first();
1873 case LFUN_VIEW_SPLIT:
1874 if (cmd.getArg(0) == "vertical")
1875 enable = doc_buffer && (d.splitter_->count() == 1 ||
1876 d.splitter_->orientation() == Qt::Vertical);
1878 enable = doc_buffer && (d.splitter_->count() == 1 ||
1879 d.splitter_->orientation() == Qt::Horizontal);
1882 case LFUN_TAB_GROUP_CLOSE:
1883 enable = d.tabWorkAreaCount() > 1;
1886 case LFUN_TOOLBAR_TOGGLE: {
1887 string const name = cmd.getArg(0);
1888 if (GuiToolbar * t = toolbar(name))
1889 flag.setOnOff(t->isVisible());
1892 docstring const msg =
1893 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1899 case LFUN_DROP_LAYOUTS_CHOICE:
1903 case LFUN_UI_TOGGLE:
1904 flag.setOnOff(isFullScreen());
1907 case LFUN_DIALOG_DISCONNECT_INSET:
1910 case LFUN_DIALOG_HIDE:
1911 // FIXME: should we check if the dialog is shown?
1914 case LFUN_DIALOG_TOGGLE:
1915 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1916 // fall through to set "enable"
1917 case LFUN_DIALOG_SHOW: {
1918 string const name = cmd.getArg(0);
1920 enable = name == "aboutlyx"
1921 || name == "file" //FIXME: should be removed.
1923 || name == "texinfo"
1924 || name == "progress"
1925 || name == "compare";
1926 else if (name == "character" || name == "symbols"
1927 || name == "mathdelimiter" || name == "mathmatrix") {
1928 if (!buf || buf->isReadonly())
1931 Cursor const & cur = currentBufferView()->cursor();
1932 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1935 else if (name == "latexlog")
1936 enable = FileName(doc_buffer->logName()).isReadableFile();
1937 else if (name == "spellchecker")
1938 enable = theSpellChecker()
1939 && !doc_buffer->isReadonly()
1940 && !doc_buffer->text().empty();
1941 else if (name == "vclog")
1942 enable = doc_buffer->lyxvc().inUse();
1946 case LFUN_DIALOG_UPDATE: {
1947 string const name = cmd.getArg(0);
1949 enable = name == "prefs";
1953 case LFUN_COMMAND_EXECUTE:
1955 case LFUN_MENU_OPEN:
1956 // Nothing to check.
1959 case LFUN_COMPLETION_INLINE:
1960 if (!d.current_work_area_
1961 || !d.current_work_area_->completer().inlinePossible(
1962 currentBufferView()->cursor()))
1966 case LFUN_COMPLETION_POPUP:
1967 if (!d.current_work_area_
1968 || !d.current_work_area_->completer().popupPossible(
1969 currentBufferView()->cursor()))
1974 if (!d.current_work_area_
1975 || !d.current_work_area_->completer().inlinePossible(
1976 currentBufferView()->cursor()))
1980 case LFUN_COMPLETION_ACCEPT:
1981 if (!d.current_work_area_
1982 || (!d.current_work_area_->completer().popupVisible()
1983 && !d.current_work_area_->completer().inlineVisible()
1984 && !d.current_work_area_->completer().completionAvailable()))
1988 case LFUN_COMPLETION_CANCEL:
1989 if (!d.current_work_area_
1990 || (!d.current_work_area_->completer().popupVisible()
1991 && !d.current_work_area_->completer().inlineVisible()))
1995 case LFUN_BUFFER_ZOOM_OUT:
1996 case LFUN_BUFFER_ZOOM_IN: {
1997 // only diff between these two is that the default for ZOOM_OUT
1999 bool const neg_zoom =
2000 convert<int>(cmd.argument()) < 0 ||
2001 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2002 if (lyxrc.zoom <= 10 && neg_zoom) {
2003 flag.message(_("Zoom level cannot be less than 10%."));
2006 enable = doc_buffer;
2009 case LFUN_BUFFER_MOVE_NEXT:
2010 case LFUN_BUFFER_MOVE_PREVIOUS:
2011 // we do not cycle when moving
2012 case LFUN_BUFFER_NEXT:
2013 case LFUN_BUFFER_PREVIOUS:
2014 // because we cycle, it doesn't matter whether on first or last
2015 enable = (d.currentTabWorkArea()->count() > 1);
2017 case LFUN_BUFFER_SWITCH:
2018 // toggle on the current buffer, but do not toggle off
2019 // the other ones (is that a good idea?)
2021 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2022 flag.setOnOff(true);
2025 case LFUN_VC_REGISTER:
2026 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2028 case LFUN_VC_RENAME:
2029 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2032 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2034 case LFUN_VC_CHECK_IN:
2035 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2037 case LFUN_VC_CHECK_OUT:
2038 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2040 case LFUN_VC_LOCKING_TOGGLE:
2041 enable = doc_buffer && !doc_buffer->isReadonly()
2042 && doc_buffer->lyxvc().lockingToggleEnabled();
2043 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2045 case LFUN_VC_REVERT:
2046 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2048 case LFUN_VC_UNDO_LAST:
2049 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2051 case LFUN_VC_REPO_UPDATE:
2052 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2054 case LFUN_VC_COMMAND: {
2055 if (cmd.argument().empty())
2057 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2061 case LFUN_VC_COMPARE:
2062 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2065 case LFUN_SERVER_GOTO_FILE_ROW:
2066 case LFUN_LYX_ACTIVATE:
2068 case LFUN_FORWARD_SEARCH:
2069 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2072 case LFUN_FILE_INSERT_PLAINTEXT:
2073 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2074 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2077 case LFUN_SPELLING_CONTINUOUSLY:
2078 flag.setOnOff(lyxrc.spellcheck_continuously);
2086 flag.setEnabled(false);
2092 static FileName selectTemplateFile()
2094 FileDialog dlg(qt_("Select template file"));
2095 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2096 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2098 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2099 QStringList(qt_("LyX Documents (*.lyx)")));
2101 if (result.first == FileDialog::Later)
2103 if (result.second.isEmpty())
2105 return FileName(fromqstr(result.second));
2109 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2113 Buffer * newBuffer = 0;
2115 newBuffer = checkAndLoadLyXFile(filename);
2116 } catch (ExceptionMessage const & e) {
2123 message(_("Document not loaded."));
2127 setBuffer(newBuffer);
2128 newBuffer->errors("Parse");
2131 theSession().lastFiles().add(filename);
2137 void GuiView::openDocument(string const & fname)
2139 string initpath = lyxrc.document_path;
2141 if (documentBufferView()) {
2142 string const trypath = documentBufferView()->buffer().filePath();
2143 // If directory is writeable, use this as default.
2144 if (FileName(trypath).isDirWritable())
2150 if (fname.empty()) {
2151 FileDialog dlg(qt_("Select document to open"));
2152 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2153 dlg.setButton2(qt_("Examples|#E#e"),
2154 toqstr(addPath(package().system_support().absFileName(), "examples")));
2156 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2157 FileDialog::Result result =
2158 dlg.open(toqstr(initpath), filter);
2160 if (result.first == FileDialog::Later)
2163 filename = fromqstr(result.second);
2165 // check selected filename
2166 if (filename.empty()) {
2167 message(_("Canceled."));
2173 // get absolute path of file and add ".lyx" to the filename if
2175 FileName const fullname =
2176 fileSearch(string(), filename, "lyx", support::may_not_exist);
2177 if (!fullname.empty())
2178 filename = fullname.absFileName();
2180 if (!fullname.onlyPath().isDirectory()) {
2181 Alert::warning(_("Invalid filename"),
2182 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2183 from_utf8(fullname.absFileName())));
2187 // if the file doesn't exist and isn't already open (bug 6645),
2188 // let the user create one
2189 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2190 !LyXVC::file_not_found_hook(fullname)) {
2191 // the user specifically chose this name. Believe him.
2192 Buffer * const b = newFile(filename, string(), true);
2198 docstring const disp_fn = makeDisplayPath(filename);
2199 message(bformat(_("Opening document %1$s..."), disp_fn));
2202 Buffer * buf = loadDocument(fullname);
2204 str2 = bformat(_("Document %1$s opened."), disp_fn);
2205 if (buf->lyxvc().inUse())
2206 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2207 " " + _("Version control detected.");
2209 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2214 // FIXME: clean that
2215 static bool import(GuiView * lv, FileName const & filename,
2216 string const & format, ErrorList & errorList)
2218 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2220 string loader_format;
2221 vector<string> loaders = theConverters().loaders();
2222 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2223 vector<string>::const_iterator it = loaders.begin();
2224 vector<string>::const_iterator en = loaders.end();
2225 for (; it != en; ++it) {
2226 if (!theConverters().isReachable(format, *it))
2229 string const tofile =
2230 support::changeExtension(filename.absFileName(),
2231 formats.extension(*it));
2232 if (!theConverters().convert(0, filename, FileName(tofile),
2233 filename, format, *it, errorList))
2235 loader_format = *it;
2238 if (loader_format.empty()) {
2239 frontend::Alert::error(_("Couldn't import file"),
2240 bformat(_("No information for importing the format %1$s."),
2241 formats.prettyName(format)));
2245 loader_format = format;
2247 if (loader_format == "lyx") {
2248 Buffer * buf = lv->loadDocument(lyxfile);
2252 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2256 bool as_paragraphs = loader_format == "textparagraph";
2257 string filename2 = (loader_format == format) ? filename.absFileName()
2258 : support::changeExtension(filename.absFileName(),
2259 formats.extension(loader_format));
2260 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2262 guiApp->setCurrentView(lv);
2263 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2270 void GuiView::importDocument(string const & argument)
2273 string filename = split(argument, format, ' ');
2275 LYXERR(Debug::INFO, format << " file: " << filename);
2277 // need user interaction
2278 if (filename.empty()) {
2279 string initpath = lyxrc.document_path;
2280 if (documentBufferView()) {
2281 string const trypath = documentBufferView()->buffer().filePath();
2282 // If directory is writeable, use this as default.
2283 if (FileName(trypath).isDirWritable())
2287 docstring const text = bformat(_("Select %1$s file to import"),
2288 formats.prettyName(format));
2290 FileDialog dlg(toqstr(text));
2291 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2292 dlg.setButton2(qt_("Examples|#E#e"),
2293 toqstr(addPath(package().system_support().absFileName(), "examples")));
2295 docstring filter = formats.prettyName(format);
2298 filter += from_utf8(formats.extensions(format));
2301 FileDialog::Result result =
2302 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2304 if (result.first == FileDialog::Later)
2307 filename = fromqstr(result.second);
2309 // check selected filename
2310 if (filename.empty())
2311 message(_("Canceled."));
2314 if (filename.empty())
2317 // get absolute path of file
2318 FileName const fullname(support::makeAbsPath(filename));
2320 // Can happen if the user entered a path into the dialog
2322 if (fullname.onlyFileName().empty()) {
2323 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2324 "Aborting import."),
2325 from_utf8(fullname.absFileName()));
2326 frontend::Alert::error(_("File name error"), msg);
2327 message(_("Canceled."));
2332 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2334 // Check if the document already is open
2335 Buffer * buf = theBufferList().getBuffer(lyxfile);
2338 if (!closeBuffer()) {
2339 message(_("Canceled."));
2344 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2346 // if the file exists already, and we didn't do
2347 // -i lyx thefile.lyx, warn
2348 if (lyxfile.exists() && fullname != lyxfile) {
2350 docstring text = bformat(_("The document %1$s already exists.\n\n"
2351 "Do you want to overwrite that document?"), displaypath);
2352 int const ret = Alert::prompt(_("Overwrite document?"),
2353 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2356 message(_("Canceled."));
2361 message(bformat(_("Importing %1$s..."), displaypath));
2362 ErrorList errorList;
2363 if (import(this, fullname, format, errorList))
2364 message(_("imported."));
2366 message(_("file not imported!"));
2368 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2372 void GuiView::newDocument(string const & filename, bool from_template)
2374 FileName initpath(lyxrc.document_path);
2375 if (documentBufferView()) {
2376 FileName const trypath(documentBufferView()->buffer().filePath());
2377 // If directory is writeable, use this as default.
2378 if (trypath.isDirWritable())
2382 string templatefile;
2383 if (from_template) {
2384 templatefile = selectTemplateFile().absFileName();
2385 if (templatefile.empty())
2390 if (filename.empty())
2391 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2393 b = newFile(filename, templatefile, true);
2398 // If no new document could be created, it is unsure
2399 // whether there is a valid BufferView.
2400 if (currentBufferView())
2401 // Ensure the cursor is correctly positioned on screen.
2402 currentBufferView()->showCursor();
2406 void GuiView::insertLyXFile(docstring const & fname)
2408 BufferView * bv = documentBufferView();
2413 FileName filename(to_utf8(fname));
2414 if (filename.empty()) {
2415 // Launch a file browser
2417 string initpath = lyxrc.document_path;
2418 string const trypath = bv->buffer().filePath();
2419 // If directory is writeable, use this as default.
2420 if (FileName(trypath).isDirWritable())
2424 FileDialog dlg(qt_("Select LyX document to insert"));
2425 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2426 dlg.setButton2(qt_("Examples|#E#e"),
2427 toqstr(addPath(package().system_support().absFileName(),
2430 FileDialog::Result result = dlg.open(toqstr(initpath),
2431 QStringList(qt_("LyX Documents (*.lyx)")));
2433 if (result.first == FileDialog::Later)
2437 filename.set(fromqstr(result.second));
2439 // check selected filename
2440 if (filename.empty()) {
2441 // emit message signal.
2442 message(_("Canceled."));
2447 bv->insertLyXFile(filename);
2448 bv->buffer().errors("Parse");
2452 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2454 FileName fname = b.fileName();
2455 FileName const oldname = fname;
2457 if (!newname.empty()) {
2459 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2461 // Switch to this Buffer.
2464 // No argument? Ask user through dialog.
2466 FileDialog dlg(qt_("Choose a filename to save document as"));
2467 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2468 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2470 if (!isLyXFileName(fname.absFileName()))
2471 fname.changeExtension(".lyx");
2473 FileDialog::Result result =
2474 dlg.save(toqstr(fname.onlyPath().absFileName()),
2475 QStringList(qt_("LyX Documents (*.lyx)")),
2476 toqstr(fname.onlyFileName()));
2478 if (result.first == FileDialog::Later)
2481 fname.set(fromqstr(result.second));
2486 if (!isLyXFileName(fname.absFileName()))
2487 fname.changeExtension(".lyx");
2490 // fname is now the new Buffer location.
2492 // if there is already a Buffer open with this name, we do not want
2493 // to have another one. (the second test makes sure we're not just
2494 // trying to overwrite ourselves, which is fine.)
2495 if (theBufferList().exists(fname) && fname != oldname
2496 && theBufferList().getBuffer(fname) != &b) {
2497 docstring const text =
2498 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2499 "Please close it before attempting to overwrite it.\n"
2500 "Do you want to choose a new filename?"),
2501 from_utf8(fname.absFileName()));
2502 int const ret = Alert::prompt(_("Chosen File Already Open"),
2503 text, 0, 1, _("&Rename"), _("&Cancel"));
2505 case 0: return renameBuffer(b, docstring(), kind);
2506 case 1: return false;
2511 bool const existsLocal = fname.exists();
2512 bool const existsInVC = LyXVC::fileInVC(fname);
2513 if (existsLocal || existsInVC) {
2514 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2515 if (kind != LV_WRITE_AS && existsInVC) {
2516 // renaming to a name that is already in VC
2518 docstring text = bformat(_("The document %1$s "
2519 "is already registered.\n\n"
2520 "Do you want to choose a new name?"),
2522 docstring const title = (kind == LV_VC_RENAME) ?
2523 _("Rename document?") : _("Copy document?");
2524 docstring const button = (kind == LV_VC_RENAME) ?
2525 _("&Rename") : _("&Copy");
2526 int const ret = Alert::prompt(title, text, 0, 1,
2527 button, _("&Cancel"));
2529 case 0: return renameBuffer(b, docstring(), kind);
2530 case 1: return false;
2535 docstring text = bformat(_("The document %1$s "
2536 "already exists.\n\n"
2537 "Do you want to overwrite that document?"),
2539 int const ret = Alert::prompt(_("Overwrite document?"),
2540 text, 0, 2, _("&Overwrite"),
2541 _("&Rename"), _("&Cancel"));
2544 case 1: return renameBuffer(b, docstring(), kind);
2545 case 2: return false;
2551 case LV_VC_RENAME: {
2552 string msg = b.lyxvc().rename(fname);
2555 message(from_utf8(msg));
2559 string msg = b.lyxvc().copy(fname);
2562 message(from_utf8(msg));
2568 // LyXVC created the file already in case of LV_VC_RENAME or
2569 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2570 // relative paths of included stuff right if we moved e.g. from
2571 // /a/b.lyx to /a/c/b.lyx.
2573 bool const saved = saveBuffer(b, fname);
2580 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2582 FileName fname = b.fileName();
2584 FileDialog dlg(qt_("Choose a filename to export the document as"));
2585 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2588 QString const anyformat = qt_("Guess from extension (*.*)");
2591 vector<Format const *> export_formats;
2592 for (Format const & f : formats)
2593 if (f.documentFormat())
2594 export_formats.push_back(&f);
2595 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2596 map<QString, string> fmap;
2599 for (Format const * f : export_formats) {
2600 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2601 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2603 from_ascii(f->extension())));
2604 types << loc_filter;
2605 fmap[loc_filter] = f->name();
2606 if (from_ascii(f->name()) == iformat) {
2607 filter = loc_filter;
2608 ext = f->extension();
2611 string ofname = fname.onlyFileName();
2613 ofname = support::changeExtension(ofname, ext);
2614 FileDialog::Result result =
2615 dlg.save(toqstr(fname.onlyPath().absFileName()),
2619 if (result.first != FileDialog::Chosen)
2623 fname.set(fromqstr(result.second));
2624 if (filter == anyformat)
2625 fmt_name = formats.getFormatFromExtension(fname.extension());
2627 fmt_name = fmap[filter];
2628 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2629 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2631 if (fmt_name.empty() || fname.empty())
2634 // fname is now the new Buffer location.
2635 if (FileName(fname).exists()) {
2636 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2637 docstring text = bformat(_("The document %1$s already "
2638 "exists.\n\nDo you want to "
2639 "overwrite that document?"),
2641 int const ret = Alert::prompt(_("Overwrite document?"),
2642 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2645 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2646 case 2: return false;
2650 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2653 return dr.dispatched();
2657 bool GuiView::saveBuffer(Buffer & b)
2659 return saveBuffer(b, FileName());
2663 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2665 if (workArea(b) && workArea(b)->inDialogMode())
2668 if (fn.empty() && b.isUnnamed())
2669 return renameBuffer(b, docstring());
2671 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2673 theSession().lastFiles().add(b.fileName());
2677 // Switch to this Buffer.
2680 // FIXME: we don't tell the user *WHY* the save failed !!
2681 docstring const file = makeDisplayPath(b.absFileName(), 30);
2682 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2683 "Do you want to rename the document and "
2684 "try again?"), file);
2685 int const ret = Alert::prompt(_("Rename and save?"),
2686 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2689 if (!renameBuffer(b, docstring()))
2698 return saveBuffer(b, fn);
2702 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2704 return closeWorkArea(wa, false);
2708 // We only want to close the buffer if it is not visible in other workareas
2709 // of the same view, nor in other views, and if this is not a child
2710 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2712 Buffer & buf = wa->bufferView().buffer();
2714 bool last_wa = d.countWorkAreasOf(buf) == 1
2715 && !inOtherView(buf) && !buf.parent();
2717 bool close_buffer = last_wa;
2720 if (lyxrc.close_buffer_with_last_view == "yes")
2722 else if (lyxrc.close_buffer_with_last_view == "no")
2723 close_buffer = false;
2726 if (buf.isUnnamed())
2727 file = from_utf8(buf.fileName().onlyFileName());
2729 file = buf.fileName().displayName(30);
2730 docstring const text = bformat(
2731 _("Last view on document %1$s is being closed.\n"
2732 "Would you like to close or hide the document?\n"
2734 "Hidden documents can be displayed back through\n"
2735 "the menu: View->Hidden->...\n"
2737 "To remove this question, set your preference in:\n"
2738 " Tools->Preferences->Look&Feel->UserInterface\n"
2740 int ret = Alert::prompt(_("Close or hide document?"),
2741 text, 0, 1, _("&Close"), _("&Hide"));
2742 close_buffer = (ret == 0);
2746 return closeWorkArea(wa, close_buffer);
2750 bool GuiView::closeBuffer()
2752 GuiWorkArea * wa = currentMainWorkArea();
2753 // coverity complained about this
2754 // it seems unnecessary, but perhaps is worth the check
2755 LASSERT(wa, return false);
2757 setCurrentWorkArea(wa);
2758 Buffer & buf = wa->bufferView().buffer();
2759 return closeWorkArea(wa, !buf.parent());
2763 void GuiView::writeSession() const {
2764 GuiWorkArea const * active_wa = currentMainWorkArea();
2765 for (int i = 0; i < d.splitter_->count(); ++i) {
2766 TabWorkArea * twa = d.tabWorkArea(i);
2767 for (int j = 0; j < twa->count(); ++j) {
2768 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2769 Buffer & buf = wa->bufferView().buffer();
2770 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2776 bool GuiView::closeBufferAll()
2778 // Close the workareas in all other views
2779 QList<int> const ids = guiApp->viewIds();
2780 for (int i = 0; i != ids.size(); ++i) {
2781 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2785 // Close our own workareas
2786 if (!closeWorkAreaAll())
2789 // Now close the hidden buffers. We prevent hidden buffers from being
2790 // dirty, so we can just close them.
2791 theBufferList().closeAll();
2796 bool GuiView::closeWorkAreaAll()
2798 setCurrentWorkArea(currentMainWorkArea());
2800 // We might be in a situation that there is still a tabWorkArea, but
2801 // there are no tabs anymore. This can happen when we get here after a
2802 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2803 // many TabWorkArea's have no documents anymore.
2806 // We have to call count() each time, because it can happen that
2807 // more than one splitter will disappear in one iteration (bug 5998).
2808 while (d.splitter_->count() > empty_twa) {
2809 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2811 if (twa->count() == 0)
2814 setCurrentWorkArea(twa->currentWorkArea());
2815 if (!closeTabWorkArea(twa))
2823 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2828 Buffer & buf = wa->bufferView().buffer();
2830 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2831 Alert::warning(_("Close document"),
2832 _("Document could not be closed because it is being processed by LyX."));
2837 return closeBuffer(buf);
2839 if (!inMultiTabs(wa))
2840 if (!saveBufferIfNeeded(buf, true))
2848 bool GuiView::closeBuffer(Buffer & buf)
2850 // If we are in a close_event all children will be closed in some time,
2851 // so no need to do it here. This will ensure that the children end up
2852 // in the session file in the correct order. If we close the master
2853 // buffer, we can close or release the child buffers here too.
2854 bool success = true;
2856 ListOfBuffers clist = buf.getChildren();
2857 ListOfBuffers::const_iterator it = clist.begin();
2858 ListOfBuffers::const_iterator const bend = clist.end();
2859 for (; it != bend; ++it) {
2860 Buffer * child_buf = *it;
2861 if (theBufferList().isOthersChild(&buf, child_buf)) {
2862 child_buf->setParent(0);
2866 // FIXME: should we look in other tabworkareas?
2867 // ANSWER: I don't think so. I've tested, and if the child is
2868 // open in some other window, it closes without a problem.
2869 GuiWorkArea * child_wa = workArea(*child_buf);
2871 success = closeWorkArea(child_wa, true);
2875 // In this case the child buffer is open but hidden.
2876 // It therefore should not (MUST NOT) be dirty!
2877 LATTEST(child_buf->isClean());
2878 theBufferList().release(child_buf);
2883 // goto bookmark to update bookmark pit.
2884 // FIXME: we should update only the bookmarks related to this buffer!
2885 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2886 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2887 guiApp->gotoBookmark(i+1, false, false);
2889 if (saveBufferIfNeeded(buf, false)) {
2890 buf.removeAutosaveFile();
2891 theBufferList().release(&buf);
2895 // open all children again to avoid a crash because of dangling
2896 // pointers (bug 6603)
2902 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2904 while (twa == d.currentTabWorkArea()) {
2905 twa->setCurrentIndex(twa->count() - 1);
2907 GuiWorkArea * wa = twa->currentWorkArea();
2908 Buffer & b = wa->bufferView().buffer();
2910 // We only want to close the buffer if the same buffer is not visible
2911 // in another view, and if this is not a child and if we are closing
2912 // a view (not a tabgroup).
2913 bool const close_buffer =
2914 !inOtherView(b) && !b.parent() && closing_;
2916 if (!closeWorkArea(wa, close_buffer))
2923 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2925 if (buf.isClean() || buf.paragraphs().empty())
2928 // Switch to this Buffer.
2933 if (buf.isUnnamed())
2934 file = from_utf8(buf.fileName().onlyFileName());
2936 file = buf.fileName().displayName(30);
2938 // Bring this window to top before asking questions.
2943 if (hiding && buf.isUnnamed()) {
2944 docstring const text = bformat(_("The document %1$s has not been "
2945 "saved yet.\n\nDo you want to save "
2946 "the document?"), file);
2947 ret = Alert::prompt(_("Save new document?"),
2948 text, 0, 1, _("&Save"), _("&Cancel"));
2952 docstring const text = bformat(_("The document %1$s has unsaved changes."
2953 "\n\nDo you want to save the document or discard the changes?"), file);
2954 ret = Alert::prompt(_("Save changed document?"),
2955 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2960 if (!saveBuffer(buf))
2964 // If we crash after this we could have no autosave file
2965 // but I guess this is really improbable (Jug).
2966 // Sometimes improbable things happen:
2967 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2968 // buf.removeAutosaveFile();
2970 // revert all changes
2981 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2983 Buffer & buf = wa->bufferView().buffer();
2985 for (int i = 0; i != d.splitter_->count(); ++i) {
2986 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2987 if (wa_ && wa_ != wa)
2990 return inOtherView(buf);
2994 bool GuiView::inOtherView(Buffer & buf)
2996 QList<int> const ids = guiApp->viewIds();
2998 for (int i = 0; i != ids.size(); ++i) {
3002 if (guiApp->view(ids[i]).workArea(buf))
3009 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3011 if (!documentBufferView())
3014 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3015 Buffer * const curbuf = &documentBufferView()->buffer();
3016 int nwa = twa->count();
3017 for (int i = 0; i < nwa; ++i) {
3018 if (&workArea(i)->bufferView().buffer() == curbuf) {
3020 if (np == NEXTBUFFER)
3021 next_index = (i == nwa - 1 ? 0 : i + 1);
3023 next_index = (i == 0 ? nwa - 1 : i - 1);
3025 twa->moveTab(i, next_index);
3027 setBuffer(&workArea(next_index)->bufferView().buffer());
3035 /// make sure the document is saved
3036 static bool ensureBufferClean(Buffer * buffer)
3038 LASSERT(buffer, return false);
3039 if (buffer->isClean() && !buffer->isUnnamed())
3042 docstring const file = buffer->fileName().displayName(30);
3045 if (!buffer->isUnnamed()) {
3046 text = bformat(_("The document %1$s has unsaved "
3047 "changes.\n\nDo you want to save "
3048 "the document?"), file);
3049 title = _("Save changed document?");
3052 text = bformat(_("The document %1$s has not been "
3053 "saved yet.\n\nDo you want to save "
3054 "the document?"), file);
3055 title = _("Save new document?");
3057 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3060 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3062 return buffer->isClean() && !buffer->isUnnamed();
3066 bool GuiView::reloadBuffer(Buffer & buf)
3068 Buffer::ReadStatus status = buf.reload();
3069 return status == Buffer::ReadSuccess;
3073 void GuiView::checkExternallyModifiedBuffers()
3075 BufferList::iterator bit = theBufferList().begin();
3076 BufferList::iterator const bend = theBufferList().end();
3077 for (; bit != bend; ++bit) {
3078 Buffer * buf = *bit;
3079 if (buf->fileName().exists()
3080 && buf->isExternallyModified(Buffer::checksum_method)) {
3081 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3082 " Reload now? Any local changes will be lost."),
3083 from_utf8(buf->absFileName()));
3084 int const ret = Alert::prompt(_("Reload externally changed document?"),
3085 text, 0, 1, _("&Reload"), _("&Cancel"));
3093 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3095 Buffer * buffer = documentBufferView()
3096 ? &(documentBufferView()->buffer()) : 0;
3098 switch (cmd.action()) {
3099 case LFUN_VC_REGISTER:
3100 if (!buffer || !ensureBufferClean(buffer))
3102 if (!buffer->lyxvc().inUse()) {
3103 if (buffer->lyxvc().registrer()) {
3104 reloadBuffer(*buffer);
3105 dr.clearMessageUpdate();
3110 case LFUN_VC_RENAME:
3111 case LFUN_VC_COPY: {
3112 if (!buffer || !ensureBufferClean(buffer))
3114 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3115 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3116 // Some changes are not yet committed.
3117 // We test here and not in getStatus(), since
3118 // this test is expensive.
3120 LyXVC::CommandResult ret =
3121 buffer->lyxvc().checkIn(log);
3123 if (ret == LyXVC::ErrorCommand ||
3124 ret == LyXVC::VCSuccess)
3125 reloadBuffer(*buffer);
3126 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3127 frontend::Alert::error(
3128 _("Revision control error."),
3129 _("Document could not be checked in."));
3133 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3134 LV_VC_RENAME : LV_VC_COPY;
3135 renameBuffer(*buffer, cmd.argument(), kind);
3140 case LFUN_VC_CHECK_IN:
3141 if (!buffer || !ensureBufferClean(buffer))
3143 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3145 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3147 // Only skip reloading if the checkin was cancelled or
3148 // an error occurred before the real checkin VCS command
3149 // was executed, since the VCS might have changed the
3150 // file even if it could not checkin successfully.
3151 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3152 reloadBuffer(*buffer);
3156 case LFUN_VC_CHECK_OUT:
3157 if (!buffer || !ensureBufferClean(buffer))
3159 if (buffer->lyxvc().inUse()) {
3160 dr.setMessage(buffer->lyxvc().checkOut());
3161 reloadBuffer(*buffer);
3165 case LFUN_VC_LOCKING_TOGGLE:
3166 LASSERT(buffer, return);
3167 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3169 if (buffer->lyxvc().inUse()) {
3170 string res = buffer->lyxvc().lockingToggle();
3172 frontend::Alert::error(_("Revision control error."),
3173 _("Error when setting the locking property."));
3176 reloadBuffer(*buffer);
3181 case LFUN_VC_REVERT:
3182 LASSERT(buffer, return);
3183 if (buffer->lyxvc().revert()) {
3184 reloadBuffer(*buffer);
3185 dr.clearMessageUpdate();
3189 case LFUN_VC_UNDO_LAST:
3190 LASSERT(buffer, return);
3191 buffer->lyxvc().undoLast();
3192 reloadBuffer(*buffer);
3193 dr.clearMessageUpdate();
3196 case LFUN_VC_REPO_UPDATE:
3197 LASSERT(buffer, return);
3198 if (ensureBufferClean(buffer)) {
3199 dr.setMessage(buffer->lyxvc().repoUpdate());
3200 checkExternallyModifiedBuffers();
3204 case LFUN_VC_COMMAND: {
3205 string flag = cmd.getArg(0);
3206 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3209 if (contains(flag, 'M')) {
3210 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3213 string path = cmd.getArg(1);
3214 if (contains(path, "$$p") && buffer)
3215 path = subst(path, "$$p", buffer->filePath());
3216 LYXERR(Debug::LYXVC, "Directory: " << path);
3218 if (!pp.isReadableDirectory()) {
3219 lyxerr << _("Directory is not accessible.") << endl;
3222 support::PathChanger p(pp);
3224 string command = cmd.getArg(2);
3225 if (command.empty())
3228 command = subst(command, "$$i", buffer->absFileName());
3229 command = subst(command, "$$p", buffer->filePath());
3231 command = subst(command, "$$m", to_utf8(message));
3232 LYXERR(Debug::LYXVC, "Command: " << command);
3234 one.startscript(Systemcall::Wait, command);
3238 if (contains(flag, 'I'))
3239 buffer->markDirty();
3240 if (contains(flag, 'R'))
3241 reloadBuffer(*buffer);
3246 case LFUN_VC_COMPARE: {
3247 if (cmd.argument().empty()) {
3248 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3252 string rev1 = cmd.getArg(0);
3256 // it seems safe to assume we have a buffer
3257 // coverity[FORWARD_NULL]
3258 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3261 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3262 f2 = buffer->absFileName();
3264 string rev2 = cmd.getArg(1);
3268 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3272 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3273 f1 << "\n" << f2 << "\n" );
3274 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3275 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3285 void GuiView::openChildDocument(string const & fname)
3287 LASSERT(documentBufferView(), return);
3288 Buffer & buffer = documentBufferView()->buffer();
3289 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3290 documentBufferView()->saveBookmark(false);
3292 if (theBufferList().exists(filename)) {
3293 child = theBufferList().getBuffer(filename);
3296 message(bformat(_("Opening child document %1$s..."),
3297 makeDisplayPath(filename.absFileName())));
3298 child = loadDocument(filename, false);
3300 // Set the parent name of the child document.
3301 // This makes insertion of citations and references in the child work,
3302 // when the target is in the parent or another child document.
3304 child->setParent(&buffer);
3308 bool GuiView::goToFileRow(string const & argument)
3312 size_t i = argument.find_last_of(' ');
3313 if (i != string::npos) {
3314 file_name = os::internal_path(trim(argument.substr(0, i)));
3315 istringstream is(argument.substr(i + 1));
3320 if (i == string::npos) {
3321 LYXERR0("Wrong argument: " << argument);
3325 string const abstmp = package().temp_dir().absFileName();
3326 string const realtmp = package().temp_dir().realPath();
3327 // We have to use os::path_prefix_is() here, instead of
3328 // simply prefixIs(), because the file name comes from
3329 // an external application and may need case adjustment.
3330 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3331 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3332 // Needed by inverse dvi search. If it is a file
3333 // in tmpdir, call the apropriated function.
3334 // If tmpdir is a symlink, we may have the real
3335 // path passed back, so we correct for that.
3336 if (!prefixIs(file_name, abstmp))
3337 file_name = subst(file_name, realtmp, abstmp);
3338 buf = theBufferList().getBufferFromTmp(file_name);
3340 // Must replace extension of the file to be .lyx
3341 // and get full path
3342 FileName const s = fileSearch(string(),
3343 support::changeExtension(file_name, ".lyx"), "lyx");
3344 // Either change buffer or load the file
3345 if (theBufferList().exists(s))
3346 buf = theBufferList().getBuffer(s);
3347 else if (s.exists()) {
3348 buf = loadDocument(s);
3353 _("File does not exist: %1$s"),
3354 makeDisplayPath(file_name)));
3360 _("No buffer for file: %1$s."),
3361 makeDisplayPath(file_name))
3366 documentBufferView()->setCursorFromRow(row);
3372 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3374 Buffer::ExportStatus const status = func(format);
3376 // the cloning operation will have produced a clone of the entire set of
3377 // documents, starting from the master. so we must delete those.
3378 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3380 busyBuffers.remove(orig);
3385 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3387 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3388 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3392 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3394 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3395 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3399 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3401 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3402 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3406 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3407 string const & argument,
3408 Buffer const * used_buffer,
3409 docstring const & msg,
3410 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3411 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3412 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3417 string format = argument;
3419 format = used_buffer->params().getDefaultOutputFormat();
3420 processing_format = format;
3422 progress_->clearMessages();
3425 #if EXPORT_in_THREAD
3426 GuiViewPrivate::busyBuffers.insert(used_buffer);
3427 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3428 if (!cloned_buffer) {
3429 Alert::error(_("Export Error"),
3430 _("Error cloning the Buffer."));
3433 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3438 setPreviewFuture(f);
3439 last_export_format = used_buffer->params().bufferFormat();
3442 // We are asynchronous, so we don't know here anything about the success
3445 Buffer::ExportStatus status;
3447 status = (used_buffer->*syncFunc)(format, true);
3448 } else if (previewFunc) {
3449 status = (used_buffer->*previewFunc)(format);
3452 handleExportStatus(gv_, status, format);
3454 return (status == Buffer::ExportSuccess
3455 || status == Buffer::PreviewSuccess);
3459 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3461 BufferView * bv = currentBufferView();
3462 LASSERT(bv, return);
3464 // Let the current BufferView dispatch its own actions.
3465 bv->dispatch(cmd, dr);
3466 if (dr.dispatched())
3469 // Try with the document BufferView dispatch if any.
3470 BufferView * doc_bv = documentBufferView();
3471 if (doc_bv && doc_bv != bv) {
3472 doc_bv->dispatch(cmd, dr);
3473 if (dr.dispatched())
3477 // Then let the current Cursor dispatch its own actions.
3478 bv->cursor().dispatch(cmd);
3480 // update completion. We do it here and not in
3481 // processKeySym to avoid another redraw just for a
3482 // changed inline completion
3483 if (cmd.origin() == FuncRequest::KEYBOARD) {
3484 if (cmd.action() == LFUN_SELF_INSERT
3485 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3486 updateCompletion(bv->cursor(), true, true);
3487 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3488 updateCompletion(bv->cursor(), false, true);
3490 updateCompletion(bv->cursor(), false, false);
3493 dr = bv->cursor().result();
3497 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3499 BufferView * bv = currentBufferView();
3500 // By default we won't need any update.
3501 dr.screenUpdate(Update::None);
3502 // assume cmd will be dispatched
3503 dr.dispatched(true);
3505 Buffer * doc_buffer = documentBufferView()
3506 ? &(documentBufferView()->buffer()) : 0;
3508 if (cmd.origin() == FuncRequest::TOC) {
3509 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3510 // FIXME: do we need to pass a DispatchResult object here?
3511 toc->doDispatch(bv->cursor(), cmd);
3515 string const argument = to_utf8(cmd.argument());
3517 switch(cmd.action()) {
3518 case LFUN_BUFFER_CHILD_OPEN:
3519 openChildDocument(to_utf8(cmd.argument()));
3522 case LFUN_BUFFER_IMPORT:
3523 importDocument(to_utf8(cmd.argument()));
3526 case LFUN_BUFFER_EXPORT: {
3529 // GCC only sees strfwd.h when building merged
3530 if (::lyx::operator==(cmd.argument(), "custom")) {
3531 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3535 string const dest = cmd.getArg(1);
3536 FileName target_dir;
3537 if (!dest.empty() && FileName::isAbsolute(dest))
3538 target_dir = FileName(support::onlyPath(dest));
3540 target_dir = doc_buffer->fileName().onlyPath();
3542 if ((dest.empty() && doc_buffer->isUnnamed())
3543 || !target_dir.isDirWritable()) {
3544 exportBufferAs(*doc_buffer, cmd.argument());
3547 /* TODO/Review: Is it a problem to also export the children?
3548 See the update_unincluded flag */
3549 d.asyncBufferProcessing(argument,
3552 &GuiViewPrivate::exportAndDestroy,
3555 // TODO Inform user about success
3559 case LFUN_BUFFER_EXPORT_AS: {
3560 LASSERT(doc_buffer, break);
3561 docstring f = cmd.argument();
3563 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3564 exportBufferAs(*doc_buffer, f);
3568 case LFUN_BUFFER_UPDATE: {
3569 d.asyncBufferProcessing(argument,
3572 &GuiViewPrivate::compileAndDestroy,
3577 case LFUN_BUFFER_VIEW: {
3578 d.asyncBufferProcessing(argument,
3580 _("Previewing ..."),
3581 &GuiViewPrivate::previewAndDestroy,
3586 case LFUN_MASTER_BUFFER_UPDATE: {
3587 d.asyncBufferProcessing(argument,
3588 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3590 &GuiViewPrivate::compileAndDestroy,
3595 case LFUN_MASTER_BUFFER_VIEW: {
3596 d.asyncBufferProcessing(argument,
3597 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3599 &GuiViewPrivate::previewAndDestroy,
3600 0, &Buffer::preview);
3603 case LFUN_BUFFER_SWITCH: {
3604 string const file_name = to_utf8(cmd.argument());
3605 if (!FileName::isAbsolute(file_name)) {
3607 dr.setMessage(_("Absolute filename expected."));
3611 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3614 dr.setMessage(_("Document not loaded"));
3618 // Do we open or switch to the buffer in this view ?
3619 if (workArea(*buffer)
3620 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3625 // Look for the buffer in other views
3626 QList<int> const ids = guiApp->viewIds();
3628 for (; i != ids.size(); ++i) {
3629 GuiView & gv = guiApp->view(ids[i]);
3630 if (gv.workArea(*buffer)) {
3632 gv.activateWindow();
3634 gv.setBuffer(buffer);
3639 // If necessary, open a new window as a last resort
3640 if (i == ids.size()) {
3641 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3647 case LFUN_BUFFER_NEXT:
3648 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3651 case LFUN_BUFFER_MOVE_NEXT:
3652 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3655 case LFUN_BUFFER_PREVIOUS:
3656 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3659 case LFUN_BUFFER_MOVE_PREVIOUS:
3660 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3663 case LFUN_COMMAND_EXECUTE: {
3664 command_execute_ = true;
3665 minibuffer_focus_ = true;
3668 case LFUN_DROP_LAYOUTS_CHOICE:
3669 d.layout_->showPopup();
3672 case LFUN_MENU_OPEN:
3673 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3674 menu->exec(QCursor::pos());
3677 case LFUN_FILE_INSERT:
3678 insertLyXFile(cmd.argument());
3681 case LFUN_FILE_INSERT_PLAINTEXT:
3682 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3683 string const fname = to_utf8(cmd.argument());
3684 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3685 dr.setMessage(_("Absolute filename expected."));
3689 FileName filename(fname);
3690 if (fname.empty()) {
3691 FileDialog dlg(qt_("Select file to insert"));
3693 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3694 QStringList(qt_("All Files (*)")));
3696 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3697 dr.setMessage(_("Canceled."));
3701 filename.set(fromqstr(result.second));
3705 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3706 bv->dispatch(new_cmd, dr);
3711 case LFUN_BUFFER_RELOAD: {
3712 LASSERT(doc_buffer, break);
3715 if (!doc_buffer->isClean()) {
3716 docstring const file =
3717 makeDisplayPath(doc_buffer->absFileName(), 20);
3718 docstring text = bformat(_("Any changes will be lost. "
3719 "Are you sure you want to revert to the saved version "
3720 "of the document %1$s?"), file);
3721 ret = Alert::prompt(_("Revert to saved document?"),
3722 text, 1, 1, _("&Revert"), _("&Cancel"));
3726 doc_buffer->markClean();
3727 reloadBuffer(*doc_buffer);
3728 dr.forceBufferUpdate();
3733 case LFUN_BUFFER_WRITE:
3734 LASSERT(doc_buffer, break);
3735 saveBuffer(*doc_buffer);
3738 case LFUN_BUFFER_WRITE_AS:
3739 LASSERT(doc_buffer, break);
3740 renameBuffer(*doc_buffer, cmd.argument());
3743 case LFUN_BUFFER_WRITE_ALL: {
3744 Buffer * first = theBufferList().first();
3747 message(_("Saving all documents..."));
3748 // We cannot use a for loop as the buffer list cycles.
3751 if (!b->isClean()) {
3753 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3755 b = theBufferList().next(b);
3756 } while (b != first);
3757 dr.setMessage(_("All documents saved."));
3761 case LFUN_BUFFER_CLOSE:
3765 case LFUN_BUFFER_CLOSE_ALL:
3769 case LFUN_TOOLBAR_TOGGLE: {
3770 string const name = cmd.getArg(0);
3771 if (GuiToolbar * t = toolbar(name))
3776 case LFUN_DIALOG_UPDATE: {
3777 string const name = to_utf8(cmd.argument());
3778 if (name == "prefs" || name == "document")
3779 updateDialog(name, string());
3780 else if (name == "paragraph")
3781 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3782 else if (currentBufferView()) {
3783 Inset * inset = currentBufferView()->editedInset(name);
3784 // Can only update a dialog connected to an existing inset
3786 // FIXME: get rid of this indirection; GuiView ask the inset
3787 // if he is kind enough to update itself...
3788 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3789 //FIXME: pass DispatchResult here?
3790 inset->dispatch(currentBufferView()->cursor(), fr);
3796 case LFUN_DIALOG_TOGGLE: {
3797 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3798 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3799 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3803 case LFUN_DIALOG_DISCONNECT_INSET:
3804 disconnectDialog(to_utf8(cmd.argument()));
3807 case LFUN_DIALOG_HIDE: {
3808 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3812 case LFUN_DIALOG_SHOW: {
3813 string const name = cmd.getArg(0);
3814 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3816 if (name == "character") {
3817 data = freefont2string();
3819 showDialog("character", data);
3820 } else if (name == "latexlog") {
3821 Buffer::LogType type;
3822 string const logfile = doc_buffer->logName(&type);
3824 case Buffer::latexlog:
3827 case Buffer::buildlog:
3831 data += Lexer::quoteString(logfile);
3832 showDialog("log", data);
3833 } else if (name == "vclog") {
3834 string const data = "vc " +
3835 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3836 showDialog("log", data);
3837 } else if (name == "symbols") {
3838 data = bv->cursor().getEncoding()->name();
3840 showDialog("symbols", data);
3842 } else if (name == "prefs" && isFullScreen()) {
3843 lfunUiToggle("fullscreen");
3844 showDialog("prefs", data);
3846 showDialog(name, data);
3851 dr.setMessage(cmd.argument());
3854 case LFUN_UI_TOGGLE: {
3855 string arg = cmd.getArg(0);
3856 if (!lfunUiToggle(arg)) {
3857 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3858 dr.setMessage(bformat(msg, from_utf8(arg)));
3860 // Make sure the keyboard focus stays in the work area.
3865 case LFUN_VIEW_SPLIT: {
3866 LASSERT(doc_buffer, break);
3867 string const orientation = cmd.getArg(0);
3868 d.splitter_->setOrientation(orientation == "vertical"
3869 ? Qt::Vertical : Qt::Horizontal);
3870 TabWorkArea * twa = addTabWorkArea();
3871 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3872 setCurrentWorkArea(wa);
3875 case LFUN_TAB_GROUP_CLOSE:
3876 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3877 closeTabWorkArea(twa);
3878 d.current_work_area_ = 0;
3879 twa = d.currentTabWorkArea();
3880 // Switch to the next GuiWorkArea in the found TabWorkArea.
3882 // Make sure the work area is up to date.
3883 setCurrentWorkArea(twa->currentWorkArea());
3885 setCurrentWorkArea(0);
3890 case LFUN_VIEW_CLOSE:
3891 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3892 closeWorkArea(twa->currentWorkArea());
3893 d.current_work_area_ = 0;
3894 twa = d.currentTabWorkArea();
3895 // Switch to the next GuiWorkArea in the found TabWorkArea.
3897 // Make sure the work area is up to date.
3898 setCurrentWorkArea(twa->currentWorkArea());
3900 setCurrentWorkArea(0);
3905 case LFUN_COMPLETION_INLINE:
3906 if (d.current_work_area_)
3907 d.current_work_area_->completer().showInline();
3910 case LFUN_COMPLETION_POPUP:
3911 if (d.current_work_area_)
3912 d.current_work_area_->completer().showPopup();
3917 if (d.current_work_area_)
3918 d.current_work_area_->completer().tab();
3921 case LFUN_COMPLETION_CANCEL:
3922 if (d.current_work_area_) {
3923 if (d.current_work_area_->completer().popupVisible())
3924 d.current_work_area_->completer().hidePopup();
3926 d.current_work_area_->completer().hideInline();
3930 case LFUN_COMPLETION_ACCEPT:
3931 if (d.current_work_area_)
3932 d.current_work_area_->completer().activate();
3935 case LFUN_BUFFER_ZOOM_IN:
3936 case LFUN_BUFFER_ZOOM_OUT: {
3937 // use a signed temp to avoid overflow
3938 int zoom = lyxrc.zoom;
3939 if (cmd.argument().empty()) {
3940 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3945 zoom += convert<int>(cmd.argument());
3951 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3953 // The global QPixmapCache is used in GuiPainter to cache text
3954 // painting so we must reset it.
3955 QPixmapCache::clear();
3956 guiApp->fontLoader().update();
3957 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3961 case LFUN_VC_REGISTER:
3962 case LFUN_VC_RENAME:
3964 case LFUN_VC_CHECK_IN:
3965 case LFUN_VC_CHECK_OUT:
3966 case LFUN_VC_REPO_UPDATE:
3967 case LFUN_VC_LOCKING_TOGGLE:
3968 case LFUN_VC_REVERT:
3969 case LFUN_VC_UNDO_LAST:
3970 case LFUN_VC_COMMAND:
3971 case LFUN_VC_COMPARE:
3972 dispatchVC(cmd, dr);
3975 case LFUN_SERVER_GOTO_FILE_ROW:
3976 goToFileRow(to_utf8(cmd.argument()));
3979 case LFUN_LYX_ACTIVATE:
3983 case LFUN_FORWARD_SEARCH: {
3984 // it seems safe to assume we have a document buffer, since
3985 // getStatus wants one.
3986 // coverity[FORWARD_NULL]
3987 Buffer const * doc_master = doc_buffer->masterBuffer();
3988 FileName const path(doc_master->temppath());
3989 string const texname = doc_master->isChild(doc_buffer)
3990 ? DocFileName(changeExtension(
3991 doc_buffer->absFileName(),
3992 "tex")).mangledFileName()
3993 : doc_buffer->latexName();
3994 string const fulltexname =
3995 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3996 string const mastername =
3997 removeExtension(doc_master->latexName());
3998 FileName const dviname(addName(path.absFileName(),
3999 addExtension(mastername, "dvi")));
4000 FileName const pdfname(addName(path.absFileName(),
4001 addExtension(mastername, "pdf")));
4002 bool const have_dvi = dviname.exists();
4003 bool const have_pdf = pdfname.exists();
4004 if (!have_dvi && !have_pdf) {
4005 dr.setMessage(_("Please, preview the document first."));
4008 string outname = dviname.onlyFileName();
4009 string command = lyxrc.forward_search_dvi;
4010 if (!have_dvi || (have_pdf &&
4011 pdfname.lastModified() > dviname.lastModified())) {
4012 outname = pdfname.onlyFileName();
4013 command = lyxrc.forward_search_pdf;
4016 DocIterator cur = bv->cursor();
4017 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4018 LYXERR(Debug::ACTION, "Forward search: row:" << row
4020 if (row == -1 || command.empty()) {
4021 dr.setMessage(_("Couldn't proceed."));
4024 string texrow = convert<string>(row);
4026 command = subst(command, "$$n", texrow);
4027 command = subst(command, "$$f", fulltexname);
4028 command = subst(command, "$$t", texname);
4029 command = subst(command, "$$o", outname);
4031 PathChanger p(path);
4033 one.startscript(Systemcall::DontWait, command);
4037 case LFUN_SPELLING_CONTINUOUSLY:
4038 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4039 dr.screenUpdate(Update::Force | Update::FitCursor);
4043 // The LFUN must be for one of BufferView, Buffer or Cursor;
4045 dispatchToBufferView(cmd, dr);
4049 // Part of automatic menu appearance feature.
4050 if (isFullScreen()) {
4051 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4055 // Need to update bv because many LFUNs here might have destroyed it
4056 bv = currentBufferView();
4058 // Clear non-empty selections
4059 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4061 Cursor & cur = bv->cursor();
4062 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4063 cur.clearSelection();
4069 bool GuiView::lfunUiToggle(string const & ui_component)
4071 if (ui_component == "scrollbar") {
4072 // hide() is of no help
4073 if (d.current_work_area_->verticalScrollBarPolicy() ==
4074 Qt::ScrollBarAlwaysOff)
4076 d.current_work_area_->setVerticalScrollBarPolicy(
4077 Qt::ScrollBarAsNeeded);
4079 d.current_work_area_->setVerticalScrollBarPolicy(
4080 Qt::ScrollBarAlwaysOff);
4081 } else if (ui_component == "statusbar") {
4082 statusBar()->setVisible(!statusBar()->isVisible());
4083 } else if (ui_component == "menubar") {
4084 menuBar()->setVisible(!menuBar()->isVisible());
4086 if (ui_component == "frame") {
4088 getContentsMargins(&l, &t, &r, &b);
4089 //are the frames in default state?
4090 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4092 setContentsMargins(-2, -2, -2, -2);
4094 setContentsMargins(0, 0, 0, 0);
4097 if (ui_component == "fullscreen") {
4105 void GuiView::toggleFullScreen()
4107 if (isFullScreen()) {
4108 for (int i = 0; i != d.splitter_->count(); ++i)
4109 d.tabWorkArea(i)->setFullScreen(false);
4110 setContentsMargins(0, 0, 0, 0);
4111 setWindowState(windowState() ^ Qt::WindowFullScreen);
4114 statusBar()->show();
4117 hideDialogs("prefs", 0);
4118 for (int i = 0; i != d.splitter_->count(); ++i)
4119 d.tabWorkArea(i)->setFullScreen(true);
4120 setContentsMargins(-2, -2, -2, -2);
4122 setWindowState(windowState() ^ Qt::WindowFullScreen);
4123 if (lyxrc.full_screen_statusbar)
4124 statusBar()->hide();
4125 if (lyxrc.full_screen_menubar)
4127 if (lyxrc.full_screen_toolbars) {
4128 ToolbarMap::iterator end = d.toolbars_.end();
4129 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4134 // give dialogs like the TOC a chance to adapt
4139 Buffer const * GuiView::updateInset(Inset const * inset)
4144 Buffer const * inset_buffer = &(inset->buffer());
4146 for (int i = 0; i != d.splitter_->count(); ++i) {
4147 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4150 Buffer const * buffer = &(wa->bufferView().buffer());
4151 if (inset_buffer == buffer)
4152 wa->scheduleRedraw();
4154 return inset_buffer;
4158 void GuiView::restartCursor()
4160 /* When we move around, or type, it's nice to be able to see
4161 * the cursor immediately after the keypress.
4163 if (d.current_work_area_)
4164 d.current_work_area_->startBlinkingCursor();
4166 // Take this occasion to update the other GUI elements.
4172 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4174 if (d.current_work_area_)
4175 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4180 // This list should be kept in sync with the list of insets in
4181 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4182 // dialog should have the same name as the inset.
4183 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4184 // docs in LyXAction.cpp.
4186 char const * const dialognames[] = {
4188 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4189 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4190 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4191 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4192 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4193 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4194 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4195 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4197 char const * const * const end_dialognames =
4198 dialognames + (sizeof(dialognames) / sizeof(char *));
4202 cmpCStr(char const * name) : name_(name) {}
4203 bool operator()(char const * other) {
4204 return strcmp(other, name_) == 0;
4211 bool isValidName(string const & name)
4213 return find_if(dialognames, end_dialognames,
4214 cmpCStr(name.c_str())) != end_dialognames;
4220 void GuiView::resetDialogs()
4222 // Make sure that no LFUN uses any GuiView.
4223 guiApp->setCurrentView(0);
4227 constructToolbars();
4228 guiApp->menus().fillMenuBar(menuBar(), this, false);
4229 d.layout_->updateContents(true);
4230 // Now update controls with current buffer.
4231 guiApp->setCurrentView(this);
4237 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4239 if (!isValidName(name))
4242 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4244 if (it != d.dialogs_.end()) {
4246 it->second->hideView();
4247 return it->second.get();
4250 Dialog * dialog = build(name);
4251 d.dialogs_[name].reset(dialog);
4252 if (lyxrc.allow_geometry_session)
4253 dialog->restoreSession();
4260 void GuiView::showDialog(string const & name, string const & data,
4263 triggerShowDialog(toqstr(name), toqstr(data), inset);
4267 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4273 const string name = fromqstr(qname);
4274 const string data = fromqstr(qdata);
4278 Dialog * dialog = findOrBuild(name, false);
4280 bool const visible = dialog->isVisibleView();
4281 dialog->showData(data);
4282 if (inset && currentBufferView())
4283 currentBufferView()->editInset(name, inset);
4284 // We only set the focus to the new dialog if it was not yet
4285 // visible in order not to change the existing previous behaviour
4287 // activateWindow is needed for floating dockviews
4288 dialog->asQWidget()->raise();
4289 dialog->asQWidget()->activateWindow();
4290 dialog->asQWidget()->setFocus();
4294 catch (ExceptionMessage const & ex) {
4302 bool GuiView::isDialogVisible(string const & name) const
4304 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4305 if (it == d.dialogs_.end())
4307 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4311 void GuiView::hideDialog(string const & name, Inset * inset)
4313 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4314 if (it == d.dialogs_.end())
4318 if (!currentBufferView())
4320 if (inset != currentBufferView()->editedInset(name))
4324 Dialog * const dialog = it->second.get();
4325 if (dialog->isVisibleView())
4327 if (currentBufferView())
4328 currentBufferView()->editInset(name, 0);
4332 void GuiView::disconnectDialog(string const & name)
4334 if (!isValidName(name))
4336 if (currentBufferView())
4337 currentBufferView()->editInset(name, 0);
4341 void GuiView::hideAll() const
4343 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4344 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4346 for(; it != end; ++it)
4347 it->second->hideView();
4351 void GuiView::updateDialogs()
4353 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4354 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4356 for(; it != end; ++it) {
4357 Dialog * dialog = it->second.get();
4359 if (dialog->needBufferOpen() && !documentBufferView())
4360 hideDialog(fromqstr(dialog->name()), 0);
4361 else if (dialog->isVisibleView())
4362 dialog->checkStatus();
4369 Dialog * createDialog(GuiView & lv, string const & name);
4371 // will be replaced by a proper factory...
4372 Dialog * createGuiAbout(GuiView & lv);
4373 Dialog * createGuiBibtex(GuiView & lv);
4374 Dialog * createGuiChanges(GuiView & lv);
4375 Dialog * createGuiCharacter(GuiView & lv);
4376 Dialog * createGuiCitation(GuiView & lv);
4377 Dialog * createGuiCompare(GuiView & lv);
4378 Dialog * createGuiCompareHistory(GuiView & lv);
4379 Dialog * createGuiDelimiter(GuiView & lv);
4380 Dialog * createGuiDocument(GuiView & lv);
4381 Dialog * createGuiErrorList(GuiView & lv);
4382 Dialog * createGuiExternal(GuiView & lv);
4383 Dialog * createGuiGraphics(GuiView & lv);
4384 Dialog * createGuiInclude(GuiView & lv);
4385 Dialog * createGuiIndex(GuiView & lv);
4386 Dialog * createGuiListings(GuiView & lv);
4387 Dialog * createGuiLog(GuiView & lv);
4388 Dialog * createGuiMathMatrix(GuiView & lv);
4389 Dialog * createGuiNote(GuiView & lv);
4390 Dialog * createGuiParagraph(GuiView & lv);
4391 Dialog * createGuiPhantom(GuiView & lv);
4392 Dialog * createGuiPreferences(GuiView & lv);
4393 Dialog * createGuiPrint(GuiView & lv);
4394 Dialog * createGuiPrintindex(GuiView & lv);
4395 Dialog * createGuiRef(GuiView & lv);
4396 Dialog * createGuiSearch(GuiView & lv);
4397 Dialog * createGuiSearchAdv(GuiView & lv);
4398 Dialog * createGuiSendTo(GuiView & lv);
4399 Dialog * createGuiShowFile(GuiView & lv);
4400 Dialog * createGuiSpellchecker(GuiView & lv);
4401 Dialog * createGuiSymbols(GuiView & lv);
4402 Dialog * createGuiTabularCreate(GuiView & lv);
4403 Dialog * createGuiTexInfo(GuiView & lv);
4404 Dialog * createGuiToc(GuiView & lv);
4405 Dialog * createGuiThesaurus(GuiView & lv);
4406 Dialog * createGuiViewSource(GuiView & lv);
4407 Dialog * createGuiWrap(GuiView & lv);
4408 Dialog * createGuiProgressView(GuiView & lv);
4412 Dialog * GuiView::build(string const & name)
4414 LASSERT(isValidName(name), return 0);
4416 Dialog * dialog = createDialog(*this, name);
4420 if (name == "aboutlyx")
4421 return createGuiAbout(*this);
4422 if (name == "bibtex")
4423 return createGuiBibtex(*this);
4424 if (name == "changes")
4425 return createGuiChanges(*this);
4426 if (name == "character")
4427 return createGuiCharacter(*this);
4428 if (name == "citation")
4429 return createGuiCitation(*this);
4430 if (name == "compare")
4431 return createGuiCompare(*this);
4432 if (name == "comparehistory")
4433 return createGuiCompareHistory(*this);
4434 if (name == "document")
4435 return createGuiDocument(*this);
4436 if (name == "errorlist")
4437 return createGuiErrorList(*this);
4438 if (name == "external")
4439 return createGuiExternal(*this);
4441 return createGuiShowFile(*this);
4442 if (name == "findreplace")
4443 return createGuiSearch(*this);
4444 if (name == "findreplaceadv")
4445 return createGuiSearchAdv(*this);
4446 if (name == "graphics")
4447 return createGuiGraphics(*this);
4448 if (name == "include")
4449 return createGuiInclude(*this);
4450 if (name == "index")
4451 return createGuiIndex(*this);
4452 if (name == "index_print")
4453 return createGuiPrintindex(*this);
4454 if (name == "listings")
4455 return createGuiListings(*this);
4457 return createGuiLog(*this);
4458 if (name == "mathdelimiter")
4459 return createGuiDelimiter(*this);
4460 if (name == "mathmatrix")
4461 return createGuiMathMatrix(*this);
4463 return createGuiNote(*this);
4464 if (name == "paragraph")
4465 return createGuiParagraph(*this);
4466 if (name == "phantom")
4467 return createGuiPhantom(*this);
4468 if (name == "prefs")
4469 return createGuiPreferences(*this);
4471 return createGuiRef(*this);
4472 if (name == "sendto")
4473 return createGuiSendTo(*this);
4474 if (name == "spellchecker")
4475 return createGuiSpellchecker(*this);
4476 if (name == "symbols")
4477 return createGuiSymbols(*this);
4478 if (name == "tabularcreate")
4479 return createGuiTabularCreate(*this);
4480 if (name == "texinfo")
4481 return createGuiTexInfo(*this);
4482 if (name == "thesaurus")
4483 return createGuiThesaurus(*this);
4485 return createGuiToc(*this);
4486 if (name == "view-source")
4487 return createGuiViewSource(*this);
4489 return createGuiWrap(*this);
4490 if (name == "progress")
4491 return createGuiProgressView(*this);
4497 } // namespace frontend
4500 #include "moc_GuiView.cpp"