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 struct GuiView::GuiViewPrivate
274 GuiViewPrivate(GuiView * gv)
275 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
276 layout_(0), autosave_timeout_(5000),
279 // hardcode here the platform specific icon size
280 smallIconSize = 16; // scaling problems
281 normalIconSize = 20; // ok, default if iconsize.png is missing
282 bigIconSize = 26; // better for some math icons
283 hugeIconSize = 32; // better for hires displays
286 // if it exists, use width of iconsize.png as normal size
287 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
288 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290 QImage image(toqstr(fn.absFileName()));
291 if (image.width() < int(smallIconSize))
292 normalIconSize = smallIconSize;
293 else if (image.width() > int(giantIconSize))
294 normalIconSize = giantIconSize;
296 normalIconSize = image.width();
299 splitter_ = new QSplitter;
300 bg_widget_ = new BackgroundWidget(400, 250);
301 stack_widget_ = new QStackedWidget;
302 stack_widget_->addWidget(bg_widget_);
303 stack_widget_->addWidget(splitter_);
306 // TODO cleanup, remove the singleton, handle multiple Windows?
307 progress_ = ProgressInterface::instance();
308 if (!dynamic_cast<GuiProgress*>(progress_)) {
309 progress_ = new GuiProgress; // TODO who deletes it
310 ProgressInterface::setInstance(progress_);
313 dynamic_cast<GuiProgress*>(progress_),
314 SIGNAL(updateStatusBarMessage(QString const&)),
315 gv, SLOT(updateStatusBarMessage(QString const&)));
317 dynamic_cast<GuiProgress*>(progress_),
318 SIGNAL(clearMessageText()),
319 gv, SLOT(clearMessageText()));
326 delete stack_widget_;
329 QMenu * toolBarPopup(GuiView * parent)
331 // FIXME: translation
332 QMenu * menu = new QMenu(parent);
333 QActionGroup * iconSizeGroup = new QActionGroup(parent);
335 QAction * smallIcons = new QAction(iconSizeGroup);
336 smallIcons->setText(qt_("Small-sized icons"));
337 smallIcons->setCheckable(true);
338 QObject::connect(smallIcons, SIGNAL(triggered()),
339 parent, SLOT(smallSizedIcons()));
340 menu->addAction(smallIcons);
342 QAction * normalIcons = new QAction(iconSizeGroup);
343 normalIcons->setText(qt_("Normal-sized icons"));
344 normalIcons->setCheckable(true);
345 QObject::connect(normalIcons, SIGNAL(triggered()),
346 parent, SLOT(normalSizedIcons()));
347 menu->addAction(normalIcons);
349 QAction * bigIcons = new QAction(iconSizeGroup);
350 bigIcons->setText(qt_("Big-sized icons"));
351 bigIcons->setCheckable(true);
352 QObject::connect(bigIcons, SIGNAL(triggered()),
353 parent, SLOT(bigSizedIcons()));
354 menu->addAction(bigIcons);
356 QAction * hugeIcons = new QAction(iconSizeGroup);
357 hugeIcons->setText(qt_("Huge-sized icons"));
358 hugeIcons->setCheckable(true);
359 QObject::connect(hugeIcons, SIGNAL(triggered()),
360 parent, SLOT(hugeSizedIcons()));
361 menu->addAction(hugeIcons);
363 QAction * giantIcons = new QAction(iconSizeGroup);
364 giantIcons->setText(qt_("Giant-sized icons"));
365 giantIcons->setCheckable(true);
366 QObject::connect(giantIcons, SIGNAL(triggered()),
367 parent, SLOT(giantSizedIcons()));
368 menu->addAction(giantIcons);
370 unsigned int cur = parent->iconSize().width();
371 if ( cur == parent->d.smallIconSize)
372 smallIcons->setChecked(true);
373 else if (cur == parent->d.normalIconSize)
374 normalIcons->setChecked(true);
375 else if (cur == parent->d.bigIconSize)
376 bigIcons->setChecked(true);
377 else if (cur == parent->d.hugeIconSize)
378 hugeIcons->setChecked(true);
379 else if (cur == parent->d.giantIconSize)
380 giantIcons->setChecked(true);
387 stack_widget_->setCurrentWidget(bg_widget_);
388 bg_widget_->setUpdatesEnabled(true);
389 bg_widget_->setFocus();
392 int tabWorkAreaCount()
394 return splitter_->count();
397 TabWorkArea * tabWorkArea(int i)
399 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
402 TabWorkArea * currentTabWorkArea()
404 int areas = tabWorkAreaCount();
406 // The first TabWorkArea is always the first one, if any.
407 return tabWorkArea(0);
409 for (int i = 0; i != areas; ++i) {
410 TabWorkArea * twa = tabWorkArea(i);
411 if (current_main_work_area_ == twa->currentWorkArea())
415 // None has the focus so we just take the first one.
416 return tabWorkArea(0);
419 int countWorkAreasOf(Buffer & buf)
421 int areas = tabWorkAreaCount();
423 for (int i = 0; i != areas; ++i) {
424 TabWorkArea * twa = tabWorkArea(i);
425 if (twa->workArea(buf))
431 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
433 if (processing_thread_watcher_.isRunning()) {
434 // we prefer to cancel this preview in order to keep a snappy
438 processing_thread_watcher_.setFuture(f);
443 GuiWorkArea * current_work_area_;
444 GuiWorkArea * current_main_work_area_;
445 QSplitter * splitter_;
446 QStackedWidget * stack_widget_;
447 BackgroundWidget * bg_widget_;
449 ToolbarMap toolbars_;
450 ProgressInterface* progress_;
451 /// The main layout box.
453 * \warning Don't Delete! The layout box is actually owned by
454 * whichever toolbar contains it. All the GuiView class needs is a
455 * means of accessing it.
457 * FIXME: replace that with a proper model so that we are not limited
458 * to only one dialog.
463 map<string, DialogPtr> dialogs_;
465 unsigned int smallIconSize;
466 unsigned int normalIconSize;
467 unsigned int bigIconSize;
468 unsigned int hugeIconSize;
469 unsigned int giantIconSize;
471 QTimer statusbar_timer_;
472 /// auto-saving of buffers
473 Timeout autosave_timeout_;
474 /// flag against a race condition due to multiclicks, see bug #1119
478 TocModels toc_models_;
481 QFutureWatcher<docstring> autosave_watcher_;
482 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
484 string last_export_format;
485 string processing_format;
487 static QSet<Buffer const *> busyBuffers;
488 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
494 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
496 // TODO syncFunc/previewFunc: use bind
497 bool asyncBufferProcessing(string const & argument,
498 Buffer const * used_buffer,
499 docstring const & msg,
500 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
501 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
502 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
504 QVector<GuiWorkArea*> guiWorkAreas();
507 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
510 GuiView::GuiView(int id)
511 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
513 // GuiToolbars *must* be initialised before the menu bar.
514 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
517 // set ourself as the current view. This is needed for the menu bar
518 // filling, at least for the static special menu item on Mac. Otherwise
519 // they are greyed out.
520 guiApp->setCurrentView(this);
522 // Fill up the menu bar.
523 guiApp->menus().fillMenuBar(menuBar(), this, true);
525 setCentralWidget(d.stack_widget_);
527 // Start autosave timer
528 if (lyxrc.autosave) {
529 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
530 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
531 d.autosave_timeout_.start();
533 connect(&d.statusbar_timer_, SIGNAL(timeout()),
534 this, SLOT(clearMessage()));
536 // We don't want to keep the window in memory if it is closed.
537 setAttribute(Qt::WA_DeleteOnClose, true);
539 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
540 // QIcon::fromTheme was introduced in Qt 4.6
541 #if (QT_VERSION >= 0x040600)
542 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
543 // since the icon is provided in the application bundle. We use a themed
544 // version when available and use the bundled one as fallback.
545 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
547 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
551 resetWindowTitleAndIconText();
553 // use tabbed dock area for multiple docks
554 // (such as "source" and "messages")
555 setDockOptions(QMainWindow::ForceTabbedDocks);
558 setAcceptDrops(true);
560 // add busy indicator to statusbar
561 QLabel * busylabel = new QLabel(statusBar());
562 statusBar()->addPermanentWidget(busylabel);
563 search_mode mode = theGuiApp()->imageSearchMode();
564 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
565 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
566 busylabel->setMovie(busyanim);
570 connect(&d.processing_thread_watcher_, SIGNAL(started()),
571 busylabel, SLOT(show()));
572 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
573 busylabel, SLOT(hide()));
575 statusBar()->setSizeGripEnabled(true);
578 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
579 SLOT(autoSaveThreadFinished()));
581 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
582 SLOT(processingThreadStarted()));
583 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
584 SLOT(processingThreadFinished()));
586 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
587 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
589 // Forbid too small unresizable window because it can happen
590 // with some window manager under X11.
591 setMinimumSize(300, 200);
593 if (lyxrc.allow_geometry_session) {
594 // Now take care of session management.
599 // no session handling, default to a sane size.
600 setGeometry(50, 50, 690, 510);
603 // clear session data if any.
605 settings.remove("views");
615 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
617 QVector<GuiWorkArea*> areas;
618 for (int i = 0; i < tabWorkAreaCount(); i++) {
619 TabWorkArea* ta = tabWorkArea(i);
620 for (int u = 0; u < ta->count(); u++) {
621 areas << ta->workArea(u);
627 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
628 string const & format)
630 docstring const fmt = formats.prettyName(format);
633 case Buffer::ExportSuccess:
634 msg = bformat(_("Successful export to format: %1$s"), fmt);
636 case Buffer::ExportCancel:
637 msg = _("Document export cancelled.");
639 case Buffer::ExportError:
640 case Buffer::ExportNoPathToFormat:
641 case Buffer::ExportTexPathHasSpaces:
642 case Buffer::ExportConverterError:
643 msg = bformat(_("Error while exporting format: %1$s"), fmt);
645 case Buffer::PreviewSuccess:
646 msg = bformat(_("Successful preview of format: %1$s"), fmt);
648 case Buffer::PreviewError:
649 msg = bformat(_("Error while previewing format: %1$s"), fmt);
656 void GuiView::processingThreadStarted()
661 void GuiView::processingThreadFinished()
663 QFutureWatcher<Buffer::ExportStatus> const * watcher =
664 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
666 Buffer::ExportStatus const status = watcher->result();
667 handleExportStatus(this, status, d.processing_format);
670 BufferView const * const bv = currentBufferView();
671 if (bv && !bv->buffer().errorList("Export").empty()) {
675 errors(d.last_export_format);
679 void GuiView::autoSaveThreadFinished()
681 QFutureWatcher<docstring> const * watcher =
682 static_cast<QFutureWatcher<docstring> const *>(sender());
683 message(watcher->result());
688 void GuiView::saveLayout() const
691 settings.beginGroup("views");
692 settings.beginGroup(QString::number(id_));
693 #if defined(Q_WS_X11) || defined(QPA_XCB)
694 settings.setValue("pos", pos());
695 settings.setValue("size", size());
697 settings.setValue("geometry", saveGeometry());
699 settings.setValue("layout", saveState(0));
700 settings.setValue("icon_size", iconSize());
704 void GuiView::saveUISettings() const
706 // Save the toolbar private states
707 ToolbarMap::iterator end = d.toolbars_.end();
708 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
709 it->second->saveSession();
710 // Now take care of all other dialogs
711 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
712 for (; it!= d.dialogs_.end(); ++it)
713 it->second->saveSession();
717 bool GuiView::restoreLayout()
720 settings.beginGroup("views");
721 settings.beginGroup(QString::number(id_));
722 QString const icon_key = "icon_size";
723 if (!settings.contains(icon_key))
726 //code below is skipped when when ~/.config/LyX is (re)created
727 QSize icon_size = settings.value(icon_key).toSize();
728 // Check whether session size changed.
729 if (icon_size.width() != int(d.smallIconSize) &&
730 icon_size.width() != int(d.normalIconSize) &&
731 icon_size.width() != int(d.bigIconSize) &&
732 icon_size.width() != int(d.hugeIconSize) &&
733 icon_size.width() != int(d.giantIconSize)) {
734 icon_size.setWidth(d.normalIconSize);
735 icon_size.setHeight(d.normalIconSize);
737 setIconSize(icon_size);
739 #if defined(Q_WS_X11) || defined(QPA_XCB)
740 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
741 QSize size = settings.value("size", QSize(690, 510)).toSize();
745 // Work-around for bug #6034: the window ends up in an undetermined
746 // state when trying to restore a maximized window when it is
747 // already maximized.
748 if (!(windowState() & Qt::WindowMaximized))
749 if (!restoreGeometry(settings.value("geometry").toByteArray()))
750 setGeometry(50, 50, 690, 510);
752 // Make sure layout is correctly oriented.
753 setLayoutDirection(qApp->layoutDirection());
755 // Allow the toc and view-source dock widget to be restored if needed.
757 if ((dialog = findOrBuild("toc", true)))
758 // see bug 5082. At least setup title and enabled state.
759 // Visibility will be adjusted by restoreState below.
760 dialog->prepareView();
761 if ((dialog = findOrBuild("view-source", true)))
762 dialog->prepareView();
763 if ((dialog = findOrBuild("progress", true)))
764 dialog->prepareView();
766 if (!restoreState(settings.value("layout").toByteArray(), 0))
769 // init the toolbars that have not been restored
770 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
771 Toolbars::Infos::iterator end = guiApp->toolbars().end();
772 for (; cit != end; ++cit) {
773 GuiToolbar * tb = toolbar(cit->name);
774 if (tb && !tb->isRestored())
775 initToolbar(cit->name);
783 GuiToolbar * GuiView::toolbar(string const & name)
785 ToolbarMap::iterator it = d.toolbars_.find(name);
786 if (it != d.toolbars_.end())
789 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
794 void GuiView::constructToolbars()
796 ToolbarMap::iterator it = d.toolbars_.begin();
797 for (; it != d.toolbars_.end(); ++it)
801 // I don't like doing this here, but the standard toolbar
802 // destroys this object when it's destroyed itself (vfr)
803 d.layout_ = new LayoutBox(*this);
804 d.stack_widget_->addWidget(d.layout_);
805 d.layout_->move(0,0);
807 // extracts the toolbars from the backend
808 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
809 Toolbars::Infos::iterator end = guiApp->toolbars().end();
810 for (; cit != end; ++cit)
811 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
815 void GuiView::initToolbars()
817 // extracts the toolbars from the backend
818 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
819 Toolbars::Infos::iterator end = guiApp->toolbars().end();
820 for (; cit != end; ++cit)
821 initToolbar(cit->name);
825 void GuiView::initToolbar(string const & name)
827 GuiToolbar * tb = toolbar(name);
830 int const visibility = guiApp->toolbars().defaultVisibility(name);
831 bool newline = !(visibility & Toolbars::SAMEROW);
832 tb->setVisible(false);
833 tb->setVisibility(visibility);
835 if (visibility & Toolbars::TOP) {
837 addToolBarBreak(Qt::TopToolBarArea);
838 addToolBar(Qt::TopToolBarArea, tb);
841 if (visibility & Toolbars::BOTTOM) {
843 addToolBarBreak(Qt::BottomToolBarArea);
844 addToolBar(Qt::BottomToolBarArea, tb);
847 if (visibility & Toolbars::LEFT) {
849 addToolBarBreak(Qt::LeftToolBarArea);
850 addToolBar(Qt::LeftToolBarArea, tb);
853 if (visibility & Toolbars::RIGHT) {
855 addToolBarBreak(Qt::RightToolBarArea);
856 addToolBar(Qt::RightToolBarArea, tb);
859 if (visibility & Toolbars::ON)
860 tb->setVisible(true);
864 TocModels & GuiView::tocModels()
866 return d.toc_models_;
870 void GuiView::setFocus()
872 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
873 QMainWindow::setFocus();
877 bool GuiView::hasFocus() const
879 if (currentWorkArea())
880 return currentWorkArea()->hasFocus();
881 if (currentMainWorkArea())
882 return currentMainWorkArea()->hasFocus();
883 return d.bg_widget_->hasFocus();
887 void GuiView::focusInEvent(QFocusEvent * e)
889 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
890 QMainWindow::focusInEvent(e);
891 // Make sure guiApp points to the correct view.
892 guiApp->setCurrentView(this);
893 if (currentWorkArea())
894 currentWorkArea()->setFocus();
895 else if (currentMainWorkArea())
896 currentMainWorkArea()->setFocus();
898 d.bg_widget_->setFocus();
902 QMenu * GuiView::createPopupMenu()
904 return d.toolBarPopup(this);
908 void GuiView::showEvent(QShowEvent * e)
910 LYXERR(Debug::GUI, "Passed Geometry "
911 << size().height() << "x" << size().width()
912 << "+" << pos().x() << "+" << pos().y());
914 if (d.splitter_->count() == 0)
915 // No work area, switch to the background widget.
919 QMainWindow::showEvent(e);
923 bool GuiView::closeScheduled()
930 bool GuiView::prepareAllBuffersForLogout()
932 Buffer * first = theBufferList().first();
936 // First, iterate over all buffers and ask the users if unsaved
937 // changes should be saved.
938 // We cannot use a for loop as the buffer list cycles.
941 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
943 b = theBufferList().next(b);
944 } while (b != first);
946 // Next, save session state
947 // When a view/window was closed before without quitting LyX, there
948 // are already entries in the lastOpened list.
949 theSession().lastOpened().clear();
956 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
957 ** is responsibility of the container (e.g., dialog)
959 void GuiView::closeEvent(QCloseEvent * close_event)
961 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
963 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
964 Alert::warning(_("Exit LyX"),
965 _("LyX could not be closed because documents are being processed by LyX."));
966 close_event->setAccepted(false);
970 // If the user pressed the x (so we didn't call closeView
971 // programmatically), we want to clear all existing entries.
973 theSession().lastOpened().clear();
978 // it can happen that this event arrives without selecting the view,
979 // e.g. when clicking the close button on a background window.
981 if (!closeWorkAreaAll()) {
983 close_event->ignore();
987 // Make sure that nothing will use this to be closed View.
988 guiApp->unregisterView(this);
990 if (isFullScreen()) {
991 // Switch off fullscreen before closing.
996 // Make sure the timer time out will not trigger a statusbar update.
997 d.statusbar_timer_.stop();
999 // Saving fullscreen requires additional tweaks in the toolbar code.
1000 // It wouldn't also work under linux natively.
1001 if (lyxrc.allow_geometry_session) {
1006 close_event->accept();
1010 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1012 if (event->mimeData()->hasUrls())
1014 /// \todo Ask lyx-devel is this is enough:
1015 /// if (event->mimeData()->hasFormat("text/plain"))
1016 /// event->acceptProposedAction();
1020 void GuiView::dropEvent(QDropEvent * event)
1022 QList<QUrl> files = event->mimeData()->urls();
1023 if (files.isEmpty())
1026 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1027 for (int i = 0; i != files.size(); ++i) {
1028 string const file = os::internal_path(fromqstr(
1029 files.at(i).toLocalFile()));
1033 string const ext = support::getExtension(file);
1034 vector<const Format *> found_formats;
1036 // Find all formats that have the correct extension.
1037 vector<const Format *> const & import_formats
1038 = theConverters().importableFormats();
1039 vector<const Format *>::const_iterator it = import_formats.begin();
1040 for (; it != import_formats.end(); ++it)
1041 if ((*it)->hasExtension(ext))
1042 found_formats.push_back(*it);
1045 if (found_formats.size() >= 1) {
1046 if (found_formats.size() > 1) {
1047 //FIXME: show a dialog to choose the correct importable format
1048 LYXERR(Debug::FILES,
1049 "Multiple importable formats found, selecting first");
1051 string const arg = found_formats[0]->name() + " " + file;
1052 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1055 //FIXME: do we have to explicitly check whether it's a lyx file?
1056 LYXERR(Debug::FILES,
1057 "No formats found, trying to open it as a lyx file");
1058 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1060 // add the functions to the queue
1061 guiApp->addToFuncRequestQueue(cmd);
1064 // now process the collected functions. We perform the events
1065 // asynchronously. This prevents potential problems in case the
1066 // BufferView is closed within an event.
1067 guiApp->processFuncRequestQueueAsync();
1071 void GuiView::message(docstring const & str)
1073 if (ForkedProcess::iAmAChild())
1076 // call is moved to GUI-thread by GuiProgress
1077 d.progress_->appendMessage(toqstr(str));
1081 void GuiView::clearMessageText()
1083 message(docstring());
1087 void GuiView::updateStatusBarMessage(QString const & str)
1089 statusBar()->showMessage(str);
1090 d.statusbar_timer_.stop();
1091 d.statusbar_timer_.start(3000);
1095 void GuiView::smallSizedIcons()
1097 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1101 void GuiView::normalSizedIcons()
1103 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1107 void GuiView::bigSizedIcons()
1109 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1113 void GuiView::hugeSizedIcons()
1115 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1119 void GuiView::giantSizedIcons()
1121 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1125 void GuiView::clearMessage()
1127 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1128 // the hasFocus function mostly returns false, even if the focus is on
1129 // a workarea in this view.
1133 d.statusbar_timer_.stop();
1137 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1139 if (wa != d.current_work_area_
1140 || wa->bufferView().buffer().isInternal())
1142 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1143 setWindowIconText(wa->windowIconText());
1144 #if (QT_VERSION >= 0x040400)
1145 // Sets the path for the window: this is used by OSX to
1146 // allow a context click on the title bar showing a menu
1147 // with the path up to the file
1148 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1153 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1155 if (d.current_work_area_)
1156 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1157 this, SLOT(setBusy(bool)));
1159 disconnectBufferView();
1160 connectBufferView(wa->bufferView());
1161 connectBuffer(wa->bufferView().buffer());
1162 d.current_work_area_ = wa;
1163 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1164 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1165 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1166 updateWindowTitle(wa);
1170 // The document settings needs to be reinitialised.
1171 updateDialog("document", "");
1173 // Buffer-dependent dialogs must be updated. This is done here because
1174 // some dialogs require buffer()->text.
1179 void GuiView::on_lastWorkAreaRemoved()
1182 // We already are in a close event. Nothing more to do.
1185 if (d.splitter_->count() > 1)
1186 // We have a splitter so don't close anything.
1189 // Reset and updates the dialogs.
1190 d.toc_models_.reset(0);
1191 updateDialog("document", "");
1194 resetWindowTitleAndIconText();
1197 if (lyxrc.open_buffers_in_tabs)
1198 // Nothing more to do, the window should stay open.
1201 if (guiApp->viewIds().size() > 1) {
1207 // On Mac we also close the last window because the application stay
1208 // resident in memory. On other platforms we don't close the last
1209 // window because this would quit the application.
1215 void GuiView::updateStatusBar()
1217 // let the user see the explicit message
1218 if (d.statusbar_timer_.isActive())
1225 void GuiView::showMessage()
1229 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1230 if (msg.isEmpty()) {
1231 BufferView const * bv = currentBufferView();
1233 msg = toqstr(bv->cursor().currentState());
1235 msg = qt_("Welcome to LyX!");
1237 statusBar()->showMessage(msg);
1241 bool GuiView::event(QEvent * e)
1245 // Useful debug code:
1246 //case QEvent::ActivationChange:
1247 //case QEvent::WindowDeactivate:
1248 //case QEvent::Paint:
1249 //case QEvent::Enter:
1250 //case QEvent::Leave:
1251 //case QEvent::HoverEnter:
1252 //case QEvent::HoverLeave:
1253 //case QEvent::HoverMove:
1254 //case QEvent::StatusTip:
1255 //case QEvent::DragEnter:
1256 //case QEvent::DragLeave:
1257 //case QEvent::Drop:
1260 case QEvent::WindowActivate: {
1261 GuiView * old_view = guiApp->currentView();
1262 if (this == old_view) {
1264 return QMainWindow::event(e);
1266 if (old_view && old_view->currentBufferView()) {
1267 // save current selection to the selection buffer to allow
1268 // middle-button paste in this window.
1269 cap::saveSelection(old_view->currentBufferView()->cursor());
1271 guiApp->setCurrentView(this);
1272 if (d.current_work_area_) {
1273 BufferView & bv = d.current_work_area_->bufferView();
1274 connectBufferView(bv);
1275 connectBuffer(bv.buffer());
1276 // The document structure, name and dialogs might have
1277 // changed in another view.
1279 // The document settings needs to be reinitialised.
1280 updateDialog("document", "");
1283 resetWindowTitleAndIconText();
1286 return QMainWindow::event(e);
1289 case QEvent::ShortcutOverride: {
1291 if (isFullScreen() && menuBar()->isHidden()) {
1292 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1293 // FIXME: we should also try to detect special LyX shortcut such as
1294 // Alt-P and Alt-M. Right now there is a hack in
1295 // GuiWorkArea::processKeySym() that hides again the menubar for
1297 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1299 return QMainWindow::event(e);
1302 return QMainWindow::event(e);
1306 return QMainWindow::event(e);
1310 void GuiView::resetWindowTitleAndIconText()
1312 setWindowTitle(qt_("LyX"));
1313 setWindowIconText(qt_("LyX"));
1316 bool GuiView::focusNextPrevChild(bool /*next*/)
1323 bool GuiView::busy() const
1329 void GuiView::setBusy(bool busy)
1331 bool const busy_before = busy_ > 0;
1332 busy ? ++busy_ : --busy_;
1333 if ((busy_ > 0) == busy_before)
1334 // busy state didn't change
1338 QApplication::setOverrideCursor(Qt::WaitCursor);
1341 QApplication::restoreOverrideCursor();
1346 double GuiView::pixelRatio() const
1348 #if QT_VERSION >= 0x050000
1349 return devicePixelRatio();
1356 GuiWorkArea * GuiView::workArea(int index)
1358 if (TabWorkArea * twa = d.currentTabWorkArea())
1359 if (index < twa->count())
1360 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1365 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1367 if (currentWorkArea()
1368 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1369 return (GuiWorkArea *) currentWorkArea();
1370 if (TabWorkArea * twa = d.currentTabWorkArea())
1371 return twa->workArea(buffer);
1376 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1378 // Automatically create a TabWorkArea if there are none yet.
1379 TabWorkArea * tab_widget = d.splitter_->count()
1380 ? d.currentTabWorkArea() : addTabWorkArea();
1381 return tab_widget->addWorkArea(buffer, *this);
1385 TabWorkArea * GuiView::addTabWorkArea()
1387 TabWorkArea * twa = new TabWorkArea;
1388 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1389 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1390 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1391 this, SLOT(on_lastWorkAreaRemoved()));
1393 d.splitter_->addWidget(twa);
1394 d.stack_widget_->setCurrentWidget(d.splitter_);
1399 GuiWorkArea const * GuiView::currentWorkArea() const
1401 return d.current_work_area_;
1405 GuiWorkArea * GuiView::currentWorkArea()
1407 return d.current_work_area_;
1411 GuiWorkArea const * GuiView::currentMainWorkArea() const
1413 if (!d.currentTabWorkArea())
1415 return d.currentTabWorkArea()->currentWorkArea();
1419 GuiWorkArea * GuiView::currentMainWorkArea()
1421 if (!d.currentTabWorkArea())
1423 return d.currentTabWorkArea()->currentWorkArea();
1427 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1429 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1431 d.current_work_area_ = 0;
1436 // FIXME: I've no clue why this is here and why it accesses
1437 // theGuiApp()->currentView, which might be 0 (bug 6464).
1438 // See also 27525 (vfr).
1439 if (theGuiApp()->currentView() == this
1440 && theGuiApp()->currentView()->currentWorkArea() == wa)
1443 if (currentBufferView())
1444 cap::saveSelection(currentBufferView()->cursor());
1446 theGuiApp()->setCurrentView(this);
1447 d.current_work_area_ = wa;
1449 // We need to reset this now, because it will need to be
1450 // right if the tabWorkArea gets reset in the for loop. We
1451 // will change it back if we aren't in that case.
1452 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1453 d.current_main_work_area_ = wa;
1455 for (int i = 0; i != d.splitter_->count(); ++i) {
1456 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1457 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1458 << ", Current main wa: " << currentMainWorkArea());
1463 d.current_main_work_area_ = old_cmwa;
1465 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1466 on_currentWorkAreaChanged(wa);
1467 BufferView & bv = wa->bufferView();
1468 bv.cursor().fixIfBroken();
1470 wa->setUpdatesEnabled(true);
1471 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1475 void GuiView::removeWorkArea(GuiWorkArea * wa)
1477 LASSERT(wa, return);
1478 if (wa == d.current_work_area_) {
1480 disconnectBufferView();
1481 d.current_work_area_ = 0;
1482 d.current_main_work_area_ = 0;
1485 bool found_twa = false;
1486 for (int i = 0; i != d.splitter_->count(); ++i) {
1487 TabWorkArea * twa = d.tabWorkArea(i);
1488 if (twa->removeWorkArea(wa)) {
1489 // Found in this tab group, and deleted the GuiWorkArea.
1491 if (twa->count() != 0) {
1492 if (d.current_work_area_ == 0)
1493 // This means that we are closing the current GuiWorkArea, so
1494 // switch to the next GuiWorkArea in the found TabWorkArea.
1495 setCurrentWorkArea(twa->currentWorkArea());
1497 // No more WorkAreas in this tab group, so delete it.
1504 // It is not a tabbed work area (i.e., the search work area), so it
1505 // should be deleted by other means.
1506 LASSERT(found_twa, return);
1508 if (d.current_work_area_ == 0) {
1509 if (d.splitter_->count() != 0) {
1510 TabWorkArea * twa = d.currentTabWorkArea();
1511 setCurrentWorkArea(twa->currentWorkArea());
1513 // No more work areas, switch to the background widget.
1514 setCurrentWorkArea(0);
1520 LayoutBox * GuiView::getLayoutDialog() const
1526 void GuiView::updateLayoutList()
1529 d.layout_->updateContents(false);
1533 void GuiView::updateToolbars()
1535 ToolbarMap::iterator end = d.toolbars_.end();
1536 if (d.current_work_area_) {
1538 d.current_work_area_->bufferView().cursor().inMathed()
1539 && !d.current_work_area_->bufferView().cursor().inRegexped();
1541 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1543 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1544 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
1545 bool const mathmacrotemplate =
1546 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1548 lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
1550 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1551 it->second->update(math, table, review, mathmacrotemplate, ipa);
1553 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1554 it->second->update(false, false, false, false, false);
1558 void GuiView::setBuffer(Buffer * newBuffer)
1560 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1561 LASSERT(newBuffer, return);
1563 GuiWorkArea * wa = workArea(*newBuffer);
1566 newBuffer->masterBuffer()->updateBuffer();
1568 wa = addWorkArea(*newBuffer);
1569 // scroll to the position when the BufferView was last closed
1570 if (lyxrc.use_lastfilepos) {
1571 LastFilePosSection::FilePos filepos =
1572 theSession().lastFilePos().load(newBuffer->fileName());
1573 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1576 //Disconnect the old buffer...there's no new one.
1579 connectBuffer(*newBuffer);
1580 connectBufferView(wa->bufferView());
1581 setCurrentWorkArea(wa);
1585 void GuiView::connectBuffer(Buffer & buf)
1587 buf.setGuiDelegate(this);
1591 void GuiView::disconnectBuffer()
1593 if (d.current_work_area_)
1594 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1598 void GuiView::connectBufferView(BufferView & bv)
1600 bv.setGuiDelegate(this);
1604 void GuiView::disconnectBufferView()
1606 if (d.current_work_area_)
1607 d.current_work_area_->bufferView().setGuiDelegate(0);
1611 void GuiView::errors(string const & error_type, bool from_master)
1613 BufferView const * const bv = currentBufferView();
1617 #if EXPORT_in_THREAD
1618 // We are called with from_master == false by default, so we
1619 // have to figure out whether that is the case or not.
1620 ErrorList & el = bv->buffer().errorList(error_type);
1622 el = bv->buffer().masterBuffer()->errorList(error_type);
1626 ErrorList const & el = from_master ?
1627 bv->buffer().masterBuffer()->errorList(error_type) :
1628 bv->buffer().errorList(error_type);
1634 string data = error_type;
1636 data = "from_master|" + error_type;
1637 showDialog("errorlist", data);
1641 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1643 d.toc_models_.updateItem(toqstr(type), dit);
1647 void GuiView::structureChanged()
1649 d.toc_models_.reset(documentBufferView());
1650 // Navigator needs more than a simple update in this case. It needs to be
1652 updateDialog("toc", "");
1656 void GuiView::updateDialog(string const & name, string const & data)
1658 if (!isDialogVisible(name))
1661 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1662 if (it == d.dialogs_.end())
1665 Dialog * const dialog = it->second.get();
1666 if (dialog->isVisibleView())
1667 dialog->initialiseParams(data);
1671 BufferView * GuiView::documentBufferView()
1673 return currentMainWorkArea()
1674 ? ¤tMainWorkArea()->bufferView()
1679 BufferView const * GuiView::documentBufferView() const
1681 return currentMainWorkArea()
1682 ? ¤tMainWorkArea()->bufferView()
1687 BufferView * GuiView::currentBufferView()
1689 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1693 BufferView const * GuiView::currentBufferView() const
1695 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1699 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1700 Buffer const * orig, Buffer * clone)
1702 bool const success = clone->autoSave();
1704 busyBuffers.remove(orig);
1706 ? _("Automatic save done.")
1707 : _("Automatic save failed!");
1711 void GuiView::autoSave()
1713 LYXERR(Debug::INFO, "Running autoSave()");
1715 Buffer * buffer = documentBufferView()
1716 ? &documentBufferView()->buffer() : 0;
1718 resetAutosaveTimers();
1722 GuiViewPrivate::busyBuffers.insert(buffer);
1723 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1724 buffer, buffer->cloneBufferOnly());
1725 d.autosave_watcher_.setFuture(f);
1726 resetAutosaveTimers();
1730 void GuiView::resetAutosaveTimers()
1733 d.autosave_timeout_.restart();
1737 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1740 Buffer * buf = currentBufferView()
1741 ? ¤tBufferView()->buffer() : 0;
1742 Buffer * doc_buffer = documentBufferView()
1743 ? &(documentBufferView()->buffer()) : 0;
1745 /* In LyX/Mac, when a dialog is open, the menus of the
1746 application can still be accessed without giving focus to
1747 the main window. In this case, we want to disable the menu
1748 entries that are buffer-related.
1750 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1755 // Check whether we need a buffer
1756 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1757 // no, exit directly
1758 flag.message(from_utf8(N_("Command not allowed with"
1759 "out any document open")));
1760 flag.setEnabled(false);
1764 if (cmd.origin() == FuncRequest::TOC) {
1765 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1766 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1767 flag.setEnabled(false);
1771 switch(cmd.action()) {
1772 case LFUN_BUFFER_IMPORT:
1775 case LFUN_MASTER_BUFFER_UPDATE:
1776 case LFUN_MASTER_BUFFER_VIEW:
1778 && (doc_buffer->parent() != 0
1779 || doc_buffer->hasChildren())
1780 && !d.processing_thread_watcher_.isRunning();
1783 case LFUN_BUFFER_UPDATE:
1784 case LFUN_BUFFER_VIEW: {
1785 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1789 string format = to_utf8(cmd.argument());
1790 if (cmd.argument().empty())
1791 format = doc_buffer->params().getDefaultOutputFormat();
1792 enable = doc_buffer->params().isExportableFormat(format);
1796 case LFUN_BUFFER_RELOAD:
1797 enable = doc_buffer && !doc_buffer->isUnnamed()
1798 && doc_buffer->fileName().exists()
1799 && (!doc_buffer->isClean()
1800 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1803 case LFUN_BUFFER_CHILD_OPEN:
1804 enable = doc_buffer;
1807 case LFUN_BUFFER_WRITE:
1808 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1811 //FIXME: This LFUN should be moved to GuiApplication.
1812 case LFUN_BUFFER_WRITE_ALL: {
1813 // We enable the command only if there are some modified buffers
1814 Buffer * first = theBufferList().first();
1819 // We cannot use a for loop as the buffer list is a cycle.
1821 if (!b->isClean()) {
1825 b = theBufferList().next(b);
1826 } while (b != first);
1830 case LFUN_BUFFER_WRITE_AS:
1831 case LFUN_BUFFER_EXPORT_AS:
1832 enable = doc_buffer;
1835 case LFUN_BUFFER_CLOSE:
1836 case LFUN_VIEW_CLOSE:
1837 enable = doc_buffer;
1840 case LFUN_BUFFER_CLOSE_ALL:
1841 enable = theBufferList().last() != theBufferList().first();
1844 case LFUN_VIEW_SPLIT:
1845 if (cmd.getArg(0) == "vertical")
1846 enable = doc_buffer && (d.splitter_->count() == 1 ||
1847 d.splitter_->orientation() == Qt::Vertical);
1849 enable = doc_buffer && (d.splitter_->count() == 1 ||
1850 d.splitter_->orientation() == Qt::Horizontal);
1853 case LFUN_TAB_GROUP_CLOSE:
1854 enable = d.tabWorkAreaCount() > 1;
1857 case LFUN_TOOLBAR_TOGGLE: {
1858 string const name = cmd.getArg(0);
1859 if (GuiToolbar * t = toolbar(name))
1860 flag.setOnOff(t->isVisible());
1863 docstring const msg =
1864 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1870 case LFUN_DROP_LAYOUTS_CHOICE:
1874 case LFUN_UI_TOGGLE:
1875 flag.setOnOff(isFullScreen());
1878 case LFUN_DIALOG_DISCONNECT_INSET:
1881 case LFUN_DIALOG_HIDE:
1882 // FIXME: should we check if the dialog is shown?
1885 case LFUN_DIALOG_TOGGLE:
1886 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1887 // fall through to set "enable"
1888 case LFUN_DIALOG_SHOW: {
1889 string const name = cmd.getArg(0);
1891 enable = name == "aboutlyx"
1892 || name == "file" //FIXME: should be removed.
1894 || name == "texinfo"
1895 || name == "progress"
1896 || name == "compare";
1897 else if (name == "character" || name == "symbols"
1898 || name == "mathdelimiter" || name == "mathmatrix") {
1899 if (!buf || buf->isReadonly())
1902 Cursor const & cur = currentBufferView()->cursor();
1903 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1906 else if (name == "latexlog")
1907 enable = FileName(doc_buffer->logName()).isReadableFile();
1908 else if (name == "spellchecker")
1909 enable = theSpellChecker()
1910 && !doc_buffer->isReadonly()
1911 && !doc_buffer->text().empty();
1912 else if (name == "vclog")
1913 enable = doc_buffer->lyxvc().inUse();
1917 case LFUN_DIALOG_UPDATE: {
1918 string const name = cmd.getArg(0);
1920 enable = name == "prefs";
1924 case LFUN_COMMAND_EXECUTE:
1926 case LFUN_MENU_OPEN:
1927 // Nothing to check.
1930 case LFUN_COMPLETION_INLINE:
1931 if (!d.current_work_area_
1932 || !d.current_work_area_->completer().inlinePossible(
1933 currentBufferView()->cursor()))
1937 case LFUN_COMPLETION_POPUP:
1938 if (!d.current_work_area_
1939 || !d.current_work_area_->completer().popupPossible(
1940 currentBufferView()->cursor()))
1945 if (!d.current_work_area_
1946 || !d.current_work_area_->completer().inlinePossible(
1947 currentBufferView()->cursor()))
1951 case LFUN_COMPLETION_ACCEPT:
1952 if (!d.current_work_area_
1953 || (!d.current_work_area_->completer().popupVisible()
1954 && !d.current_work_area_->completer().inlineVisible()
1955 && !d.current_work_area_->completer().completionAvailable()))
1959 case LFUN_COMPLETION_CANCEL:
1960 if (!d.current_work_area_
1961 || (!d.current_work_area_->completer().popupVisible()
1962 && !d.current_work_area_->completer().inlineVisible()))
1966 case LFUN_BUFFER_ZOOM_OUT:
1967 enable = doc_buffer && lyxrc.zoom > 10;
1970 case LFUN_BUFFER_ZOOM_IN:
1971 enable = doc_buffer;
1974 case LFUN_BUFFER_MOVE_NEXT:
1975 case LFUN_BUFFER_MOVE_PREVIOUS:
1976 // we do not cycle when moving
1977 case LFUN_BUFFER_NEXT:
1978 case LFUN_BUFFER_PREVIOUS:
1979 // because we cycle, it doesn't matter whether on first or last
1980 enable = (d.currentTabWorkArea()->count() > 1);
1982 case LFUN_BUFFER_SWITCH:
1983 // toggle on the current buffer, but do not toggle off
1984 // the other ones (is that a good idea?)
1986 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1987 flag.setOnOff(true);
1990 case LFUN_VC_REGISTER:
1991 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1993 case LFUN_VC_RENAME:
1994 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
1997 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
1999 case LFUN_VC_CHECK_IN:
2000 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2002 case LFUN_VC_CHECK_OUT:
2003 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2005 case LFUN_VC_LOCKING_TOGGLE:
2006 enable = doc_buffer && !doc_buffer->isReadonly()
2007 && doc_buffer->lyxvc().lockingToggleEnabled();
2008 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2010 case LFUN_VC_REVERT:
2011 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2013 case LFUN_VC_UNDO_LAST:
2014 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2016 case LFUN_VC_REPO_UPDATE:
2017 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2019 case LFUN_VC_COMMAND: {
2020 if (cmd.argument().empty())
2022 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2026 case LFUN_VC_COMPARE:
2027 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2030 case LFUN_SERVER_GOTO_FILE_ROW:
2032 case LFUN_FORWARD_SEARCH:
2033 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2036 case LFUN_FILE_INSERT_PLAINTEXT:
2037 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2038 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2041 case LFUN_SPELLING_CONTINUOUSLY:
2042 flag.setOnOff(lyxrc.spellcheck_continuously);
2050 flag.setEnabled(false);
2056 static FileName selectTemplateFile()
2058 FileDialog dlg(qt_("Select template file"));
2059 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2060 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2062 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2063 QStringList(qt_("LyX Documents (*.lyx)")));
2065 if (result.first == FileDialog::Later)
2067 if (result.second.isEmpty())
2069 return FileName(fromqstr(result.second));
2073 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2077 Buffer * newBuffer = 0;
2079 newBuffer = checkAndLoadLyXFile(filename);
2080 } catch (ExceptionMessage const & e) {
2087 message(_("Document not loaded."));
2091 setBuffer(newBuffer);
2092 newBuffer->errors("Parse");
2095 theSession().lastFiles().add(filename);
2101 void GuiView::openDocument(string const & fname)
2103 string initpath = lyxrc.document_path;
2105 if (documentBufferView()) {
2106 string const trypath = documentBufferView()->buffer().filePath();
2107 // If directory is writeable, use this as default.
2108 if (FileName(trypath).isDirWritable())
2114 if (fname.empty()) {
2115 FileDialog dlg(qt_("Select document to open"));
2116 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2117 dlg.setButton2(qt_("Examples|#E#e"),
2118 toqstr(addPath(package().system_support().absFileName(), "examples")));
2120 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2121 FileDialog::Result result =
2122 dlg.open(toqstr(initpath), filter);
2124 if (result.first == FileDialog::Later)
2127 filename = fromqstr(result.second);
2129 // check selected filename
2130 if (filename.empty()) {
2131 message(_("Canceled."));
2137 // get absolute path of file and add ".lyx" to the filename if
2139 FileName const fullname =
2140 fileSearch(string(), filename, "lyx", support::may_not_exist);
2141 if (!fullname.empty())
2142 filename = fullname.absFileName();
2144 if (!fullname.onlyPath().isDirectory()) {
2145 Alert::warning(_("Invalid filename"),
2146 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2147 from_utf8(fullname.absFileName())));
2151 // if the file doesn't exist and isn't already open (bug 6645),
2152 // let the user create one
2153 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2154 !LyXVC::file_not_found_hook(fullname)) {
2155 // the user specifically chose this name. Believe him.
2156 Buffer * const b = newFile(filename, string(), true);
2162 docstring const disp_fn = makeDisplayPath(filename);
2163 message(bformat(_("Opening document %1$s..."), disp_fn));
2166 Buffer * buf = loadDocument(fullname);
2168 str2 = bformat(_("Document %1$s opened."), disp_fn);
2169 if (buf->lyxvc().inUse())
2170 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2171 " " + _("Version control detected.");
2173 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2178 // FIXME: clean that
2179 static bool import(GuiView * lv, FileName const & filename,
2180 string const & format, ErrorList & errorList)
2182 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2184 string loader_format;
2185 vector<string> loaders = theConverters().loaders();
2186 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2187 vector<string>::const_iterator it = loaders.begin();
2188 vector<string>::const_iterator en = loaders.end();
2189 for (; it != en; ++it) {
2190 if (!theConverters().isReachable(format, *it))
2193 string const tofile =
2194 support::changeExtension(filename.absFileName(),
2195 formats.extension(*it));
2196 if (!theConverters().convert(0, filename, FileName(tofile),
2197 filename, format, *it, errorList))
2199 loader_format = *it;
2202 if (loader_format.empty()) {
2203 frontend::Alert::error(_("Couldn't import file"),
2204 bformat(_("No information for importing the format %1$s."),
2205 formats.prettyName(format)));
2209 loader_format = format;
2211 if (loader_format == "lyx") {
2212 Buffer * buf = lv->loadDocument(lyxfile);
2216 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2220 bool as_paragraphs = loader_format == "textparagraph";
2221 string filename2 = (loader_format == format) ? filename.absFileName()
2222 : support::changeExtension(filename.absFileName(),
2223 formats.extension(loader_format));
2224 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2226 guiApp->setCurrentView(lv);
2227 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2234 void GuiView::importDocument(string const & argument)
2237 string filename = split(argument, format, ' ');
2239 LYXERR(Debug::INFO, format << " file: " << filename);
2241 // need user interaction
2242 if (filename.empty()) {
2243 string initpath = lyxrc.document_path;
2244 if (documentBufferView()) {
2245 string const trypath = documentBufferView()->buffer().filePath();
2246 // If directory is writeable, use this as default.
2247 if (FileName(trypath).isDirWritable())
2251 docstring const text = bformat(_("Select %1$s file to import"),
2252 formats.prettyName(format));
2254 FileDialog dlg(toqstr(text));
2255 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2256 dlg.setButton2(qt_("Examples|#E#e"),
2257 toqstr(addPath(package().system_support().absFileName(), "examples")));
2259 docstring filter = formats.prettyName(format);
2262 filter += from_utf8(formats.extensions(format));
2265 FileDialog::Result result =
2266 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2268 if (result.first == FileDialog::Later)
2271 filename = fromqstr(result.second);
2273 // check selected filename
2274 if (filename.empty())
2275 message(_("Canceled."));
2278 if (filename.empty())
2281 // get absolute path of file
2282 FileName const fullname(support::makeAbsPath(filename));
2284 // Can happen if the user entered a path into the dialog
2286 if (fullname.onlyFileName().empty()) {
2287 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2288 "Aborting import."),
2289 from_utf8(fullname.absFileName()));
2290 frontend::Alert::error(_("File name error"), msg);
2291 message(_("Canceled."));
2296 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2298 // Check if the document already is open
2299 Buffer * buf = theBufferList().getBuffer(lyxfile);
2302 if (!closeBuffer()) {
2303 message(_("Canceled."));
2308 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2310 // if the file exists already, and we didn't do
2311 // -i lyx thefile.lyx, warn
2312 if (lyxfile.exists() && fullname != lyxfile) {
2314 docstring text = bformat(_("The document %1$s already exists.\n\n"
2315 "Do you want to overwrite that document?"), displaypath);
2316 int const ret = Alert::prompt(_("Overwrite document?"),
2317 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2320 message(_("Canceled."));
2325 message(bformat(_("Importing %1$s..."), displaypath));
2326 ErrorList errorList;
2327 if (import(this, fullname, format, errorList))
2328 message(_("imported."));
2330 message(_("file not imported!"));
2332 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2336 void GuiView::newDocument(string const & filename, bool from_template)
2338 FileName initpath(lyxrc.document_path);
2339 if (documentBufferView()) {
2340 FileName const trypath(documentBufferView()->buffer().filePath());
2341 // If directory is writeable, use this as default.
2342 if (trypath.isDirWritable())
2346 string templatefile;
2347 if (from_template) {
2348 templatefile = selectTemplateFile().absFileName();
2349 if (templatefile.empty())
2354 if (filename.empty())
2355 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2357 b = newFile(filename, templatefile, true);
2362 // If no new document could be created, it is unsure
2363 // whether there is a valid BufferView.
2364 if (currentBufferView())
2365 // Ensure the cursor is correctly positioned on screen.
2366 currentBufferView()->showCursor();
2370 void GuiView::insertLyXFile(docstring const & fname)
2372 BufferView * bv = documentBufferView();
2377 FileName filename(to_utf8(fname));
2378 if (filename.empty()) {
2379 // Launch a file browser
2381 string initpath = lyxrc.document_path;
2382 string const trypath = bv->buffer().filePath();
2383 // If directory is writeable, use this as default.
2384 if (FileName(trypath).isDirWritable())
2388 FileDialog dlg(qt_("Select LyX document to insert"));
2389 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2390 dlg.setButton2(qt_("Examples|#E#e"),
2391 toqstr(addPath(package().system_support().absFileName(),
2394 FileDialog::Result result = dlg.open(toqstr(initpath),
2395 QStringList(qt_("LyX Documents (*.lyx)")));
2397 if (result.first == FileDialog::Later)
2401 filename.set(fromqstr(result.second));
2403 // check selected filename
2404 if (filename.empty()) {
2405 // emit message signal.
2406 message(_("Canceled."));
2411 bv->insertLyXFile(filename);
2412 bv->buffer().errors("Parse");
2416 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2418 FileName fname = b.fileName();
2419 FileName const oldname = fname;
2421 if (!newname.empty()) {
2423 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2425 // Switch to this Buffer.
2428 // No argument? Ask user through dialog.
2430 FileDialog dlg(qt_("Choose a filename to save document as"));
2431 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2432 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2434 if (!isLyXFileName(fname.absFileName()))
2435 fname.changeExtension(".lyx");
2437 FileDialog::Result result =
2438 dlg.save(toqstr(fname.onlyPath().absFileName()),
2439 QStringList(qt_("LyX Documents (*.lyx)")),
2440 toqstr(fname.onlyFileName()));
2442 if (result.first == FileDialog::Later)
2445 fname.set(fromqstr(result.second));
2450 if (!isLyXFileName(fname.absFileName()))
2451 fname.changeExtension(".lyx");
2454 // fname is now the new Buffer location.
2456 // if there is already a Buffer open with this name, we do not want
2457 // to have another one. (the second test makes sure we're not just
2458 // trying to overwrite ourselves, which is fine.)
2459 if (theBufferList().exists(fname) && fname != oldname
2460 && theBufferList().getBuffer(fname) != &b) {
2461 docstring const text =
2462 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2463 "Please close it before attempting to overwrite it.\n"
2464 "Do you want to choose a new filename?"),
2465 from_utf8(fname.absFileName()));
2466 int const ret = Alert::prompt(_("Chosen File Already Open"),
2467 text, 0, 1, _("&Rename"), _("&Cancel"));
2469 case 0: return renameBuffer(b, docstring(), kind);
2470 case 1: return false;
2475 bool const existsLocal = fname.exists();
2476 bool const existsInVC = LyXVC::fileInVC(fname);
2477 if (existsLocal || existsInVC) {
2478 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2479 if (kind != LV_WRITE_AS && existsInVC) {
2480 // renaming to a name that is already in VC
2482 docstring text = bformat(_("The document %1$s "
2483 "is already registered.\n\n"
2484 "Do you want to choose a new name?"),
2486 docstring const title = (kind == LV_VC_RENAME) ?
2487 _("Rename document?") : _("Copy document?");
2488 docstring const button = (kind == LV_VC_RENAME) ?
2489 _("&Rename") : _("&Copy");
2490 int const ret = Alert::prompt(title, text, 0, 1,
2491 button, _("&Cancel"));
2493 case 0: return renameBuffer(b, docstring(), kind);
2494 case 1: return false;
2499 docstring text = bformat(_("The document %1$s "
2500 "already exists.\n\n"
2501 "Do you want to overwrite that document?"),
2503 int const ret = Alert::prompt(_("Overwrite document?"),
2504 text, 0, 2, _("&Overwrite"),
2505 _("&Rename"), _("&Cancel"));
2508 case 1: return renameBuffer(b, docstring(), kind);
2509 case 2: return false;
2515 case LV_VC_RENAME: {
2516 string msg = b.lyxvc().rename(fname);
2519 message(from_utf8(msg));
2523 string msg = b.lyxvc().copy(fname);
2526 message(from_utf8(msg));
2532 // LyXVC created the file already in case of LV_VC_RENAME or
2533 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2534 // relative paths of included stuff right if we moved e.g. from
2535 // /a/b.lyx to /a/c/b.lyx.
2537 bool const saved = saveBuffer(b, fname);
2544 struct PrettyNameComparator
2546 bool operator()(Format const *first, Format const *second) const {
2547 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2548 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2553 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2555 FileName fname = b.fileName();
2557 FileDialog dlg(qt_("Choose a filename to export the document as"));
2558 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2561 QString const anyformat = qt_("Guess from extension (*.*)");
2563 Formats::const_iterator it = formats.begin();
2564 vector<Format const *> export_formats;
2565 for (; it != formats.end(); ++it)
2566 if (it->documentFormat())
2567 export_formats.push_back(&(*it));
2568 PrettyNameComparator cmp;
2569 sort(export_formats.begin(), export_formats.end(), cmp);
2570 vector<Format const *>::const_iterator fit = export_formats.begin();
2571 map<QString, string> fmap;
2574 for (; fit != export_formats.end(); ++fit) {
2575 docstring const loc_prettyname =
2576 translateIfPossible(from_utf8((*fit)->prettyname()));
2577 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2579 from_ascii((*fit)->extension())));
2580 types << loc_filter;
2581 fmap[loc_filter] = (*fit)->name();
2582 if (from_ascii((*fit)->name()) == iformat) {
2583 filter = loc_filter;
2584 ext = (*fit)->extension();
2587 string ofname = fname.onlyFileName();
2589 ofname = support::changeExtension(ofname, ext);
2590 FileDialog::Result result =
2591 dlg.save(toqstr(fname.onlyPath().absFileName()),
2595 if (result.first != FileDialog::Chosen)
2599 fname.set(fromqstr(result.second));
2600 if (filter == anyformat)
2601 fmt_name = formats.getFormatFromExtension(fname.extension());
2603 fmt_name = fmap[filter];
2604 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2605 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2607 if (fmt_name.empty() || fname.empty())
2610 // fname is now the new Buffer location.
2611 if (FileName(fname).exists()) {
2612 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2613 docstring text = bformat(_("The document %1$s already "
2614 "exists.\n\nDo you want to "
2615 "overwrite that document?"),
2617 int const ret = Alert::prompt(_("Overwrite document?"),
2618 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2621 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2622 case 2: return false;
2626 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2629 return dr.dispatched();
2633 bool GuiView::saveBuffer(Buffer & b)
2635 return saveBuffer(b, FileName());
2639 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2641 if (workArea(b) && workArea(b)->inDialogMode())
2644 if (fn.empty() && b.isUnnamed())
2645 return renameBuffer(b, docstring());
2647 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2649 theSession().lastFiles().add(b.fileName());
2653 // Switch to this Buffer.
2656 // FIXME: we don't tell the user *WHY* the save failed !!
2657 docstring const file = makeDisplayPath(b.absFileName(), 30);
2658 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2659 "Do you want to rename the document and "
2660 "try again?"), file);
2661 int const ret = Alert::prompt(_("Rename and save?"),
2662 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2665 if (!renameBuffer(b, docstring()))
2674 return saveBuffer(b, fn);
2678 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2680 return closeWorkArea(wa, false);
2684 // We only want to close the buffer if it is not visible in other workareas
2685 // of the same view, nor in other views, and if this is not a child
2686 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2688 Buffer & buf = wa->bufferView().buffer();
2690 bool last_wa = d.countWorkAreasOf(buf) == 1
2691 && !inOtherView(buf) && !buf.parent();
2693 bool close_buffer = last_wa;
2696 if (lyxrc.close_buffer_with_last_view == "yes")
2698 else if (lyxrc.close_buffer_with_last_view == "no")
2699 close_buffer = false;
2702 if (buf.isUnnamed())
2703 file = from_utf8(buf.fileName().onlyFileName());
2705 file = buf.fileName().displayName(30);
2706 docstring const text = bformat(
2707 _("Last view on document %1$s is being closed.\n"
2708 "Would you like to close or hide the document?\n"
2710 "Hidden documents can be displayed back through\n"
2711 "the menu: View->Hidden->...\n"
2713 "To remove this question, set your preference in:\n"
2714 " Tools->Preferences->Look&Feel->UserInterface\n"
2716 int ret = Alert::prompt(_("Close or hide document?"),
2717 text, 0, 1, _("&Close"), _("&Hide"));
2718 close_buffer = (ret == 0);
2722 return closeWorkArea(wa, close_buffer);
2726 bool GuiView::closeBuffer()
2728 GuiWorkArea * wa = currentMainWorkArea();
2729 setCurrentWorkArea(wa);
2730 Buffer & buf = wa->bufferView().buffer();
2731 return wa && closeWorkArea(wa, !buf.parent());
2735 void GuiView::writeSession() const {
2736 GuiWorkArea const * active_wa = currentMainWorkArea();
2737 for (int i = 0; i < d.splitter_->count(); ++i) {
2738 TabWorkArea * twa = d.tabWorkArea(i);
2739 for (int j = 0; j < twa->count(); ++j) {
2740 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2741 Buffer & buf = wa->bufferView().buffer();
2742 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2748 bool GuiView::closeBufferAll()
2750 // Close the workareas in all other views
2751 QList<int> const ids = guiApp->viewIds();
2752 for (int i = 0; i != ids.size(); ++i) {
2753 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2757 // Close our own workareas
2758 if (!closeWorkAreaAll())
2761 // Now close the hidden buffers. We prevent hidden buffers from being
2762 // dirty, so we can just close them.
2763 theBufferList().closeAll();
2768 bool GuiView::closeWorkAreaAll()
2770 setCurrentWorkArea(currentMainWorkArea());
2772 // We might be in a situation that there is still a tabWorkArea, but
2773 // there are no tabs anymore. This can happen when we get here after a
2774 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2775 // many TabWorkArea's have no documents anymore.
2778 // We have to call count() each time, because it can happen that
2779 // more than one splitter will disappear in one iteration (bug 5998).
2780 for (; d.splitter_->count() > empty_twa; ) {
2781 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2783 if (twa->count() == 0)
2786 setCurrentWorkArea(twa->currentWorkArea());
2787 if (!closeTabWorkArea(twa))
2795 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2800 Buffer & buf = wa->bufferView().buffer();
2802 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2803 Alert::warning(_("Close document"),
2804 _("Document could not be closed because it is being processed by LyX."));
2809 return closeBuffer(buf);
2811 if (!inMultiTabs(wa))
2812 if (!saveBufferIfNeeded(buf, true))
2820 bool GuiView::closeBuffer(Buffer & buf)
2822 // If we are in a close_event all children will be closed in some time,
2823 // so no need to do it here. This will ensure that the children end up
2824 // in the session file in the correct order. If we close the master
2825 // buffer, we can close or release the child buffers here too.
2826 bool success = true;
2828 ListOfBuffers clist = buf.getChildren();
2829 ListOfBuffers::const_iterator it = clist.begin();
2830 ListOfBuffers::const_iterator const bend = clist.end();
2831 for (; it != bend; ++it) {
2832 // If a child is dirty, do not close
2833 // without user intervention
2834 //FIXME: should we look in other tabworkareas?
2835 Buffer * child_buf = *it;
2836 GuiWorkArea * child_wa = workArea(*child_buf);
2838 if (!closeWorkArea(child_wa, true)) {
2843 theBufferList().releaseChild(&buf, child_buf);
2847 // goto bookmark to update bookmark pit.
2848 //FIXME: we should update only the bookmarks related to this buffer!
2849 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2850 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2851 guiApp->gotoBookmark(i+1, false, false);
2853 if (saveBufferIfNeeded(buf, false)) {
2854 buf.removeAutosaveFile();
2855 theBufferList().release(&buf);
2859 // open all children again to avoid a crash because of dangling
2860 // pointers (bug 6603)
2866 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2868 while (twa == d.currentTabWorkArea()) {
2869 twa->setCurrentIndex(twa->count()-1);
2871 GuiWorkArea * wa = twa->currentWorkArea();
2872 Buffer & b = wa->bufferView().buffer();
2874 // We only want to close the buffer if the same buffer is not visible
2875 // in another view, and if this is not a child and if we are closing
2876 // a view (not a tabgroup).
2877 bool const close_buffer =
2878 !inOtherView(b) && !b.parent() && closing_;
2880 if (!closeWorkArea(wa, close_buffer))
2887 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2889 if (buf.isClean() || buf.paragraphs().empty())
2892 // Switch to this Buffer.
2897 if (buf.isUnnamed())
2898 file = from_utf8(buf.fileName().onlyFileName());
2900 file = buf.fileName().displayName(30);
2902 // Bring this window to top before asking questions.
2907 if (hiding && buf.isUnnamed()) {
2908 docstring const text = bformat(_("The document %1$s has not been "
2909 "saved yet.\n\nDo you want to save "
2910 "the document?"), file);
2911 ret = Alert::prompt(_("Save new document?"),
2912 text, 0, 1, _("&Save"), _("&Cancel"));
2916 docstring const text = bformat(_("The document %1$s has unsaved changes."
2917 "\n\nDo you want to save the document or discard the changes?"), file);
2918 ret = Alert::prompt(_("Save changed document?"),
2919 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2924 if (!saveBuffer(buf))
2928 // If we crash after this we could have no autosave file
2929 // but I guess this is really improbable (Jug).
2930 // Sometimes improbable things happen:
2931 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2932 // buf.removeAutosaveFile();
2934 // revert all changes
2945 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2947 Buffer & buf = wa->bufferView().buffer();
2949 for (int i = 0; i != d.splitter_->count(); ++i) {
2950 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2951 if (wa_ && wa_ != wa)
2954 return inOtherView(buf);
2958 bool GuiView::inOtherView(Buffer & buf)
2960 QList<int> const ids = guiApp->viewIds();
2962 for (int i = 0; i != ids.size(); ++i) {
2966 if (guiApp->view(ids[i]).workArea(buf))
2973 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2975 if (!documentBufferView())
2978 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2979 Buffer * const curbuf = &documentBufferView()->buffer();
2980 int nwa = twa->count();
2981 for (int i = 0; i < nwa; ++i) {
2982 if (&workArea(i)->bufferView().buffer() == curbuf) {
2984 if (np == NEXTBUFFER)
2985 next_index = (i == nwa - 1 ? 0 : i + 1);
2987 next_index = (i == 0 ? nwa - 1 : i - 1);
2989 twa->moveTab(i, next_index);
2991 setBuffer(&workArea(next_index)->bufferView().buffer());
2999 /// make sure the document is saved
3000 static bool ensureBufferClean(Buffer * buffer)
3002 LASSERT(buffer, return false);
3003 if (buffer->isClean() && !buffer->isUnnamed())
3006 docstring const file = buffer->fileName().displayName(30);
3009 if (!buffer->isUnnamed()) {
3010 text = bformat(_("The document %1$s has unsaved "
3011 "changes.\n\nDo you want to save "
3012 "the document?"), file);
3013 title = _("Save changed document?");
3016 text = bformat(_("The document %1$s has not been "
3017 "saved yet.\n\nDo you want to save "
3018 "the document?"), file);
3019 title = _("Save new document?");
3021 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3024 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3026 return buffer->isClean() && !buffer->isUnnamed();
3030 bool GuiView::reloadBuffer(Buffer & buf)
3032 Buffer::ReadStatus status = buf.reload();
3033 return status == Buffer::ReadSuccess;
3037 void GuiView::checkExternallyModifiedBuffers()
3039 BufferList::iterator bit = theBufferList().begin();
3040 BufferList::iterator const bend = theBufferList().end();
3041 for (; bit != bend; ++bit) {
3042 Buffer * buf = *bit;
3043 if (buf->fileName().exists()
3044 && buf->isExternallyModified(Buffer::checksum_method)) {
3045 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3046 " Reload now? Any local changes will be lost."),
3047 from_utf8(buf->absFileName()));
3048 int const ret = Alert::prompt(_("Reload externally changed document?"),
3049 text, 0, 1, _("&Reload"), _("&Cancel"));
3057 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3059 Buffer * buffer = documentBufferView()
3060 ? &(documentBufferView()->buffer()) : 0;
3062 switch (cmd.action()) {
3063 case LFUN_VC_REGISTER:
3064 if (!buffer || !ensureBufferClean(buffer))
3066 if (!buffer->lyxvc().inUse()) {
3067 if (buffer->lyxvc().registrer()) {
3068 reloadBuffer(*buffer);
3069 dr.clearMessageUpdate();
3074 case LFUN_VC_RENAME:
3075 case LFUN_VC_COPY: {
3076 if (!buffer || !ensureBufferClean(buffer))
3078 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3079 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3080 // Some changes are not yet committed.
3081 // We test here and not in getStatus(), since
3082 // this test is expensive.
3084 LyXVC::CommandResult ret =
3085 buffer->lyxvc().checkIn(log);
3087 if (ret == LyXVC::ErrorCommand ||
3088 ret == LyXVC::VCSuccess)
3089 reloadBuffer(*buffer);
3090 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3091 frontend::Alert::error(
3092 _("Revision control error."),
3093 _("Document could not be checked in."));
3097 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3098 LV_VC_RENAME : LV_VC_COPY;
3099 renameBuffer(*buffer, cmd.argument(), kind);
3104 case LFUN_VC_CHECK_IN:
3105 if (!buffer || !ensureBufferClean(buffer))
3107 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3109 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3111 // Only skip reloading if the checkin was cancelled or
3112 // an error occurred before the real checkin VCS command
3113 // was executed, since the VCS might have changed the
3114 // file even if it could not checkin successfully.
3115 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3116 reloadBuffer(*buffer);
3120 case LFUN_VC_CHECK_OUT:
3121 if (!buffer || !ensureBufferClean(buffer))
3123 if (buffer->lyxvc().inUse()) {
3124 dr.setMessage(buffer->lyxvc().checkOut());
3125 reloadBuffer(*buffer);
3129 case LFUN_VC_LOCKING_TOGGLE:
3130 LASSERT(buffer, return);
3131 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3133 if (buffer->lyxvc().inUse()) {
3134 string res = buffer->lyxvc().lockingToggle();
3136 frontend::Alert::error(_("Revision control error."),
3137 _("Error when setting the locking property."));
3140 reloadBuffer(*buffer);
3145 case LFUN_VC_REVERT:
3146 LASSERT(buffer, return);
3147 if (buffer->lyxvc().revert()) {
3148 reloadBuffer(*buffer);
3149 dr.clearMessageUpdate();
3153 case LFUN_VC_UNDO_LAST:
3154 LASSERT(buffer, return);
3155 buffer->lyxvc().undoLast();
3156 reloadBuffer(*buffer);
3157 dr.clearMessageUpdate();
3160 case LFUN_VC_REPO_UPDATE:
3161 LASSERT(buffer, return);
3162 if (ensureBufferClean(buffer)) {
3163 dr.setMessage(buffer->lyxvc().repoUpdate());
3164 checkExternallyModifiedBuffers();
3168 case LFUN_VC_COMMAND: {
3169 string flag = cmd.getArg(0);
3170 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3173 if (contains(flag, 'M')) {
3174 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3177 string path = cmd.getArg(1);
3178 if (contains(path, "$$p") && buffer)
3179 path = subst(path, "$$p", buffer->filePath());
3180 LYXERR(Debug::LYXVC, "Directory: " << path);
3182 if (!pp.isReadableDirectory()) {
3183 lyxerr << _("Directory is not accessible.") << endl;
3186 support::PathChanger p(pp);
3188 string command = cmd.getArg(2);
3189 if (command.empty())
3192 command = subst(command, "$$i", buffer->absFileName());
3193 command = subst(command, "$$p", buffer->filePath());
3195 command = subst(command, "$$m", to_utf8(message));
3196 LYXERR(Debug::LYXVC, "Command: " << command);
3198 one.startscript(Systemcall::Wait, command);
3202 if (contains(flag, 'I'))
3203 buffer->markDirty();
3204 if (contains(flag, 'R'))
3205 reloadBuffer(*buffer);
3210 case LFUN_VC_COMPARE: {
3212 if (cmd.argument().empty()) {
3213 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3217 string rev1 = cmd.getArg(0);
3221 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3224 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3225 f2 = buffer->absFileName();
3227 string rev2 = cmd.getArg(1);
3231 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3235 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3236 f1 << "\n" << f2 << "\n" );
3237 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3238 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3248 void GuiView::openChildDocument(string const & fname)
3250 LASSERT(documentBufferView(), return);
3251 Buffer & buffer = documentBufferView()->buffer();
3252 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3253 documentBufferView()->saveBookmark(false);
3255 if (theBufferList().exists(filename)) {
3256 child = theBufferList().getBuffer(filename);
3259 message(bformat(_("Opening child document %1$s..."),
3260 makeDisplayPath(filename.absFileName())));
3261 child = loadDocument(filename, false);
3263 // Set the parent name of the child document.
3264 // This makes insertion of citations and references in the child work,
3265 // when the target is in the parent or another child document.
3267 child->setParent(&buffer);
3271 bool GuiView::goToFileRow(string const & argument)
3275 size_t i = argument.find_last_of(' ');
3276 if (i != string::npos) {
3277 file_name = os::internal_path(trim(argument.substr(0, i)));
3278 istringstream is(argument.substr(i + 1));
3283 if (i == string::npos) {
3284 LYXERR0("Wrong argument: " << argument);
3288 string const abstmp = package().temp_dir().absFileName();
3289 string const realtmp = package().temp_dir().realPath();
3290 // We have to use os::path_prefix_is() here, instead of
3291 // simply prefixIs(), because the file name comes from
3292 // an external application and may need case adjustment.
3293 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3294 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3295 // Needed by inverse dvi search. If it is a file
3296 // in tmpdir, call the apropriated function.
3297 // If tmpdir is a symlink, we may have the real
3298 // path passed back, so we correct for that.
3299 if (!prefixIs(file_name, abstmp))
3300 file_name = subst(file_name, realtmp, abstmp);
3301 buf = theBufferList().getBufferFromTmp(file_name);
3303 // Must replace extension of the file to be .lyx
3304 // and get full path
3305 FileName const s = fileSearch(string(),
3306 support::changeExtension(file_name, ".lyx"), "lyx");
3307 // Either change buffer or load the file
3308 if (theBufferList().exists(s))
3309 buf = theBufferList().getBuffer(s);
3310 else if (s.exists()) {
3311 buf = loadDocument(s);
3316 _("File does not exist: %1$s"),
3317 makeDisplayPath(file_name)));
3323 _("No buffer for file: %1$s."),
3324 makeDisplayPath(file_name))
3329 documentBufferView()->setCursorFromRow(row);
3335 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3337 Buffer::ExportStatus const status = func(format);
3339 // the cloning operation will have produced a clone of the entire set of
3340 // documents, starting from the master. so we must delete those.
3341 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3343 busyBuffers.remove(orig);
3348 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3350 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3351 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3355 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3357 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3358 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3362 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3364 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3365 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3369 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3370 string const & argument,
3371 Buffer const * used_buffer,
3372 docstring const & msg,
3373 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3374 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3375 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3380 string format = argument;
3382 format = used_buffer->params().getDefaultOutputFormat();
3383 processing_format = format;
3385 progress_->clearMessages();
3388 #if EXPORT_in_THREAD
3389 GuiViewPrivate::busyBuffers.insert(used_buffer);
3390 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3391 if (!cloned_buffer) {
3392 Alert::error(_("Export Error"),
3393 _("Error cloning the Buffer."));
3396 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3401 setPreviewFuture(f);
3402 last_export_format = used_buffer->params().bufferFormat();
3405 // We are asynchronous, so we don't know here anything about the success
3408 Buffer::ExportStatus status;
3410 // TODO check here if it breaks exporting with Qt < 4.4
3411 status = (used_buffer->*syncFunc)(format, true);
3412 } else if (previewFunc) {
3413 status = (used_buffer->*previewFunc)(format);
3416 handleExportStatus(gv_, status, format);
3418 return (status == Buffer::ExportSuccess
3419 || status == Buffer::PreviewSuccess);
3423 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3425 BufferView * bv = currentBufferView();
3426 LASSERT(bv, return);
3428 // Let the current BufferView dispatch its own actions.
3429 bv->dispatch(cmd, dr);
3430 if (dr.dispatched())
3433 // Try with the document BufferView dispatch if any.
3434 BufferView * doc_bv = documentBufferView();
3435 if (doc_bv && doc_bv != bv) {
3436 doc_bv->dispatch(cmd, dr);
3437 if (dr.dispatched())
3441 // Then let the current Cursor dispatch its own actions.
3442 bv->cursor().dispatch(cmd);
3444 // update completion. We do it here and not in
3445 // processKeySym to avoid another redraw just for a
3446 // changed inline completion
3447 if (cmd.origin() == FuncRequest::KEYBOARD) {
3448 if (cmd.action() == LFUN_SELF_INSERT
3449 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3450 updateCompletion(bv->cursor(), true, true);
3451 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3452 updateCompletion(bv->cursor(), false, true);
3454 updateCompletion(bv->cursor(), false, false);
3457 dr = bv->cursor().result();
3461 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3463 BufferView * bv = currentBufferView();
3464 // By default we won't need any update.
3465 dr.screenUpdate(Update::None);
3466 // assume cmd will be dispatched
3467 dr.dispatched(true);
3469 Buffer * doc_buffer = documentBufferView()
3470 ? &(documentBufferView()->buffer()) : 0;
3472 if (cmd.origin() == FuncRequest::TOC) {
3473 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3474 // FIXME: do we need to pass a DispatchResult object here?
3475 toc->doDispatch(bv->cursor(), cmd);
3479 string const argument = to_utf8(cmd.argument());
3481 switch(cmd.action()) {
3482 case LFUN_BUFFER_CHILD_OPEN:
3483 openChildDocument(to_utf8(cmd.argument()));
3486 case LFUN_BUFFER_IMPORT:
3487 importDocument(to_utf8(cmd.argument()));
3490 case LFUN_BUFFER_EXPORT: {
3493 FileName target_dir = doc_buffer->fileName().onlyPath();
3494 string const dest = cmd.getArg(1);
3495 if (!dest.empty() && FileName::isAbsolute(dest))
3496 target_dir = FileName(support::onlyPath(dest));
3497 // GCC only sees strfwd.h when building merged
3498 if (::lyx::operator==(cmd.argument(), "custom")) {
3499 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3502 if (!target_dir.isDirWritable()) {
3503 exportBufferAs(*doc_buffer, cmd.argument());
3506 /* TODO/Review: Is it a problem to also export the children?
3507 See the update_unincluded flag */
3508 d.asyncBufferProcessing(argument,
3511 &GuiViewPrivate::exportAndDestroy,
3514 // TODO Inform user about success
3518 case LFUN_BUFFER_EXPORT_AS: {
3519 LASSERT(doc_buffer, break);
3520 docstring f = cmd.argument();
3522 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3523 exportBufferAs(*doc_buffer, f);
3527 case LFUN_BUFFER_UPDATE: {
3528 d.asyncBufferProcessing(argument,
3531 &GuiViewPrivate::compileAndDestroy,
3536 case LFUN_BUFFER_VIEW: {
3537 d.asyncBufferProcessing(argument,
3539 _("Previewing ..."),
3540 &GuiViewPrivate::previewAndDestroy,
3545 case LFUN_MASTER_BUFFER_UPDATE: {
3546 d.asyncBufferProcessing(argument,
3547 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3549 &GuiViewPrivate::compileAndDestroy,
3554 case LFUN_MASTER_BUFFER_VIEW: {
3555 d.asyncBufferProcessing(argument,
3556 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3558 &GuiViewPrivate::previewAndDestroy,
3559 0, &Buffer::preview);
3562 case LFUN_BUFFER_SWITCH: {
3563 string const file_name = to_utf8(cmd.argument());
3564 if (!FileName::isAbsolute(file_name)) {
3566 dr.setMessage(_("Absolute filename expected."));
3570 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3573 dr.setMessage(_("Document not loaded"));
3577 // Do we open or switch to the buffer in this view ?
3578 if (workArea(*buffer)
3579 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3584 // Look for the buffer in other views
3585 QList<int> const ids = guiApp->viewIds();
3587 for (; i != ids.size(); ++i) {
3588 GuiView & gv = guiApp->view(ids[i]);
3589 if (gv.workArea(*buffer)) {
3590 gv.activateWindow();
3591 gv.setBuffer(buffer);
3596 // If necessary, open a new window as a last resort
3597 if (i == ids.size()) {
3598 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3604 case LFUN_BUFFER_NEXT:
3605 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3608 case LFUN_BUFFER_MOVE_NEXT:
3609 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3612 case LFUN_BUFFER_PREVIOUS:
3613 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3616 case LFUN_BUFFER_MOVE_PREVIOUS:
3617 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3620 case LFUN_COMMAND_EXECUTE: {
3621 bool const show_it = cmd.argument() != "off";
3622 // FIXME: this is a hack, "minibuffer" should not be
3624 if (GuiToolbar * t = toolbar("minibuffer")) {
3625 t->setVisible(show_it);
3626 if (show_it && t->commandBuffer())
3627 t->commandBuffer()->setFocus();
3631 case LFUN_DROP_LAYOUTS_CHOICE:
3632 d.layout_->showPopup();
3635 case LFUN_MENU_OPEN:
3636 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3637 menu->exec(QCursor::pos());
3640 case LFUN_FILE_INSERT:
3641 insertLyXFile(cmd.argument());
3644 case LFUN_FILE_INSERT_PLAINTEXT:
3645 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3646 string const fname = to_utf8(cmd.argument());
3647 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3648 dr.setMessage(_("Absolute filename expected."));
3652 FileName filename(fname);
3653 if (fname.empty()) {
3654 FileDialog dlg(qt_("Select file to insert"));
3656 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3657 QStringList(qt_("All Files (*)")));
3659 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3660 dr.setMessage(_("Canceled."));
3664 filename.set(fromqstr(result.second));
3668 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3669 bv->dispatch(new_cmd, dr);
3674 case LFUN_BUFFER_RELOAD: {
3675 LASSERT(doc_buffer, break);
3678 if (!doc_buffer->isClean()) {
3679 docstring const file =
3680 makeDisplayPath(doc_buffer->absFileName(), 20);
3681 docstring text = bformat(_("Any changes will be lost. "
3682 "Are you sure you want to revert to the saved version "
3683 "of the document %1$s?"), file);
3684 ret = Alert::prompt(_("Revert to saved document?"),
3685 text, 1, 1, _("&Revert"), _("&Cancel"));
3689 doc_buffer->markClean();
3690 reloadBuffer(*doc_buffer);
3691 dr.forceBufferUpdate();
3696 case LFUN_BUFFER_WRITE:
3697 LASSERT(doc_buffer, break);
3698 saveBuffer(*doc_buffer);
3701 case LFUN_BUFFER_WRITE_AS:
3702 LASSERT(doc_buffer, break);
3703 renameBuffer(*doc_buffer, cmd.argument());
3706 case LFUN_BUFFER_WRITE_ALL: {
3707 Buffer * first = theBufferList().first();
3710 message(_("Saving all documents..."));
3711 // We cannot use a for loop as the buffer list cycles.
3714 if (!b->isClean()) {
3716 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3718 b = theBufferList().next(b);
3719 } while (b != first);
3720 dr.setMessage(_("All documents saved."));
3724 case LFUN_BUFFER_CLOSE:
3728 case LFUN_BUFFER_CLOSE_ALL:
3732 case LFUN_TOOLBAR_TOGGLE: {
3733 string const name = cmd.getArg(0);
3734 if (GuiToolbar * t = toolbar(name))
3739 case LFUN_DIALOG_UPDATE: {
3740 string const name = to_utf8(cmd.argument());
3741 if (name == "prefs" || name == "document")
3742 updateDialog(name, string());
3743 else if (name == "paragraph")
3744 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3745 else if (currentBufferView()) {
3746 Inset * inset = currentBufferView()->editedInset(name);
3747 // Can only update a dialog connected to an existing inset
3749 // FIXME: get rid of this indirection; GuiView ask the inset
3750 // if he is kind enough to update itself...
3751 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3752 //FIXME: pass DispatchResult here?
3753 inset->dispatch(currentBufferView()->cursor(), fr);
3759 case LFUN_DIALOG_TOGGLE: {
3760 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3761 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3762 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3766 case LFUN_DIALOG_DISCONNECT_INSET:
3767 disconnectDialog(to_utf8(cmd.argument()));
3770 case LFUN_DIALOG_HIDE: {
3771 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3775 case LFUN_DIALOG_SHOW: {
3776 string const name = cmd.getArg(0);
3777 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3779 if (name == "character") {
3780 data = freefont2string();
3782 showDialog("character", data);
3783 } else if (name == "latexlog") {
3784 Buffer::LogType type;
3785 string const logfile = doc_buffer->logName(&type);
3787 case Buffer::latexlog:
3790 case Buffer::buildlog:
3794 data += Lexer::quoteString(logfile);
3795 showDialog("log", data);
3796 } else if (name == "vclog") {
3797 string const data = "vc " +
3798 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3799 showDialog("log", data);
3800 } else if (name == "symbols") {
3801 data = bv->cursor().getEncoding()->name();
3803 showDialog("symbols", data);
3805 } else if (name == "prefs" && isFullScreen()) {
3806 lfunUiToggle("fullscreen");
3807 showDialog("prefs", data);
3809 showDialog(name, data);
3814 dr.setMessage(cmd.argument());
3817 case LFUN_UI_TOGGLE: {
3818 string arg = cmd.getArg(0);
3819 if (!lfunUiToggle(arg)) {
3820 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3821 dr.setMessage(bformat(msg, from_utf8(arg)));
3823 // Make sure the keyboard focus stays in the work area.
3828 case LFUN_VIEW_SPLIT: {
3829 LASSERT(doc_buffer, break);
3830 string const orientation = cmd.getArg(0);
3831 d.splitter_->setOrientation(orientation == "vertical"
3832 ? Qt::Vertical : Qt::Horizontal);
3833 TabWorkArea * twa = addTabWorkArea();
3834 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3835 setCurrentWorkArea(wa);
3838 case LFUN_TAB_GROUP_CLOSE:
3839 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3840 closeTabWorkArea(twa);
3841 d.current_work_area_ = 0;
3842 twa = d.currentTabWorkArea();
3843 // Switch to the next GuiWorkArea in the found TabWorkArea.
3845 // Make sure the work area is up to date.
3846 setCurrentWorkArea(twa->currentWorkArea());
3848 setCurrentWorkArea(0);
3853 case LFUN_VIEW_CLOSE:
3854 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3855 closeWorkArea(twa->currentWorkArea());
3856 d.current_work_area_ = 0;
3857 twa = d.currentTabWorkArea();
3858 // Switch to the next GuiWorkArea in the found TabWorkArea.
3860 // Make sure the work area is up to date.
3861 setCurrentWorkArea(twa->currentWorkArea());
3863 setCurrentWorkArea(0);
3868 case LFUN_COMPLETION_INLINE:
3869 if (d.current_work_area_)
3870 d.current_work_area_->completer().showInline();
3873 case LFUN_COMPLETION_POPUP:
3874 if (d.current_work_area_)
3875 d.current_work_area_->completer().showPopup();
3880 if (d.current_work_area_)
3881 d.current_work_area_->completer().tab();
3884 case LFUN_COMPLETION_CANCEL:
3885 if (d.current_work_area_) {
3886 if (d.current_work_area_->completer().popupVisible())
3887 d.current_work_area_->completer().hidePopup();
3889 d.current_work_area_->completer().hideInline();
3893 case LFUN_COMPLETION_ACCEPT:
3894 if (d.current_work_area_)
3895 d.current_work_area_->completer().activate();
3898 case LFUN_BUFFER_ZOOM_IN:
3899 case LFUN_BUFFER_ZOOM_OUT:
3900 if (cmd.argument().empty()) {
3901 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3906 lyxrc.zoom += convert<int>(cmd.argument());
3908 if (lyxrc.zoom < 10)
3911 // The global QPixmapCache is used in GuiPainter to cache text
3912 // painting so we must reset it.
3913 QPixmapCache::clear();
3914 guiApp->fontLoader().update();
3915 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3918 case LFUN_VC_REGISTER:
3919 case LFUN_VC_RENAME:
3921 case LFUN_VC_CHECK_IN:
3922 case LFUN_VC_CHECK_OUT:
3923 case LFUN_VC_REPO_UPDATE:
3924 case LFUN_VC_LOCKING_TOGGLE:
3925 case LFUN_VC_REVERT:
3926 case LFUN_VC_UNDO_LAST:
3927 case LFUN_VC_COMMAND:
3928 case LFUN_VC_COMPARE:
3929 dispatchVC(cmd, dr);
3932 case LFUN_SERVER_GOTO_FILE_ROW:
3933 goToFileRow(to_utf8(cmd.argument()));
3936 case LFUN_FORWARD_SEARCH: {
3937 Buffer const * doc_master = doc_buffer->masterBuffer();
3938 FileName const path(doc_master->temppath());
3939 string const texname = doc_master->isChild(doc_buffer)
3940 ? DocFileName(changeExtension(
3941 doc_buffer->absFileName(),
3942 "tex")).mangledFileName()
3943 : doc_buffer->latexName();
3944 string const fulltexname =
3945 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3946 string const mastername =
3947 removeExtension(doc_master->latexName());
3948 FileName const dviname(addName(path.absFileName(),
3949 addExtension(mastername, "dvi")));
3950 FileName const pdfname(addName(path.absFileName(),
3951 addExtension(mastername, "pdf")));
3952 bool const have_dvi = dviname.exists();
3953 bool const have_pdf = pdfname.exists();
3954 if (!have_dvi && !have_pdf) {
3955 dr.setMessage(_("Please, preview the document first."));
3958 string outname = dviname.onlyFileName();
3959 string command = lyxrc.forward_search_dvi;
3960 if (!have_dvi || (have_pdf &&
3961 pdfname.lastModified() > dviname.lastModified())) {
3962 outname = pdfname.onlyFileName();
3963 command = lyxrc.forward_search_pdf;
3966 DocIterator tmpcur = bv->cursor();
3968 while (tmpcur.inMathed())
3970 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3971 tmpcur.paragraph().id(), tmpcur.pos());
3972 LYXERR(Debug::ACTION, "Forward search: row:" << row
3973 << " id:" << tmpcur.paragraph().id());
3974 if (!row || command.empty()) {
3975 dr.setMessage(_("Couldn't proceed."));
3978 string texrow = convert<string>(row);
3980 command = subst(command, "$$n", texrow);
3981 command = subst(command, "$$f", fulltexname);
3982 command = subst(command, "$$t", texname);
3983 command = subst(command, "$$o", outname);
3985 PathChanger p(path);
3987 one.startscript(Systemcall::DontWait, command);
3991 case LFUN_SPELLING_CONTINUOUSLY:
3992 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3993 dr.screenUpdate(Update::Force | Update::FitCursor);
3997 // The LFUN must be for one of BufferView, Buffer or Cursor;
3999 dispatchToBufferView(cmd, dr);
4003 // Part of automatic menu appearance feature.
4004 if (isFullScreen()) {
4005 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4009 // Need to update bv because many LFUNs here might have destroyed it
4010 bv = currentBufferView();
4012 // Clear non-empty selections
4013 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4015 Cursor & cur = bv->cursor();
4016 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4017 cur.clearSelection();
4023 bool GuiView::lfunUiToggle(string const & ui_component)
4025 if (ui_component == "scrollbar") {
4026 // hide() is of no help
4027 if (d.current_work_area_->verticalScrollBarPolicy() ==
4028 Qt::ScrollBarAlwaysOff)
4030 d.current_work_area_->setVerticalScrollBarPolicy(
4031 Qt::ScrollBarAsNeeded);
4033 d.current_work_area_->setVerticalScrollBarPolicy(
4034 Qt::ScrollBarAlwaysOff);
4035 } else if (ui_component == "statusbar") {
4036 statusBar()->setVisible(!statusBar()->isVisible());
4037 } else if (ui_component == "menubar") {
4038 menuBar()->setVisible(!menuBar()->isVisible());
4040 if (ui_component == "frame") {
4042 getContentsMargins(&l, &t, &r, &b);
4043 //are the frames in default state?
4044 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4046 setContentsMargins(-2, -2, -2, -2);
4048 setContentsMargins(0, 0, 0, 0);
4051 if (ui_component == "fullscreen") {
4059 void GuiView::toggleFullScreen()
4061 if (isFullScreen()) {
4062 for (int i = 0; i != d.splitter_->count(); ++i)
4063 d.tabWorkArea(i)->setFullScreen(false);
4064 setContentsMargins(0, 0, 0, 0);
4065 setWindowState(windowState() ^ Qt::WindowFullScreen);
4068 statusBar()->show();
4071 hideDialogs("prefs", 0);
4072 for (int i = 0; i != d.splitter_->count(); ++i)
4073 d.tabWorkArea(i)->setFullScreen(true);
4074 setContentsMargins(-2, -2, -2, -2);
4076 setWindowState(windowState() ^ Qt::WindowFullScreen);
4077 if (lyxrc.full_screen_statusbar)
4078 statusBar()->hide();
4079 if (lyxrc.full_screen_menubar)
4081 if (lyxrc.full_screen_toolbars) {
4082 ToolbarMap::iterator end = d.toolbars_.end();
4083 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4088 // give dialogs like the TOC a chance to adapt
4093 Buffer const * GuiView::updateInset(Inset const * inset)
4098 Buffer const * inset_buffer = &(inset->buffer());
4100 for (int i = 0; i != d.splitter_->count(); ++i) {
4101 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4104 Buffer const * buffer = &(wa->bufferView().buffer());
4105 if (inset_buffer == buffer)
4106 wa->scheduleRedraw();
4108 return inset_buffer;
4112 void GuiView::restartCursor()
4114 /* When we move around, or type, it's nice to be able to see
4115 * the cursor immediately after the keypress.
4117 if (d.current_work_area_)
4118 d.current_work_area_->startBlinkingCursor();
4120 // Take this occasion to update the other GUI elements.
4126 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4128 if (d.current_work_area_)
4129 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4134 // This list should be kept in sync with the list of insets in
4135 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4136 // dialog should have the same name as the inset.
4137 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4138 // docs in LyXAction.cpp.
4140 char const * const dialognames[] = {
4142 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4143 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4144 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4145 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4146 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4147 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4148 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4149 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4151 char const * const * const end_dialognames =
4152 dialognames + (sizeof(dialognames) / sizeof(char *));
4156 cmpCStr(char const * name) : name_(name) {}
4157 bool operator()(char const * other) {
4158 return strcmp(other, name_) == 0;
4165 bool isValidName(string const & name)
4167 return find_if(dialognames, end_dialognames,
4168 cmpCStr(name.c_str())) != end_dialognames;
4174 void GuiView::resetDialogs()
4176 // Make sure that no LFUN uses any GuiView.
4177 guiApp->setCurrentView(0);
4181 constructToolbars();
4182 guiApp->menus().fillMenuBar(menuBar(), this, false);
4183 d.layout_->updateContents(true);
4184 // Now update controls with current buffer.
4185 guiApp->setCurrentView(this);
4191 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4193 if (!isValidName(name))
4196 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4198 if (it != d.dialogs_.end()) {
4200 it->second->hideView();
4201 return it->second.get();
4204 Dialog * dialog = build(name);
4205 d.dialogs_[name].reset(dialog);
4206 if (lyxrc.allow_geometry_session)
4207 dialog->restoreSession();
4214 void GuiView::showDialog(string const & name, string const & data,
4217 triggerShowDialog(toqstr(name), toqstr(data), inset);
4221 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4227 const string name = fromqstr(qname);
4228 const string data = fromqstr(qdata);
4232 Dialog * dialog = findOrBuild(name, false);
4234 bool const visible = dialog->isVisibleView();
4235 dialog->showData(data);
4236 if (inset && currentBufferView())
4237 currentBufferView()->editInset(name, inset);
4238 // We only set the focus to the new dialog if it was not yet
4239 // visible in order not to change the existing previous behaviour
4241 // activateWindow is needed for floating dockviews
4242 dialog->asQWidget()->raise();
4243 dialog->asQWidget()->activateWindow();
4244 dialog->asQWidget()->setFocus();
4248 catch (ExceptionMessage const & ex) {
4256 bool GuiView::isDialogVisible(string const & name) const
4258 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4259 if (it == d.dialogs_.end())
4261 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4265 void GuiView::hideDialog(string const & name, Inset * inset)
4267 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4268 if (it == d.dialogs_.end())
4272 if (!currentBufferView())
4274 if (inset != currentBufferView()->editedInset(name))
4278 Dialog * const dialog = it->second.get();
4279 if (dialog->isVisibleView())
4281 if (currentBufferView())
4282 currentBufferView()->editInset(name, 0);
4286 void GuiView::disconnectDialog(string const & name)
4288 if (!isValidName(name))
4290 if (currentBufferView())
4291 currentBufferView()->editInset(name, 0);
4295 void GuiView::hideAll() const
4297 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4298 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4300 for(; it != end; ++it)
4301 it->second->hideView();
4305 void GuiView::updateDialogs()
4307 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4308 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4310 for(; it != end; ++it) {
4311 Dialog * dialog = it->second.get();
4313 if (dialog->needBufferOpen() && !documentBufferView())
4314 hideDialog(fromqstr(dialog->name()), 0);
4315 else if (dialog->isVisibleView())
4316 dialog->checkStatus();
4323 Dialog * createDialog(GuiView & lv, string const & name);
4325 // will be replaced by a proper factory...
4326 Dialog * createGuiAbout(GuiView & lv);
4327 Dialog * createGuiBibtex(GuiView & lv);
4328 Dialog * createGuiChanges(GuiView & lv);
4329 Dialog * createGuiCharacter(GuiView & lv);
4330 Dialog * createGuiCitation(GuiView & lv);
4331 Dialog * createGuiCompare(GuiView & lv);
4332 Dialog * createGuiCompareHistory(GuiView & lv);
4333 Dialog * createGuiDelimiter(GuiView & lv);
4334 Dialog * createGuiDocument(GuiView & lv);
4335 Dialog * createGuiErrorList(GuiView & lv);
4336 Dialog * createGuiExternal(GuiView & lv);
4337 Dialog * createGuiGraphics(GuiView & lv);
4338 Dialog * createGuiInclude(GuiView & lv);
4339 Dialog * createGuiIndex(GuiView & lv);
4340 Dialog * createGuiListings(GuiView & lv);
4341 Dialog * createGuiLog(GuiView & lv);
4342 Dialog * createGuiMathMatrix(GuiView & lv);
4343 Dialog * createGuiNote(GuiView & lv);
4344 Dialog * createGuiParagraph(GuiView & lv);
4345 Dialog * createGuiPhantom(GuiView & lv);
4346 Dialog * createGuiPreferences(GuiView & lv);
4347 Dialog * createGuiPrint(GuiView & lv);
4348 Dialog * createGuiPrintindex(GuiView & lv);
4349 Dialog * createGuiRef(GuiView & lv);
4350 Dialog * createGuiSearch(GuiView & lv);
4351 Dialog * createGuiSearchAdv(GuiView & lv);
4352 Dialog * createGuiSendTo(GuiView & lv);
4353 Dialog * createGuiShowFile(GuiView & lv);
4354 Dialog * createGuiSpellchecker(GuiView & lv);
4355 Dialog * createGuiSymbols(GuiView & lv);
4356 Dialog * createGuiTabularCreate(GuiView & lv);
4357 Dialog * createGuiTexInfo(GuiView & lv);
4358 Dialog * createGuiToc(GuiView & lv);
4359 Dialog * createGuiThesaurus(GuiView & lv);
4360 Dialog * createGuiViewSource(GuiView & lv);
4361 Dialog * createGuiWrap(GuiView & lv);
4362 Dialog * createGuiProgressView(GuiView & lv);
4366 Dialog * GuiView::build(string const & name)
4368 LASSERT(isValidName(name), return 0);
4370 Dialog * dialog = createDialog(*this, name);
4374 if (name == "aboutlyx")
4375 return createGuiAbout(*this);
4376 if (name == "bibtex")
4377 return createGuiBibtex(*this);
4378 if (name == "changes")
4379 return createGuiChanges(*this);
4380 if (name == "character")
4381 return createGuiCharacter(*this);
4382 if (name == "citation")
4383 return createGuiCitation(*this);
4384 if (name == "compare")
4385 return createGuiCompare(*this);
4386 if (name == "comparehistory")
4387 return createGuiCompareHistory(*this);
4388 if (name == "document")
4389 return createGuiDocument(*this);
4390 if (name == "errorlist")
4391 return createGuiErrorList(*this);
4392 if (name == "external")
4393 return createGuiExternal(*this);
4395 return createGuiShowFile(*this);
4396 if (name == "findreplace")
4397 return createGuiSearch(*this);
4398 if (name == "findreplaceadv")
4399 return createGuiSearchAdv(*this);
4400 if (name == "graphics")
4401 return createGuiGraphics(*this);
4402 if (name == "include")
4403 return createGuiInclude(*this);
4404 if (name == "index")
4405 return createGuiIndex(*this);
4406 if (name == "index_print")
4407 return createGuiPrintindex(*this);
4408 if (name == "listings")
4409 return createGuiListings(*this);
4411 return createGuiLog(*this);
4412 if (name == "mathdelimiter")
4413 return createGuiDelimiter(*this);
4414 if (name == "mathmatrix")
4415 return createGuiMathMatrix(*this);
4417 return createGuiNote(*this);
4418 if (name == "paragraph")
4419 return createGuiParagraph(*this);
4420 if (name == "phantom")
4421 return createGuiPhantom(*this);
4422 if (name == "prefs")
4423 return createGuiPreferences(*this);
4425 return createGuiRef(*this);
4426 if (name == "sendto")
4427 return createGuiSendTo(*this);
4428 if (name == "spellchecker")
4429 return createGuiSpellchecker(*this);
4430 if (name == "symbols")
4431 return createGuiSymbols(*this);
4432 if (name == "tabularcreate")
4433 return createGuiTabularCreate(*this);
4434 if (name == "texinfo")
4435 return createGuiTexInfo(*this);
4436 if (name == "thesaurus")
4437 return createGuiThesaurus(*this);
4439 return createGuiToc(*this);
4440 if (name == "view-source")
4441 return createGuiViewSource(*this);
4443 return createGuiWrap(*this);
4444 if (name == "progress")
4445 return createGuiProgressView(*this);
4451 } // namespace frontend
4454 #include "moc_GuiView.cpp"