3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #if QT_VERSION >= 0x050000
114 #include <QSvgRenderer>
116 #include <QtConcurrentRun>
124 // sync with GuiAlert.cpp
125 #define EXPORT_in_THREAD 1
128 #include "support/bind.h"
132 #ifdef HAVE_SYS_TIME_H
133 # include <sys/time.h>
141 using namespace lyx::support;
145 using support::addExtension;
146 using support::changeExtension;
147 using support::removeExtension;
153 class BackgroundWidget : public QWidget
156 BackgroundWidget(int width, int height)
157 : width_(width), height_(height)
159 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
160 if (!lyxrc.show_banner)
162 /// The text to be written on top of the pixmap
163 QString const text = lyx_version ?
164 qt_("version ") + lyx_version : qt_("unknown version");
165 #if QT_VERSION >= 0x050000
166 QString imagedir = "images/";
167 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
168 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
169 if (svgRenderer.isValid()) {
170 splash_ = QPixmap(splashSize());
171 QPainter painter(&splash_);
172 svgRenderer.render(&painter);
173 splash_.setDevicePixelRatio(pixelRatio());
175 splash_ = getPixmap("images/", "banner", "png");
178 splash_ = getPixmap("images/", "banner", "svgz,png");
181 QPainter pain(&splash_);
182 pain.setPen(QColor(0, 0, 0));
183 qreal const fsize = fontSize();
184 QPointF const position = textPosition();
186 "widget pixel ratio: " << pixelRatio() <<
187 " splash pixel ratio: " << splashPixelRatio() <<
188 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
190 // The font used to display the version info
191 font.setStyleHint(QFont::SansSerif);
192 font.setWeight(QFont::Bold);
193 font.setPointSizeF(fsize);
195 pain.drawText(position, text);
196 setFocusPolicy(Qt::StrongFocus);
199 void paintEvent(QPaintEvent *)
201 int const w = width_;
202 int const h = height_;
203 int const x = (width() - w) / 2;
204 int const y = (height() - h) / 2;
206 "widget pixel ratio: " << pixelRatio() <<
207 " splash pixel ratio: " << splashPixelRatio() <<
208 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
210 pain.drawPixmap(x, y, w, h, splash_);
213 void keyPressEvent(QKeyEvent * ev)
216 setKeySymbol(&sym, ev);
218 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
230 /// Current ratio between physical pixels and device-independent pixels
231 double pixelRatio() const {
232 #if QT_VERSION >= 0x050000
233 return devicePixelRatio();
239 qreal fontSize() const {
240 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
243 QPointF textPosition() const {
244 return QPointF(width_/2 - 18, height_/2 + 45);
247 QSize splashSize() const {
249 static_cast<unsigned int>(width_ * pixelRatio()),
250 static_cast<unsigned int>(height_ * pixelRatio()));
253 /// Ratio between physical pixels and device-independent pixels of splash image
254 double splashPixelRatio() const {
255 #if QT_VERSION >= 0x050000
256 return splash_.devicePixelRatio();
264 /// Toolbar store providing access to individual toolbars by name.
265 typedef map<string, GuiToolbar *> ToolbarMap;
267 typedef shared_ptr<Dialog> DialogPtr;
272 struct GuiView::GuiViewPrivate
274 GuiViewPrivate(GuiView * gv)
275 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
276 layout_(0), autosave_timeout_(5000),
279 // hardcode here the platform specific icon size
280 smallIconSize = 16; // scaling problems
281 normalIconSize = 20; // ok, default if iconsize.png is missing
282 bigIconSize = 26; // better for some math icons
283 hugeIconSize = 32; // better for hires displays
286 // if it exists, use width of iconsize.png as normal size
287 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
288 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290 QImage image(toqstr(fn.absFileName()));
291 if (image.width() < int(smallIconSize))
292 normalIconSize = smallIconSize;
293 else if (image.width() > int(giantIconSize))
294 normalIconSize = giantIconSize;
296 normalIconSize = image.width();
299 splitter_ = new QSplitter;
300 bg_widget_ = new BackgroundWidget(400, 250);
301 stack_widget_ = new QStackedWidget;
302 stack_widget_->addWidget(bg_widget_);
303 stack_widget_->addWidget(splitter_);
306 // TODO cleanup, remove the singleton, handle multiple Windows?
307 progress_ = ProgressInterface::instance();
308 if (!dynamic_cast<GuiProgress*>(progress_)) {
309 progress_ = new GuiProgress; // TODO who deletes it
310 ProgressInterface::setInstance(progress_);
313 dynamic_cast<GuiProgress*>(progress_),
314 SIGNAL(updateStatusBarMessage(QString const&)),
315 gv, SLOT(updateStatusBarMessage(QString const&)));
317 dynamic_cast<GuiProgress*>(progress_),
318 SIGNAL(clearMessageText()),
319 gv, SLOT(clearMessageText()));
326 delete stack_widget_;
329 QMenu * toolBarPopup(GuiView * parent)
331 // FIXME: translation
332 QMenu * menu = new QMenu(parent);
333 QActionGroup * iconSizeGroup = new QActionGroup(parent);
335 QAction * smallIcons = new QAction(iconSizeGroup);
336 smallIcons->setText(qt_("Small-sized icons"));
337 smallIcons->setCheckable(true);
338 QObject::connect(smallIcons, SIGNAL(triggered()),
339 parent, SLOT(smallSizedIcons()));
340 menu->addAction(smallIcons);
342 QAction * normalIcons = new QAction(iconSizeGroup);
343 normalIcons->setText(qt_("Normal-sized icons"));
344 normalIcons->setCheckable(true);
345 QObject::connect(normalIcons, SIGNAL(triggered()),
346 parent, SLOT(normalSizedIcons()));
347 menu->addAction(normalIcons);
349 QAction * bigIcons = new QAction(iconSizeGroup);
350 bigIcons->setText(qt_("Big-sized icons"));
351 bigIcons->setCheckable(true);
352 QObject::connect(bigIcons, SIGNAL(triggered()),
353 parent, SLOT(bigSizedIcons()));
354 menu->addAction(bigIcons);
356 QAction * hugeIcons = new QAction(iconSizeGroup);
357 hugeIcons->setText(qt_("Huge-sized icons"));
358 hugeIcons->setCheckable(true);
359 QObject::connect(hugeIcons, SIGNAL(triggered()),
360 parent, SLOT(hugeSizedIcons()));
361 menu->addAction(hugeIcons);
363 QAction * giantIcons = new QAction(iconSizeGroup);
364 giantIcons->setText(qt_("Giant-sized icons"));
365 giantIcons->setCheckable(true);
366 QObject::connect(giantIcons, SIGNAL(triggered()),
367 parent, SLOT(giantSizedIcons()));
368 menu->addAction(giantIcons);
370 unsigned int cur = parent->iconSize().width();
371 if ( cur == parent->d.smallIconSize)
372 smallIcons->setChecked(true);
373 else if (cur == parent->d.normalIconSize)
374 normalIcons->setChecked(true);
375 else if (cur == parent->d.bigIconSize)
376 bigIcons->setChecked(true);
377 else if (cur == parent->d.hugeIconSize)
378 hugeIcons->setChecked(true);
379 else if (cur == parent->d.giantIconSize)
380 giantIcons->setChecked(true);
387 stack_widget_->setCurrentWidget(bg_widget_);
388 bg_widget_->setUpdatesEnabled(true);
389 bg_widget_->setFocus();
392 int tabWorkAreaCount()
394 return splitter_->count();
397 TabWorkArea * tabWorkArea(int i)
399 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
402 TabWorkArea * currentTabWorkArea()
404 int areas = tabWorkAreaCount();
406 // The first TabWorkArea is always the first one, if any.
407 return tabWorkArea(0);
409 for (int i = 0; i != areas; ++i) {
410 TabWorkArea * twa = tabWorkArea(i);
411 if (current_main_work_area_ == twa->currentWorkArea())
415 // None has the focus so we just take the first one.
416 return tabWorkArea(0);
419 int countWorkAreasOf(Buffer & buf)
421 int areas = tabWorkAreaCount();
423 for (int i = 0; i != areas; ++i) {
424 TabWorkArea * twa = tabWorkArea(i);
425 if (twa->workArea(buf))
431 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
433 if (processing_thread_watcher_.isRunning()) {
434 // we prefer to cancel this preview in order to keep a snappy
438 processing_thread_watcher_.setFuture(f);
443 GuiWorkArea * current_work_area_;
444 GuiWorkArea * current_main_work_area_;
445 QSplitter * splitter_;
446 QStackedWidget * stack_widget_;
447 BackgroundWidget * bg_widget_;
449 ToolbarMap toolbars_;
450 ProgressInterface* progress_;
451 /// The main layout box.
453 * \warning Don't Delete! The layout box is actually owned by
454 * whichever toolbar contains it. All the GuiView class needs is a
455 * means of accessing it.
457 * FIXME: replace that with a proper model so that we are not limited
458 * to only one dialog.
463 map<string, DialogPtr> dialogs_;
465 unsigned int smallIconSize;
466 unsigned int normalIconSize;
467 unsigned int bigIconSize;
468 unsigned int hugeIconSize;
469 unsigned int giantIconSize;
471 QTimer statusbar_timer_;
472 /// auto-saving of buffers
473 Timeout autosave_timeout_;
474 /// flag against a race condition due to multiclicks, see bug #1119
478 TocModels toc_models_;
481 QFutureWatcher<docstring> autosave_watcher_;
482 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
484 string last_export_format;
485 string processing_format;
487 static QSet<Buffer const *> busyBuffers;
488 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
494 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
496 // TODO syncFunc/previewFunc: use bind
497 bool asyncBufferProcessing(string const & argument,
498 Buffer const * used_buffer,
499 docstring const & msg,
500 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
501 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
502 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
504 QVector<GuiWorkArea*> guiWorkAreas();
507 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
510 GuiView::GuiView(int id)
511 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
513 // GuiToolbars *must* be initialised before the menu bar.
514 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
517 // set ourself as the current view. This is needed for the menu bar
518 // filling, at least for the static special menu item on Mac. Otherwise
519 // they are greyed out.
520 guiApp->setCurrentView(this);
522 // Fill up the menu bar.
523 guiApp->menus().fillMenuBar(menuBar(), this, true);
525 setCentralWidget(d.stack_widget_);
527 // Start autosave timer
528 if (lyxrc.autosave) {
529 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
530 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
531 d.autosave_timeout_.start();
533 connect(&d.statusbar_timer_, SIGNAL(timeout()),
534 this, SLOT(clearMessage()));
536 // We don't want to keep the window in memory if it is closed.
537 setAttribute(Qt::WA_DeleteOnClose, true);
539 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
540 // QIcon::fromTheme was introduced in Qt 4.6
541 #if (QT_VERSION >= 0x040600)
542 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
543 // since the icon is provided in the application bundle. We use a themed
544 // version when available and use the bundled one as fallback.
545 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
547 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
551 resetWindowTitleAndIconText();
553 // use tabbed dock area for multiple docks
554 // (such as "source" and "messages")
555 setDockOptions(QMainWindow::ForceTabbedDocks);
558 setAcceptDrops(true);
560 // add busy indicator to statusbar
561 QLabel * busylabel = new QLabel(statusBar());
562 statusBar()->addPermanentWidget(busylabel);
563 search_mode mode = theGuiApp()->imageSearchMode();
564 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
565 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
566 busylabel->setMovie(busyanim);
570 connect(&d.processing_thread_watcher_, SIGNAL(started()),
571 busylabel, SLOT(show()));
572 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
573 busylabel, SLOT(hide()));
575 statusBar()->setSizeGripEnabled(true);
578 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
579 SLOT(autoSaveThreadFinished()));
581 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
582 SLOT(processingThreadStarted()));
583 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
584 SLOT(processingThreadFinished()));
586 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
587 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
589 // Forbid too small unresizable window because it can happen
590 // with some window manager under X11.
591 setMinimumSize(300, 200);
593 if (lyxrc.allow_geometry_session) {
594 // Now take care of session management.
599 // no session handling, default to a sane size.
600 setGeometry(50, 50, 690, 510);
603 // clear session data if any.
605 settings.remove("views");
615 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
617 QVector<GuiWorkArea*> areas;
618 for (int i = 0; i < tabWorkAreaCount(); i++) {
619 TabWorkArea* ta = tabWorkArea(i);
620 for (int u = 0; u < ta->count(); u++) {
621 areas << ta->workArea(u);
627 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
628 string const & format)
630 docstring const fmt = formats.prettyName(format);
633 case Buffer::ExportSuccess:
634 msg = bformat(_("Successful export to format: %1$s"), fmt);
636 case Buffer::ExportCancel:
637 msg = _("Document export cancelled.");
639 case Buffer::ExportError:
640 case Buffer::ExportNoPathToFormat:
641 case Buffer::ExportTexPathHasSpaces:
642 case Buffer::ExportConverterError:
643 msg = bformat(_("Error while exporting format: %1$s"), fmt);
645 case Buffer::PreviewSuccess:
646 msg = bformat(_("Successful preview of format: %1$s"), fmt);
648 case Buffer::PreviewError:
649 msg = bformat(_("Error while previewing format: %1$s"), fmt);
656 void GuiView::processingThreadStarted()
661 void GuiView::processingThreadFinished()
663 QFutureWatcher<Buffer::ExportStatus> const * watcher =
664 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
666 Buffer::ExportStatus const status = watcher->result();
667 handleExportStatus(this, status, d.processing_format);
670 BufferView const * const bv = currentBufferView();
671 if (bv && !bv->buffer().errorList("Export").empty()) {
675 errors(d.last_export_format);
679 void GuiView::autoSaveThreadFinished()
681 QFutureWatcher<docstring> const * watcher =
682 static_cast<QFutureWatcher<docstring> const *>(sender());
683 message(watcher->result());
688 void GuiView::saveLayout() const
691 settings.beginGroup("views");
692 settings.beginGroup(QString::number(id_));
693 #if defined(Q_WS_X11) || defined(QPA_XCB)
694 settings.setValue("pos", pos());
695 settings.setValue("size", size());
697 settings.setValue("geometry", saveGeometry());
699 settings.setValue("layout", saveState(0));
700 settings.setValue("icon_size", iconSize());
704 void GuiView::saveUISettings() const
706 // Save the toolbar private states
707 ToolbarMap::iterator end = d.toolbars_.end();
708 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
709 it->second->saveSession();
710 // Now take care of all other dialogs
711 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
712 for (; it!= d.dialogs_.end(); ++it)
713 it->second->saveSession();
717 bool GuiView::restoreLayout()
720 settings.beginGroup("views");
721 settings.beginGroup(QString::number(id_));
722 QString const icon_key = "icon_size";
723 if (!settings.contains(icon_key))
726 //code below is skipped when when ~/.config/LyX is (re)created
727 QSize icon_size = settings.value(icon_key).toSize();
728 // Check whether session size changed.
729 if (icon_size.width() != int(d.smallIconSize) &&
730 icon_size.width() != int(d.normalIconSize) &&
731 icon_size.width() != int(d.bigIconSize) &&
732 icon_size.width() != int(d.hugeIconSize) &&
733 icon_size.width() != int(d.giantIconSize)) {
734 icon_size.setWidth(d.normalIconSize);
735 icon_size.setHeight(d.normalIconSize);
737 setIconSize(icon_size);
739 #if defined(Q_WS_X11) || defined(QPA_XCB)
740 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
741 QSize size = settings.value("size", QSize(690, 510)).toSize();
745 // Work-around for bug #6034: the window ends up in an undetermined
746 // state when trying to restore a maximized window when it is
747 // already maximized.
748 if (!(windowState() & Qt::WindowMaximized))
749 if (!restoreGeometry(settings.value("geometry").toByteArray()))
750 setGeometry(50, 50, 690, 510);
752 // Make sure layout is correctly oriented.
753 setLayoutDirection(qApp->layoutDirection());
755 // Allow the toc and view-source dock widget to be restored if needed.
757 if ((dialog = findOrBuild("toc", true)))
758 // see bug 5082. At least setup title and enabled state.
759 // Visibility will be adjusted by restoreState below.
760 dialog->prepareView();
761 if ((dialog = findOrBuild("view-source", true)))
762 dialog->prepareView();
763 if ((dialog = findOrBuild("progress", true)))
764 dialog->prepareView();
766 if (!restoreState(settings.value("layout").toByteArray(), 0))
769 // init the toolbars that have not been restored
770 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
771 Toolbars::Infos::iterator end = guiApp->toolbars().end();
772 for (; cit != end; ++cit) {
773 GuiToolbar * tb = toolbar(cit->name);
774 if (tb && !tb->isRestored())
775 initToolbar(cit->name);
783 GuiToolbar * GuiView::toolbar(string const & name)
785 ToolbarMap::iterator it = d.toolbars_.find(name);
786 if (it != d.toolbars_.end())
789 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
794 void GuiView::constructToolbars()
796 ToolbarMap::iterator it = d.toolbars_.begin();
797 for (; it != d.toolbars_.end(); ++it)
801 // I don't like doing this here, but the standard toolbar
802 // destroys this object when it's destroyed itself (vfr)
803 d.layout_ = new LayoutBox(*this);
804 d.stack_widget_->addWidget(d.layout_);
805 d.layout_->move(0,0);
807 // extracts the toolbars from the backend
808 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
809 Toolbars::Infos::iterator end = guiApp->toolbars().end();
810 for (; cit != end; ++cit)
811 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
815 void GuiView::initToolbars()
817 // extracts the toolbars from the backend
818 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
819 Toolbars::Infos::iterator end = guiApp->toolbars().end();
820 for (; cit != end; ++cit)
821 initToolbar(cit->name);
825 void GuiView::initToolbar(string const & name)
827 GuiToolbar * tb = toolbar(name);
830 int const visibility = guiApp->toolbars().defaultVisibility(name);
831 bool newline = !(visibility & Toolbars::SAMEROW);
832 tb->setVisible(false);
833 tb->setVisibility(visibility);
835 if (visibility & Toolbars::TOP) {
837 addToolBarBreak(Qt::TopToolBarArea);
838 addToolBar(Qt::TopToolBarArea, tb);
841 if (visibility & Toolbars::BOTTOM) {
843 addToolBarBreak(Qt::BottomToolBarArea);
844 addToolBar(Qt::BottomToolBarArea, tb);
847 if (visibility & Toolbars::LEFT) {
849 addToolBarBreak(Qt::LeftToolBarArea);
850 addToolBar(Qt::LeftToolBarArea, tb);
853 if (visibility & Toolbars::RIGHT) {
855 addToolBarBreak(Qt::RightToolBarArea);
856 addToolBar(Qt::RightToolBarArea, tb);
859 if (visibility & Toolbars::ON)
860 tb->setVisible(true);
864 TocModels & GuiView::tocModels()
866 return d.toc_models_;
870 void GuiView::setFocus()
872 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
873 QMainWindow::setFocus();
877 bool GuiView::hasFocus() const
879 if (currentWorkArea())
880 return currentWorkArea()->hasFocus();
881 if (currentMainWorkArea())
882 return currentMainWorkArea()->hasFocus();
883 return d.bg_widget_->hasFocus();
887 void GuiView::focusInEvent(QFocusEvent * e)
889 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
890 QMainWindow::focusInEvent(e);
891 // Make sure guiApp points to the correct view.
892 guiApp->setCurrentView(this);
893 if (currentWorkArea())
894 currentWorkArea()->setFocus();
895 else if (currentMainWorkArea())
896 currentMainWorkArea()->setFocus();
898 d.bg_widget_->setFocus();
902 QMenu * GuiView::createPopupMenu()
904 return d.toolBarPopup(this);
908 void GuiView::showEvent(QShowEvent * e)
910 LYXERR(Debug::GUI, "Passed Geometry "
911 << size().height() << "x" << size().width()
912 << "+" << pos().x() << "+" << pos().y());
914 if (d.splitter_->count() == 0)
915 // No work area, switch to the background widget.
919 QMainWindow::showEvent(e);
923 bool GuiView::closeScheduled()
930 bool GuiView::prepareAllBuffersForLogout()
932 Buffer * first = theBufferList().first();
936 // First, iterate over all buffers and ask the users if unsaved
937 // changes should be saved.
938 // We cannot use a for loop as the buffer list cycles.
941 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
943 b = theBufferList().next(b);
944 } while (b != first);
946 // Next, save session state
947 // When a view/window was closed before without quitting LyX, there
948 // are already entries in the lastOpened list.
949 theSession().lastOpened().clear();
956 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
957 ** is responsibility of the container (e.g., dialog)
959 void GuiView::closeEvent(QCloseEvent * close_event)
961 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
963 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
964 Alert::warning(_("Exit LyX"),
965 _("LyX could not be closed because documents are being processed by LyX."));
966 close_event->setAccepted(false);
970 // If the user pressed the x (so we didn't call closeView
971 // programmatically), we want to clear all existing entries.
973 theSession().lastOpened().clear();
978 // it can happen that this event arrives without selecting the view,
979 // e.g. when clicking the close button on a background window.
981 if (!closeWorkAreaAll()) {
983 close_event->ignore();
987 // Make sure that nothing will use this to be closed View.
988 guiApp->unregisterView(this);
990 if (isFullScreen()) {
991 // Switch off fullscreen before closing.
996 // Make sure the timer time out will not trigger a statusbar update.
997 d.statusbar_timer_.stop();
999 // Saving fullscreen requires additional tweaks in the toolbar code.
1000 // It wouldn't also work under linux natively.
1001 if (lyxrc.allow_geometry_session) {
1006 close_event->accept();
1010 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1012 if (event->mimeData()->hasUrls())
1014 /// \todo Ask lyx-devel is this is enough:
1015 /// if (event->mimeData()->hasFormat("text/plain"))
1016 /// event->acceptProposedAction();
1020 void GuiView::dropEvent(QDropEvent * event)
1022 QList<QUrl> files = event->mimeData()->urls();
1023 if (files.isEmpty())
1026 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1027 for (int i = 0; i != files.size(); ++i) {
1028 string const file = os::internal_path(fromqstr(
1029 files.at(i).toLocalFile()));
1033 string const ext = support::getExtension(file);
1034 vector<const Format *> found_formats;
1036 // Find all formats that have the correct extension.
1037 vector<const Format *> const & import_formats
1038 = theConverters().importableFormats();
1039 vector<const Format *>::const_iterator it = import_formats.begin();
1040 for (; it != import_formats.end(); ++it)
1041 if ((*it)->hasExtension(ext))
1042 found_formats.push_back(*it);
1045 if (found_formats.size() >= 1) {
1046 if (found_formats.size() > 1) {
1047 //FIXME: show a dialog to choose the correct importable format
1048 LYXERR(Debug::FILES,
1049 "Multiple importable formats found, selecting first");
1051 string const arg = found_formats[0]->name() + " " + file;
1052 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1055 //FIXME: do we have to explicitly check whether it's a lyx file?
1056 LYXERR(Debug::FILES,
1057 "No formats found, trying to open it as a lyx file");
1058 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1060 // add the functions to the queue
1061 guiApp->addToFuncRequestQueue(cmd);
1064 // now process the collected functions. We perform the events
1065 // asynchronously. This prevents potential problems in case the
1066 // BufferView is closed within an event.
1067 guiApp->processFuncRequestQueueAsync();
1071 void GuiView::message(docstring const & str)
1073 if (ForkedProcess::iAmAChild())
1076 // call is moved to GUI-thread by GuiProgress
1077 d.progress_->appendMessage(toqstr(str));
1081 void GuiView::clearMessageText()
1083 message(docstring());
1087 void GuiView::updateStatusBarMessage(QString const & str)
1089 statusBar()->showMessage(str);
1090 d.statusbar_timer_.stop();
1091 d.statusbar_timer_.start(3000);
1095 void GuiView::smallSizedIcons()
1097 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1101 void GuiView::normalSizedIcons()
1103 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1107 void GuiView::bigSizedIcons()
1109 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1113 void GuiView::hugeSizedIcons()
1115 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1119 void GuiView::giantSizedIcons()
1121 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1125 void GuiView::clearMessage()
1127 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1128 // the hasFocus function mostly returns false, even if the focus is on
1129 // a workarea in this view.
1133 d.statusbar_timer_.stop();
1137 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1139 if (wa != d.current_work_area_
1140 || wa->bufferView().buffer().isInternal())
1142 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1143 setWindowIconText(wa->windowIconText());
1144 #if (QT_VERSION >= 0x040400)
1145 // Sets the path for the window: this is used by OSX to
1146 // allow a context click on the title bar showing a menu
1147 // with the path up to the file
1148 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1153 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1155 if (d.current_work_area_)
1156 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1157 this, SLOT(setBusy(bool)));
1159 disconnectBufferView();
1160 connectBufferView(wa->bufferView());
1161 connectBuffer(wa->bufferView().buffer());
1162 d.current_work_area_ = wa;
1163 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1164 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1165 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1166 updateWindowTitle(wa);
1170 // The document settings needs to be reinitialised.
1171 updateDialog("document", "");
1173 // Buffer-dependent dialogs must be updated. This is done here because
1174 // some dialogs require buffer()->text.
1179 void GuiView::on_lastWorkAreaRemoved()
1182 // We already are in a close event. Nothing more to do.
1185 if (d.splitter_->count() > 1)
1186 // We have a splitter so don't close anything.
1189 // Reset and updates the dialogs.
1190 d.toc_models_.reset(0);
1191 updateDialog("document", "");
1194 resetWindowTitleAndIconText();
1197 if (lyxrc.open_buffers_in_tabs)
1198 // Nothing more to do, the window should stay open.
1201 if (guiApp->viewIds().size() > 1) {
1207 // On Mac we also close the last window because the application stay
1208 // resident in memory. On other platforms we don't close the last
1209 // window because this would quit the application.
1215 void GuiView::updateStatusBar()
1217 // let the user see the explicit message
1218 if (d.statusbar_timer_.isActive())
1225 void GuiView::showMessage()
1229 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1230 if (msg.isEmpty()) {
1231 BufferView const * bv = currentBufferView();
1233 msg = toqstr(bv->cursor().currentState());
1235 msg = qt_("Welcome to LyX!");
1237 statusBar()->showMessage(msg);
1241 bool GuiView::event(QEvent * e)
1245 // Useful debug code:
1246 //case QEvent::ActivationChange:
1247 //case QEvent::WindowDeactivate:
1248 //case QEvent::Paint:
1249 //case QEvent::Enter:
1250 //case QEvent::Leave:
1251 //case QEvent::HoverEnter:
1252 //case QEvent::HoverLeave:
1253 //case QEvent::HoverMove:
1254 //case QEvent::StatusTip:
1255 //case QEvent::DragEnter:
1256 //case QEvent::DragLeave:
1257 //case QEvent::Drop:
1260 case QEvent::WindowActivate: {
1261 GuiView * old_view = guiApp->currentView();
1262 if (this == old_view) {
1264 return QMainWindow::event(e);
1266 if (old_view && old_view->currentBufferView()) {
1267 // save current selection to the selection buffer to allow
1268 // middle-button paste in this window.
1269 cap::saveSelection(old_view->currentBufferView()->cursor());
1271 guiApp->setCurrentView(this);
1272 if (d.current_work_area_) {
1273 BufferView & bv = d.current_work_area_->bufferView();
1274 connectBufferView(bv);
1275 connectBuffer(bv.buffer());
1276 // The document structure, name and dialogs might have
1277 // changed in another view.
1279 // The document settings needs to be reinitialised.
1280 updateDialog("document", "");
1283 resetWindowTitleAndIconText();
1286 return QMainWindow::event(e);
1289 case QEvent::ShortcutOverride: {
1291 if (isFullScreen() && menuBar()->isHidden()) {
1292 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1293 // FIXME: we should also try to detect special LyX shortcut such as
1294 // Alt-P and Alt-M. Right now there is a hack in
1295 // GuiWorkArea::processKeySym() that hides again the menubar for
1297 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1299 return QMainWindow::event(e);
1302 return QMainWindow::event(e);
1306 return QMainWindow::event(e);
1310 void GuiView::resetWindowTitleAndIconText()
1312 setWindowTitle(qt_("LyX"));
1313 setWindowIconText(qt_("LyX"));
1316 bool GuiView::focusNextPrevChild(bool /*next*/)
1323 bool GuiView::busy() const
1329 void GuiView::setBusy(bool busy)
1331 bool const busy_before = busy_ > 0;
1332 busy ? ++busy_ : --busy_;
1333 if ((busy_ > 0) == busy_before)
1334 // busy state didn't change
1338 QApplication::setOverrideCursor(Qt::WaitCursor);
1341 QApplication::restoreOverrideCursor();
1346 double GuiView::pixelRatio() const
1348 #if QT_VERSION >= 0x050000
1349 return devicePixelRatio();
1356 GuiWorkArea * GuiView::workArea(int index)
1358 if (TabWorkArea * twa = d.currentTabWorkArea())
1359 if (index < twa->count())
1360 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1365 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1367 if (currentWorkArea()
1368 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1369 return (GuiWorkArea *) currentWorkArea();
1370 if (TabWorkArea * twa = d.currentTabWorkArea())
1371 return twa->workArea(buffer);
1376 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1378 // Automatically create a TabWorkArea if there are none yet.
1379 TabWorkArea * tab_widget = d.splitter_->count()
1380 ? d.currentTabWorkArea() : addTabWorkArea();
1381 return tab_widget->addWorkArea(buffer, *this);
1385 TabWorkArea * GuiView::addTabWorkArea()
1387 TabWorkArea * twa = new TabWorkArea;
1388 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1389 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1390 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1391 this, SLOT(on_lastWorkAreaRemoved()));
1393 d.splitter_->addWidget(twa);
1394 d.stack_widget_->setCurrentWidget(d.splitter_);
1399 GuiWorkArea const * GuiView::currentWorkArea() const
1401 return d.current_work_area_;
1405 GuiWorkArea * GuiView::currentWorkArea()
1407 return d.current_work_area_;
1411 GuiWorkArea const * GuiView::currentMainWorkArea() const
1413 if (!d.currentTabWorkArea())
1415 return d.currentTabWorkArea()->currentWorkArea();
1419 GuiWorkArea * GuiView::currentMainWorkArea()
1421 if (!d.currentTabWorkArea())
1423 return d.currentTabWorkArea()->currentWorkArea();
1427 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1429 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1431 d.current_work_area_ = 0;
1436 // FIXME: I've no clue why this is here and why it accesses
1437 // theGuiApp()->currentView, which might be 0 (bug 6464).
1438 // See also 27525 (vfr).
1439 if (theGuiApp()->currentView() == this
1440 && theGuiApp()->currentView()->currentWorkArea() == wa)
1443 if (currentBufferView())
1444 cap::saveSelection(currentBufferView()->cursor());
1446 theGuiApp()->setCurrentView(this);
1447 d.current_work_area_ = wa;
1449 // We need to reset this now, because it will need to be
1450 // right if the tabWorkArea gets reset in the for loop. We
1451 // will change it back if we aren't in that case.
1452 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1453 d.current_main_work_area_ = wa;
1455 for (int i = 0; i != d.splitter_->count(); ++i) {
1456 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1457 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1458 << ", Current main wa: " << currentMainWorkArea());
1463 d.current_main_work_area_ = old_cmwa;
1465 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1466 on_currentWorkAreaChanged(wa);
1467 BufferView & bv = wa->bufferView();
1468 bv.cursor().fixIfBroken();
1470 wa->setUpdatesEnabled(true);
1471 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1475 void GuiView::removeWorkArea(GuiWorkArea * wa)
1477 LASSERT(wa, return);
1478 if (wa == d.current_work_area_) {
1480 disconnectBufferView();
1481 d.current_work_area_ = 0;
1482 d.current_main_work_area_ = 0;
1485 bool found_twa = false;
1486 for (int i = 0; i != d.splitter_->count(); ++i) {
1487 TabWorkArea * twa = d.tabWorkArea(i);
1488 if (twa->removeWorkArea(wa)) {
1489 // Found in this tab group, and deleted the GuiWorkArea.
1491 if (twa->count() != 0) {
1492 if (d.current_work_area_ == 0)
1493 // This means that we are closing the current GuiWorkArea, so
1494 // switch to the next GuiWorkArea in the found TabWorkArea.
1495 setCurrentWorkArea(twa->currentWorkArea());
1497 // No more WorkAreas in this tab group, so delete it.
1504 // It is not a tabbed work area (i.e., the search work area), so it
1505 // should be deleted by other means.
1506 LASSERT(found_twa, return);
1508 if (d.current_work_area_ == 0) {
1509 if (d.splitter_->count() != 0) {
1510 TabWorkArea * twa = d.currentTabWorkArea();
1511 setCurrentWorkArea(twa->currentWorkArea());
1513 // No more work areas, switch to the background widget.
1514 setCurrentWorkArea(0);
1520 LayoutBox * GuiView::getLayoutDialog() const
1526 void GuiView::updateLayoutList()
1529 d.layout_->updateContents(false);
1533 void GuiView::updateToolbars()
1535 ToolbarMap::iterator end = d.toolbars_.end();
1536 if (d.current_work_area_) {
1538 d.current_work_area_->bufferView().cursor().inMathed()
1539 && !d.current_work_area_->bufferView().cursor().inRegexped();
1541 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1543 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1544 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
1545 bool const mathmacrotemplate =
1546 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1548 lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
1550 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1551 it->second->update(math, table, review, mathmacrotemplate, ipa);
1553 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1554 it->second->update(false, false, false, false, false);
1558 void GuiView::setBuffer(Buffer * newBuffer)
1560 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1561 LASSERT(newBuffer, return);
1563 GuiWorkArea * wa = workArea(*newBuffer);
1566 newBuffer->masterBuffer()->updateBuffer();
1568 wa = addWorkArea(*newBuffer);
1569 // scroll to the position when the BufferView was last closed
1570 if (lyxrc.use_lastfilepos) {
1571 LastFilePosSection::FilePos filepos =
1572 theSession().lastFilePos().load(newBuffer->fileName());
1573 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1576 //Disconnect the old buffer...there's no new one.
1579 connectBuffer(*newBuffer);
1580 connectBufferView(wa->bufferView());
1581 setCurrentWorkArea(wa);
1585 void GuiView::connectBuffer(Buffer & buf)
1587 buf.setGuiDelegate(this);
1591 void GuiView::disconnectBuffer()
1593 if (d.current_work_area_)
1594 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1598 void GuiView::connectBufferView(BufferView & bv)
1600 bv.setGuiDelegate(this);
1604 void GuiView::disconnectBufferView()
1606 if (d.current_work_area_)
1607 d.current_work_area_->bufferView().setGuiDelegate(0);
1611 void GuiView::errors(string const & error_type, bool from_master)
1613 BufferView const * const bv = currentBufferView();
1617 #if EXPORT_in_THREAD
1618 // We are called with from_master == false by default, so we
1619 // have to figure out whether that is the case or not.
1620 ErrorList & el = bv->buffer().errorList(error_type);
1622 el = bv->buffer().masterBuffer()->errorList(error_type);
1626 ErrorList const & el = from_master ?
1627 bv->buffer().masterBuffer()->errorList(error_type) :
1628 bv->buffer().errorList(error_type);
1634 string data = error_type;
1636 data = "from_master|" + error_type;
1637 showDialog("errorlist", data);
1641 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1643 d.toc_models_.updateItem(toqstr(type), dit);
1647 void GuiView::structureChanged()
1649 d.toc_models_.reset(documentBufferView());
1650 // Navigator needs more than a simple update in this case. It needs to be
1652 updateDialog("toc", "");
1656 void GuiView::updateDialog(string const & name, string const & data)
1658 if (!isDialogVisible(name))
1661 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1662 if (it == d.dialogs_.end())
1665 Dialog * const dialog = it->second.get();
1666 if (dialog->isVisibleView())
1667 dialog->initialiseParams(data);
1671 BufferView * GuiView::documentBufferView()
1673 return currentMainWorkArea()
1674 ? ¤tMainWorkArea()->bufferView()
1679 BufferView const * GuiView::documentBufferView() const
1681 return currentMainWorkArea()
1682 ? ¤tMainWorkArea()->bufferView()
1687 BufferView * GuiView::currentBufferView()
1689 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1693 BufferView const * GuiView::currentBufferView() const
1695 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1699 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1700 Buffer const * orig, Buffer * clone)
1702 bool const success = clone->autoSave();
1704 busyBuffers.remove(orig);
1706 ? _("Automatic save done.")
1707 : _("Automatic save failed!");
1711 void GuiView::autoSave()
1713 LYXERR(Debug::INFO, "Running autoSave()");
1715 Buffer * buffer = documentBufferView()
1716 ? &documentBufferView()->buffer() : 0;
1718 resetAutosaveTimers();
1722 GuiViewPrivate::busyBuffers.insert(buffer);
1723 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1724 buffer, buffer->cloneBufferOnly());
1725 d.autosave_watcher_.setFuture(f);
1726 resetAutosaveTimers();
1730 void GuiView::resetAutosaveTimers()
1733 d.autosave_timeout_.restart();
1737 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1740 Buffer * buf = currentBufferView()
1741 ? ¤tBufferView()->buffer() : 0;
1742 Buffer * doc_buffer = documentBufferView()
1743 ? &(documentBufferView()->buffer()) : 0;
1746 /* In LyX/Mac, when a dialog is open, the menus of the
1747 application can still be accessed without giving focus to
1748 the main window. In this case, we want to disable the menu
1749 entries that are buffer-related.
1751 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1757 // Check whether we need a buffer
1758 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1759 // no, exit directly
1760 flag.message(from_utf8(N_("Command not allowed with"
1761 "out any document open")));
1762 flag.setEnabled(false);
1766 if (cmd.origin() == FuncRequest::TOC) {
1767 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1768 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1769 flag.setEnabled(false);
1773 switch(cmd.action()) {
1774 case LFUN_BUFFER_IMPORT:
1777 case LFUN_MASTER_BUFFER_UPDATE:
1778 case LFUN_MASTER_BUFFER_VIEW:
1780 && (doc_buffer->parent() != 0
1781 || doc_buffer->hasChildren())
1782 && !d.processing_thread_watcher_.isRunning();
1785 case LFUN_BUFFER_UPDATE:
1786 case LFUN_BUFFER_VIEW: {
1787 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1791 string format = to_utf8(cmd.argument());
1792 if (cmd.argument().empty())
1793 format = doc_buffer->params().getDefaultOutputFormat();
1794 enable = doc_buffer->params().isExportableFormat(format);
1798 case LFUN_BUFFER_RELOAD:
1799 enable = doc_buffer && !doc_buffer->isUnnamed()
1800 && doc_buffer->fileName().exists()
1801 && (!doc_buffer->isClean()
1802 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1805 case LFUN_BUFFER_CHILD_OPEN:
1806 enable = doc_buffer;
1809 case LFUN_BUFFER_WRITE:
1810 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1813 //FIXME: This LFUN should be moved to GuiApplication.
1814 case LFUN_BUFFER_WRITE_ALL: {
1815 // We enable the command only if there are some modified buffers
1816 Buffer * first = theBufferList().first();
1821 // We cannot use a for loop as the buffer list is a cycle.
1823 if (!b->isClean()) {
1827 b = theBufferList().next(b);
1828 } while (b != first);
1832 case LFUN_BUFFER_WRITE_AS:
1833 case LFUN_BUFFER_EXPORT_AS:
1834 enable = doc_buffer;
1837 case LFUN_BUFFER_CLOSE:
1838 case LFUN_VIEW_CLOSE:
1839 enable = doc_buffer;
1842 case LFUN_BUFFER_CLOSE_ALL:
1843 enable = theBufferList().last() != theBufferList().first();
1846 case LFUN_VIEW_SPLIT:
1847 if (cmd.getArg(0) == "vertical")
1848 enable = doc_buffer && (d.splitter_->count() == 1 ||
1849 d.splitter_->orientation() == Qt::Vertical);
1851 enable = doc_buffer && (d.splitter_->count() == 1 ||
1852 d.splitter_->orientation() == Qt::Horizontal);
1855 case LFUN_TAB_GROUP_CLOSE:
1856 enable = d.tabWorkAreaCount() > 1;
1859 case LFUN_TOOLBAR_TOGGLE: {
1860 string const name = cmd.getArg(0);
1861 if (GuiToolbar * t = toolbar(name))
1862 flag.setOnOff(t->isVisible());
1865 docstring const msg =
1866 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1872 case LFUN_DROP_LAYOUTS_CHOICE:
1876 case LFUN_UI_TOGGLE:
1877 flag.setOnOff(isFullScreen());
1880 case LFUN_DIALOG_DISCONNECT_INSET:
1883 case LFUN_DIALOG_HIDE:
1884 // FIXME: should we check if the dialog is shown?
1887 case LFUN_DIALOG_TOGGLE:
1888 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1889 // fall through to set "enable"
1890 case LFUN_DIALOG_SHOW: {
1891 string const name = cmd.getArg(0);
1893 enable = name == "aboutlyx"
1894 || name == "file" //FIXME: should be removed.
1896 || name == "texinfo"
1897 || name == "progress"
1898 || name == "compare";
1899 else if (name == "character" || name == "symbols"
1900 || name == "mathdelimiter" || name == "mathmatrix") {
1901 if (!buf || buf->isReadonly())
1904 Cursor const & cur = currentBufferView()->cursor();
1905 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1908 else if (name == "latexlog")
1909 enable = FileName(doc_buffer->logName()).isReadableFile();
1910 else if (name == "spellchecker")
1911 enable = theSpellChecker()
1912 && !doc_buffer->isReadonly()
1913 && !doc_buffer->text().empty();
1914 else if (name == "vclog")
1915 enable = doc_buffer->lyxvc().inUse();
1919 case LFUN_DIALOG_UPDATE: {
1920 string const name = cmd.getArg(0);
1922 enable = name == "prefs";
1926 case LFUN_COMMAND_EXECUTE:
1928 case LFUN_MENU_OPEN:
1929 // Nothing to check.
1932 case LFUN_COMPLETION_INLINE:
1933 if (!d.current_work_area_
1934 || !d.current_work_area_->completer().inlinePossible(
1935 currentBufferView()->cursor()))
1939 case LFUN_COMPLETION_POPUP:
1940 if (!d.current_work_area_
1941 || !d.current_work_area_->completer().popupPossible(
1942 currentBufferView()->cursor()))
1947 if (!d.current_work_area_
1948 || !d.current_work_area_->completer().inlinePossible(
1949 currentBufferView()->cursor()))
1953 case LFUN_COMPLETION_ACCEPT:
1954 if (!d.current_work_area_
1955 || (!d.current_work_area_->completer().popupVisible()
1956 && !d.current_work_area_->completer().inlineVisible()
1957 && !d.current_work_area_->completer().completionAvailable()))
1961 case LFUN_COMPLETION_CANCEL:
1962 if (!d.current_work_area_
1963 || (!d.current_work_area_->completer().popupVisible()
1964 && !d.current_work_area_->completer().inlineVisible()))
1968 case LFUN_BUFFER_ZOOM_OUT:
1969 enable = doc_buffer && lyxrc.zoom > 10;
1972 case LFUN_BUFFER_ZOOM_IN:
1973 enable = doc_buffer;
1976 case LFUN_BUFFER_MOVE_NEXT:
1977 case LFUN_BUFFER_MOVE_PREVIOUS:
1978 // we do not cycle when moving
1979 case LFUN_BUFFER_NEXT:
1980 case LFUN_BUFFER_PREVIOUS:
1981 // because we cycle, it doesn't matter whether on first or last
1982 enable = (d.currentTabWorkArea()->count() > 1);
1984 case LFUN_BUFFER_SWITCH:
1985 // toggle on the current buffer, but do not toggle off
1986 // the other ones (is that a good idea?)
1988 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1989 flag.setOnOff(true);
1992 case LFUN_VC_REGISTER:
1993 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1995 case LFUN_VC_RENAME:
1996 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
1999 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2001 case LFUN_VC_CHECK_IN:
2002 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2004 case LFUN_VC_CHECK_OUT:
2005 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2007 case LFUN_VC_LOCKING_TOGGLE:
2008 enable = doc_buffer && !doc_buffer->isReadonly()
2009 && doc_buffer->lyxvc().lockingToggleEnabled();
2010 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2012 case LFUN_VC_REVERT:
2013 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2015 case LFUN_VC_UNDO_LAST:
2016 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2018 case LFUN_VC_REPO_UPDATE:
2019 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2021 case LFUN_VC_COMMAND: {
2022 if (cmd.argument().empty())
2024 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2028 case LFUN_VC_COMPARE:
2029 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2032 case LFUN_SERVER_GOTO_FILE_ROW:
2034 case LFUN_FORWARD_SEARCH:
2035 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2038 case LFUN_FILE_INSERT_PLAINTEXT:
2039 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2040 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2043 case LFUN_SPELLING_CONTINUOUSLY:
2044 flag.setOnOff(lyxrc.spellcheck_continuously);
2052 flag.setEnabled(false);
2058 static FileName selectTemplateFile()
2060 FileDialog dlg(qt_("Select template file"));
2061 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2062 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2064 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2065 QStringList(qt_("LyX Documents (*.lyx)")));
2067 if (result.first == FileDialog::Later)
2069 if (result.second.isEmpty())
2071 return FileName(fromqstr(result.second));
2075 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2079 Buffer * newBuffer = 0;
2081 newBuffer = checkAndLoadLyXFile(filename);
2082 } catch (ExceptionMessage const & e) {
2089 message(_("Document not loaded."));
2093 setBuffer(newBuffer);
2094 newBuffer->errors("Parse");
2097 theSession().lastFiles().add(filename);
2103 void GuiView::openDocument(string const & fname)
2105 string initpath = lyxrc.document_path;
2107 if (documentBufferView()) {
2108 string const trypath = documentBufferView()->buffer().filePath();
2109 // If directory is writeable, use this as default.
2110 if (FileName(trypath).isDirWritable())
2116 if (fname.empty()) {
2117 FileDialog dlg(qt_("Select document to open"));
2118 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2119 dlg.setButton2(qt_("Examples|#E#e"),
2120 toqstr(addPath(package().system_support().absFileName(), "examples")));
2122 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2123 FileDialog::Result result =
2124 dlg.open(toqstr(initpath), filter);
2126 if (result.first == FileDialog::Later)
2129 filename = fromqstr(result.second);
2131 // check selected filename
2132 if (filename.empty()) {
2133 message(_("Canceled."));
2139 // get absolute path of file and add ".lyx" to the filename if
2141 FileName const fullname =
2142 fileSearch(string(), filename, "lyx", support::may_not_exist);
2143 if (!fullname.empty())
2144 filename = fullname.absFileName();
2146 if (!fullname.onlyPath().isDirectory()) {
2147 Alert::warning(_("Invalid filename"),
2148 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2149 from_utf8(fullname.absFileName())));
2153 // if the file doesn't exist and isn't already open (bug 6645),
2154 // let the user create one
2155 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2156 !LyXVC::file_not_found_hook(fullname)) {
2157 // the user specifically chose this name. Believe him.
2158 Buffer * const b = newFile(filename, string(), true);
2164 docstring const disp_fn = makeDisplayPath(filename);
2165 message(bformat(_("Opening document %1$s..."), disp_fn));
2168 Buffer * buf = loadDocument(fullname);
2170 str2 = bformat(_("Document %1$s opened."), disp_fn);
2171 if (buf->lyxvc().inUse())
2172 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2173 " " + _("Version control detected.");
2175 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2180 // FIXME: clean that
2181 static bool import(GuiView * lv, FileName const & filename,
2182 string const & format, ErrorList & errorList)
2184 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2186 string loader_format;
2187 vector<string> loaders = theConverters().loaders();
2188 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2189 vector<string>::const_iterator it = loaders.begin();
2190 vector<string>::const_iterator en = loaders.end();
2191 for (; it != en; ++it) {
2192 if (!theConverters().isReachable(format, *it))
2195 string const tofile =
2196 support::changeExtension(filename.absFileName(),
2197 formats.extension(*it));
2198 if (!theConverters().convert(0, filename, FileName(tofile),
2199 filename, format, *it, errorList))
2201 loader_format = *it;
2204 if (loader_format.empty()) {
2205 frontend::Alert::error(_("Couldn't import file"),
2206 bformat(_("No information for importing the format %1$s."),
2207 formats.prettyName(format)));
2211 loader_format = format;
2213 if (loader_format == "lyx") {
2214 Buffer * buf = lv->loadDocument(lyxfile);
2218 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2222 bool as_paragraphs = loader_format == "textparagraph";
2223 string filename2 = (loader_format == format) ? filename.absFileName()
2224 : support::changeExtension(filename.absFileName(),
2225 formats.extension(loader_format));
2226 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2228 guiApp->setCurrentView(lv);
2229 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2236 void GuiView::importDocument(string const & argument)
2239 string filename = split(argument, format, ' ');
2241 LYXERR(Debug::INFO, format << " file: " << filename);
2243 // need user interaction
2244 if (filename.empty()) {
2245 string initpath = lyxrc.document_path;
2246 if (documentBufferView()) {
2247 string const trypath = documentBufferView()->buffer().filePath();
2248 // If directory is writeable, use this as default.
2249 if (FileName(trypath).isDirWritable())
2253 docstring const text = bformat(_("Select %1$s file to import"),
2254 formats.prettyName(format));
2256 FileDialog dlg(toqstr(text));
2257 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2258 dlg.setButton2(qt_("Examples|#E#e"),
2259 toqstr(addPath(package().system_support().absFileName(), "examples")));
2261 docstring filter = formats.prettyName(format);
2264 filter += from_utf8(formats.extensions(format));
2267 FileDialog::Result result =
2268 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2270 if (result.first == FileDialog::Later)
2273 filename = fromqstr(result.second);
2275 // check selected filename
2276 if (filename.empty())
2277 message(_("Canceled."));
2280 if (filename.empty())
2283 // get absolute path of file
2284 FileName const fullname(support::makeAbsPath(filename));
2286 // Can happen if the user entered a path into the dialog
2288 if (fullname.onlyFileName().empty()) {
2289 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2290 "Aborting import."),
2291 from_utf8(fullname.absFileName()));
2292 frontend::Alert::error(_("File name error"), msg);
2293 message(_("Canceled."));
2298 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2300 // Check if the document already is open
2301 Buffer * buf = theBufferList().getBuffer(lyxfile);
2304 if (!closeBuffer()) {
2305 message(_("Canceled."));
2310 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2312 // if the file exists already, and we didn't do
2313 // -i lyx thefile.lyx, warn
2314 if (lyxfile.exists() && fullname != lyxfile) {
2316 docstring text = bformat(_("The document %1$s already exists.\n\n"
2317 "Do you want to overwrite that document?"), displaypath);
2318 int const ret = Alert::prompt(_("Overwrite document?"),
2319 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2322 message(_("Canceled."));
2327 message(bformat(_("Importing %1$s..."), displaypath));
2328 ErrorList errorList;
2329 if (import(this, fullname, format, errorList))
2330 message(_("imported."));
2332 message(_("file not imported!"));
2334 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2338 void GuiView::newDocument(string const & filename, bool from_template)
2340 FileName initpath(lyxrc.document_path);
2341 if (documentBufferView()) {
2342 FileName const trypath(documentBufferView()->buffer().filePath());
2343 // If directory is writeable, use this as default.
2344 if (trypath.isDirWritable())
2348 string templatefile;
2349 if (from_template) {
2350 templatefile = selectTemplateFile().absFileName();
2351 if (templatefile.empty())
2356 if (filename.empty())
2357 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2359 b = newFile(filename, templatefile, true);
2364 // If no new document could be created, it is unsure
2365 // whether there is a valid BufferView.
2366 if (currentBufferView())
2367 // Ensure the cursor is correctly positioned on screen.
2368 currentBufferView()->showCursor();
2372 void GuiView::insertLyXFile(docstring const & fname)
2374 BufferView * bv = documentBufferView();
2379 FileName filename(to_utf8(fname));
2380 if (filename.empty()) {
2381 // Launch a file browser
2383 string initpath = lyxrc.document_path;
2384 string const trypath = bv->buffer().filePath();
2385 // If directory is writeable, use this as default.
2386 if (FileName(trypath).isDirWritable())
2390 FileDialog dlg(qt_("Select LyX document to insert"));
2391 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2392 dlg.setButton2(qt_("Examples|#E#e"),
2393 toqstr(addPath(package().system_support().absFileName(),
2396 FileDialog::Result result = dlg.open(toqstr(initpath),
2397 QStringList(qt_("LyX Documents (*.lyx)")));
2399 if (result.first == FileDialog::Later)
2403 filename.set(fromqstr(result.second));
2405 // check selected filename
2406 if (filename.empty()) {
2407 // emit message signal.
2408 message(_("Canceled."));
2413 bv->insertLyXFile(filename);
2414 bv->buffer().errors("Parse");
2418 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2420 FileName fname = b.fileName();
2421 FileName const oldname = fname;
2423 if (!newname.empty()) {
2425 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2427 // Switch to this Buffer.
2430 // No argument? Ask user through dialog.
2432 FileDialog dlg(qt_("Choose a filename to save document as"));
2433 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2434 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2436 if (!isLyXFileName(fname.absFileName()))
2437 fname.changeExtension(".lyx");
2439 FileDialog::Result result =
2440 dlg.save(toqstr(fname.onlyPath().absFileName()),
2441 QStringList(qt_("LyX Documents (*.lyx)")),
2442 toqstr(fname.onlyFileName()));
2444 if (result.first == FileDialog::Later)
2447 fname.set(fromqstr(result.second));
2452 if (!isLyXFileName(fname.absFileName()))
2453 fname.changeExtension(".lyx");
2456 // fname is now the new Buffer location.
2458 // if there is already a Buffer open with this name, we do not want
2459 // to have another one. (the second test makes sure we're not just
2460 // trying to overwrite ourselves, which is fine.)
2461 if (theBufferList().exists(fname) && fname != oldname
2462 && theBufferList().getBuffer(fname) != &b) {
2463 docstring const text =
2464 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2465 "Please close it before attempting to overwrite it.\n"
2466 "Do you want to choose a new filename?"),
2467 from_utf8(fname.absFileName()));
2468 int const ret = Alert::prompt(_("Chosen File Already Open"),
2469 text, 0, 1, _("&Rename"), _("&Cancel"));
2471 case 0: return renameBuffer(b, docstring(), kind);
2472 case 1: return false;
2477 bool const existsLocal = fname.exists();
2478 bool const existsInVC = LyXVC::fileInVC(fname);
2479 if (existsLocal || existsInVC) {
2480 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2481 if (kind != LV_WRITE_AS && existsInVC) {
2482 // renaming to a name that is already in VC
2484 docstring text = bformat(_("The document %1$s "
2485 "is already registered.\n\n"
2486 "Do you want to choose a new name?"),
2488 docstring const title = (kind == LV_VC_RENAME) ?
2489 _("Rename document?") : _("Copy document?");
2490 docstring const button = (kind == LV_VC_RENAME) ?
2491 _("&Rename") : _("&Copy");
2492 int const ret = Alert::prompt(title, text, 0, 1,
2493 button, _("&Cancel"));
2495 case 0: return renameBuffer(b, docstring(), kind);
2496 case 1: return false;
2501 docstring text = bformat(_("The document %1$s "
2502 "already exists.\n\n"
2503 "Do you want to overwrite that document?"),
2505 int const ret = Alert::prompt(_("Overwrite document?"),
2506 text, 0, 2, _("&Overwrite"),
2507 _("&Rename"), _("&Cancel"));
2510 case 1: return renameBuffer(b, docstring(), kind);
2511 case 2: return false;
2517 case LV_VC_RENAME: {
2518 string msg = b.lyxvc().rename(fname);
2521 message(from_utf8(msg));
2525 string msg = b.lyxvc().copy(fname);
2528 message(from_utf8(msg));
2534 // LyXVC created the file already in case of LV_VC_RENAME or
2535 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2536 // relative paths of included stuff right if we moved e.g. from
2537 // /a/b.lyx to /a/c/b.lyx.
2539 bool const saved = saveBuffer(b, fname);
2546 struct PrettyNameComparator
2548 bool operator()(Format const *first, Format const *second) const {
2549 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2550 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2555 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2557 FileName fname = b.fileName();
2559 FileDialog dlg(qt_("Choose a filename to export the document as"));
2560 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2563 QString const anyformat = qt_("Guess from extension (*.*)");
2565 Formats::const_iterator it = formats.begin();
2566 vector<Format const *> export_formats;
2567 for (; it != formats.end(); ++it)
2568 if (it->documentFormat())
2569 export_formats.push_back(&(*it));
2570 PrettyNameComparator cmp;
2571 sort(export_formats.begin(), export_formats.end(), cmp);
2572 vector<Format const *>::const_iterator fit = export_formats.begin();
2573 map<QString, string> fmap;
2576 for (; fit != export_formats.end(); ++fit) {
2577 docstring const loc_prettyname =
2578 translateIfPossible(from_utf8((*fit)->prettyname()));
2579 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2581 from_ascii((*fit)->extension())));
2582 types << loc_filter;
2583 fmap[loc_filter] = (*fit)->name();
2584 if (from_ascii((*fit)->name()) == iformat) {
2585 filter = loc_filter;
2586 ext = (*fit)->extension();
2589 string ofname = fname.onlyFileName();
2591 ofname = support::changeExtension(ofname, ext);
2592 FileDialog::Result result =
2593 dlg.save(toqstr(fname.onlyPath().absFileName()),
2597 if (result.first != FileDialog::Chosen)
2601 fname.set(fromqstr(result.second));
2602 if (filter == anyformat)
2603 fmt_name = formats.getFormatFromExtension(fname.extension());
2605 fmt_name = fmap[filter];
2606 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2607 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2609 if (fmt_name.empty() || fname.empty())
2612 // fname is now the new Buffer location.
2613 if (FileName(fname).exists()) {
2614 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2615 docstring text = bformat(_("The document %1$s already "
2616 "exists.\n\nDo you want to "
2617 "overwrite that document?"),
2619 int const ret = Alert::prompt(_("Overwrite document?"),
2620 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2623 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2624 case 2: return false;
2628 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2631 return dr.dispatched();
2635 bool GuiView::saveBuffer(Buffer & b)
2637 return saveBuffer(b, FileName());
2641 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2643 if (workArea(b) && workArea(b)->inDialogMode())
2646 if (fn.empty() && b.isUnnamed())
2647 return renameBuffer(b, docstring());
2649 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2651 theSession().lastFiles().add(b.fileName());
2655 // Switch to this Buffer.
2658 // FIXME: we don't tell the user *WHY* the save failed !!
2659 docstring const file = makeDisplayPath(b.absFileName(), 30);
2660 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2661 "Do you want to rename the document and "
2662 "try again?"), file);
2663 int const ret = Alert::prompt(_("Rename and save?"),
2664 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2667 if (!renameBuffer(b, docstring()))
2676 return saveBuffer(b, fn);
2680 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2682 return closeWorkArea(wa, false);
2686 // We only want to close the buffer if it is not visible in other workareas
2687 // of the same view, nor in other views, and if this is not a child
2688 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2690 Buffer & buf = wa->bufferView().buffer();
2692 bool last_wa = d.countWorkAreasOf(buf) == 1
2693 && !inOtherView(buf) && !buf.parent();
2695 bool close_buffer = last_wa;
2698 if (lyxrc.close_buffer_with_last_view == "yes")
2700 else if (lyxrc.close_buffer_with_last_view == "no")
2701 close_buffer = false;
2704 if (buf.isUnnamed())
2705 file = from_utf8(buf.fileName().onlyFileName());
2707 file = buf.fileName().displayName(30);
2708 docstring const text = bformat(
2709 _("Last view on document %1$s is being closed.\n"
2710 "Would you like to close or hide the document?\n"
2712 "Hidden documents can be displayed back through\n"
2713 "the menu: View->Hidden->...\n"
2715 "To remove this question, set your preference in:\n"
2716 " Tools->Preferences->Look&Feel->UserInterface\n"
2718 int ret = Alert::prompt(_("Close or hide document?"),
2719 text, 0, 1, _("&Close"), _("&Hide"));
2720 close_buffer = (ret == 0);
2724 return closeWorkArea(wa, close_buffer);
2728 bool GuiView::closeBuffer()
2730 GuiWorkArea * wa = currentMainWorkArea();
2731 setCurrentWorkArea(wa);
2732 Buffer & buf = wa->bufferView().buffer();
2733 return wa && closeWorkArea(wa, !buf.parent());
2737 void GuiView::writeSession() const {
2738 GuiWorkArea const * active_wa = currentMainWorkArea();
2739 for (int i = 0; i < d.splitter_->count(); ++i) {
2740 TabWorkArea * twa = d.tabWorkArea(i);
2741 for (int j = 0; j < twa->count(); ++j) {
2742 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2743 Buffer & buf = wa->bufferView().buffer();
2744 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2750 bool GuiView::closeBufferAll()
2752 // Close the workareas in all other views
2753 QList<int> const ids = guiApp->viewIds();
2754 for (int i = 0; i != ids.size(); ++i) {
2755 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2759 // Close our own workareas
2760 if (!closeWorkAreaAll())
2763 // Now close the hidden buffers. We prevent hidden buffers from being
2764 // dirty, so we can just close them.
2765 theBufferList().closeAll();
2770 bool GuiView::closeWorkAreaAll()
2772 setCurrentWorkArea(currentMainWorkArea());
2774 // We might be in a situation that there is still a tabWorkArea, but
2775 // there are no tabs anymore. This can happen when we get here after a
2776 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2777 // many TabWorkArea's have no documents anymore.
2780 // We have to call count() each time, because it can happen that
2781 // more than one splitter will disappear in one iteration (bug 5998).
2782 for (; d.splitter_->count() > empty_twa; ) {
2783 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2785 if (twa->count() == 0)
2788 setCurrentWorkArea(twa->currentWorkArea());
2789 if (!closeTabWorkArea(twa))
2797 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2802 Buffer & buf = wa->bufferView().buffer();
2804 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2805 Alert::warning(_("Close document"),
2806 _("Document could not be closed because it is being processed by LyX."));
2811 return closeBuffer(buf);
2813 if (!inMultiTabs(wa))
2814 if (!saveBufferIfNeeded(buf, true))
2822 bool GuiView::closeBuffer(Buffer & buf)
2824 // If we are in a close_event all children will be closed in some time,
2825 // so no need to do it here. This will ensure that the children end up
2826 // in the session file in the correct order. If we close the master
2827 // buffer, we can close or release the child buffers here too.
2828 bool success = true;
2830 ListOfBuffers clist = buf.getChildren();
2831 ListOfBuffers::const_iterator it = clist.begin();
2832 ListOfBuffers::const_iterator const bend = clist.end();
2833 for (; it != bend; ++it) {
2834 // If a child is dirty, do not close
2835 // without user intervention
2836 //FIXME: should we look in other tabworkareas?
2837 Buffer * child_buf = *it;
2838 GuiWorkArea * child_wa = workArea(*child_buf);
2840 if (!closeWorkArea(child_wa, true)) {
2845 theBufferList().releaseChild(&buf, child_buf);
2849 // goto bookmark to update bookmark pit.
2850 //FIXME: we should update only the bookmarks related to this buffer!
2851 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2852 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2853 guiApp->gotoBookmark(i+1, false, false);
2855 if (saveBufferIfNeeded(buf, false)) {
2856 buf.removeAutosaveFile();
2857 theBufferList().release(&buf);
2861 // open all children again to avoid a crash because of dangling
2862 // pointers (bug 6603)
2868 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2870 while (twa == d.currentTabWorkArea()) {
2871 twa->setCurrentIndex(twa->count()-1);
2873 GuiWorkArea * wa = twa->currentWorkArea();
2874 Buffer & b = wa->bufferView().buffer();
2876 // We only want to close the buffer if the same buffer is not visible
2877 // in another view, and if this is not a child and if we are closing
2878 // a view (not a tabgroup).
2879 bool const close_buffer =
2880 !inOtherView(b) && !b.parent() && closing_;
2882 if (!closeWorkArea(wa, close_buffer))
2889 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2891 if (buf.isClean() || buf.paragraphs().empty())
2894 // Switch to this Buffer.
2899 if (buf.isUnnamed())
2900 file = from_utf8(buf.fileName().onlyFileName());
2902 file = buf.fileName().displayName(30);
2904 // Bring this window to top before asking questions.
2909 if (hiding && buf.isUnnamed()) {
2910 docstring const text = bformat(_("The document %1$s has not been "
2911 "saved yet.\n\nDo you want to save "
2912 "the document?"), file);
2913 ret = Alert::prompt(_("Save new document?"),
2914 text, 0, 1, _("&Save"), _("&Cancel"));
2918 docstring const text = bformat(_("The document %1$s has unsaved changes."
2919 "\n\nDo you want to save the document or discard the changes?"), file);
2920 ret = Alert::prompt(_("Save changed document?"),
2921 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2926 if (!saveBuffer(buf))
2930 // If we crash after this we could have no autosave file
2931 // but I guess this is really improbable (Jug).
2932 // Sometimes improbable things happen:
2933 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2934 // buf.removeAutosaveFile();
2936 // revert all changes
2947 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2949 Buffer & buf = wa->bufferView().buffer();
2951 for (int i = 0; i != d.splitter_->count(); ++i) {
2952 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2953 if (wa_ && wa_ != wa)
2956 return inOtherView(buf);
2960 bool GuiView::inOtherView(Buffer & buf)
2962 QList<int> const ids = guiApp->viewIds();
2964 for (int i = 0; i != ids.size(); ++i) {
2968 if (guiApp->view(ids[i]).workArea(buf))
2975 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2977 if (!documentBufferView())
2980 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2981 Buffer * const curbuf = &documentBufferView()->buffer();
2982 int nwa = twa->count();
2983 for (int i = 0; i < nwa; ++i) {
2984 if (&workArea(i)->bufferView().buffer() == curbuf) {
2986 if (np == NEXTBUFFER)
2987 next_index = (i == nwa - 1 ? 0 : i + 1);
2989 next_index = (i == 0 ? nwa - 1 : i - 1);
2991 twa->moveTab(i, next_index);
2993 setBuffer(&workArea(next_index)->bufferView().buffer());
3001 /// make sure the document is saved
3002 static bool ensureBufferClean(Buffer * buffer)
3004 LASSERT(buffer, return false);
3005 if (buffer->isClean() && !buffer->isUnnamed())
3008 docstring const file = buffer->fileName().displayName(30);
3011 if (!buffer->isUnnamed()) {
3012 text = bformat(_("The document %1$s has unsaved "
3013 "changes.\n\nDo you want to save "
3014 "the document?"), file);
3015 title = _("Save changed document?");
3018 text = bformat(_("The document %1$s has not been "
3019 "saved yet.\n\nDo you want to save "
3020 "the document?"), file);
3021 title = _("Save new document?");
3023 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3026 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3028 return buffer->isClean() && !buffer->isUnnamed();
3032 bool GuiView::reloadBuffer(Buffer & buf)
3034 Buffer::ReadStatus status = buf.reload();
3035 return status == Buffer::ReadSuccess;
3039 void GuiView::checkExternallyModifiedBuffers()
3041 BufferList::iterator bit = theBufferList().begin();
3042 BufferList::iterator const bend = theBufferList().end();
3043 for (; bit != bend; ++bit) {
3044 Buffer * buf = *bit;
3045 if (buf->fileName().exists()
3046 && buf->isExternallyModified(Buffer::checksum_method)) {
3047 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3048 " Reload now? Any local changes will be lost."),
3049 from_utf8(buf->absFileName()));
3050 int const ret = Alert::prompt(_("Reload externally changed document?"),
3051 text, 0, 1, _("&Reload"), _("&Cancel"));
3059 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3061 Buffer * buffer = documentBufferView()
3062 ? &(documentBufferView()->buffer()) : 0;
3064 switch (cmd.action()) {
3065 case LFUN_VC_REGISTER:
3066 if (!buffer || !ensureBufferClean(buffer))
3068 if (!buffer->lyxvc().inUse()) {
3069 if (buffer->lyxvc().registrer()) {
3070 reloadBuffer(*buffer);
3071 dr.clearMessageUpdate();
3076 case LFUN_VC_RENAME:
3077 case LFUN_VC_COPY: {
3078 if (!buffer || !ensureBufferClean(buffer))
3080 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3081 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3082 // Some changes are not yet committed.
3083 // We test here and not in getStatus(), since
3084 // this test is expensive.
3086 LyXVC::CommandResult ret =
3087 buffer->lyxvc().checkIn(log);
3089 if (ret == LyXVC::ErrorCommand ||
3090 ret == LyXVC::VCSuccess)
3091 reloadBuffer(*buffer);
3092 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3093 frontend::Alert::error(
3094 _("Revision control error."),
3095 _("Document could not be checked in."));
3099 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3100 LV_VC_RENAME : LV_VC_COPY;
3101 renameBuffer(*buffer, cmd.argument(), kind);
3106 case LFUN_VC_CHECK_IN:
3107 if (!buffer || !ensureBufferClean(buffer))
3109 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3111 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3113 // Only skip reloading if the checkin was cancelled or
3114 // an error occurred before the real checkin VCS command
3115 // was executed, since the VCS might have changed the
3116 // file even if it could not checkin successfully.
3117 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3118 reloadBuffer(*buffer);
3122 case LFUN_VC_CHECK_OUT:
3123 if (!buffer || !ensureBufferClean(buffer))
3125 if (buffer->lyxvc().inUse()) {
3126 dr.setMessage(buffer->lyxvc().checkOut());
3127 reloadBuffer(*buffer);
3131 case LFUN_VC_LOCKING_TOGGLE:
3132 LASSERT(buffer, return);
3133 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3135 if (buffer->lyxvc().inUse()) {
3136 string res = buffer->lyxvc().lockingToggle();
3138 frontend::Alert::error(_("Revision control error."),
3139 _("Error when setting the locking property."));
3142 reloadBuffer(*buffer);
3147 case LFUN_VC_REVERT:
3148 LASSERT(buffer, return);
3149 if (buffer->lyxvc().revert()) {
3150 reloadBuffer(*buffer);
3151 dr.clearMessageUpdate();
3155 case LFUN_VC_UNDO_LAST:
3156 LASSERT(buffer, return);
3157 buffer->lyxvc().undoLast();
3158 reloadBuffer(*buffer);
3159 dr.clearMessageUpdate();
3162 case LFUN_VC_REPO_UPDATE:
3163 LASSERT(buffer, return);
3164 if (ensureBufferClean(buffer)) {
3165 dr.setMessage(buffer->lyxvc().repoUpdate());
3166 checkExternallyModifiedBuffers();
3170 case LFUN_VC_COMMAND: {
3171 string flag = cmd.getArg(0);
3172 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3175 if (contains(flag, 'M')) {
3176 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3179 string path = cmd.getArg(1);
3180 if (contains(path, "$$p") && buffer)
3181 path = subst(path, "$$p", buffer->filePath());
3182 LYXERR(Debug::LYXVC, "Directory: " << path);
3184 if (!pp.isReadableDirectory()) {
3185 lyxerr << _("Directory is not accessible.") << endl;
3188 support::PathChanger p(pp);
3190 string command = cmd.getArg(2);
3191 if (command.empty())
3194 command = subst(command, "$$i", buffer->absFileName());
3195 command = subst(command, "$$p", buffer->filePath());
3197 command = subst(command, "$$m", to_utf8(message));
3198 LYXERR(Debug::LYXVC, "Command: " << command);
3200 one.startscript(Systemcall::Wait, command);
3204 if (contains(flag, 'I'))
3205 buffer->markDirty();
3206 if (contains(flag, 'R'))
3207 reloadBuffer(*buffer);
3212 case LFUN_VC_COMPARE: {
3214 if (cmd.argument().empty()) {
3215 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3219 string rev1 = cmd.getArg(0);
3223 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3226 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3227 f2 = buffer->absFileName();
3229 string rev2 = cmd.getArg(1);
3233 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3237 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3238 f1 << "\n" << f2 << "\n" );
3239 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3240 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3250 void GuiView::openChildDocument(string const & fname)
3252 LASSERT(documentBufferView(), return);
3253 Buffer & buffer = documentBufferView()->buffer();
3254 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3255 documentBufferView()->saveBookmark(false);
3257 if (theBufferList().exists(filename)) {
3258 child = theBufferList().getBuffer(filename);
3261 message(bformat(_("Opening child document %1$s..."),
3262 makeDisplayPath(filename.absFileName())));
3263 child = loadDocument(filename, false);
3265 // Set the parent name of the child document.
3266 // This makes insertion of citations and references in the child work,
3267 // when the target is in the parent or another child document.
3269 child->setParent(&buffer);
3273 bool GuiView::goToFileRow(string const & argument)
3277 size_t i = argument.find_last_of(' ');
3278 if (i != string::npos) {
3279 file_name = os::internal_path(trim(argument.substr(0, i)));
3280 istringstream is(argument.substr(i + 1));
3285 if (i == string::npos) {
3286 LYXERR0("Wrong argument: " << argument);
3290 string const abstmp = package().temp_dir().absFileName();
3291 string const realtmp = package().temp_dir().realPath();
3292 // We have to use os::path_prefix_is() here, instead of
3293 // simply prefixIs(), because the file name comes from
3294 // an external application and may need case adjustment.
3295 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3296 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3297 // Needed by inverse dvi search. If it is a file
3298 // in tmpdir, call the apropriated function.
3299 // If tmpdir is a symlink, we may have the real
3300 // path passed back, so we correct for that.
3301 if (!prefixIs(file_name, abstmp))
3302 file_name = subst(file_name, realtmp, abstmp);
3303 buf = theBufferList().getBufferFromTmp(file_name);
3305 // Must replace extension of the file to be .lyx
3306 // and get full path
3307 FileName const s = fileSearch(string(),
3308 support::changeExtension(file_name, ".lyx"), "lyx");
3309 // Either change buffer or load the file
3310 if (theBufferList().exists(s))
3311 buf = theBufferList().getBuffer(s);
3312 else if (s.exists()) {
3313 buf = loadDocument(s);
3318 _("File does not exist: %1$s"),
3319 makeDisplayPath(file_name)));
3325 _("No buffer for file: %1$s."),
3326 makeDisplayPath(file_name))
3331 documentBufferView()->setCursorFromRow(row);
3337 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3339 Buffer::ExportStatus const status = func(format);
3341 // the cloning operation will have produced a clone of the entire set of
3342 // documents, starting from the master. so we must delete those.
3343 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3345 busyBuffers.remove(orig);
3350 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3352 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3353 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3357 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3359 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3360 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3364 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3366 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3367 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3371 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3372 string const & argument,
3373 Buffer const * used_buffer,
3374 docstring const & msg,
3375 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3376 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3377 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3382 string format = argument;
3384 format = used_buffer->params().getDefaultOutputFormat();
3385 processing_format = format;
3387 progress_->clearMessages();
3390 #if EXPORT_in_THREAD
3391 GuiViewPrivate::busyBuffers.insert(used_buffer);
3392 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3393 if (!cloned_buffer) {
3394 Alert::error(_("Export Error"),
3395 _("Error cloning the Buffer."));
3398 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3403 setPreviewFuture(f);
3404 last_export_format = used_buffer->params().bufferFormat();
3407 // We are asynchronous, so we don't know here anything about the success
3410 Buffer::ExportStatus status;
3412 // TODO check here if it breaks exporting with Qt < 4.4
3413 status = (used_buffer->*syncFunc)(format, true);
3414 } else if (previewFunc) {
3415 status = (used_buffer->*previewFunc)(format);
3418 handleExportStatus(gv_, status, format);
3420 return (status == Buffer::ExportSuccess
3421 || status == Buffer::PreviewSuccess);
3425 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3427 BufferView * bv = currentBufferView();
3428 LASSERT(bv, return);
3430 // Let the current BufferView dispatch its own actions.
3431 bv->dispatch(cmd, dr);
3432 if (dr.dispatched())
3435 // Try with the document BufferView dispatch if any.
3436 BufferView * doc_bv = documentBufferView();
3437 if (doc_bv && doc_bv != bv) {
3438 doc_bv->dispatch(cmd, dr);
3439 if (dr.dispatched())
3443 // Then let the current Cursor dispatch its own actions.
3444 bv->cursor().dispatch(cmd);
3446 // update completion. We do it here and not in
3447 // processKeySym to avoid another redraw just for a
3448 // changed inline completion
3449 if (cmd.origin() == FuncRequest::KEYBOARD) {
3450 if (cmd.action() == LFUN_SELF_INSERT
3451 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3452 updateCompletion(bv->cursor(), true, true);
3453 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3454 updateCompletion(bv->cursor(), false, true);
3456 updateCompletion(bv->cursor(), false, false);
3459 dr = bv->cursor().result();
3463 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3465 BufferView * bv = currentBufferView();
3466 // By default we won't need any update.
3467 dr.screenUpdate(Update::None);
3468 // assume cmd will be dispatched
3469 dr.dispatched(true);
3471 Buffer * doc_buffer = documentBufferView()
3472 ? &(documentBufferView()->buffer()) : 0;
3474 if (cmd.origin() == FuncRequest::TOC) {
3475 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3476 // FIXME: do we need to pass a DispatchResult object here?
3477 toc->doDispatch(bv->cursor(), cmd);
3481 string const argument = to_utf8(cmd.argument());
3483 switch(cmd.action()) {
3484 case LFUN_BUFFER_CHILD_OPEN:
3485 openChildDocument(to_utf8(cmd.argument()));
3488 case LFUN_BUFFER_IMPORT:
3489 importDocument(to_utf8(cmd.argument()));
3492 case LFUN_BUFFER_EXPORT: {
3495 FileName target_dir = doc_buffer->fileName().onlyPath();
3496 string const dest = cmd.getArg(1);
3497 if (!dest.empty() && FileName::isAbsolute(dest))
3498 target_dir = FileName(support::onlyPath(dest));
3499 // GCC only sees strfwd.h when building merged
3500 if (::lyx::operator==(cmd.argument(), "custom")) {
3501 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3504 if (!target_dir.isDirWritable()) {
3505 exportBufferAs(*doc_buffer, cmd.argument());
3508 /* TODO/Review: Is it a problem to also export the children?
3509 See the update_unincluded flag */
3510 d.asyncBufferProcessing(argument,
3513 &GuiViewPrivate::exportAndDestroy,
3516 // TODO Inform user about success
3520 case LFUN_BUFFER_EXPORT_AS: {
3521 LASSERT(doc_buffer, break);
3522 docstring f = cmd.argument();
3524 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3525 exportBufferAs(*doc_buffer, f);
3529 case LFUN_BUFFER_UPDATE: {
3530 d.asyncBufferProcessing(argument,
3533 &GuiViewPrivate::compileAndDestroy,
3538 case LFUN_BUFFER_VIEW: {
3539 d.asyncBufferProcessing(argument,
3541 _("Previewing ..."),
3542 &GuiViewPrivate::previewAndDestroy,
3547 case LFUN_MASTER_BUFFER_UPDATE: {
3548 d.asyncBufferProcessing(argument,
3549 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3551 &GuiViewPrivate::compileAndDestroy,
3556 case LFUN_MASTER_BUFFER_VIEW: {
3557 d.asyncBufferProcessing(argument,
3558 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3560 &GuiViewPrivate::previewAndDestroy,
3561 0, &Buffer::preview);
3564 case LFUN_BUFFER_SWITCH: {
3565 string const file_name = to_utf8(cmd.argument());
3566 if (!FileName::isAbsolute(file_name)) {
3568 dr.setMessage(_("Absolute filename expected."));
3572 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3575 dr.setMessage(_("Document not loaded"));
3579 // Do we open or switch to the buffer in this view ?
3580 if (workArea(*buffer)
3581 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3586 // Look for the buffer in other views
3587 QList<int> const ids = guiApp->viewIds();
3589 for (; i != ids.size(); ++i) {
3590 GuiView & gv = guiApp->view(ids[i]);
3591 if (gv.workArea(*buffer)) {
3592 gv.activateWindow();
3593 gv.setBuffer(buffer);
3598 // If necessary, open a new window as a last resort
3599 if (i == ids.size()) {
3600 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3606 case LFUN_BUFFER_NEXT:
3607 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3610 case LFUN_BUFFER_MOVE_NEXT:
3611 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3614 case LFUN_BUFFER_PREVIOUS:
3615 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3618 case LFUN_BUFFER_MOVE_PREVIOUS:
3619 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3622 case LFUN_COMMAND_EXECUTE: {
3623 bool const show_it = cmd.argument() != "off";
3624 // FIXME: this is a hack, "minibuffer" should not be
3626 if (GuiToolbar * t = toolbar("minibuffer")) {
3627 t->setVisible(show_it);
3628 if (show_it && t->commandBuffer())
3629 t->commandBuffer()->setFocus();
3633 case LFUN_DROP_LAYOUTS_CHOICE:
3634 d.layout_->showPopup();
3637 case LFUN_MENU_OPEN:
3638 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3639 menu->exec(QCursor::pos());
3642 case LFUN_FILE_INSERT:
3643 insertLyXFile(cmd.argument());
3646 case LFUN_FILE_INSERT_PLAINTEXT:
3647 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3648 string const fname = to_utf8(cmd.argument());
3649 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3650 dr.setMessage(_("Absolute filename expected."));
3654 FileName filename(fname);
3655 if (fname.empty()) {
3656 FileDialog dlg(qt_("Select file to insert"));
3658 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3659 QStringList(qt_("All Files (*)")));
3661 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3662 dr.setMessage(_("Canceled."));
3666 filename.set(fromqstr(result.second));
3670 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3671 bv->dispatch(new_cmd, dr);
3676 case LFUN_BUFFER_RELOAD: {
3677 LASSERT(doc_buffer, break);
3680 if (!doc_buffer->isClean()) {
3681 docstring const file =
3682 makeDisplayPath(doc_buffer->absFileName(), 20);
3683 docstring text = bformat(_("Any changes will be lost. "
3684 "Are you sure you want to revert to the saved version "
3685 "of the document %1$s?"), file);
3686 ret = Alert::prompt(_("Revert to saved document?"),
3687 text, 1, 1, _("&Revert"), _("&Cancel"));
3691 doc_buffer->markClean();
3692 reloadBuffer(*doc_buffer);
3693 dr.forceBufferUpdate();
3698 case LFUN_BUFFER_WRITE:
3699 LASSERT(doc_buffer, break);
3700 saveBuffer(*doc_buffer);
3703 case LFUN_BUFFER_WRITE_AS:
3704 LASSERT(doc_buffer, break);
3705 renameBuffer(*doc_buffer, cmd.argument());
3708 case LFUN_BUFFER_WRITE_ALL: {
3709 Buffer * first = theBufferList().first();
3712 message(_("Saving all documents..."));
3713 // We cannot use a for loop as the buffer list cycles.
3716 if (!b->isClean()) {
3718 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3720 b = theBufferList().next(b);
3721 } while (b != first);
3722 dr.setMessage(_("All documents saved."));
3726 case LFUN_BUFFER_CLOSE:
3730 case LFUN_BUFFER_CLOSE_ALL:
3734 case LFUN_TOOLBAR_TOGGLE: {
3735 string const name = cmd.getArg(0);
3736 if (GuiToolbar * t = toolbar(name))
3741 case LFUN_DIALOG_UPDATE: {
3742 string const name = to_utf8(cmd.argument());
3743 if (name == "prefs" || name == "document")
3744 updateDialog(name, string());
3745 else if (name == "paragraph")
3746 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3747 else if (currentBufferView()) {
3748 Inset * inset = currentBufferView()->editedInset(name);
3749 // Can only update a dialog connected to an existing inset
3751 // FIXME: get rid of this indirection; GuiView ask the inset
3752 // if he is kind enough to update itself...
3753 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3754 //FIXME: pass DispatchResult here?
3755 inset->dispatch(currentBufferView()->cursor(), fr);
3761 case LFUN_DIALOG_TOGGLE: {
3762 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3763 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3764 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3768 case LFUN_DIALOG_DISCONNECT_INSET:
3769 disconnectDialog(to_utf8(cmd.argument()));
3772 case LFUN_DIALOG_HIDE: {
3773 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3777 case LFUN_DIALOG_SHOW: {
3778 string const name = cmd.getArg(0);
3779 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3781 if (name == "character") {
3782 data = freefont2string();
3784 showDialog("character", data);
3785 } else if (name == "latexlog") {
3786 Buffer::LogType type;
3787 string const logfile = doc_buffer->logName(&type);
3789 case Buffer::latexlog:
3792 case Buffer::buildlog:
3796 data += Lexer::quoteString(logfile);
3797 showDialog("log", data);
3798 } else if (name == "vclog") {
3799 string const data = "vc " +
3800 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3801 showDialog("log", data);
3802 } else if (name == "symbols") {
3803 data = bv->cursor().getEncoding()->name();
3805 showDialog("symbols", data);
3807 } else if (name == "prefs" && isFullScreen()) {
3808 lfunUiToggle("fullscreen");
3809 showDialog("prefs", data);
3811 showDialog(name, data);
3816 dr.setMessage(cmd.argument());
3819 case LFUN_UI_TOGGLE: {
3820 string arg = cmd.getArg(0);
3821 if (!lfunUiToggle(arg)) {
3822 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3823 dr.setMessage(bformat(msg, from_utf8(arg)));
3825 // Make sure the keyboard focus stays in the work area.
3830 case LFUN_VIEW_SPLIT: {
3831 LASSERT(doc_buffer, break);
3832 string const orientation = cmd.getArg(0);
3833 d.splitter_->setOrientation(orientation == "vertical"
3834 ? Qt::Vertical : Qt::Horizontal);
3835 TabWorkArea * twa = addTabWorkArea();
3836 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3837 setCurrentWorkArea(wa);
3840 case LFUN_TAB_GROUP_CLOSE:
3841 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3842 closeTabWorkArea(twa);
3843 d.current_work_area_ = 0;
3844 twa = d.currentTabWorkArea();
3845 // Switch to the next GuiWorkArea in the found TabWorkArea.
3847 // Make sure the work area is up to date.
3848 setCurrentWorkArea(twa->currentWorkArea());
3850 setCurrentWorkArea(0);
3855 case LFUN_VIEW_CLOSE:
3856 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3857 closeWorkArea(twa->currentWorkArea());
3858 d.current_work_area_ = 0;
3859 twa = d.currentTabWorkArea();
3860 // Switch to the next GuiWorkArea in the found TabWorkArea.
3862 // Make sure the work area is up to date.
3863 setCurrentWorkArea(twa->currentWorkArea());
3865 setCurrentWorkArea(0);
3870 case LFUN_COMPLETION_INLINE:
3871 if (d.current_work_area_)
3872 d.current_work_area_->completer().showInline();
3875 case LFUN_COMPLETION_POPUP:
3876 if (d.current_work_area_)
3877 d.current_work_area_->completer().showPopup();
3882 if (d.current_work_area_)
3883 d.current_work_area_->completer().tab();
3886 case LFUN_COMPLETION_CANCEL:
3887 if (d.current_work_area_) {
3888 if (d.current_work_area_->completer().popupVisible())
3889 d.current_work_area_->completer().hidePopup();
3891 d.current_work_area_->completer().hideInline();
3895 case LFUN_COMPLETION_ACCEPT:
3896 if (d.current_work_area_)
3897 d.current_work_area_->completer().activate();
3900 case LFUN_BUFFER_ZOOM_IN:
3901 case LFUN_BUFFER_ZOOM_OUT:
3902 if (cmd.argument().empty()) {
3903 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3908 lyxrc.zoom += convert<int>(cmd.argument());
3910 if (lyxrc.zoom < 10)
3913 // The global QPixmapCache is used in GuiPainter to cache text
3914 // painting so we must reset it.
3915 QPixmapCache::clear();
3916 guiApp->fontLoader().update();
3917 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3920 case LFUN_VC_REGISTER:
3921 case LFUN_VC_RENAME:
3923 case LFUN_VC_CHECK_IN:
3924 case LFUN_VC_CHECK_OUT:
3925 case LFUN_VC_REPO_UPDATE:
3926 case LFUN_VC_LOCKING_TOGGLE:
3927 case LFUN_VC_REVERT:
3928 case LFUN_VC_UNDO_LAST:
3929 case LFUN_VC_COMMAND:
3930 case LFUN_VC_COMPARE:
3931 dispatchVC(cmd, dr);
3934 case LFUN_SERVER_GOTO_FILE_ROW:
3935 goToFileRow(to_utf8(cmd.argument()));
3938 case LFUN_FORWARD_SEARCH: {
3939 Buffer const * doc_master = doc_buffer->masterBuffer();
3940 FileName const path(doc_master->temppath());
3941 string const texname = doc_master->isChild(doc_buffer)
3942 ? DocFileName(changeExtension(
3943 doc_buffer->absFileName(),
3944 "tex")).mangledFileName()
3945 : doc_buffer->latexName();
3946 string const fulltexname =
3947 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3948 string const mastername =
3949 removeExtension(doc_master->latexName());
3950 FileName const dviname(addName(path.absFileName(),
3951 addExtension(mastername, "dvi")));
3952 FileName const pdfname(addName(path.absFileName(),
3953 addExtension(mastername, "pdf")));
3954 bool const have_dvi = dviname.exists();
3955 bool const have_pdf = pdfname.exists();
3956 if (!have_dvi && !have_pdf) {
3957 dr.setMessage(_("Please, preview the document first."));
3960 string outname = dviname.onlyFileName();
3961 string command = lyxrc.forward_search_dvi;
3962 if (!have_dvi || (have_pdf &&
3963 pdfname.lastModified() > dviname.lastModified())) {
3964 outname = pdfname.onlyFileName();
3965 command = lyxrc.forward_search_pdf;
3968 DocIterator tmpcur = bv->cursor();
3970 while (tmpcur.inMathed())
3972 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3973 tmpcur.paragraph().id(), tmpcur.pos());
3974 LYXERR(Debug::ACTION, "Forward search: row:" << row
3975 << " id:" << tmpcur.paragraph().id());
3976 if (!row || command.empty()) {
3977 dr.setMessage(_("Couldn't proceed."));
3980 string texrow = convert<string>(row);
3982 command = subst(command, "$$n", texrow);
3983 command = subst(command, "$$f", fulltexname);
3984 command = subst(command, "$$t", texname);
3985 command = subst(command, "$$o", outname);
3987 PathChanger p(path);
3989 one.startscript(Systemcall::DontWait, command);
3993 case LFUN_SPELLING_CONTINUOUSLY:
3994 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3995 dr.screenUpdate(Update::Force | Update::FitCursor);
3999 // The LFUN must be for one of BufferView, Buffer or Cursor;
4001 dispatchToBufferView(cmd, dr);
4005 // Part of automatic menu appearance feature.
4006 if (isFullScreen()) {
4007 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4011 // Need to update bv because many LFUNs here might have destroyed it
4012 bv = currentBufferView();
4014 // Clear non-empty selections
4015 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4017 Cursor & cur = bv->cursor();
4018 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4019 cur.clearSelection();
4025 bool GuiView::lfunUiToggle(string const & ui_component)
4027 if (ui_component == "scrollbar") {
4028 // hide() is of no help
4029 if (d.current_work_area_->verticalScrollBarPolicy() ==
4030 Qt::ScrollBarAlwaysOff)
4032 d.current_work_area_->setVerticalScrollBarPolicy(
4033 Qt::ScrollBarAsNeeded);
4035 d.current_work_area_->setVerticalScrollBarPolicy(
4036 Qt::ScrollBarAlwaysOff);
4037 } else if (ui_component == "statusbar") {
4038 statusBar()->setVisible(!statusBar()->isVisible());
4039 } else if (ui_component == "menubar") {
4040 menuBar()->setVisible(!menuBar()->isVisible());
4042 if (ui_component == "frame") {
4044 getContentsMargins(&l, &t, &r, &b);
4045 //are the frames in default state?
4046 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4048 setContentsMargins(-2, -2, -2, -2);
4050 setContentsMargins(0, 0, 0, 0);
4053 if (ui_component == "fullscreen") {
4061 void GuiView::toggleFullScreen()
4063 if (isFullScreen()) {
4064 for (int i = 0; i != d.splitter_->count(); ++i)
4065 d.tabWorkArea(i)->setFullScreen(false);
4066 setContentsMargins(0, 0, 0, 0);
4067 setWindowState(windowState() ^ Qt::WindowFullScreen);
4070 statusBar()->show();
4073 hideDialogs("prefs", 0);
4074 for (int i = 0; i != d.splitter_->count(); ++i)
4075 d.tabWorkArea(i)->setFullScreen(true);
4076 setContentsMargins(-2, -2, -2, -2);
4078 setWindowState(windowState() ^ Qt::WindowFullScreen);
4079 if (lyxrc.full_screen_statusbar)
4080 statusBar()->hide();
4081 if (lyxrc.full_screen_menubar)
4083 if (lyxrc.full_screen_toolbars) {
4084 ToolbarMap::iterator end = d.toolbars_.end();
4085 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4090 // give dialogs like the TOC a chance to adapt
4095 Buffer const * GuiView::updateInset(Inset const * inset)
4100 Buffer const * inset_buffer = &(inset->buffer());
4102 for (int i = 0; i != d.splitter_->count(); ++i) {
4103 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4106 Buffer const * buffer = &(wa->bufferView().buffer());
4107 if (inset_buffer == buffer)
4108 wa->scheduleRedraw();
4110 return inset_buffer;
4114 void GuiView::restartCursor()
4116 /* When we move around, or type, it's nice to be able to see
4117 * the cursor immediately after the keypress.
4119 if (d.current_work_area_)
4120 d.current_work_area_->startBlinkingCursor();
4122 // Take this occasion to update the other GUI elements.
4128 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4130 if (d.current_work_area_)
4131 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4136 // This list should be kept in sync with the list of insets in
4137 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4138 // dialog should have the same name as the inset.
4139 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4140 // docs in LyXAction.cpp.
4142 char const * const dialognames[] = {
4144 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4145 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4146 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4147 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4148 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4149 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4150 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4151 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4153 char const * const * const end_dialognames =
4154 dialognames + (sizeof(dialognames) / sizeof(char *));
4158 cmpCStr(char const * name) : name_(name) {}
4159 bool operator()(char const * other) {
4160 return strcmp(other, name_) == 0;
4167 bool isValidName(string const & name)
4169 return find_if(dialognames, end_dialognames,
4170 cmpCStr(name.c_str())) != end_dialognames;
4176 void GuiView::resetDialogs()
4178 // Make sure that no LFUN uses any GuiView.
4179 guiApp->setCurrentView(0);
4183 constructToolbars();
4184 guiApp->menus().fillMenuBar(menuBar(), this, false);
4185 d.layout_->updateContents(true);
4186 // Now update controls with current buffer.
4187 guiApp->setCurrentView(this);
4193 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4195 if (!isValidName(name))
4198 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4200 if (it != d.dialogs_.end()) {
4202 it->second->hideView();
4203 return it->second.get();
4206 Dialog * dialog = build(name);
4207 d.dialogs_[name].reset(dialog);
4208 if (lyxrc.allow_geometry_session)
4209 dialog->restoreSession();
4216 void GuiView::showDialog(string const & name, string const & data,
4219 triggerShowDialog(toqstr(name), toqstr(data), inset);
4223 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4229 const string name = fromqstr(qname);
4230 const string data = fromqstr(qdata);
4234 Dialog * dialog = findOrBuild(name, false);
4236 bool const visible = dialog->isVisibleView();
4237 dialog->showData(data);
4238 if (inset && currentBufferView())
4239 currentBufferView()->editInset(name, inset);
4240 // We only set the focus to the new dialog if it was not yet
4241 // visible in order not to change the existing previous behaviour
4243 // activateWindow is needed for floating dockviews
4244 dialog->asQWidget()->raise();
4245 dialog->asQWidget()->activateWindow();
4246 dialog->asQWidget()->setFocus();
4250 catch (ExceptionMessage const & ex) {
4258 bool GuiView::isDialogVisible(string const & name) const
4260 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4261 if (it == d.dialogs_.end())
4263 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4267 void GuiView::hideDialog(string const & name, Inset * inset)
4269 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4270 if (it == d.dialogs_.end())
4274 if (!currentBufferView())
4276 if (inset != currentBufferView()->editedInset(name))
4280 Dialog * const dialog = it->second.get();
4281 if (dialog->isVisibleView())
4283 if (currentBufferView())
4284 currentBufferView()->editInset(name, 0);
4288 void GuiView::disconnectDialog(string const & name)
4290 if (!isValidName(name))
4292 if (currentBufferView())
4293 currentBufferView()->editInset(name, 0);
4297 void GuiView::hideAll() const
4299 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4300 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4302 for(; it != end; ++it)
4303 it->second->hideView();
4307 void GuiView::updateDialogs()
4309 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4310 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4312 for(; it != end; ++it) {
4313 Dialog * dialog = it->second.get();
4315 if (dialog->needBufferOpen() && !documentBufferView())
4316 hideDialog(fromqstr(dialog->name()), 0);
4317 else if (dialog->isVisibleView())
4318 dialog->checkStatus();
4325 Dialog * createDialog(GuiView & lv, string const & name);
4327 // will be replaced by a proper factory...
4328 Dialog * createGuiAbout(GuiView & lv);
4329 Dialog * createGuiBibtex(GuiView & lv);
4330 Dialog * createGuiChanges(GuiView & lv);
4331 Dialog * createGuiCharacter(GuiView & lv);
4332 Dialog * createGuiCitation(GuiView & lv);
4333 Dialog * createGuiCompare(GuiView & lv);
4334 Dialog * createGuiCompareHistory(GuiView & lv);
4335 Dialog * createGuiDelimiter(GuiView & lv);
4336 Dialog * createGuiDocument(GuiView & lv);
4337 Dialog * createGuiErrorList(GuiView & lv);
4338 Dialog * createGuiExternal(GuiView & lv);
4339 Dialog * createGuiGraphics(GuiView & lv);
4340 Dialog * createGuiInclude(GuiView & lv);
4341 Dialog * createGuiIndex(GuiView & lv);
4342 Dialog * createGuiListings(GuiView & lv);
4343 Dialog * createGuiLog(GuiView & lv);
4344 Dialog * createGuiMathMatrix(GuiView & lv);
4345 Dialog * createGuiNote(GuiView & lv);
4346 Dialog * createGuiParagraph(GuiView & lv);
4347 Dialog * createGuiPhantom(GuiView & lv);
4348 Dialog * createGuiPreferences(GuiView & lv);
4349 Dialog * createGuiPrint(GuiView & lv);
4350 Dialog * createGuiPrintindex(GuiView & lv);
4351 Dialog * createGuiRef(GuiView & lv);
4352 Dialog * createGuiSearch(GuiView & lv);
4353 Dialog * createGuiSearchAdv(GuiView & lv);
4354 Dialog * createGuiSendTo(GuiView & lv);
4355 Dialog * createGuiShowFile(GuiView & lv);
4356 Dialog * createGuiSpellchecker(GuiView & lv);
4357 Dialog * createGuiSymbols(GuiView & lv);
4358 Dialog * createGuiTabularCreate(GuiView & lv);
4359 Dialog * createGuiTexInfo(GuiView & lv);
4360 Dialog * createGuiToc(GuiView & lv);
4361 Dialog * createGuiThesaurus(GuiView & lv);
4362 Dialog * createGuiViewSource(GuiView & lv);
4363 Dialog * createGuiWrap(GuiView & lv);
4364 Dialog * createGuiProgressView(GuiView & lv);
4368 Dialog * GuiView::build(string const & name)
4370 LASSERT(isValidName(name), return 0);
4372 Dialog * dialog = createDialog(*this, name);
4376 if (name == "aboutlyx")
4377 return createGuiAbout(*this);
4378 if (name == "bibtex")
4379 return createGuiBibtex(*this);
4380 if (name == "changes")
4381 return createGuiChanges(*this);
4382 if (name == "character")
4383 return createGuiCharacter(*this);
4384 if (name == "citation")
4385 return createGuiCitation(*this);
4386 if (name == "compare")
4387 return createGuiCompare(*this);
4388 if (name == "comparehistory")
4389 return createGuiCompareHistory(*this);
4390 if (name == "document")
4391 return createGuiDocument(*this);
4392 if (name == "errorlist")
4393 return createGuiErrorList(*this);
4394 if (name == "external")
4395 return createGuiExternal(*this);
4397 return createGuiShowFile(*this);
4398 if (name == "findreplace")
4399 return createGuiSearch(*this);
4400 if (name == "findreplaceadv")
4401 return createGuiSearchAdv(*this);
4402 if (name == "graphics")
4403 return createGuiGraphics(*this);
4404 if (name == "include")
4405 return createGuiInclude(*this);
4406 if (name == "index")
4407 return createGuiIndex(*this);
4408 if (name == "index_print")
4409 return createGuiPrintindex(*this);
4410 if (name == "listings")
4411 return createGuiListings(*this);
4413 return createGuiLog(*this);
4414 if (name == "mathdelimiter")
4415 return createGuiDelimiter(*this);
4416 if (name == "mathmatrix")
4417 return createGuiMathMatrix(*this);
4419 return createGuiNote(*this);
4420 if (name == "paragraph")
4421 return createGuiParagraph(*this);
4422 if (name == "phantom")
4423 return createGuiPhantom(*this);
4424 if (name == "prefs")
4425 return createGuiPreferences(*this);
4427 return createGuiRef(*this);
4428 if (name == "sendto")
4429 return createGuiSendTo(*this);
4430 if (name == "spellchecker")
4431 return createGuiSpellchecker(*this);
4432 if (name == "symbols")
4433 return createGuiSymbols(*this);
4434 if (name == "tabularcreate")
4435 return createGuiTabularCreate(*this);
4436 if (name == "texinfo")
4437 return createGuiTexInfo(*this);
4438 if (name == "thesaurus")
4439 return createGuiThesaurus(*this);
4441 return createGuiToc(*this);
4442 if (name == "view-source")
4443 return createGuiViewSource(*this);
4445 return createGuiWrap(*this);
4446 if (name == "progress")
4447 return createGuiProgressView(*this);
4453 } // namespace frontend
4456 #include "moc_GuiView.cpp"