3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #include <QSvgRenderer>
114 #include <QtConcurrentRun>
122 // sync with GuiAlert.cpp
123 #define EXPORT_in_THREAD 1
126 #include "support/bind.h"
130 #ifdef HAVE_SYS_TIME_H
131 # include <sys/time.h>
139 using namespace lyx::support;
143 using support::addExtension;
144 using support::changeExtension;
145 using support::removeExtension;
151 class BackgroundWidget : public QWidget
154 BackgroundWidget(int width, int height)
155 : width_(width), height_(height)
157 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
158 if (!lyxrc.show_banner)
160 /// The text to be written on top of the pixmap
161 QString const text = lyx_version ?
162 qt_("version ") + lyx_version : qt_("unknown version");
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 QPainter pain(&splash_);
176 pain.setPen(QColor(0, 0, 0));
177 qreal const fsize = fontSize();
178 QPointF const position = textPosition();
180 "widget pixel ratio: " << pixelRatio() <<
181 " splash pixel ratio: " << splashPixelRatio() <<
182 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
184 // The font used to display the version info
185 font.setStyleHint(QFont::SansSerif);
186 font.setWeight(QFont::Bold);
187 font.setPointSizeF(fsize);
189 pain.drawText(position, text);
190 setFocusPolicy(Qt::StrongFocus);
193 void paintEvent(QPaintEvent *)
195 int const w = width_;
196 int const h = height_;
197 int const x = (width() - w) / 2;
198 int const y = (height() - h) / 2;
200 "widget pixel ratio: " << pixelRatio() <<
201 " splash pixel ratio: " << splashPixelRatio() <<
202 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
204 pain.drawPixmap(x, y, w, h, splash_);
207 void keyPressEvent(QKeyEvent * ev)
210 setKeySymbol(&sym, ev);
212 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
224 /// Current ratio between physical pixels and device-independent pixels
225 double pixelRatio() const {
226 #if QT_VERSION >= 0x050000
227 return devicePixelRatio();
234 return toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble();
237 QPointF textPosition() {
238 return QPointF(splashWidth()/2 - 16, splashHeigth() - 40);
242 return QSize(width_ * pixelRatio(),height_ * pixelRatio());
245 double splashWidth() {
246 return splash_.width()/splashPixelRatio();
249 double splashHeigth() {
250 return splash_.height()/splashPixelRatio();
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 == "print")
1898 enable = doc_buffer->params().isExportable("dvi")
1899 && lyxrc.print_command != "none";
1900 else if (name == "character" || name == "symbols"
1901 || name == "mathdelimiter" || name == "mathmatrix") {
1902 if (!buf || buf->isReadonly())
1905 Cursor const & cur = currentBufferView()->cursor();
1906 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1909 else if (name == "latexlog")
1910 enable = FileName(doc_buffer->logName()).isReadableFile();
1911 else if (name == "spellchecker")
1912 enable = theSpellChecker()
1913 && !doc_buffer->isReadonly()
1914 && !doc_buffer->text().empty();
1915 else if (name == "vclog")
1916 enable = doc_buffer->lyxvc().inUse();
1920 case LFUN_DIALOG_UPDATE: {
1921 string const name = cmd.getArg(0);
1923 enable = name == "prefs";
1927 case LFUN_COMMAND_EXECUTE:
1929 case LFUN_MENU_OPEN:
1930 // Nothing to check.
1933 case LFUN_COMPLETION_INLINE:
1934 if (!d.current_work_area_
1935 || !d.current_work_area_->completer().inlinePossible(
1936 currentBufferView()->cursor()))
1940 case LFUN_COMPLETION_POPUP:
1941 if (!d.current_work_area_
1942 || !d.current_work_area_->completer().popupPossible(
1943 currentBufferView()->cursor()))
1948 if (!d.current_work_area_
1949 || !d.current_work_area_->completer().inlinePossible(
1950 currentBufferView()->cursor()))
1954 case LFUN_COMPLETION_ACCEPT:
1955 if (!d.current_work_area_
1956 || (!d.current_work_area_->completer().popupVisible()
1957 && !d.current_work_area_->completer().inlineVisible()
1958 && !d.current_work_area_->completer().completionAvailable()))
1962 case LFUN_COMPLETION_CANCEL:
1963 if (!d.current_work_area_
1964 || (!d.current_work_area_->completer().popupVisible()
1965 && !d.current_work_area_->completer().inlineVisible()))
1969 case LFUN_BUFFER_ZOOM_OUT:
1970 enable = doc_buffer && lyxrc.zoom > 10;
1973 case LFUN_BUFFER_ZOOM_IN:
1974 enable = doc_buffer;
1977 case LFUN_BUFFER_MOVE_NEXT:
1978 case LFUN_BUFFER_MOVE_PREVIOUS:
1979 // we do not cycle when moving
1980 case LFUN_BUFFER_NEXT:
1981 case LFUN_BUFFER_PREVIOUS:
1982 // because we cycle, it doesn't matter whether on first or last
1983 enable = (d.currentTabWorkArea()->count() > 1);
1985 case LFUN_BUFFER_SWITCH:
1986 // toggle on the current buffer, but do not toggle off
1987 // the other ones (is that a good idea?)
1989 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1990 flag.setOnOff(true);
1993 case LFUN_VC_REGISTER:
1994 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1996 case LFUN_VC_RENAME:
1997 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2000 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2002 case LFUN_VC_CHECK_IN:
2003 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2005 case LFUN_VC_CHECK_OUT:
2006 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2008 case LFUN_VC_LOCKING_TOGGLE:
2009 enable = doc_buffer && !doc_buffer->isReadonly()
2010 && doc_buffer->lyxvc().lockingToggleEnabled();
2011 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2013 case LFUN_VC_REVERT:
2014 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2016 case LFUN_VC_UNDO_LAST:
2017 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2019 case LFUN_VC_REPO_UPDATE:
2020 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2022 case LFUN_VC_COMMAND: {
2023 if (cmd.argument().empty())
2025 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2029 case LFUN_VC_COMPARE:
2030 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2033 case LFUN_SERVER_GOTO_FILE_ROW:
2035 case LFUN_FORWARD_SEARCH:
2036 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2039 case LFUN_FILE_INSERT_PLAINTEXT:
2040 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2041 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2044 case LFUN_SPELLING_CONTINUOUSLY:
2045 flag.setOnOff(lyxrc.spellcheck_continuously);
2053 flag.setEnabled(false);
2059 static FileName selectTemplateFile()
2061 FileDialog dlg(qt_("Select template file"));
2062 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2063 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2065 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2066 QStringList(qt_("LyX Documents (*.lyx)")));
2068 if (result.first == FileDialog::Later)
2070 if (result.second.isEmpty())
2072 return FileName(fromqstr(result.second));
2076 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2080 Buffer * newBuffer = 0;
2082 newBuffer = checkAndLoadLyXFile(filename);
2083 } catch (ExceptionMessage const & e) {
2090 message(_("Document not loaded."));
2094 setBuffer(newBuffer);
2095 newBuffer->errors("Parse");
2098 theSession().lastFiles().add(filename);
2104 void GuiView::openDocument(string const & fname)
2106 string initpath = lyxrc.document_path;
2108 if (documentBufferView()) {
2109 string const trypath = documentBufferView()->buffer().filePath();
2110 // If directory is writeable, use this as default.
2111 if (FileName(trypath).isDirWritable())
2117 if (fname.empty()) {
2118 FileDialog dlg(qt_("Select document to open"));
2119 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2120 dlg.setButton2(qt_("Examples|#E#e"),
2121 toqstr(addPath(package().system_support().absFileName(), "examples")));
2123 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2124 FileDialog::Result result =
2125 dlg.open(toqstr(initpath), filter);
2127 if (result.first == FileDialog::Later)
2130 filename = fromqstr(result.second);
2132 // check selected filename
2133 if (filename.empty()) {
2134 message(_("Canceled."));
2140 // get absolute path of file and add ".lyx" to the filename if
2142 FileName const fullname =
2143 fileSearch(string(), filename, "lyx", support::may_not_exist);
2144 if (!fullname.empty())
2145 filename = fullname.absFileName();
2147 if (!fullname.onlyPath().isDirectory()) {
2148 Alert::warning(_("Invalid filename"),
2149 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2150 from_utf8(fullname.absFileName())));
2154 // if the file doesn't exist and isn't already open (bug 6645),
2155 // let the user create one
2156 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2157 !LyXVC::file_not_found_hook(fullname)) {
2158 // the user specifically chose this name. Believe him.
2159 Buffer * const b = newFile(filename, string(), true);
2165 docstring const disp_fn = makeDisplayPath(filename);
2166 message(bformat(_("Opening document %1$s..."), disp_fn));
2169 Buffer * buf = loadDocument(fullname);
2171 str2 = bformat(_("Document %1$s opened."), disp_fn);
2172 if (buf->lyxvc().inUse())
2173 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2174 " " + _("Version control detected.");
2176 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2181 // FIXME: clean that
2182 static bool import(GuiView * lv, FileName const & filename,
2183 string const & format, ErrorList & errorList)
2185 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2187 string loader_format;
2188 vector<string> loaders = theConverters().loaders();
2189 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2190 vector<string>::const_iterator it = loaders.begin();
2191 vector<string>::const_iterator en = loaders.end();
2192 for (; it != en; ++it) {
2193 if (!theConverters().isReachable(format, *it))
2196 string const tofile =
2197 support::changeExtension(filename.absFileName(),
2198 formats.extension(*it));
2199 if (!theConverters().convert(0, filename, FileName(tofile),
2200 filename, format, *it, errorList))
2202 loader_format = *it;
2205 if (loader_format.empty()) {
2206 frontend::Alert::error(_("Couldn't import file"),
2207 bformat(_("No information for importing the format %1$s."),
2208 formats.prettyName(format)));
2212 loader_format = format;
2214 if (loader_format == "lyx") {
2215 Buffer * buf = lv->loadDocument(lyxfile);
2219 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2223 bool as_paragraphs = loader_format == "textparagraph";
2224 string filename2 = (loader_format == format) ? filename.absFileName()
2225 : support::changeExtension(filename.absFileName(),
2226 formats.extension(loader_format));
2227 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2229 guiApp->setCurrentView(lv);
2230 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2237 void GuiView::importDocument(string const & argument)
2240 string filename = split(argument, format, ' ');
2242 LYXERR(Debug::INFO, format << " file: " << filename);
2244 // need user interaction
2245 if (filename.empty()) {
2246 string initpath = lyxrc.document_path;
2247 if (documentBufferView()) {
2248 string const trypath = documentBufferView()->buffer().filePath();
2249 // If directory is writeable, use this as default.
2250 if (FileName(trypath).isDirWritable())
2254 docstring const text = bformat(_("Select %1$s file to import"),
2255 formats.prettyName(format));
2257 FileDialog dlg(toqstr(text));
2258 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2259 dlg.setButton2(qt_("Examples|#E#e"),
2260 toqstr(addPath(package().system_support().absFileName(), "examples")));
2262 docstring filter = formats.prettyName(format);
2265 filter += from_utf8(formats.extensions(format));
2268 FileDialog::Result result =
2269 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2271 if (result.first == FileDialog::Later)
2274 filename = fromqstr(result.second);
2276 // check selected filename
2277 if (filename.empty())
2278 message(_("Canceled."));
2281 if (filename.empty())
2284 // get absolute path of file
2285 FileName const fullname(support::makeAbsPath(filename));
2287 // Can happen if the user entered a path into the dialog
2289 if (fullname.onlyFileName().empty()) {
2290 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2291 "Aborting import."),
2292 from_utf8(fullname.absFileName()));
2293 frontend::Alert::error(_("File name error"), msg);
2294 message(_("Canceled."));
2299 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2301 // Check if the document already is open
2302 Buffer * buf = theBufferList().getBuffer(lyxfile);
2305 if (!closeBuffer()) {
2306 message(_("Canceled."));
2311 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2313 // if the file exists already, and we didn't do
2314 // -i lyx thefile.lyx, warn
2315 if (lyxfile.exists() && fullname != lyxfile) {
2317 docstring text = bformat(_("The document %1$s already exists.\n\n"
2318 "Do you want to overwrite that document?"), displaypath);
2319 int const ret = Alert::prompt(_("Overwrite document?"),
2320 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2323 message(_("Canceled."));
2328 message(bformat(_("Importing %1$s..."), displaypath));
2329 ErrorList errorList;
2330 if (import(this, fullname, format, errorList))
2331 message(_("imported."));
2333 message(_("file not imported!"));
2335 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2339 void GuiView::newDocument(string const & filename, bool from_template)
2341 FileName initpath(lyxrc.document_path);
2342 if (documentBufferView()) {
2343 FileName const trypath(documentBufferView()->buffer().filePath());
2344 // If directory is writeable, use this as default.
2345 if (trypath.isDirWritable())
2349 string templatefile;
2350 if (from_template) {
2351 templatefile = selectTemplateFile().absFileName();
2352 if (templatefile.empty())
2357 if (filename.empty())
2358 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2360 b = newFile(filename, templatefile, true);
2365 // If no new document could be created, it is unsure
2366 // whether there is a valid BufferView.
2367 if (currentBufferView())
2368 // Ensure the cursor is correctly positioned on screen.
2369 currentBufferView()->showCursor();
2373 void GuiView::insertLyXFile(docstring const & fname)
2375 BufferView * bv = documentBufferView();
2380 FileName filename(to_utf8(fname));
2381 if (filename.empty()) {
2382 // Launch a file browser
2384 string initpath = lyxrc.document_path;
2385 string const trypath = bv->buffer().filePath();
2386 // If directory is writeable, use this as default.
2387 if (FileName(trypath).isDirWritable())
2391 FileDialog dlg(qt_("Select LyX document to insert"));
2392 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2393 dlg.setButton2(qt_("Examples|#E#e"),
2394 toqstr(addPath(package().system_support().absFileName(),
2397 FileDialog::Result result = dlg.open(toqstr(initpath),
2398 QStringList(qt_("LyX Documents (*.lyx)")));
2400 if (result.first == FileDialog::Later)
2404 filename.set(fromqstr(result.second));
2406 // check selected filename
2407 if (filename.empty()) {
2408 // emit message signal.
2409 message(_("Canceled."));
2414 bv->insertLyXFile(filename);
2415 bv->buffer().errors("Parse");
2419 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2421 FileName fname = b.fileName();
2422 FileName const oldname = fname;
2424 if (!newname.empty()) {
2426 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2428 // Switch to this Buffer.
2431 // No argument? Ask user through dialog.
2433 FileDialog dlg(qt_("Choose a filename to save document as"));
2434 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2435 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2437 if (!isLyXFileName(fname.absFileName()))
2438 fname.changeExtension(".lyx");
2440 FileDialog::Result result =
2441 dlg.save(toqstr(fname.onlyPath().absFileName()),
2442 QStringList(qt_("LyX Documents (*.lyx)")),
2443 toqstr(fname.onlyFileName()));
2445 if (result.first == FileDialog::Later)
2448 fname.set(fromqstr(result.second));
2453 if (!isLyXFileName(fname.absFileName()))
2454 fname.changeExtension(".lyx");
2457 // fname is now the new Buffer location.
2459 // if there is already a Buffer open with this name, we do not want
2460 // to have another one. (the second test makes sure we're not just
2461 // trying to overwrite ourselves, which is fine.)
2462 if (theBufferList().exists(fname) && fname != oldname
2463 && theBufferList().getBuffer(fname) != &b) {
2464 docstring const text =
2465 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2466 "Please close it before attempting to overwrite it.\n"
2467 "Do you want to choose a new filename?"),
2468 from_utf8(fname.absFileName()));
2469 int const ret = Alert::prompt(_("Chosen File Already Open"),
2470 text, 0, 1, _("&Rename"), _("&Cancel"));
2472 case 0: return renameBuffer(b, docstring(), kind);
2473 case 1: return false;
2478 bool const existsLocal = fname.exists();
2479 bool const existsInVC = LyXVC::fileInVC(fname);
2480 if (existsLocal || existsInVC) {
2481 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2482 if (kind != LV_WRITE_AS && existsInVC) {
2483 // renaming to a name that is already in VC
2485 docstring text = bformat(_("The document %1$s "
2486 "is already registered.\n\n"
2487 "Do you want to choose a new name?"),
2489 docstring const title = (kind == LV_VC_RENAME) ?
2490 _("Rename document?") : _("Copy document?");
2491 docstring const button = (kind == LV_VC_RENAME) ?
2492 _("&Rename") : _("&Copy");
2493 int const ret = Alert::prompt(title, text, 0, 1,
2494 button, _("&Cancel"));
2496 case 0: return renameBuffer(b, docstring(), kind);
2497 case 1: return false;
2502 docstring text = bformat(_("The document %1$s "
2503 "already exists.\n\n"
2504 "Do you want to overwrite that document?"),
2506 int const ret = Alert::prompt(_("Overwrite document?"),
2507 text, 0, 2, _("&Overwrite"),
2508 _("&Rename"), _("&Cancel"));
2511 case 1: return renameBuffer(b, docstring(), kind);
2512 case 2: return false;
2518 case LV_VC_RENAME: {
2519 string msg = b.lyxvc().rename(fname);
2522 message(from_utf8(msg));
2526 string msg = b.lyxvc().copy(fname);
2529 message(from_utf8(msg));
2535 // LyXVC created the file already in case of LV_VC_RENAME or
2536 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2537 // relative paths of included stuff right if we moved e.g. from
2538 // /a/b.lyx to /a/c/b.lyx.
2540 bool const saved = saveBuffer(b, fname);
2547 struct PrettyNameComparator
2549 bool operator()(Format const *first, Format const *second) const {
2550 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2551 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2556 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2558 FileName fname = b.fileName();
2560 FileDialog dlg(qt_("Choose a filename to export the document as"));
2561 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2564 QString const anyformat = qt_("Guess from extension (*.*)");
2566 Formats::const_iterator it = formats.begin();
2567 vector<Format const *> export_formats;
2568 for (; it != formats.end(); ++it)
2569 if (it->documentFormat())
2570 export_formats.push_back(&(*it));
2571 PrettyNameComparator cmp;
2572 sort(export_formats.begin(), export_formats.end(), cmp);
2573 vector<Format const *>::const_iterator fit = export_formats.begin();
2574 map<QString, string> fmap;
2577 for (; fit != export_formats.end(); ++fit) {
2578 docstring const loc_prettyname =
2579 translateIfPossible(from_utf8((*fit)->prettyname()));
2580 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2582 from_ascii((*fit)->extension())));
2583 types << loc_filter;
2584 fmap[loc_filter] = (*fit)->name();
2585 if (from_ascii((*fit)->name()) == iformat) {
2586 filter = loc_filter;
2587 ext = (*fit)->extension();
2590 string ofname = fname.onlyFileName();
2592 ofname = support::changeExtension(ofname, ext);
2593 FileDialog::Result result =
2594 dlg.save(toqstr(fname.onlyPath().absFileName()),
2598 if (result.first != FileDialog::Chosen)
2602 fname.set(fromqstr(result.second));
2603 if (filter == anyformat)
2604 fmt_name = formats.getFormatFromExtension(fname.extension());
2606 fmt_name = fmap[filter];
2607 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2608 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2610 if (fmt_name.empty() || fname.empty())
2613 // fname is now the new Buffer location.
2614 if (FileName(fname).exists()) {
2615 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2616 docstring text = bformat(_("The document %1$s already "
2617 "exists.\n\nDo you want to "
2618 "overwrite that document?"),
2620 int const ret = Alert::prompt(_("Overwrite document?"),
2621 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2624 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2625 case 2: return false;
2629 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2632 return dr.dispatched();
2636 bool GuiView::saveBuffer(Buffer & b)
2638 return saveBuffer(b, FileName());
2642 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2644 if (workArea(b) && workArea(b)->inDialogMode())
2647 if (fn.empty() && b.isUnnamed())
2648 return renameBuffer(b, docstring());
2650 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2652 theSession().lastFiles().add(b.fileName());
2656 // Switch to this Buffer.
2659 // FIXME: we don't tell the user *WHY* the save failed !!
2660 docstring const file = makeDisplayPath(b.absFileName(), 30);
2661 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2662 "Do you want to rename the document and "
2663 "try again?"), file);
2664 int const ret = Alert::prompt(_("Rename and save?"),
2665 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2668 if (!renameBuffer(b, docstring()))
2677 return saveBuffer(b, fn);
2681 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2683 return closeWorkArea(wa, false);
2687 // We only want to close the buffer if it is not visible in other workareas
2688 // of the same view, nor in other views, and if this is not a child
2689 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2691 Buffer & buf = wa->bufferView().buffer();
2693 bool last_wa = d.countWorkAreasOf(buf) == 1
2694 && !inOtherView(buf) && !buf.parent();
2696 bool close_buffer = last_wa;
2699 if (lyxrc.close_buffer_with_last_view == "yes")
2701 else if (lyxrc.close_buffer_with_last_view == "no")
2702 close_buffer = false;
2705 if (buf.isUnnamed())
2706 file = from_utf8(buf.fileName().onlyFileName());
2708 file = buf.fileName().displayName(30);
2709 docstring const text = bformat(
2710 _("Last view on document %1$s is being closed.\n"
2711 "Would you like to close or hide the document?\n"
2713 "Hidden documents can be displayed back through\n"
2714 "the menu: View->Hidden->...\n"
2716 "To remove this question, set your preference in:\n"
2717 " Tools->Preferences->Look&Feel->UserInterface\n"
2719 int ret = Alert::prompt(_("Close or hide document?"),
2720 text, 0, 1, _("&Close"), _("&Hide"));
2721 close_buffer = (ret == 0);
2725 return closeWorkArea(wa, close_buffer);
2729 bool GuiView::closeBuffer()
2731 GuiWorkArea * wa = currentMainWorkArea();
2732 setCurrentWorkArea(wa);
2733 Buffer & buf = wa->bufferView().buffer();
2734 return wa && closeWorkArea(wa, !buf.parent());
2738 void GuiView::writeSession() const {
2739 GuiWorkArea const * active_wa = currentMainWorkArea();
2740 for (int i = 0; i < d.splitter_->count(); ++i) {
2741 TabWorkArea * twa = d.tabWorkArea(i);
2742 for (int j = 0; j < twa->count(); ++j) {
2743 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2744 Buffer & buf = wa->bufferView().buffer();
2745 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2751 bool GuiView::closeBufferAll()
2753 // Close the workareas in all other views
2754 QList<int> const ids = guiApp->viewIds();
2755 for (int i = 0; i != ids.size(); ++i) {
2756 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2760 // Close our own workareas
2761 if (!closeWorkAreaAll())
2764 // Now close the hidden buffers. We prevent hidden buffers from being
2765 // dirty, so we can just close them.
2766 theBufferList().closeAll();
2771 bool GuiView::closeWorkAreaAll()
2773 setCurrentWorkArea(currentMainWorkArea());
2775 // We might be in a situation that there is still a tabWorkArea, but
2776 // there are no tabs anymore. This can happen when we get here after a
2777 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2778 // many TabWorkArea's have no documents anymore.
2781 // We have to call count() each time, because it can happen that
2782 // more than one splitter will disappear in one iteration (bug 5998).
2783 for (; d.splitter_->count() > empty_twa; ) {
2784 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2786 if (twa->count() == 0)
2789 setCurrentWorkArea(twa->currentWorkArea());
2790 if (!closeTabWorkArea(twa))
2798 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2803 Buffer & buf = wa->bufferView().buffer();
2805 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2806 Alert::warning(_("Close document"),
2807 _("Document could not be closed because it is being processed by LyX."));
2812 return closeBuffer(buf);
2814 if (!inMultiTabs(wa))
2815 if (!saveBufferIfNeeded(buf, true))
2823 bool GuiView::closeBuffer(Buffer & buf)
2825 // If we are in a close_event all children will be closed in some time,
2826 // so no need to do it here. This will ensure that the children end up
2827 // in the session file in the correct order. If we close the master
2828 // buffer, we can close or release the child buffers here too.
2829 bool success = true;
2831 ListOfBuffers clist = buf.getChildren();
2832 ListOfBuffers::const_iterator it = clist.begin();
2833 ListOfBuffers::const_iterator const bend = clist.end();
2834 for (; it != bend; ++it) {
2835 // If a child is dirty, do not close
2836 // without user intervention
2837 //FIXME: should we look in other tabworkareas?
2838 Buffer * child_buf = *it;
2839 GuiWorkArea * child_wa = workArea(*child_buf);
2841 if (!closeWorkArea(child_wa, true)) {
2846 theBufferList().releaseChild(&buf, child_buf);
2850 // goto bookmark to update bookmark pit.
2851 //FIXME: we should update only the bookmarks related to this buffer!
2852 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2853 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2854 guiApp->gotoBookmark(i+1, false, false);
2856 if (saveBufferIfNeeded(buf, false)) {
2857 buf.removeAutosaveFile();
2858 theBufferList().release(&buf);
2862 // open all children again to avoid a crash because of dangling
2863 // pointers (bug 6603)
2869 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2871 while (twa == d.currentTabWorkArea()) {
2872 twa->setCurrentIndex(twa->count()-1);
2874 GuiWorkArea * wa = twa->currentWorkArea();
2875 Buffer & b = wa->bufferView().buffer();
2877 // We only want to close the buffer if the same buffer is not visible
2878 // in another view, and if this is not a child and if we are closing
2879 // a view (not a tabgroup).
2880 bool const close_buffer =
2881 !inOtherView(b) && !b.parent() && closing_;
2883 if (!closeWorkArea(wa, close_buffer))
2890 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2892 if (buf.isClean() || buf.paragraphs().empty())
2895 // Switch to this Buffer.
2900 if (buf.isUnnamed())
2901 file = from_utf8(buf.fileName().onlyFileName());
2903 file = buf.fileName().displayName(30);
2905 // Bring this window to top before asking questions.
2910 if (hiding && buf.isUnnamed()) {
2911 docstring const text = bformat(_("The document %1$s has not been "
2912 "saved yet.\n\nDo you want to save "
2913 "the document?"), file);
2914 ret = Alert::prompt(_("Save new document?"),
2915 text, 0, 1, _("&Save"), _("&Cancel"));
2919 docstring const text = bformat(_("The document %1$s has unsaved changes."
2920 "\n\nDo you want to save the document or discard the changes?"), file);
2921 ret = Alert::prompt(_("Save changed document?"),
2922 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2927 if (!saveBuffer(buf))
2931 // If we crash after this we could have no autosave file
2932 // but I guess this is really improbable (Jug).
2933 // Sometimes improbable things happen:
2934 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2935 // buf.removeAutosaveFile();
2937 // revert all changes
2948 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2950 Buffer & buf = wa->bufferView().buffer();
2952 for (int i = 0; i != d.splitter_->count(); ++i) {
2953 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2954 if (wa_ && wa_ != wa)
2957 return inOtherView(buf);
2961 bool GuiView::inOtherView(Buffer & buf)
2963 QList<int> const ids = guiApp->viewIds();
2965 for (int i = 0; i != ids.size(); ++i) {
2969 if (guiApp->view(ids[i]).workArea(buf))
2976 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2978 if (!documentBufferView())
2981 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2982 Buffer * const curbuf = &documentBufferView()->buffer();
2983 int nwa = twa->count();
2984 for (int i = 0; i < nwa; ++i) {
2985 if (&workArea(i)->bufferView().buffer() == curbuf) {
2987 if (np == NEXTBUFFER)
2988 next_index = (i == nwa - 1 ? 0 : i + 1);
2990 next_index = (i == 0 ? nwa - 1 : i - 1);
2992 twa->moveTab(i, next_index);
2994 setBuffer(&workArea(next_index)->bufferView().buffer());
3002 /// make sure the document is saved
3003 static bool ensureBufferClean(Buffer * buffer)
3005 LASSERT(buffer, return false);
3006 if (buffer->isClean() && !buffer->isUnnamed())
3009 docstring const file = buffer->fileName().displayName(30);
3012 if (!buffer->isUnnamed()) {
3013 text = bformat(_("The document %1$s has unsaved "
3014 "changes.\n\nDo you want to save "
3015 "the document?"), file);
3016 title = _("Save changed document?");
3019 text = bformat(_("The document %1$s has not been "
3020 "saved yet.\n\nDo you want to save "
3021 "the document?"), file);
3022 title = _("Save new document?");
3024 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3027 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3029 return buffer->isClean() && !buffer->isUnnamed();
3033 bool GuiView::reloadBuffer(Buffer & buf)
3035 Buffer::ReadStatus status = buf.reload();
3036 return status == Buffer::ReadSuccess;
3040 void GuiView::checkExternallyModifiedBuffers()
3042 BufferList::iterator bit = theBufferList().begin();
3043 BufferList::iterator const bend = theBufferList().end();
3044 for (; bit != bend; ++bit) {
3045 Buffer * buf = *bit;
3046 if (buf->fileName().exists()
3047 && buf->isExternallyModified(Buffer::checksum_method)) {
3048 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3049 " Reload now? Any local changes will be lost."),
3050 from_utf8(buf->absFileName()));
3051 int const ret = Alert::prompt(_("Reload externally changed document?"),
3052 text, 0, 1, _("&Reload"), _("&Cancel"));
3060 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3062 Buffer * buffer = documentBufferView()
3063 ? &(documentBufferView()->buffer()) : 0;
3065 switch (cmd.action()) {
3066 case LFUN_VC_REGISTER:
3067 if (!buffer || !ensureBufferClean(buffer))
3069 if (!buffer->lyxvc().inUse()) {
3070 if (buffer->lyxvc().registrer()) {
3071 reloadBuffer(*buffer);
3072 dr.clearMessageUpdate();
3077 case LFUN_VC_RENAME:
3078 case LFUN_VC_COPY: {
3079 if (!buffer || !ensureBufferClean(buffer))
3081 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3082 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3083 // Some changes are not yet committed.
3084 // We test here and not in getStatus(), since
3085 // this test is expensive.
3087 LyXVC::CommandResult ret =
3088 buffer->lyxvc().checkIn(log);
3090 if (ret == LyXVC::ErrorCommand ||
3091 ret == LyXVC::VCSuccess)
3092 reloadBuffer(*buffer);
3093 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3094 frontend::Alert::error(
3095 _("Revision control error."),
3096 _("Document could not be checked in."));
3100 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3101 LV_VC_RENAME : LV_VC_COPY;
3102 renameBuffer(*buffer, cmd.argument(), kind);
3107 case LFUN_VC_CHECK_IN:
3108 if (!buffer || !ensureBufferClean(buffer))
3110 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3112 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3114 // Only skip reloading if the checkin was cancelled or
3115 // an error occurred before the real checkin VCS command
3116 // was executed, since the VCS might have changed the
3117 // file even if it could not checkin successfully.
3118 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3119 reloadBuffer(*buffer);
3123 case LFUN_VC_CHECK_OUT:
3124 if (!buffer || !ensureBufferClean(buffer))
3126 if (buffer->lyxvc().inUse()) {
3127 dr.setMessage(buffer->lyxvc().checkOut());
3128 reloadBuffer(*buffer);
3132 case LFUN_VC_LOCKING_TOGGLE:
3133 LASSERT(buffer, return);
3134 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3136 if (buffer->lyxvc().inUse()) {
3137 string res = buffer->lyxvc().lockingToggle();
3139 frontend::Alert::error(_("Revision control error."),
3140 _("Error when setting the locking property."));
3143 reloadBuffer(*buffer);
3148 case LFUN_VC_REVERT:
3149 LASSERT(buffer, return);
3150 if (buffer->lyxvc().revert()) {
3151 reloadBuffer(*buffer);
3152 dr.clearMessageUpdate();
3156 case LFUN_VC_UNDO_LAST:
3157 LASSERT(buffer, return);
3158 buffer->lyxvc().undoLast();
3159 reloadBuffer(*buffer);
3160 dr.clearMessageUpdate();
3163 case LFUN_VC_REPO_UPDATE:
3164 LASSERT(buffer, return);
3165 if (ensureBufferClean(buffer)) {
3166 dr.setMessage(buffer->lyxvc().repoUpdate());
3167 checkExternallyModifiedBuffers();
3171 case LFUN_VC_COMMAND: {
3172 string flag = cmd.getArg(0);
3173 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3176 if (contains(flag, 'M')) {
3177 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3180 string path = cmd.getArg(1);
3181 if (contains(path, "$$p") && buffer)
3182 path = subst(path, "$$p", buffer->filePath());
3183 LYXERR(Debug::LYXVC, "Directory: " << path);
3185 if (!pp.isReadableDirectory()) {
3186 lyxerr << _("Directory is not accessible.") << endl;
3189 support::PathChanger p(pp);
3191 string command = cmd.getArg(2);
3192 if (command.empty())
3195 command = subst(command, "$$i", buffer->absFileName());
3196 command = subst(command, "$$p", buffer->filePath());
3198 command = subst(command, "$$m", to_utf8(message));
3199 LYXERR(Debug::LYXVC, "Command: " << command);
3201 one.startscript(Systemcall::Wait, command);
3205 if (contains(flag, 'I'))
3206 buffer->markDirty();
3207 if (contains(flag, 'R'))
3208 reloadBuffer(*buffer);
3213 case LFUN_VC_COMPARE: {
3215 if (cmd.argument().empty()) {
3216 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3220 string rev1 = cmd.getArg(0);
3224 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3227 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3228 f2 = buffer->absFileName();
3230 string rev2 = cmd.getArg(1);
3234 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3238 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3239 f1 << "\n" << f2 << "\n" );
3240 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3241 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3251 void GuiView::openChildDocument(string const & fname)
3253 LASSERT(documentBufferView(), return);
3254 Buffer & buffer = documentBufferView()->buffer();
3255 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3256 documentBufferView()->saveBookmark(false);
3258 if (theBufferList().exists(filename)) {
3259 child = theBufferList().getBuffer(filename);
3262 message(bformat(_("Opening child document %1$s..."),
3263 makeDisplayPath(filename.absFileName())));
3264 child = loadDocument(filename, false);
3266 // Set the parent name of the child document.
3267 // This makes insertion of citations and references in the child work,
3268 // when the target is in the parent or another child document.
3270 child->setParent(&buffer);
3274 bool GuiView::goToFileRow(string const & argument)
3278 size_t i = argument.find_last_of(' ');
3279 if (i != string::npos) {
3280 file_name = os::internal_path(trim(argument.substr(0, i)));
3281 istringstream is(argument.substr(i + 1));
3286 if (i == string::npos) {
3287 LYXERR0("Wrong argument: " << argument);
3291 string const abstmp = package().temp_dir().absFileName();
3292 string const realtmp = package().temp_dir().realPath();
3293 // We have to use os::path_prefix_is() here, instead of
3294 // simply prefixIs(), because the file name comes from
3295 // an external application and may need case adjustment.
3296 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3297 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3298 // Needed by inverse dvi search. If it is a file
3299 // in tmpdir, call the apropriated function.
3300 // If tmpdir is a symlink, we may have the real
3301 // path passed back, so we correct for that.
3302 if (!prefixIs(file_name, abstmp))
3303 file_name = subst(file_name, realtmp, abstmp);
3304 buf = theBufferList().getBufferFromTmp(file_name);
3306 // Must replace extension of the file to be .lyx
3307 // and get full path
3308 FileName const s = fileSearch(string(),
3309 support::changeExtension(file_name, ".lyx"), "lyx");
3310 // Either change buffer or load the file
3311 if (theBufferList().exists(s))
3312 buf = theBufferList().getBuffer(s);
3313 else if (s.exists()) {
3314 buf = loadDocument(s);
3319 _("File does not exist: %1$s"),
3320 makeDisplayPath(file_name)));
3326 _("No buffer for file: %1$s."),
3327 makeDisplayPath(file_name))
3332 documentBufferView()->setCursorFromRow(row);
3338 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3340 Buffer::ExportStatus const status = func(format);
3342 // the cloning operation will have produced a clone of the entire set of
3343 // documents, starting from the master. so we must delete those.
3344 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3346 busyBuffers.remove(orig);
3351 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3353 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3354 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3358 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3360 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3361 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3365 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3367 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3368 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3372 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3373 string const & argument,
3374 Buffer const * used_buffer,
3375 docstring const & msg,
3376 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3377 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3378 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3383 string format = argument;
3385 format = used_buffer->params().getDefaultOutputFormat();
3386 processing_format = format;
3388 progress_->clearMessages();
3391 #if EXPORT_in_THREAD
3392 GuiViewPrivate::busyBuffers.insert(used_buffer);
3393 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3394 if (!cloned_buffer) {
3395 Alert::error(_("Export Error"),
3396 _("Error cloning the Buffer."));
3399 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3404 setPreviewFuture(f);
3405 last_export_format = used_buffer->params().bufferFormat();
3408 // We are asynchronous, so we don't know here anything about the success
3411 Buffer::ExportStatus status;
3413 // TODO check here if it breaks exporting with Qt < 4.4
3414 status = (used_buffer->*syncFunc)(format, true);
3415 } else if (previewFunc) {
3416 status = (used_buffer->*previewFunc)(format);
3419 handleExportStatus(gv_, status, format);
3421 return (status == Buffer::ExportSuccess
3422 || status == Buffer::PreviewSuccess);
3426 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3428 BufferView * bv = currentBufferView();
3429 LASSERT(bv, return);
3431 // Let the current BufferView dispatch its own actions.
3432 bv->dispatch(cmd, dr);
3433 if (dr.dispatched())
3436 // Try with the document BufferView dispatch if any.
3437 BufferView * doc_bv = documentBufferView();
3438 if (doc_bv && doc_bv != bv) {
3439 doc_bv->dispatch(cmd, dr);
3440 if (dr.dispatched())
3444 // Then let the current Cursor dispatch its own actions.
3445 bv->cursor().dispatch(cmd);
3447 // update completion. We do it here and not in
3448 // processKeySym to avoid another redraw just for a
3449 // changed inline completion
3450 if (cmd.origin() == FuncRequest::KEYBOARD) {
3451 if (cmd.action() == LFUN_SELF_INSERT
3452 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3453 updateCompletion(bv->cursor(), true, true);
3454 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3455 updateCompletion(bv->cursor(), false, true);
3457 updateCompletion(bv->cursor(), false, false);
3460 dr = bv->cursor().result();
3464 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3466 BufferView * bv = currentBufferView();
3467 // By default we won't need any update.
3468 dr.screenUpdate(Update::None);
3469 // assume cmd will be dispatched
3470 dr.dispatched(true);
3472 Buffer * doc_buffer = documentBufferView()
3473 ? &(documentBufferView()->buffer()) : 0;
3475 if (cmd.origin() == FuncRequest::TOC) {
3476 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3477 // FIXME: do we need to pass a DispatchResult object here?
3478 toc->doDispatch(bv->cursor(), cmd);
3482 string const argument = to_utf8(cmd.argument());
3484 switch(cmd.action()) {
3485 case LFUN_BUFFER_CHILD_OPEN:
3486 openChildDocument(to_utf8(cmd.argument()));
3489 case LFUN_BUFFER_IMPORT:
3490 importDocument(to_utf8(cmd.argument()));
3493 case LFUN_BUFFER_EXPORT: {
3496 FileName target_dir = doc_buffer->fileName().onlyPath();
3497 string const dest = cmd.getArg(1);
3498 if (!dest.empty() && FileName::isAbsolute(dest))
3499 target_dir = FileName(support::onlyPath(dest));
3500 // GCC only sees strfwd.h when building merged
3501 if (::lyx::operator==(cmd.argument(), "custom")) {
3502 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3505 if (!target_dir.isDirWritable()) {
3506 exportBufferAs(*doc_buffer, cmd.argument());
3509 /* TODO/Review: Is it a problem to also export the children?
3510 See the update_unincluded flag */
3511 d.asyncBufferProcessing(argument,
3514 &GuiViewPrivate::exportAndDestroy,
3517 // TODO Inform user about success
3521 case LFUN_BUFFER_EXPORT_AS: {
3522 LASSERT(doc_buffer, break);
3523 docstring f = cmd.argument();
3525 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3526 exportBufferAs(*doc_buffer, f);
3530 case LFUN_BUFFER_UPDATE: {
3531 d.asyncBufferProcessing(argument,
3534 &GuiViewPrivate::compileAndDestroy,
3539 case LFUN_BUFFER_VIEW: {
3540 d.asyncBufferProcessing(argument,
3542 _("Previewing ..."),
3543 &GuiViewPrivate::previewAndDestroy,
3548 case LFUN_MASTER_BUFFER_UPDATE: {
3549 d.asyncBufferProcessing(argument,
3550 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3552 &GuiViewPrivate::compileAndDestroy,
3557 case LFUN_MASTER_BUFFER_VIEW: {
3558 d.asyncBufferProcessing(argument,
3559 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3561 &GuiViewPrivate::previewAndDestroy,
3562 0, &Buffer::preview);
3565 case LFUN_BUFFER_SWITCH: {
3566 string const file_name = to_utf8(cmd.argument());
3567 if (!FileName::isAbsolute(file_name)) {
3569 dr.setMessage(_("Absolute filename expected."));
3573 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3576 dr.setMessage(_("Document not loaded"));
3580 // Do we open or switch to the buffer in this view ?
3581 if (workArea(*buffer)
3582 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3587 // Look for the buffer in other views
3588 QList<int> const ids = guiApp->viewIds();
3590 for (; i != ids.size(); ++i) {
3591 GuiView & gv = guiApp->view(ids[i]);
3592 if (gv.workArea(*buffer)) {
3593 gv.activateWindow();
3594 gv.setBuffer(buffer);
3599 // If necessary, open a new window as a last resort
3600 if (i == ids.size()) {
3601 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3607 case LFUN_BUFFER_NEXT:
3608 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3611 case LFUN_BUFFER_MOVE_NEXT:
3612 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3615 case LFUN_BUFFER_PREVIOUS:
3616 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3619 case LFUN_BUFFER_MOVE_PREVIOUS:
3620 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3623 case LFUN_COMMAND_EXECUTE: {
3624 bool const show_it = cmd.argument() != "off";
3625 // FIXME: this is a hack, "minibuffer" should not be
3627 if (GuiToolbar * t = toolbar("minibuffer")) {
3628 t->setVisible(show_it);
3629 if (show_it && t->commandBuffer())
3630 t->commandBuffer()->setFocus();
3634 case LFUN_DROP_LAYOUTS_CHOICE:
3635 d.layout_->showPopup();
3638 case LFUN_MENU_OPEN:
3639 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3640 menu->exec(QCursor::pos());
3643 case LFUN_FILE_INSERT:
3644 insertLyXFile(cmd.argument());
3647 case LFUN_FILE_INSERT_PLAINTEXT:
3648 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3649 string const fname = to_utf8(cmd.argument());
3650 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3651 dr.setMessage(_("Absolute filename expected."));
3655 FileName filename(fname);
3656 if (fname.empty()) {
3657 FileDialog dlg(qt_("Select file to insert"));
3659 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3660 QStringList(qt_("All Files (*)")));
3662 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3663 dr.setMessage(_("Canceled."));
3667 filename.set(fromqstr(result.second));
3671 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3672 bv->dispatch(new_cmd, dr);
3677 case LFUN_BUFFER_RELOAD: {
3678 LASSERT(doc_buffer, break);
3681 if (!doc_buffer->isClean()) {
3682 docstring const file =
3683 makeDisplayPath(doc_buffer->absFileName(), 20);
3684 docstring text = bformat(_("Any changes will be lost. "
3685 "Are you sure you want to revert to the saved version "
3686 "of the document %1$s?"), file);
3687 ret = Alert::prompt(_("Revert to saved document?"),
3688 text, 1, 1, _("&Revert"), _("&Cancel"));
3692 doc_buffer->markClean();
3693 reloadBuffer(*doc_buffer);
3694 dr.forceBufferUpdate();
3699 case LFUN_BUFFER_WRITE:
3700 LASSERT(doc_buffer, break);
3701 saveBuffer(*doc_buffer);
3704 case LFUN_BUFFER_WRITE_AS:
3705 LASSERT(doc_buffer, break);
3706 renameBuffer(*doc_buffer, cmd.argument());
3709 case LFUN_BUFFER_WRITE_ALL: {
3710 Buffer * first = theBufferList().first();
3713 message(_("Saving all documents..."));
3714 // We cannot use a for loop as the buffer list cycles.
3717 if (!b->isClean()) {
3719 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3721 b = theBufferList().next(b);
3722 } while (b != first);
3723 dr.setMessage(_("All documents saved."));
3727 case LFUN_BUFFER_CLOSE:
3731 case LFUN_BUFFER_CLOSE_ALL:
3735 case LFUN_TOOLBAR_TOGGLE: {
3736 string const name = cmd.getArg(0);
3737 if (GuiToolbar * t = toolbar(name))
3742 case LFUN_DIALOG_UPDATE: {
3743 string const name = to_utf8(cmd.argument());
3744 if (name == "prefs" || name == "document")
3745 updateDialog(name, string());
3746 else if (name == "paragraph")
3747 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3748 else if (currentBufferView()) {
3749 Inset * inset = currentBufferView()->editedInset(name);
3750 // Can only update a dialog connected to an existing inset
3752 // FIXME: get rid of this indirection; GuiView ask the inset
3753 // if he is kind enough to update itself...
3754 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3755 //FIXME: pass DispatchResult here?
3756 inset->dispatch(currentBufferView()->cursor(), fr);
3762 case LFUN_DIALOG_TOGGLE: {
3763 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3764 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3765 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3769 case LFUN_DIALOG_DISCONNECT_INSET:
3770 disconnectDialog(to_utf8(cmd.argument()));
3773 case LFUN_DIALOG_HIDE: {
3774 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3778 case LFUN_DIALOG_SHOW: {
3779 string const name = cmd.getArg(0);
3780 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3782 if (name == "character") {
3783 data = freefont2string();
3785 showDialog("character", data);
3786 } else if (name == "latexlog") {
3787 Buffer::LogType type;
3788 string const logfile = doc_buffer->logName(&type);
3790 case Buffer::latexlog:
3793 case Buffer::buildlog:
3797 data += Lexer::quoteString(logfile);
3798 showDialog("log", data);
3799 } else if (name == "vclog") {
3800 string const data = "vc " +
3801 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3802 showDialog("log", data);
3803 } else if (name == "symbols") {
3804 data = bv->cursor().getEncoding()->name();
3806 showDialog("symbols", data);
3808 } else if (name == "prefs" && isFullScreen()) {
3809 lfunUiToggle("fullscreen");
3810 showDialog("prefs", data);
3812 showDialog(name, data);
3817 dr.setMessage(cmd.argument());
3820 case LFUN_UI_TOGGLE: {
3821 string arg = cmd.getArg(0);
3822 if (!lfunUiToggle(arg)) {
3823 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3824 dr.setMessage(bformat(msg, from_utf8(arg)));
3826 // Make sure the keyboard focus stays in the work area.
3831 case LFUN_VIEW_SPLIT: {
3832 LASSERT(doc_buffer, break);
3833 string const orientation = cmd.getArg(0);
3834 d.splitter_->setOrientation(orientation == "vertical"
3835 ? Qt::Vertical : Qt::Horizontal);
3836 TabWorkArea * twa = addTabWorkArea();
3837 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3838 setCurrentWorkArea(wa);
3841 case LFUN_TAB_GROUP_CLOSE:
3842 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3843 closeTabWorkArea(twa);
3844 d.current_work_area_ = 0;
3845 twa = d.currentTabWorkArea();
3846 // Switch to the next GuiWorkArea in the found TabWorkArea.
3848 // Make sure the work area is up to date.
3849 setCurrentWorkArea(twa->currentWorkArea());
3851 setCurrentWorkArea(0);
3856 case LFUN_VIEW_CLOSE:
3857 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3858 closeWorkArea(twa->currentWorkArea());
3859 d.current_work_area_ = 0;
3860 twa = d.currentTabWorkArea();
3861 // Switch to the next GuiWorkArea in the found TabWorkArea.
3863 // Make sure the work area is up to date.
3864 setCurrentWorkArea(twa->currentWorkArea());
3866 setCurrentWorkArea(0);
3871 case LFUN_COMPLETION_INLINE:
3872 if (d.current_work_area_)
3873 d.current_work_area_->completer().showInline();
3876 case LFUN_COMPLETION_POPUP:
3877 if (d.current_work_area_)
3878 d.current_work_area_->completer().showPopup();
3883 if (d.current_work_area_)
3884 d.current_work_area_->completer().tab();
3887 case LFUN_COMPLETION_CANCEL:
3888 if (d.current_work_area_) {
3889 if (d.current_work_area_->completer().popupVisible())
3890 d.current_work_area_->completer().hidePopup();
3892 d.current_work_area_->completer().hideInline();
3896 case LFUN_COMPLETION_ACCEPT:
3897 if (d.current_work_area_)
3898 d.current_work_area_->completer().activate();
3901 case LFUN_BUFFER_ZOOM_IN:
3902 case LFUN_BUFFER_ZOOM_OUT:
3903 if (cmd.argument().empty()) {
3904 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3909 lyxrc.zoom += convert<int>(cmd.argument());
3911 if (lyxrc.zoom < 10)
3914 // The global QPixmapCache is used in GuiPainter to cache text
3915 // painting so we must reset it.
3916 QPixmapCache::clear();
3917 guiApp->fontLoader().update();
3918 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3921 case LFUN_VC_REGISTER:
3922 case LFUN_VC_RENAME:
3924 case LFUN_VC_CHECK_IN:
3925 case LFUN_VC_CHECK_OUT:
3926 case LFUN_VC_REPO_UPDATE:
3927 case LFUN_VC_LOCKING_TOGGLE:
3928 case LFUN_VC_REVERT:
3929 case LFUN_VC_UNDO_LAST:
3930 case LFUN_VC_COMMAND:
3931 case LFUN_VC_COMPARE:
3932 dispatchVC(cmd, dr);
3935 case LFUN_SERVER_GOTO_FILE_ROW:
3936 goToFileRow(to_utf8(cmd.argument()));
3939 case LFUN_FORWARD_SEARCH: {
3940 Buffer const * doc_master = doc_buffer->masterBuffer();
3941 FileName const path(doc_master->temppath());
3942 string const texname = doc_master->isChild(doc_buffer)
3943 ? DocFileName(changeExtension(
3944 doc_buffer->absFileName(),
3945 "tex")).mangledFileName()
3946 : doc_buffer->latexName();
3947 string const fulltexname =
3948 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3949 string const mastername =
3950 removeExtension(doc_master->latexName());
3951 FileName const dviname(addName(path.absFileName(),
3952 addExtension(mastername, "dvi")));
3953 FileName const pdfname(addName(path.absFileName(),
3954 addExtension(mastername, "pdf")));
3955 bool const have_dvi = dviname.exists();
3956 bool const have_pdf = pdfname.exists();
3957 if (!have_dvi && !have_pdf) {
3958 dr.setMessage(_("Please, preview the document first."));
3961 string outname = dviname.onlyFileName();
3962 string command = lyxrc.forward_search_dvi;
3963 if (!have_dvi || (have_pdf &&
3964 pdfname.lastModified() > dviname.lastModified())) {
3965 outname = pdfname.onlyFileName();
3966 command = lyxrc.forward_search_pdf;
3969 DocIterator tmpcur = bv->cursor();
3971 while (tmpcur.inMathed())
3973 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3974 tmpcur.paragraph().id(), tmpcur.pos());
3975 LYXERR(Debug::ACTION, "Forward search: row:" << row
3976 << " id:" << tmpcur.paragraph().id());
3977 if (!row || command.empty()) {
3978 dr.setMessage(_("Couldn't proceed."));
3981 string texrow = convert<string>(row);
3983 command = subst(command, "$$n", texrow);
3984 command = subst(command, "$$f", fulltexname);
3985 command = subst(command, "$$t", texname);
3986 command = subst(command, "$$o", outname);
3988 PathChanger p(path);
3990 one.startscript(Systemcall::DontWait, command);
3994 case LFUN_SPELLING_CONTINUOUSLY:
3995 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3996 dr.screenUpdate(Update::Force | Update::FitCursor);
4000 // The LFUN must be for one of BufferView, Buffer or Cursor;
4002 dispatchToBufferView(cmd, dr);
4006 // Part of automatic menu appearance feature.
4007 if (isFullScreen()) {
4008 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4012 // Need to update bv because many LFUNs here might have destroyed it
4013 bv = currentBufferView();
4015 // Clear non-empty selections
4016 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4018 Cursor & cur = bv->cursor();
4019 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4020 cur.clearSelection();
4026 bool GuiView::lfunUiToggle(string const & ui_component)
4028 if (ui_component == "scrollbar") {
4029 // hide() is of no help
4030 if (d.current_work_area_->verticalScrollBarPolicy() ==
4031 Qt::ScrollBarAlwaysOff)
4033 d.current_work_area_->setVerticalScrollBarPolicy(
4034 Qt::ScrollBarAsNeeded);
4036 d.current_work_area_->setVerticalScrollBarPolicy(
4037 Qt::ScrollBarAlwaysOff);
4038 } else if (ui_component == "statusbar") {
4039 statusBar()->setVisible(!statusBar()->isVisible());
4040 } else if (ui_component == "menubar") {
4041 menuBar()->setVisible(!menuBar()->isVisible());
4043 if (ui_component == "frame") {
4045 getContentsMargins(&l, &t, &r, &b);
4046 //are the frames in default state?
4047 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4049 setContentsMargins(-2, -2, -2, -2);
4051 setContentsMargins(0, 0, 0, 0);
4054 if (ui_component == "fullscreen") {
4062 void GuiView::toggleFullScreen()
4064 if (isFullScreen()) {
4065 for (int i = 0; i != d.splitter_->count(); ++i)
4066 d.tabWorkArea(i)->setFullScreen(false);
4067 setContentsMargins(0, 0, 0, 0);
4068 setWindowState(windowState() ^ Qt::WindowFullScreen);
4071 statusBar()->show();
4074 hideDialogs("prefs", 0);
4075 for (int i = 0; i != d.splitter_->count(); ++i)
4076 d.tabWorkArea(i)->setFullScreen(true);
4077 setContentsMargins(-2, -2, -2, -2);
4079 setWindowState(windowState() ^ Qt::WindowFullScreen);
4080 if (lyxrc.full_screen_statusbar)
4081 statusBar()->hide();
4082 if (lyxrc.full_screen_menubar)
4084 if (lyxrc.full_screen_toolbars) {
4085 ToolbarMap::iterator end = d.toolbars_.end();
4086 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4091 // give dialogs like the TOC a chance to adapt
4096 Buffer const * GuiView::updateInset(Inset const * inset)
4101 Buffer const * inset_buffer = &(inset->buffer());
4103 for (int i = 0; i != d.splitter_->count(); ++i) {
4104 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4107 Buffer const * buffer = &(wa->bufferView().buffer());
4108 if (inset_buffer == buffer)
4109 wa->scheduleRedraw();
4111 return inset_buffer;
4115 void GuiView::restartCursor()
4117 /* When we move around, or type, it's nice to be able to see
4118 * the cursor immediately after the keypress.
4120 if (d.current_work_area_)
4121 d.current_work_area_->startBlinkingCursor();
4123 // Take this occasion to update the other GUI elements.
4129 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4131 if (d.current_work_area_)
4132 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4137 // This list should be kept in sync with the list of insets in
4138 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4139 // dialog should have the same name as the inset.
4140 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4141 // docs in LyXAction.cpp.
4143 char const * const dialognames[] = {
4145 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4146 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4147 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4148 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4149 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4150 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4151 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4152 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4154 char const * const * const end_dialognames =
4155 dialognames + (sizeof(dialognames) / sizeof(char *));
4159 cmpCStr(char const * name) : name_(name) {}
4160 bool operator()(char const * other) {
4161 return strcmp(other, name_) == 0;
4168 bool isValidName(string const & name)
4170 return find_if(dialognames, end_dialognames,
4171 cmpCStr(name.c_str())) != end_dialognames;
4177 void GuiView::resetDialogs()
4179 // Make sure that no LFUN uses any GuiView.
4180 guiApp->setCurrentView(0);
4184 constructToolbars();
4185 guiApp->menus().fillMenuBar(menuBar(), this, false);
4186 d.layout_->updateContents(true);
4187 // Now update controls with current buffer.
4188 guiApp->setCurrentView(this);
4194 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4196 if (!isValidName(name))
4199 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4201 if (it != d.dialogs_.end()) {
4203 it->second->hideView();
4204 return it->second.get();
4207 Dialog * dialog = build(name);
4208 d.dialogs_[name].reset(dialog);
4209 if (lyxrc.allow_geometry_session)
4210 dialog->restoreSession();
4217 void GuiView::showDialog(string const & name, string const & data,
4220 triggerShowDialog(toqstr(name), toqstr(data), inset);
4224 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4230 const string name = fromqstr(qname);
4231 const string data = fromqstr(qdata);
4235 Dialog * dialog = findOrBuild(name, false);
4237 bool const visible = dialog->isVisibleView();
4238 dialog->showData(data);
4239 if (inset && currentBufferView())
4240 currentBufferView()->editInset(name, inset);
4241 // We only set the focus to the new dialog if it was not yet
4242 // visible in order not to change the existing previous behaviour
4244 // activateWindow is needed for floating dockviews
4245 dialog->asQWidget()->raise();
4246 dialog->asQWidget()->activateWindow();
4247 dialog->asQWidget()->setFocus();
4251 catch (ExceptionMessage const & ex) {
4259 bool GuiView::isDialogVisible(string const & name) const
4261 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4262 if (it == d.dialogs_.end())
4264 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4268 void GuiView::hideDialog(string const & name, Inset * inset)
4270 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4271 if (it == d.dialogs_.end())
4275 if (!currentBufferView())
4277 if (inset != currentBufferView()->editedInset(name))
4281 Dialog * const dialog = it->second.get();
4282 if (dialog->isVisibleView())
4284 if (currentBufferView())
4285 currentBufferView()->editInset(name, 0);
4289 void GuiView::disconnectDialog(string const & name)
4291 if (!isValidName(name))
4293 if (currentBufferView())
4294 currentBufferView()->editInset(name, 0);
4298 void GuiView::hideAll() const
4300 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4301 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4303 for(; it != end; ++it)
4304 it->second->hideView();
4308 void GuiView::updateDialogs()
4310 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4311 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4313 for(; it != end; ++it) {
4314 Dialog * dialog = it->second.get();
4316 if (dialog->needBufferOpen() && !documentBufferView())
4317 hideDialog(fromqstr(dialog->name()), 0);
4318 else if (dialog->isVisibleView())
4319 dialog->checkStatus();
4326 Dialog * createDialog(GuiView & lv, string const & name);
4328 // will be replaced by a proper factory...
4329 Dialog * createGuiAbout(GuiView & lv);
4330 Dialog * createGuiBibtex(GuiView & lv);
4331 Dialog * createGuiChanges(GuiView & lv);
4332 Dialog * createGuiCharacter(GuiView & lv);
4333 Dialog * createGuiCitation(GuiView & lv);
4334 Dialog * createGuiCompare(GuiView & lv);
4335 Dialog * createGuiCompareHistory(GuiView & lv);
4336 Dialog * createGuiDelimiter(GuiView & lv);
4337 Dialog * createGuiDocument(GuiView & lv);
4338 Dialog * createGuiErrorList(GuiView & lv);
4339 Dialog * createGuiExternal(GuiView & lv);
4340 Dialog * createGuiGraphics(GuiView & lv);
4341 Dialog * createGuiInclude(GuiView & lv);
4342 Dialog * createGuiIndex(GuiView & lv);
4343 Dialog * createGuiListings(GuiView & lv);
4344 Dialog * createGuiLog(GuiView & lv);
4345 Dialog * createGuiMathMatrix(GuiView & lv);
4346 Dialog * createGuiNote(GuiView & lv);
4347 Dialog * createGuiParagraph(GuiView & lv);
4348 Dialog * createGuiPhantom(GuiView & lv);
4349 Dialog * createGuiPreferences(GuiView & lv);
4350 Dialog * createGuiPrint(GuiView & lv);
4351 Dialog * createGuiPrintindex(GuiView & lv);
4352 Dialog * createGuiRef(GuiView & lv);
4353 Dialog * createGuiSearch(GuiView & lv);
4354 Dialog * createGuiSearchAdv(GuiView & lv);
4355 Dialog * createGuiSendTo(GuiView & lv);
4356 Dialog * createGuiShowFile(GuiView & lv);
4357 Dialog * createGuiSpellchecker(GuiView & lv);
4358 Dialog * createGuiSymbols(GuiView & lv);
4359 Dialog * createGuiTabularCreate(GuiView & lv);
4360 Dialog * createGuiTexInfo(GuiView & lv);
4361 Dialog * createGuiToc(GuiView & lv);
4362 Dialog * createGuiThesaurus(GuiView & lv);
4363 Dialog * createGuiViewSource(GuiView & lv);
4364 Dialog * createGuiWrap(GuiView & lv);
4365 Dialog * createGuiProgressView(GuiView & lv);
4369 Dialog * GuiView::build(string const & name)
4371 LASSERT(isValidName(name), return 0);
4373 Dialog * dialog = createDialog(*this, name);
4377 if (name == "aboutlyx")
4378 return createGuiAbout(*this);
4379 if (name == "bibtex")
4380 return createGuiBibtex(*this);
4381 if (name == "changes")
4382 return createGuiChanges(*this);
4383 if (name == "character")
4384 return createGuiCharacter(*this);
4385 if (name == "citation")
4386 return createGuiCitation(*this);
4387 if (name == "compare")
4388 return createGuiCompare(*this);
4389 if (name == "comparehistory")
4390 return createGuiCompareHistory(*this);
4391 if (name == "document")
4392 return createGuiDocument(*this);
4393 if (name == "errorlist")
4394 return createGuiErrorList(*this);
4395 if (name == "external")
4396 return createGuiExternal(*this);
4398 return createGuiShowFile(*this);
4399 if (name == "findreplace")
4400 return createGuiSearch(*this);
4401 if (name == "findreplaceadv")
4402 return createGuiSearchAdv(*this);
4403 if (name == "graphics")
4404 return createGuiGraphics(*this);
4405 if (name == "include")
4406 return createGuiInclude(*this);
4407 if (name == "index")
4408 return createGuiIndex(*this);
4409 if (name == "index_print")
4410 return createGuiPrintindex(*this);
4411 if (name == "listings")
4412 return createGuiListings(*this);
4414 return createGuiLog(*this);
4415 if (name == "mathdelimiter")
4416 return createGuiDelimiter(*this);
4417 if (name == "mathmatrix")
4418 return createGuiMathMatrix(*this);
4420 return createGuiNote(*this);
4421 if (name == "paragraph")
4422 return createGuiParagraph(*this);
4423 if (name == "phantom")
4424 return createGuiPhantom(*this);
4425 if (name == "prefs")
4426 return createGuiPreferences(*this);
4427 if (name == "print")
4428 return createGuiPrint(*this);
4430 return createGuiRef(*this);
4431 if (name == "sendto")
4432 return createGuiSendTo(*this);
4433 if (name == "spellchecker")
4434 return createGuiSpellchecker(*this);
4435 if (name == "symbols")
4436 return createGuiSymbols(*this);
4437 if (name == "tabularcreate")
4438 return createGuiTabularCreate(*this);
4439 if (name == "texinfo")
4440 return createGuiTexInfo(*this);
4441 if (name == "thesaurus")
4442 return createGuiThesaurus(*this);
4444 return createGuiToc(*this);
4445 if (name == "view-source")
4446 return createGuiViewSource(*this);
4448 return createGuiWrap(*this);
4449 if (name == "progress")
4450 return createGuiProgressView(*this);
4456 } // namespace frontend
4459 #include "moc_GuiView.cpp"