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 if (d.current_work_area_->bufferView().cursor().inMathed()
1539 && !d.current_work_area_->bufferView().cursor().inRegexped())
1540 context |= Toolbars::MATH;
1541 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1542 context |= Toolbars::TABLE;
1543 if (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1544 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1545 context |= Toolbars::REVIEW;
1546 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1547 context |= Toolbars::MATHMACROTEMPLATE;
1548 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1549 context |= Toolbars::IPA;
1551 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1552 it->second->update(context);
1554 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1555 it->second->update();
1559 void GuiView::setBuffer(Buffer * newBuffer)
1561 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1562 LASSERT(newBuffer, return);
1564 GuiWorkArea * wa = workArea(*newBuffer);
1567 newBuffer->masterBuffer()->updateBuffer();
1569 wa = addWorkArea(*newBuffer);
1570 // scroll to the position when the BufferView was last closed
1571 if (lyxrc.use_lastfilepos) {
1572 LastFilePosSection::FilePos filepos =
1573 theSession().lastFilePos().load(newBuffer->fileName());
1574 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1577 //Disconnect the old buffer...there's no new one.
1580 connectBuffer(*newBuffer);
1581 connectBufferView(wa->bufferView());
1582 setCurrentWorkArea(wa);
1586 void GuiView::connectBuffer(Buffer & buf)
1588 buf.setGuiDelegate(this);
1592 void GuiView::disconnectBuffer()
1594 if (d.current_work_area_)
1595 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1599 void GuiView::connectBufferView(BufferView & bv)
1601 bv.setGuiDelegate(this);
1605 void GuiView::disconnectBufferView()
1607 if (d.current_work_area_)
1608 d.current_work_area_->bufferView().setGuiDelegate(0);
1612 void GuiView::errors(string const & error_type, bool from_master)
1614 BufferView const * const bv = currentBufferView();
1618 #if EXPORT_in_THREAD
1619 // We are called with from_master == false by default, so we
1620 // have to figure out whether that is the case or not.
1621 ErrorList & el = bv->buffer().errorList(error_type);
1623 el = bv->buffer().masterBuffer()->errorList(error_type);
1627 ErrorList const & el = from_master ?
1628 bv->buffer().masterBuffer()->errorList(error_type) :
1629 bv->buffer().errorList(error_type);
1635 string data = error_type;
1637 data = "from_master|" + error_type;
1638 showDialog("errorlist", data);
1642 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1644 d.toc_models_.updateItem(toqstr(type), dit);
1648 void GuiView::structureChanged()
1650 d.toc_models_.reset(documentBufferView());
1651 // Navigator needs more than a simple update in this case. It needs to be
1653 updateDialog("toc", "");
1657 void GuiView::updateDialog(string const & name, string const & data)
1659 if (!isDialogVisible(name))
1662 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1663 if (it == d.dialogs_.end())
1666 Dialog * const dialog = it->second.get();
1667 if (dialog->isVisibleView())
1668 dialog->initialiseParams(data);
1672 BufferView * GuiView::documentBufferView()
1674 return currentMainWorkArea()
1675 ? ¤tMainWorkArea()->bufferView()
1680 BufferView const * GuiView::documentBufferView() const
1682 return currentMainWorkArea()
1683 ? ¤tMainWorkArea()->bufferView()
1688 BufferView * GuiView::currentBufferView()
1690 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1694 BufferView const * GuiView::currentBufferView() const
1696 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1700 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1701 Buffer const * orig, Buffer * clone)
1703 bool const success = clone->autoSave();
1705 busyBuffers.remove(orig);
1707 ? _("Automatic save done.")
1708 : _("Automatic save failed!");
1712 void GuiView::autoSave()
1714 LYXERR(Debug::INFO, "Running autoSave()");
1716 Buffer * buffer = documentBufferView()
1717 ? &documentBufferView()->buffer() : 0;
1719 resetAutosaveTimers();
1723 GuiViewPrivate::busyBuffers.insert(buffer);
1724 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1725 buffer, buffer->cloneBufferOnly());
1726 d.autosave_watcher_.setFuture(f);
1727 resetAutosaveTimers();
1731 void GuiView::resetAutosaveTimers()
1734 d.autosave_timeout_.restart();
1738 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1741 Buffer * buf = currentBufferView()
1742 ? ¤tBufferView()->buffer() : 0;
1743 Buffer * doc_buffer = documentBufferView()
1744 ? &(documentBufferView()->buffer()) : 0;
1747 /* In LyX/Mac, when a dialog is open, the menus of the
1748 application can still be accessed without giving focus to
1749 the main window. In this case, we want to disable the menu
1750 entries that are buffer-related.
1751 This code must not be used on Linux and Windows, since it
1752 would disable buffer-related entries when hovering over the
1753 menu (see bug #9574).
1755 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1761 // Check whether we need a buffer
1762 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1763 // no, exit directly
1764 flag.message(from_utf8(N_("Command not allowed with"
1765 "out any document open")));
1766 flag.setEnabled(false);
1770 if (cmd.origin() == FuncRequest::TOC) {
1771 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1772 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1773 flag.setEnabled(false);
1777 switch(cmd.action()) {
1778 case LFUN_BUFFER_IMPORT:
1781 case LFUN_MASTER_BUFFER_UPDATE:
1782 case LFUN_MASTER_BUFFER_VIEW:
1784 && (doc_buffer->parent() != 0
1785 || doc_buffer->hasChildren())
1786 && !d.processing_thread_watcher_.isRunning();
1789 case LFUN_BUFFER_UPDATE:
1790 case LFUN_BUFFER_VIEW: {
1791 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1795 string format = to_utf8(cmd.argument());
1796 if (cmd.argument().empty())
1797 format = doc_buffer->params().getDefaultOutputFormat();
1798 enable = doc_buffer->params().isExportableFormat(format);
1802 case LFUN_BUFFER_RELOAD:
1803 enable = doc_buffer && !doc_buffer->isUnnamed()
1804 && doc_buffer->fileName().exists()
1805 && (!doc_buffer->isClean()
1806 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1809 case LFUN_BUFFER_CHILD_OPEN:
1810 enable = doc_buffer;
1813 case LFUN_BUFFER_WRITE:
1814 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1817 //FIXME: This LFUN should be moved to GuiApplication.
1818 case LFUN_BUFFER_WRITE_ALL: {
1819 // We enable the command only if there are some modified buffers
1820 Buffer * first = theBufferList().first();
1825 // We cannot use a for loop as the buffer list is a cycle.
1827 if (!b->isClean()) {
1831 b = theBufferList().next(b);
1832 } while (b != first);
1836 case LFUN_BUFFER_WRITE_AS:
1837 case LFUN_BUFFER_EXPORT_AS:
1838 enable = doc_buffer;
1841 case LFUN_BUFFER_CLOSE:
1842 case LFUN_VIEW_CLOSE:
1843 enable = doc_buffer;
1846 case LFUN_BUFFER_CLOSE_ALL:
1847 enable = theBufferList().last() != theBufferList().first();
1850 case LFUN_VIEW_SPLIT:
1851 if (cmd.getArg(0) == "vertical")
1852 enable = doc_buffer && (d.splitter_->count() == 1 ||
1853 d.splitter_->orientation() == Qt::Vertical);
1855 enable = doc_buffer && (d.splitter_->count() == 1 ||
1856 d.splitter_->orientation() == Qt::Horizontal);
1859 case LFUN_TAB_GROUP_CLOSE:
1860 enable = d.tabWorkAreaCount() > 1;
1863 case LFUN_TOOLBAR_TOGGLE: {
1864 string const name = cmd.getArg(0);
1865 if (GuiToolbar * t = toolbar(name))
1866 flag.setOnOff(t->isVisible());
1869 docstring const msg =
1870 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1876 case LFUN_DROP_LAYOUTS_CHOICE:
1880 case LFUN_UI_TOGGLE:
1881 flag.setOnOff(isFullScreen());
1884 case LFUN_DIALOG_DISCONNECT_INSET:
1887 case LFUN_DIALOG_HIDE:
1888 // FIXME: should we check if the dialog is shown?
1891 case LFUN_DIALOG_TOGGLE:
1892 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1893 // fall through to set "enable"
1894 case LFUN_DIALOG_SHOW: {
1895 string const name = cmd.getArg(0);
1897 enable = name == "aboutlyx"
1898 || name == "file" //FIXME: should be removed.
1900 || name == "texinfo"
1901 || name == "progress"
1902 || name == "compare";
1903 else if (name == "character" || name == "symbols"
1904 || name == "mathdelimiter" || name == "mathmatrix") {
1905 if (!buf || buf->isReadonly())
1908 Cursor const & cur = currentBufferView()->cursor();
1909 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1912 else if (name == "latexlog")
1913 enable = FileName(doc_buffer->logName()).isReadableFile();
1914 else if (name == "spellchecker")
1915 enable = theSpellChecker()
1916 && !doc_buffer->isReadonly()
1917 && !doc_buffer->text().empty();
1918 else if (name == "vclog")
1919 enable = doc_buffer->lyxvc().inUse();
1923 case LFUN_DIALOG_UPDATE: {
1924 string const name = cmd.getArg(0);
1926 enable = name == "prefs";
1930 case LFUN_COMMAND_EXECUTE:
1932 case LFUN_MENU_OPEN:
1933 // Nothing to check.
1936 case LFUN_COMPLETION_INLINE:
1937 if (!d.current_work_area_
1938 || !d.current_work_area_->completer().inlinePossible(
1939 currentBufferView()->cursor()))
1943 case LFUN_COMPLETION_POPUP:
1944 if (!d.current_work_area_
1945 || !d.current_work_area_->completer().popupPossible(
1946 currentBufferView()->cursor()))
1951 if (!d.current_work_area_
1952 || !d.current_work_area_->completer().inlinePossible(
1953 currentBufferView()->cursor()))
1957 case LFUN_COMPLETION_ACCEPT:
1958 if (!d.current_work_area_
1959 || (!d.current_work_area_->completer().popupVisible()
1960 && !d.current_work_area_->completer().inlineVisible()
1961 && !d.current_work_area_->completer().completionAvailable()))
1965 case LFUN_COMPLETION_CANCEL:
1966 if (!d.current_work_area_
1967 || (!d.current_work_area_->completer().popupVisible()
1968 && !d.current_work_area_->completer().inlineVisible()))
1972 case LFUN_BUFFER_ZOOM_OUT:
1973 enable = doc_buffer && lyxrc.zoom > 10;
1976 case LFUN_BUFFER_ZOOM_IN:
1977 enable = doc_buffer;
1980 case LFUN_BUFFER_MOVE_NEXT:
1981 case LFUN_BUFFER_MOVE_PREVIOUS:
1982 // we do not cycle when moving
1983 case LFUN_BUFFER_NEXT:
1984 case LFUN_BUFFER_PREVIOUS:
1985 // because we cycle, it doesn't matter whether on first or last
1986 enable = (d.currentTabWorkArea()->count() > 1);
1988 case LFUN_BUFFER_SWITCH:
1989 // toggle on the current buffer, but do not toggle off
1990 // the other ones (is that a good idea?)
1992 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1993 flag.setOnOff(true);
1996 case LFUN_VC_REGISTER:
1997 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1999 case LFUN_VC_RENAME:
2000 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2003 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2005 case LFUN_VC_CHECK_IN:
2006 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2008 case LFUN_VC_CHECK_OUT:
2009 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2011 case LFUN_VC_LOCKING_TOGGLE:
2012 enable = doc_buffer && !doc_buffer->isReadonly()
2013 && doc_buffer->lyxvc().lockingToggleEnabled();
2014 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2016 case LFUN_VC_REVERT:
2017 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2019 case LFUN_VC_UNDO_LAST:
2020 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2022 case LFUN_VC_REPO_UPDATE:
2023 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2025 case LFUN_VC_COMMAND: {
2026 if (cmd.argument().empty())
2028 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2032 case LFUN_VC_COMPARE:
2033 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2036 case LFUN_SERVER_GOTO_FILE_ROW:
2038 case LFUN_FORWARD_SEARCH:
2039 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2042 case LFUN_FILE_INSERT_PLAINTEXT:
2043 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2044 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2047 case LFUN_SPELLING_CONTINUOUSLY:
2048 flag.setOnOff(lyxrc.spellcheck_continuously);
2056 flag.setEnabled(false);
2062 static FileName selectTemplateFile()
2064 FileDialog dlg(qt_("Select template file"));
2065 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2066 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2068 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2069 QStringList(qt_("LyX Documents (*.lyx)")));
2071 if (result.first == FileDialog::Later)
2073 if (result.second.isEmpty())
2075 return FileName(fromqstr(result.second));
2079 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2083 Buffer * newBuffer = 0;
2085 newBuffer = checkAndLoadLyXFile(filename);
2086 } catch (ExceptionMessage const & e) {
2093 message(_("Document not loaded."));
2097 setBuffer(newBuffer);
2098 newBuffer->errors("Parse");
2101 theSession().lastFiles().add(filename);
2107 void GuiView::openDocument(string const & fname)
2109 string initpath = lyxrc.document_path;
2111 if (documentBufferView()) {
2112 string const trypath = documentBufferView()->buffer().filePath();
2113 // If directory is writeable, use this as default.
2114 if (FileName(trypath).isDirWritable())
2120 if (fname.empty()) {
2121 FileDialog dlg(qt_("Select document to open"));
2122 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2123 dlg.setButton2(qt_("Examples|#E#e"),
2124 toqstr(addPath(package().system_support().absFileName(), "examples")));
2126 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2127 FileDialog::Result result =
2128 dlg.open(toqstr(initpath), filter);
2130 if (result.first == FileDialog::Later)
2133 filename = fromqstr(result.second);
2135 // check selected filename
2136 if (filename.empty()) {
2137 message(_("Canceled."));
2143 // get absolute path of file and add ".lyx" to the filename if
2145 FileName const fullname =
2146 fileSearch(string(), filename, "lyx", support::may_not_exist);
2147 if (!fullname.empty())
2148 filename = fullname.absFileName();
2150 if (!fullname.onlyPath().isDirectory()) {
2151 Alert::warning(_("Invalid filename"),
2152 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2153 from_utf8(fullname.absFileName())));
2157 // if the file doesn't exist and isn't already open (bug 6645),
2158 // let the user create one
2159 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2160 !LyXVC::file_not_found_hook(fullname)) {
2161 // the user specifically chose this name. Believe him.
2162 Buffer * const b = newFile(filename, string(), true);
2168 docstring const disp_fn = makeDisplayPath(filename);
2169 message(bformat(_("Opening document %1$s..."), disp_fn));
2172 Buffer * buf = loadDocument(fullname);
2174 str2 = bformat(_("Document %1$s opened."), disp_fn);
2175 if (buf->lyxvc().inUse())
2176 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2177 " " + _("Version control detected.");
2179 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2184 // FIXME: clean that
2185 static bool import(GuiView * lv, FileName const & filename,
2186 string const & format, ErrorList & errorList)
2188 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2190 string loader_format;
2191 vector<string> loaders = theConverters().loaders();
2192 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2193 vector<string>::const_iterator it = loaders.begin();
2194 vector<string>::const_iterator en = loaders.end();
2195 for (; it != en; ++it) {
2196 if (!theConverters().isReachable(format, *it))
2199 string const tofile =
2200 support::changeExtension(filename.absFileName(),
2201 formats.extension(*it));
2202 if (!theConverters().convert(0, filename, FileName(tofile),
2203 filename, format, *it, errorList))
2205 loader_format = *it;
2208 if (loader_format.empty()) {
2209 frontend::Alert::error(_("Couldn't import file"),
2210 bformat(_("No information for importing the format %1$s."),
2211 formats.prettyName(format)));
2215 loader_format = format;
2217 if (loader_format == "lyx") {
2218 Buffer * buf = lv->loadDocument(lyxfile);
2222 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2226 bool as_paragraphs = loader_format == "textparagraph";
2227 string filename2 = (loader_format == format) ? filename.absFileName()
2228 : support::changeExtension(filename.absFileName(),
2229 formats.extension(loader_format));
2230 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2232 guiApp->setCurrentView(lv);
2233 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2240 void GuiView::importDocument(string const & argument)
2243 string filename = split(argument, format, ' ');
2245 LYXERR(Debug::INFO, format << " file: " << filename);
2247 // need user interaction
2248 if (filename.empty()) {
2249 string initpath = lyxrc.document_path;
2250 if (documentBufferView()) {
2251 string const trypath = documentBufferView()->buffer().filePath();
2252 // If directory is writeable, use this as default.
2253 if (FileName(trypath).isDirWritable())
2257 docstring const text = bformat(_("Select %1$s file to import"),
2258 formats.prettyName(format));
2260 FileDialog dlg(toqstr(text));
2261 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2262 dlg.setButton2(qt_("Examples|#E#e"),
2263 toqstr(addPath(package().system_support().absFileName(), "examples")));
2265 docstring filter = formats.prettyName(format);
2268 filter += from_utf8(formats.extensions(format));
2271 FileDialog::Result result =
2272 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2274 if (result.first == FileDialog::Later)
2277 filename = fromqstr(result.second);
2279 // check selected filename
2280 if (filename.empty())
2281 message(_("Canceled."));
2284 if (filename.empty())
2287 // get absolute path of file
2288 FileName const fullname(support::makeAbsPath(filename));
2290 // Can happen if the user entered a path into the dialog
2292 if (fullname.onlyFileName().empty()) {
2293 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2294 "Aborting import."),
2295 from_utf8(fullname.absFileName()));
2296 frontend::Alert::error(_("File name error"), msg);
2297 message(_("Canceled."));
2302 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2304 // Check if the document already is open
2305 Buffer * buf = theBufferList().getBuffer(lyxfile);
2308 if (!closeBuffer()) {
2309 message(_("Canceled."));
2314 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2316 // if the file exists already, and we didn't do
2317 // -i lyx thefile.lyx, warn
2318 if (lyxfile.exists() && fullname != lyxfile) {
2320 docstring text = bformat(_("The document %1$s already exists.\n\n"
2321 "Do you want to overwrite that document?"), displaypath);
2322 int const ret = Alert::prompt(_("Overwrite document?"),
2323 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2326 message(_("Canceled."));
2331 message(bformat(_("Importing %1$s..."), displaypath));
2332 ErrorList errorList;
2333 if (import(this, fullname, format, errorList))
2334 message(_("imported."));
2336 message(_("file not imported!"));
2338 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2342 void GuiView::newDocument(string const & filename, bool from_template)
2344 FileName initpath(lyxrc.document_path);
2345 if (documentBufferView()) {
2346 FileName const trypath(documentBufferView()->buffer().filePath());
2347 // If directory is writeable, use this as default.
2348 if (trypath.isDirWritable())
2352 string templatefile;
2353 if (from_template) {
2354 templatefile = selectTemplateFile().absFileName();
2355 if (templatefile.empty())
2360 if (filename.empty())
2361 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2363 b = newFile(filename, templatefile, true);
2368 // If no new document could be created, it is unsure
2369 // whether there is a valid BufferView.
2370 if (currentBufferView())
2371 // Ensure the cursor is correctly positioned on screen.
2372 currentBufferView()->showCursor();
2376 void GuiView::insertLyXFile(docstring const & fname)
2378 BufferView * bv = documentBufferView();
2383 FileName filename(to_utf8(fname));
2384 if (filename.empty()) {
2385 // Launch a file browser
2387 string initpath = lyxrc.document_path;
2388 string const trypath = bv->buffer().filePath();
2389 // If directory is writeable, use this as default.
2390 if (FileName(trypath).isDirWritable())
2394 FileDialog dlg(qt_("Select LyX document to insert"));
2395 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2396 dlg.setButton2(qt_("Examples|#E#e"),
2397 toqstr(addPath(package().system_support().absFileName(),
2400 FileDialog::Result result = dlg.open(toqstr(initpath),
2401 QStringList(qt_("LyX Documents (*.lyx)")));
2403 if (result.first == FileDialog::Later)
2407 filename.set(fromqstr(result.second));
2409 // check selected filename
2410 if (filename.empty()) {
2411 // emit message signal.
2412 message(_("Canceled."));
2417 bv->insertLyXFile(filename);
2418 bv->buffer().errors("Parse");
2422 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2424 FileName fname = b.fileName();
2425 FileName const oldname = fname;
2427 if (!newname.empty()) {
2429 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2431 // Switch to this Buffer.
2434 // No argument? Ask user through dialog.
2436 FileDialog dlg(qt_("Choose a filename to save document as"));
2437 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2438 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2440 if (!isLyXFileName(fname.absFileName()))
2441 fname.changeExtension(".lyx");
2443 FileDialog::Result result =
2444 dlg.save(toqstr(fname.onlyPath().absFileName()),
2445 QStringList(qt_("LyX Documents (*.lyx)")),
2446 toqstr(fname.onlyFileName()));
2448 if (result.first == FileDialog::Later)
2451 fname.set(fromqstr(result.second));
2456 if (!isLyXFileName(fname.absFileName()))
2457 fname.changeExtension(".lyx");
2460 // fname is now the new Buffer location.
2462 // if there is already a Buffer open with this name, we do not want
2463 // to have another one. (the second test makes sure we're not just
2464 // trying to overwrite ourselves, which is fine.)
2465 if (theBufferList().exists(fname) && fname != oldname
2466 && theBufferList().getBuffer(fname) != &b) {
2467 docstring const text =
2468 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2469 "Please close it before attempting to overwrite it.\n"
2470 "Do you want to choose a new filename?"),
2471 from_utf8(fname.absFileName()));
2472 int const ret = Alert::prompt(_("Chosen File Already Open"),
2473 text, 0, 1, _("&Rename"), _("&Cancel"));
2475 case 0: return renameBuffer(b, docstring(), kind);
2476 case 1: return false;
2481 bool const existsLocal = fname.exists();
2482 bool const existsInVC = LyXVC::fileInVC(fname);
2483 if (existsLocal || existsInVC) {
2484 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2485 if (kind != LV_WRITE_AS && existsInVC) {
2486 // renaming to a name that is already in VC
2488 docstring text = bformat(_("The document %1$s "
2489 "is already registered.\n\n"
2490 "Do you want to choose a new name?"),
2492 docstring const title = (kind == LV_VC_RENAME) ?
2493 _("Rename document?") : _("Copy document?");
2494 docstring const button = (kind == LV_VC_RENAME) ?
2495 _("&Rename") : _("&Copy");
2496 int const ret = Alert::prompt(title, text, 0, 1,
2497 button, _("&Cancel"));
2499 case 0: return renameBuffer(b, docstring(), kind);
2500 case 1: return false;
2505 docstring text = bformat(_("The document %1$s "
2506 "already exists.\n\n"
2507 "Do you want to overwrite that document?"),
2509 int const ret = Alert::prompt(_("Overwrite document?"),
2510 text, 0, 2, _("&Overwrite"),
2511 _("&Rename"), _("&Cancel"));
2514 case 1: return renameBuffer(b, docstring(), kind);
2515 case 2: return false;
2521 case LV_VC_RENAME: {
2522 string msg = b.lyxvc().rename(fname);
2525 message(from_utf8(msg));
2529 string msg = b.lyxvc().copy(fname);
2532 message(from_utf8(msg));
2538 // LyXVC created the file already in case of LV_VC_RENAME or
2539 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2540 // relative paths of included stuff right if we moved e.g. from
2541 // /a/b.lyx to /a/c/b.lyx.
2543 bool const saved = saveBuffer(b, fname);
2550 struct PrettyNameComparator
2552 bool operator()(Format const *first, Format const *second) const {
2553 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2554 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2559 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2561 FileName fname = b.fileName();
2563 FileDialog dlg(qt_("Choose a filename to export the document as"));
2564 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2567 QString const anyformat = qt_("Guess from extension (*.*)");
2569 Formats::const_iterator it = formats.begin();
2570 vector<Format const *> export_formats;
2571 for (; it != formats.end(); ++it)
2572 if (it->documentFormat())
2573 export_formats.push_back(&(*it));
2574 PrettyNameComparator cmp;
2575 sort(export_formats.begin(), export_formats.end(), cmp);
2576 vector<Format const *>::const_iterator fit = export_formats.begin();
2577 map<QString, string> fmap;
2580 for (; fit != export_formats.end(); ++fit) {
2581 docstring const loc_prettyname =
2582 translateIfPossible(from_utf8((*fit)->prettyname()));
2583 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2585 from_ascii((*fit)->extension())));
2586 types << loc_filter;
2587 fmap[loc_filter] = (*fit)->name();
2588 if (from_ascii((*fit)->name()) == iformat) {
2589 filter = loc_filter;
2590 ext = (*fit)->extension();
2593 string ofname = fname.onlyFileName();
2595 ofname = support::changeExtension(ofname, ext);
2596 FileDialog::Result result =
2597 dlg.save(toqstr(fname.onlyPath().absFileName()),
2601 if (result.first != FileDialog::Chosen)
2605 fname.set(fromqstr(result.second));
2606 if (filter == anyformat)
2607 fmt_name = formats.getFormatFromExtension(fname.extension());
2609 fmt_name = fmap[filter];
2610 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2611 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2613 if (fmt_name.empty() || fname.empty())
2616 // fname is now the new Buffer location.
2617 if (FileName(fname).exists()) {
2618 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2619 docstring text = bformat(_("The document %1$s already "
2620 "exists.\n\nDo you want to "
2621 "overwrite that document?"),
2623 int const ret = Alert::prompt(_("Overwrite document?"),
2624 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2627 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2628 case 2: return false;
2632 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2635 return dr.dispatched();
2639 bool GuiView::saveBuffer(Buffer & b)
2641 return saveBuffer(b, FileName());
2645 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2647 if (workArea(b) && workArea(b)->inDialogMode())
2650 if (fn.empty() && b.isUnnamed())
2651 return renameBuffer(b, docstring());
2653 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2655 theSession().lastFiles().add(b.fileName());
2659 // Switch to this Buffer.
2662 // FIXME: we don't tell the user *WHY* the save failed !!
2663 docstring const file = makeDisplayPath(b.absFileName(), 30);
2664 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2665 "Do you want to rename the document and "
2666 "try again?"), file);
2667 int const ret = Alert::prompt(_("Rename and save?"),
2668 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2671 if (!renameBuffer(b, docstring()))
2680 return saveBuffer(b, fn);
2684 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2686 return closeWorkArea(wa, false);
2690 // We only want to close the buffer if it is not visible in other workareas
2691 // of the same view, nor in other views, and if this is not a child
2692 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2694 Buffer & buf = wa->bufferView().buffer();
2696 bool last_wa = d.countWorkAreasOf(buf) == 1
2697 && !inOtherView(buf) && !buf.parent();
2699 bool close_buffer = last_wa;
2702 if (lyxrc.close_buffer_with_last_view == "yes")
2704 else if (lyxrc.close_buffer_with_last_view == "no")
2705 close_buffer = false;
2708 if (buf.isUnnamed())
2709 file = from_utf8(buf.fileName().onlyFileName());
2711 file = buf.fileName().displayName(30);
2712 docstring const text = bformat(
2713 _("Last view on document %1$s is being closed.\n"
2714 "Would you like to close or hide the document?\n"
2716 "Hidden documents can be displayed back through\n"
2717 "the menu: View->Hidden->...\n"
2719 "To remove this question, set your preference in:\n"
2720 " Tools->Preferences->Look&Feel->UserInterface\n"
2722 int ret = Alert::prompt(_("Close or hide document?"),
2723 text, 0, 1, _("&Close"), _("&Hide"));
2724 close_buffer = (ret == 0);
2728 return closeWorkArea(wa, close_buffer);
2732 bool GuiView::closeBuffer()
2734 GuiWorkArea * wa = currentMainWorkArea();
2735 setCurrentWorkArea(wa);
2736 Buffer & buf = wa->bufferView().buffer();
2737 return wa && closeWorkArea(wa, !buf.parent());
2741 void GuiView::writeSession() const {
2742 GuiWorkArea const * active_wa = currentMainWorkArea();
2743 for (int i = 0; i < d.splitter_->count(); ++i) {
2744 TabWorkArea * twa = d.tabWorkArea(i);
2745 for (int j = 0; j < twa->count(); ++j) {
2746 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2747 Buffer & buf = wa->bufferView().buffer();
2748 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2754 bool GuiView::closeBufferAll()
2756 // Close the workareas in all other views
2757 QList<int> const ids = guiApp->viewIds();
2758 for (int i = 0; i != ids.size(); ++i) {
2759 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2763 // Close our own workareas
2764 if (!closeWorkAreaAll())
2767 // Now close the hidden buffers. We prevent hidden buffers from being
2768 // dirty, so we can just close them.
2769 theBufferList().closeAll();
2774 bool GuiView::closeWorkAreaAll()
2776 setCurrentWorkArea(currentMainWorkArea());
2778 // We might be in a situation that there is still a tabWorkArea, but
2779 // there are no tabs anymore. This can happen when we get here after a
2780 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2781 // many TabWorkArea's have no documents anymore.
2784 // We have to call count() each time, because it can happen that
2785 // more than one splitter will disappear in one iteration (bug 5998).
2786 for (; d.splitter_->count() > empty_twa; ) {
2787 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2789 if (twa->count() == 0)
2792 setCurrentWorkArea(twa->currentWorkArea());
2793 if (!closeTabWorkArea(twa))
2801 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2806 Buffer & buf = wa->bufferView().buffer();
2808 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2809 Alert::warning(_("Close document"),
2810 _("Document could not be closed because it is being processed by LyX."));
2815 return closeBuffer(buf);
2817 if (!inMultiTabs(wa))
2818 if (!saveBufferIfNeeded(buf, true))
2826 bool GuiView::closeBuffer(Buffer & buf)
2828 // If we are in a close_event all children will be closed in some time,
2829 // so no need to do it here. This will ensure that the children end up
2830 // in the session file in the correct order. If we close the master
2831 // buffer, we can close or release the child buffers here too.
2832 bool success = true;
2834 ListOfBuffers clist = buf.getChildren();
2835 ListOfBuffers::const_iterator it = clist.begin();
2836 ListOfBuffers::const_iterator const bend = clist.end();
2837 for (; it != bend; ++it) {
2838 // If a child is dirty, do not close
2839 // without user intervention
2840 //FIXME: should we look in other tabworkareas?
2841 Buffer * child_buf = *it;
2842 GuiWorkArea * child_wa = workArea(*child_buf);
2844 if (!closeWorkArea(child_wa, true)) {
2849 theBufferList().releaseChild(&buf, child_buf);
2853 // goto bookmark to update bookmark pit.
2854 //FIXME: we should update only the bookmarks related to this buffer!
2855 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2856 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2857 guiApp->gotoBookmark(i+1, false, false);
2859 if (saveBufferIfNeeded(buf, false)) {
2860 buf.removeAutosaveFile();
2861 theBufferList().release(&buf);
2865 // open all children again to avoid a crash because of dangling
2866 // pointers (bug 6603)
2872 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2874 while (twa == d.currentTabWorkArea()) {
2875 twa->setCurrentIndex(twa->count()-1);
2877 GuiWorkArea * wa = twa->currentWorkArea();
2878 Buffer & b = wa->bufferView().buffer();
2880 // We only want to close the buffer if the same buffer is not visible
2881 // in another view, and if this is not a child and if we are closing
2882 // a view (not a tabgroup).
2883 bool const close_buffer =
2884 !inOtherView(b) && !b.parent() && closing_;
2886 if (!closeWorkArea(wa, close_buffer))
2893 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2895 if (buf.isClean() || buf.paragraphs().empty())
2898 // Switch to this Buffer.
2903 if (buf.isUnnamed())
2904 file = from_utf8(buf.fileName().onlyFileName());
2906 file = buf.fileName().displayName(30);
2908 // Bring this window to top before asking questions.
2913 if (hiding && buf.isUnnamed()) {
2914 docstring const text = bformat(_("The document %1$s has not been "
2915 "saved yet.\n\nDo you want to save "
2916 "the document?"), file);
2917 ret = Alert::prompt(_("Save new document?"),
2918 text, 0, 1, _("&Save"), _("&Cancel"));
2922 docstring const text = bformat(_("The document %1$s has unsaved changes."
2923 "\n\nDo you want to save the document or discard the changes?"), file);
2924 ret = Alert::prompt(_("Save changed document?"),
2925 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2930 if (!saveBuffer(buf))
2934 // If we crash after this we could have no autosave file
2935 // but I guess this is really improbable (Jug).
2936 // Sometimes improbable things happen:
2937 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2938 // buf.removeAutosaveFile();
2940 // revert all changes
2951 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2953 Buffer & buf = wa->bufferView().buffer();
2955 for (int i = 0; i != d.splitter_->count(); ++i) {
2956 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2957 if (wa_ && wa_ != wa)
2960 return inOtherView(buf);
2964 bool GuiView::inOtherView(Buffer & buf)
2966 QList<int> const ids = guiApp->viewIds();
2968 for (int i = 0; i != ids.size(); ++i) {
2972 if (guiApp->view(ids[i]).workArea(buf))
2979 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2981 if (!documentBufferView())
2984 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2985 Buffer * const curbuf = &documentBufferView()->buffer();
2986 int nwa = twa->count();
2987 for (int i = 0; i < nwa; ++i) {
2988 if (&workArea(i)->bufferView().buffer() == curbuf) {
2990 if (np == NEXTBUFFER)
2991 next_index = (i == nwa - 1 ? 0 : i + 1);
2993 next_index = (i == 0 ? nwa - 1 : i - 1);
2995 twa->moveTab(i, next_index);
2997 setBuffer(&workArea(next_index)->bufferView().buffer());
3005 /// make sure the document is saved
3006 static bool ensureBufferClean(Buffer * buffer)
3008 LASSERT(buffer, return false);
3009 if (buffer->isClean() && !buffer->isUnnamed())
3012 docstring const file = buffer->fileName().displayName(30);
3015 if (!buffer->isUnnamed()) {
3016 text = bformat(_("The document %1$s has unsaved "
3017 "changes.\n\nDo you want to save "
3018 "the document?"), file);
3019 title = _("Save changed document?");
3022 text = bformat(_("The document %1$s has not been "
3023 "saved yet.\n\nDo you want to save "
3024 "the document?"), file);
3025 title = _("Save new document?");
3027 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3030 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3032 return buffer->isClean() && !buffer->isUnnamed();
3036 bool GuiView::reloadBuffer(Buffer & buf)
3038 Buffer::ReadStatus status = buf.reload();
3039 return status == Buffer::ReadSuccess;
3043 void GuiView::checkExternallyModifiedBuffers()
3045 BufferList::iterator bit = theBufferList().begin();
3046 BufferList::iterator const bend = theBufferList().end();
3047 for (; bit != bend; ++bit) {
3048 Buffer * buf = *bit;
3049 if (buf->fileName().exists()
3050 && buf->isExternallyModified(Buffer::checksum_method)) {
3051 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3052 " Reload now? Any local changes will be lost."),
3053 from_utf8(buf->absFileName()));
3054 int const ret = Alert::prompt(_("Reload externally changed document?"),
3055 text, 0, 1, _("&Reload"), _("&Cancel"));
3063 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3065 Buffer * buffer = documentBufferView()
3066 ? &(documentBufferView()->buffer()) : 0;
3068 switch (cmd.action()) {
3069 case LFUN_VC_REGISTER:
3070 if (!buffer || !ensureBufferClean(buffer))
3072 if (!buffer->lyxvc().inUse()) {
3073 if (buffer->lyxvc().registrer()) {
3074 reloadBuffer(*buffer);
3075 dr.clearMessageUpdate();
3080 case LFUN_VC_RENAME:
3081 case LFUN_VC_COPY: {
3082 if (!buffer || !ensureBufferClean(buffer))
3084 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3085 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3086 // Some changes are not yet committed.
3087 // We test here and not in getStatus(), since
3088 // this test is expensive.
3090 LyXVC::CommandResult ret =
3091 buffer->lyxvc().checkIn(log);
3093 if (ret == LyXVC::ErrorCommand ||
3094 ret == LyXVC::VCSuccess)
3095 reloadBuffer(*buffer);
3096 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3097 frontend::Alert::error(
3098 _("Revision control error."),
3099 _("Document could not be checked in."));
3103 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3104 LV_VC_RENAME : LV_VC_COPY;
3105 renameBuffer(*buffer, cmd.argument(), kind);
3110 case LFUN_VC_CHECK_IN:
3111 if (!buffer || !ensureBufferClean(buffer))
3113 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3115 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3117 // Only skip reloading if the checkin was cancelled or
3118 // an error occurred before the real checkin VCS command
3119 // was executed, since the VCS might have changed the
3120 // file even if it could not checkin successfully.
3121 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3122 reloadBuffer(*buffer);
3126 case LFUN_VC_CHECK_OUT:
3127 if (!buffer || !ensureBufferClean(buffer))
3129 if (buffer->lyxvc().inUse()) {
3130 dr.setMessage(buffer->lyxvc().checkOut());
3131 reloadBuffer(*buffer);
3135 case LFUN_VC_LOCKING_TOGGLE:
3136 LASSERT(buffer, return);
3137 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3139 if (buffer->lyxvc().inUse()) {
3140 string res = buffer->lyxvc().lockingToggle();
3142 frontend::Alert::error(_("Revision control error."),
3143 _("Error when setting the locking property."));
3146 reloadBuffer(*buffer);
3151 case LFUN_VC_REVERT:
3152 LASSERT(buffer, return);
3153 if (buffer->lyxvc().revert()) {
3154 reloadBuffer(*buffer);
3155 dr.clearMessageUpdate();
3159 case LFUN_VC_UNDO_LAST:
3160 LASSERT(buffer, return);
3161 buffer->lyxvc().undoLast();
3162 reloadBuffer(*buffer);
3163 dr.clearMessageUpdate();
3166 case LFUN_VC_REPO_UPDATE:
3167 LASSERT(buffer, return);
3168 if (ensureBufferClean(buffer)) {
3169 dr.setMessage(buffer->lyxvc().repoUpdate());
3170 checkExternallyModifiedBuffers();
3174 case LFUN_VC_COMMAND: {
3175 string flag = cmd.getArg(0);
3176 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3179 if (contains(flag, 'M')) {
3180 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3183 string path = cmd.getArg(1);
3184 if (contains(path, "$$p") && buffer)
3185 path = subst(path, "$$p", buffer->filePath());
3186 LYXERR(Debug::LYXVC, "Directory: " << path);
3188 if (!pp.isReadableDirectory()) {
3189 lyxerr << _("Directory is not accessible.") << endl;
3192 support::PathChanger p(pp);
3194 string command = cmd.getArg(2);
3195 if (command.empty())
3198 command = subst(command, "$$i", buffer->absFileName());
3199 command = subst(command, "$$p", buffer->filePath());
3201 command = subst(command, "$$m", to_utf8(message));
3202 LYXERR(Debug::LYXVC, "Command: " << command);
3204 one.startscript(Systemcall::Wait, command);
3208 if (contains(flag, 'I'))
3209 buffer->markDirty();
3210 if (contains(flag, 'R'))
3211 reloadBuffer(*buffer);
3216 case LFUN_VC_COMPARE: {
3218 if (cmd.argument().empty()) {
3219 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3223 string rev1 = cmd.getArg(0);
3227 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3230 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3231 f2 = buffer->absFileName();
3233 string rev2 = cmd.getArg(1);
3237 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3241 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3242 f1 << "\n" << f2 << "\n" );
3243 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3244 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3254 void GuiView::openChildDocument(string const & fname)
3256 LASSERT(documentBufferView(), return);
3257 Buffer & buffer = documentBufferView()->buffer();
3258 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3259 documentBufferView()->saveBookmark(false);
3261 if (theBufferList().exists(filename)) {
3262 child = theBufferList().getBuffer(filename);
3265 message(bformat(_("Opening child document %1$s..."),
3266 makeDisplayPath(filename.absFileName())));
3267 child = loadDocument(filename, false);
3269 // Set the parent name of the child document.
3270 // This makes insertion of citations and references in the child work,
3271 // when the target is in the parent or another child document.
3273 child->setParent(&buffer);
3277 bool GuiView::goToFileRow(string const & argument)
3281 size_t i = argument.find_last_of(' ');
3282 if (i != string::npos) {
3283 file_name = os::internal_path(trim(argument.substr(0, i)));
3284 istringstream is(argument.substr(i + 1));
3289 if (i == string::npos) {
3290 LYXERR0("Wrong argument: " << argument);
3294 string const abstmp = package().temp_dir().absFileName();
3295 string const realtmp = package().temp_dir().realPath();
3296 // We have to use os::path_prefix_is() here, instead of
3297 // simply prefixIs(), because the file name comes from
3298 // an external application and may need case adjustment.
3299 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3300 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3301 // Needed by inverse dvi search. If it is a file
3302 // in tmpdir, call the apropriated function.
3303 // If tmpdir is a symlink, we may have the real
3304 // path passed back, so we correct for that.
3305 if (!prefixIs(file_name, abstmp))
3306 file_name = subst(file_name, realtmp, abstmp);
3307 buf = theBufferList().getBufferFromTmp(file_name);
3309 // Must replace extension of the file to be .lyx
3310 // and get full path
3311 FileName const s = fileSearch(string(),
3312 support::changeExtension(file_name, ".lyx"), "lyx");
3313 // Either change buffer or load the file
3314 if (theBufferList().exists(s))
3315 buf = theBufferList().getBuffer(s);
3316 else if (s.exists()) {
3317 buf = loadDocument(s);
3322 _("File does not exist: %1$s"),
3323 makeDisplayPath(file_name)));
3329 _("No buffer for file: %1$s."),
3330 makeDisplayPath(file_name))
3335 documentBufferView()->setCursorFromRow(row);
3341 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3343 Buffer::ExportStatus const status = func(format);
3345 // the cloning operation will have produced a clone of the entire set of
3346 // documents, starting from the master. so we must delete those.
3347 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3349 busyBuffers.remove(orig);
3354 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3356 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3357 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3361 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3363 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3364 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3368 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3370 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3371 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3375 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3376 string const & argument,
3377 Buffer const * used_buffer,
3378 docstring const & msg,
3379 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3380 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3381 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3386 string format = argument;
3388 format = used_buffer->params().getDefaultOutputFormat();
3389 processing_format = format;
3391 progress_->clearMessages();
3394 #if EXPORT_in_THREAD
3395 GuiViewPrivate::busyBuffers.insert(used_buffer);
3396 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3397 if (!cloned_buffer) {
3398 Alert::error(_("Export Error"),
3399 _("Error cloning the Buffer."));
3402 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3407 setPreviewFuture(f);
3408 last_export_format = used_buffer->params().bufferFormat();
3411 // We are asynchronous, so we don't know here anything about the success
3414 Buffer::ExportStatus status;
3416 // TODO check here if it breaks exporting with Qt < 4.4
3417 status = (used_buffer->*syncFunc)(format, true);
3418 } else if (previewFunc) {
3419 status = (used_buffer->*previewFunc)(format);
3422 handleExportStatus(gv_, status, format);
3424 return (status == Buffer::ExportSuccess
3425 || status == Buffer::PreviewSuccess);
3429 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3431 BufferView * bv = currentBufferView();
3432 LASSERT(bv, return);
3434 // Let the current BufferView dispatch its own actions.
3435 bv->dispatch(cmd, dr);
3436 if (dr.dispatched())
3439 // Try with the document BufferView dispatch if any.
3440 BufferView * doc_bv = documentBufferView();
3441 if (doc_bv && doc_bv != bv) {
3442 doc_bv->dispatch(cmd, dr);
3443 if (dr.dispatched())
3447 // Then let the current Cursor dispatch its own actions.
3448 bv->cursor().dispatch(cmd);
3450 // update completion. We do it here and not in
3451 // processKeySym to avoid another redraw just for a
3452 // changed inline completion
3453 if (cmd.origin() == FuncRequest::KEYBOARD) {
3454 if (cmd.action() == LFUN_SELF_INSERT
3455 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3456 updateCompletion(bv->cursor(), true, true);
3457 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3458 updateCompletion(bv->cursor(), false, true);
3460 updateCompletion(bv->cursor(), false, false);
3463 dr = bv->cursor().result();
3467 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3469 BufferView * bv = currentBufferView();
3470 // By default we won't need any update.
3471 dr.screenUpdate(Update::None);
3472 // assume cmd will be dispatched
3473 dr.dispatched(true);
3475 Buffer * doc_buffer = documentBufferView()
3476 ? &(documentBufferView()->buffer()) : 0;
3478 if (cmd.origin() == FuncRequest::TOC) {
3479 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3480 // FIXME: do we need to pass a DispatchResult object here?
3481 toc->doDispatch(bv->cursor(), cmd);
3485 string const argument = to_utf8(cmd.argument());
3487 switch(cmd.action()) {
3488 case LFUN_BUFFER_CHILD_OPEN:
3489 openChildDocument(to_utf8(cmd.argument()));
3492 case LFUN_BUFFER_IMPORT:
3493 importDocument(to_utf8(cmd.argument()));
3496 case LFUN_BUFFER_EXPORT: {
3499 FileName target_dir = doc_buffer->fileName().onlyPath();
3500 string const dest = cmd.getArg(1);
3501 if (!dest.empty() && FileName::isAbsolute(dest))
3502 target_dir = FileName(support::onlyPath(dest));
3503 // GCC only sees strfwd.h when building merged
3504 if (::lyx::operator==(cmd.argument(), "custom")) {
3505 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3508 if (!target_dir.isDirWritable()) {
3509 exportBufferAs(*doc_buffer, cmd.argument());
3512 /* TODO/Review: Is it a problem to also export the children?
3513 See the update_unincluded flag */
3514 d.asyncBufferProcessing(argument,
3517 &GuiViewPrivate::exportAndDestroy,
3520 // TODO Inform user about success
3524 case LFUN_BUFFER_EXPORT_AS: {
3525 LASSERT(doc_buffer, break);
3526 docstring f = cmd.argument();
3528 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3529 exportBufferAs(*doc_buffer, f);
3533 case LFUN_BUFFER_UPDATE: {
3534 d.asyncBufferProcessing(argument,
3537 &GuiViewPrivate::compileAndDestroy,
3542 case LFUN_BUFFER_VIEW: {
3543 d.asyncBufferProcessing(argument,
3545 _("Previewing ..."),
3546 &GuiViewPrivate::previewAndDestroy,
3551 case LFUN_MASTER_BUFFER_UPDATE: {
3552 d.asyncBufferProcessing(argument,
3553 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3555 &GuiViewPrivate::compileAndDestroy,
3560 case LFUN_MASTER_BUFFER_VIEW: {
3561 d.asyncBufferProcessing(argument,
3562 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3564 &GuiViewPrivate::previewAndDestroy,
3565 0, &Buffer::preview);
3568 case LFUN_BUFFER_SWITCH: {
3569 string const file_name = to_utf8(cmd.argument());
3570 if (!FileName::isAbsolute(file_name)) {
3572 dr.setMessage(_("Absolute filename expected."));
3576 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3579 dr.setMessage(_("Document not loaded"));
3583 // Do we open or switch to the buffer in this view ?
3584 if (workArea(*buffer)
3585 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3590 // Look for the buffer in other views
3591 QList<int> const ids = guiApp->viewIds();
3593 for (; i != ids.size(); ++i) {
3594 GuiView & gv = guiApp->view(ids[i]);
3595 if (gv.workArea(*buffer)) {
3596 gv.activateWindow();
3597 gv.setBuffer(buffer);
3602 // If necessary, open a new window as a last resort
3603 if (i == ids.size()) {
3604 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3610 case LFUN_BUFFER_NEXT:
3611 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3614 case LFUN_BUFFER_MOVE_NEXT:
3615 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3618 case LFUN_BUFFER_PREVIOUS:
3619 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3622 case LFUN_BUFFER_MOVE_PREVIOUS:
3623 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3626 case LFUN_COMMAND_EXECUTE: {
3627 bool const show_it = cmd.argument() != "off";
3628 // FIXME: this is a hack, "minibuffer" should not be
3630 if (GuiToolbar * t = toolbar("minibuffer")) {
3631 t->setVisible(show_it);
3632 if (show_it && t->commandBuffer())
3633 t->commandBuffer()->setFocus();
3637 case LFUN_DROP_LAYOUTS_CHOICE:
3638 d.layout_->showPopup();
3641 case LFUN_MENU_OPEN:
3642 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3643 menu->exec(QCursor::pos());
3646 case LFUN_FILE_INSERT:
3647 insertLyXFile(cmd.argument());
3650 case LFUN_FILE_INSERT_PLAINTEXT:
3651 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3652 string const fname = to_utf8(cmd.argument());
3653 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3654 dr.setMessage(_("Absolute filename expected."));
3658 FileName filename(fname);
3659 if (fname.empty()) {
3660 FileDialog dlg(qt_("Select file to insert"));
3662 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3663 QStringList(qt_("All Files (*)")));
3665 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3666 dr.setMessage(_("Canceled."));
3670 filename.set(fromqstr(result.second));
3674 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3675 bv->dispatch(new_cmd, dr);
3680 case LFUN_BUFFER_RELOAD: {
3681 LASSERT(doc_buffer, break);
3684 if (!doc_buffer->isClean()) {
3685 docstring const file =
3686 makeDisplayPath(doc_buffer->absFileName(), 20);
3687 docstring text = bformat(_("Any changes will be lost. "
3688 "Are you sure you want to revert to the saved version "
3689 "of the document %1$s?"), file);
3690 ret = Alert::prompt(_("Revert to saved document?"),
3691 text, 1, 1, _("&Revert"), _("&Cancel"));
3695 doc_buffer->markClean();
3696 reloadBuffer(*doc_buffer);
3697 dr.forceBufferUpdate();
3702 case LFUN_BUFFER_WRITE:
3703 LASSERT(doc_buffer, break);
3704 saveBuffer(*doc_buffer);
3707 case LFUN_BUFFER_WRITE_AS:
3708 LASSERT(doc_buffer, break);
3709 renameBuffer(*doc_buffer, cmd.argument());
3712 case LFUN_BUFFER_WRITE_ALL: {
3713 Buffer * first = theBufferList().first();
3716 message(_("Saving all documents..."));
3717 // We cannot use a for loop as the buffer list cycles.
3720 if (!b->isClean()) {
3722 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3724 b = theBufferList().next(b);
3725 } while (b != first);
3726 dr.setMessage(_("All documents saved."));
3730 case LFUN_BUFFER_CLOSE:
3734 case LFUN_BUFFER_CLOSE_ALL:
3738 case LFUN_TOOLBAR_TOGGLE: {
3739 string const name = cmd.getArg(0);
3740 if (GuiToolbar * t = toolbar(name))
3745 case LFUN_DIALOG_UPDATE: {
3746 string const name = to_utf8(cmd.argument());
3747 if (name == "prefs" || name == "document")
3748 updateDialog(name, string());
3749 else if (name == "paragraph")
3750 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3751 else if (currentBufferView()) {
3752 Inset * inset = currentBufferView()->editedInset(name);
3753 // Can only update a dialog connected to an existing inset
3755 // FIXME: get rid of this indirection; GuiView ask the inset
3756 // if he is kind enough to update itself...
3757 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3758 //FIXME: pass DispatchResult here?
3759 inset->dispatch(currentBufferView()->cursor(), fr);
3765 case LFUN_DIALOG_TOGGLE: {
3766 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3767 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3768 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3772 case LFUN_DIALOG_DISCONNECT_INSET:
3773 disconnectDialog(to_utf8(cmd.argument()));
3776 case LFUN_DIALOG_HIDE: {
3777 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3781 case LFUN_DIALOG_SHOW: {
3782 string const name = cmd.getArg(0);
3783 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3785 if (name == "character") {
3786 data = freefont2string();
3788 showDialog("character", data);
3789 } else if (name == "latexlog") {
3790 Buffer::LogType type;
3791 string const logfile = doc_buffer->logName(&type);
3793 case Buffer::latexlog:
3796 case Buffer::buildlog:
3800 data += Lexer::quoteString(logfile);
3801 showDialog("log", data);
3802 } else if (name == "vclog") {
3803 string const data = "vc " +
3804 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3805 showDialog("log", data);
3806 } else if (name == "symbols") {
3807 data = bv->cursor().getEncoding()->name();
3809 showDialog("symbols", data);
3811 } else if (name == "prefs" && isFullScreen()) {
3812 lfunUiToggle("fullscreen");
3813 showDialog("prefs", data);
3815 showDialog(name, data);
3820 dr.setMessage(cmd.argument());
3823 case LFUN_UI_TOGGLE: {
3824 string arg = cmd.getArg(0);
3825 if (!lfunUiToggle(arg)) {
3826 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3827 dr.setMessage(bformat(msg, from_utf8(arg)));
3829 // Make sure the keyboard focus stays in the work area.
3834 case LFUN_VIEW_SPLIT: {
3835 LASSERT(doc_buffer, break);
3836 string const orientation = cmd.getArg(0);
3837 d.splitter_->setOrientation(orientation == "vertical"
3838 ? Qt::Vertical : Qt::Horizontal);
3839 TabWorkArea * twa = addTabWorkArea();
3840 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3841 setCurrentWorkArea(wa);
3844 case LFUN_TAB_GROUP_CLOSE:
3845 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3846 closeTabWorkArea(twa);
3847 d.current_work_area_ = 0;
3848 twa = d.currentTabWorkArea();
3849 // Switch to the next GuiWorkArea in the found TabWorkArea.
3851 // Make sure the work area is up to date.
3852 setCurrentWorkArea(twa->currentWorkArea());
3854 setCurrentWorkArea(0);
3859 case LFUN_VIEW_CLOSE:
3860 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3861 closeWorkArea(twa->currentWorkArea());
3862 d.current_work_area_ = 0;
3863 twa = d.currentTabWorkArea();
3864 // Switch to the next GuiWorkArea in the found TabWorkArea.
3866 // Make sure the work area is up to date.
3867 setCurrentWorkArea(twa->currentWorkArea());
3869 setCurrentWorkArea(0);
3874 case LFUN_COMPLETION_INLINE:
3875 if (d.current_work_area_)
3876 d.current_work_area_->completer().showInline();
3879 case LFUN_COMPLETION_POPUP:
3880 if (d.current_work_area_)
3881 d.current_work_area_->completer().showPopup();
3886 if (d.current_work_area_)
3887 d.current_work_area_->completer().tab();
3890 case LFUN_COMPLETION_CANCEL:
3891 if (d.current_work_area_) {
3892 if (d.current_work_area_->completer().popupVisible())
3893 d.current_work_area_->completer().hidePopup();
3895 d.current_work_area_->completer().hideInline();
3899 case LFUN_COMPLETION_ACCEPT:
3900 if (d.current_work_area_)
3901 d.current_work_area_->completer().activate();
3904 case LFUN_BUFFER_ZOOM_IN:
3905 case LFUN_BUFFER_ZOOM_OUT:
3906 if (cmd.argument().empty()) {
3907 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3912 lyxrc.zoom += convert<int>(cmd.argument());
3914 if (lyxrc.zoom < 10)
3917 // The global QPixmapCache is used in GuiPainter to cache text
3918 // painting so we must reset it.
3919 QPixmapCache::clear();
3920 guiApp->fontLoader().update();
3921 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3924 case LFUN_VC_REGISTER:
3925 case LFUN_VC_RENAME:
3927 case LFUN_VC_CHECK_IN:
3928 case LFUN_VC_CHECK_OUT:
3929 case LFUN_VC_REPO_UPDATE:
3930 case LFUN_VC_LOCKING_TOGGLE:
3931 case LFUN_VC_REVERT:
3932 case LFUN_VC_UNDO_LAST:
3933 case LFUN_VC_COMMAND:
3934 case LFUN_VC_COMPARE:
3935 dispatchVC(cmd, dr);
3938 case LFUN_SERVER_GOTO_FILE_ROW:
3939 goToFileRow(to_utf8(cmd.argument()));
3942 case LFUN_FORWARD_SEARCH: {
3943 Buffer const * doc_master = doc_buffer->masterBuffer();
3944 FileName const path(doc_master->temppath());
3945 string const texname = doc_master->isChild(doc_buffer)
3946 ? DocFileName(changeExtension(
3947 doc_buffer->absFileName(),
3948 "tex")).mangledFileName()
3949 : doc_buffer->latexName();
3950 string const fulltexname =
3951 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3952 string const mastername =
3953 removeExtension(doc_master->latexName());
3954 FileName const dviname(addName(path.absFileName(),
3955 addExtension(mastername, "dvi")));
3956 FileName const pdfname(addName(path.absFileName(),
3957 addExtension(mastername, "pdf")));
3958 bool const have_dvi = dviname.exists();
3959 bool const have_pdf = pdfname.exists();
3960 if (!have_dvi && !have_pdf) {
3961 dr.setMessage(_("Please, preview the document first."));
3964 string outname = dviname.onlyFileName();
3965 string command = lyxrc.forward_search_dvi;
3966 if (!have_dvi || (have_pdf &&
3967 pdfname.lastModified() > dviname.lastModified())) {
3968 outname = pdfname.onlyFileName();
3969 command = lyxrc.forward_search_pdf;
3972 DocIterator tmpcur = bv->cursor();
3974 while (tmpcur.inMathed())
3976 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3977 tmpcur.paragraph().id(), tmpcur.pos());
3978 LYXERR(Debug::ACTION, "Forward search: row:" << row
3979 << " id:" << tmpcur.paragraph().id());
3980 if (!row || command.empty()) {
3981 dr.setMessage(_("Couldn't proceed."));
3984 string texrow = convert<string>(row);
3986 command = subst(command, "$$n", texrow);
3987 command = subst(command, "$$f", fulltexname);
3988 command = subst(command, "$$t", texname);
3989 command = subst(command, "$$o", outname);
3991 PathChanger p(path);
3993 one.startscript(Systemcall::DontWait, command);
3997 case LFUN_SPELLING_CONTINUOUSLY:
3998 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3999 dr.screenUpdate(Update::Force | Update::FitCursor);
4003 // The LFUN must be for one of BufferView, Buffer or Cursor;
4005 dispatchToBufferView(cmd, dr);
4009 // Part of automatic menu appearance feature.
4010 if (isFullScreen()) {
4011 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4015 // Need to update bv because many LFUNs here might have destroyed it
4016 bv = currentBufferView();
4018 // Clear non-empty selections
4019 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4021 Cursor & cur = bv->cursor();
4022 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4023 cur.clearSelection();
4029 bool GuiView::lfunUiToggle(string const & ui_component)
4031 if (ui_component == "scrollbar") {
4032 // hide() is of no help
4033 if (d.current_work_area_->verticalScrollBarPolicy() ==
4034 Qt::ScrollBarAlwaysOff)
4036 d.current_work_area_->setVerticalScrollBarPolicy(
4037 Qt::ScrollBarAsNeeded);
4039 d.current_work_area_->setVerticalScrollBarPolicy(
4040 Qt::ScrollBarAlwaysOff);
4041 } else if (ui_component == "statusbar") {
4042 statusBar()->setVisible(!statusBar()->isVisible());
4043 } else if (ui_component == "menubar") {
4044 menuBar()->setVisible(!menuBar()->isVisible());
4046 if (ui_component == "frame") {
4048 getContentsMargins(&l, &t, &r, &b);
4049 //are the frames in default state?
4050 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4052 setContentsMargins(-2, -2, -2, -2);
4054 setContentsMargins(0, 0, 0, 0);
4057 if (ui_component == "fullscreen") {
4065 void GuiView::toggleFullScreen()
4067 if (isFullScreen()) {
4068 for (int i = 0; i != d.splitter_->count(); ++i)
4069 d.tabWorkArea(i)->setFullScreen(false);
4070 setContentsMargins(0, 0, 0, 0);
4071 setWindowState(windowState() ^ Qt::WindowFullScreen);
4074 statusBar()->show();
4077 hideDialogs("prefs", 0);
4078 for (int i = 0; i != d.splitter_->count(); ++i)
4079 d.tabWorkArea(i)->setFullScreen(true);
4080 setContentsMargins(-2, -2, -2, -2);
4082 setWindowState(windowState() ^ Qt::WindowFullScreen);
4083 if (lyxrc.full_screen_statusbar)
4084 statusBar()->hide();
4085 if (lyxrc.full_screen_menubar)
4087 if (lyxrc.full_screen_toolbars) {
4088 ToolbarMap::iterator end = d.toolbars_.end();
4089 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4094 // give dialogs like the TOC a chance to adapt
4099 Buffer const * GuiView::updateInset(Inset const * inset)
4104 Buffer const * inset_buffer = &(inset->buffer());
4106 for (int i = 0; i != d.splitter_->count(); ++i) {
4107 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4110 Buffer const * buffer = &(wa->bufferView().buffer());
4111 if (inset_buffer == buffer)
4112 wa->scheduleRedraw();
4114 return inset_buffer;
4118 void GuiView::restartCursor()
4120 /* When we move around, or type, it's nice to be able to see
4121 * the cursor immediately after the keypress.
4123 if (d.current_work_area_)
4124 d.current_work_area_->startBlinkingCursor();
4126 // Take this occasion to update the other GUI elements.
4132 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4134 if (d.current_work_area_)
4135 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4140 // This list should be kept in sync with the list of insets in
4141 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4142 // dialog should have the same name as the inset.
4143 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4144 // docs in LyXAction.cpp.
4146 char const * const dialognames[] = {
4148 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4149 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4150 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4151 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4152 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4153 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4154 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4155 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4157 char const * const * const end_dialognames =
4158 dialognames + (sizeof(dialognames) / sizeof(char *));
4162 cmpCStr(char const * name) : name_(name) {}
4163 bool operator()(char const * other) {
4164 return strcmp(other, name_) == 0;
4171 bool isValidName(string const & name)
4173 return find_if(dialognames, end_dialognames,
4174 cmpCStr(name.c_str())) != end_dialognames;
4180 void GuiView::resetDialogs()
4182 // Make sure that no LFUN uses any GuiView.
4183 guiApp->setCurrentView(0);
4187 constructToolbars();
4188 guiApp->menus().fillMenuBar(menuBar(), this, false);
4189 d.layout_->updateContents(true);
4190 // Now update controls with current buffer.
4191 guiApp->setCurrentView(this);
4197 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4199 if (!isValidName(name))
4202 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4204 if (it != d.dialogs_.end()) {
4206 it->second->hideView();
4207 return it->second.get();
4210 Dialog * dialog = build(name);
4211 d.dialogs_[name].reset(dialog);
4212 if (lyxrc.allow_geometry_session)
4213 dialog->restoreSession();
4220 void GuiView::showDialog(string const & name, string const & data,
4223 triggerShowDialog(toqstr(name), toqstr(data), inset);
4227 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4233 const string name = fromqstr(qname);
4234 const string data = fromqstr(qdata);
4238 Dialog * dialog = findOrBuild(name, false);
4240 bool const visible = dialog->isVisibleView();
4241 dialog->showData(data);
4242 if (inset && currentBufferView())
4243 currentBufferView()->editInset(name, inset);
4244 // We only set the focus to the new dialog if it was not yet
4245 // visible in order not to change the existing previous behaviour
4247 // activateWindow is needed for floating dockviews
4248 dialog->asQWidget()->raise();
4249 dialog->asQWidget()->activateWindow();
4250 dialog->asQWidget()->setFocus();
4254 catch (ExceptionMessage const & ex) {
4262 bool GuiView::isDialogVisible(string const & name) const
4264 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4265 if (it == d.dialogs_.end())
4267 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4271 void GuiView::hideDialog(string const & name, Inset * inset)
4273 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4274 if (it == d.dialogs_.end())
4278 if (!currentBufferView())
4280 if (inset != currentBufferView()->editedInset(name))
4284 Dialog * const dialog = it->second.get();
4285 if (dialog->isVisibleView())
4287 if (currentBufferView())
4288 currentBufferView()->editInset(name, 0);
4292 void GuiView::disconnectDialog(string const & name)
4294 if (!isValidName(name))
4296 if (currentBufferView())
4297 currentBufferView()->editInset(name, 0);
4301 void GuiView::hideAll() const
4303 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4304 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4306 for(; it != end; ++it)
4307 it->second->hideView();
4311 void GuiView::updateDialogs()
4313 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4314 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4316 for(; it != end; ++it) {
4317 Dialog * dialog = it->second.get();
4319 if (dialog->needBufferOpen() && !documentBufferView())
4320 hideDialog(fromqstr(dialog->name()), 0);
4321 else if (dialog->isVisibleView())
4322 dialog->checkStatus();
4329 Dialog * createDialog(GuiView & lv, string const & name);
4331 // will be replaced by a proper factory...
4332 Dialog * createGuiAbout(GuiView & lv);
4333 Dialog * createGuiBibtex(GuiView & lv);
4334 Dialog * createGuiChanges(GuiView & lv);
4335 Dialog * createGuiCharacter(GuiView & lv);
4336 Dialog * createGuiCitation(GuiView & lv);
4337 Dialog * createGuiCompare(GuiView & lv);
4338 Dialog * createGuiCompareHistory(GuiView & lv);
4339 Dialog * createGuiDelimiter(GuiView & lv);
4340 Dialog * createGuiDocument(GuiView & lv);
4341 Dialog * createGuiErrorList(GuiView & lv);
4342 Dialog * createGuiExternal(GuiView & lv);
4343 Dialog * createGuiGraphics(GuiView & lv);
4344 Dialog * createGuiInclude(GuiView & lv);
4345 Dialog * createGuiIndex(GuiView & lv);
4346 Dialog * createGuiListings(GuiView & lv);
4347 Dialog * createGuiLog(GuiView & lv);
4348 Dialog * createGuiMathMatrix(GuiView & lv);
4349 Dialog * createGuiNote(GuiView & lv);
4350 Dialog * createGuiParagraph(GuiView & lv);
4351 Dialog * createGuiPhantom(GuiView & lv);
4352 Dialog * createGuiPreferences(GuiView & lv);
4353 Dialog * createGuiPrint(GuiView & lv);
4354 Dialog * createGuiPrintindex(GuiView & lv);
4355 Dialog * createGuiRef(GuiView & lv);
4356 Dialog * createGuiSearch(GuiView & lv);
4357 Dialog * createGuiSearchAdv(GuiView & lv);
4358 Dialog * createGuiSendTo(GuiView & lv);
4359 Dialog * createGuiShowFile(GuiView & lv);
4360 Dialog * createGuiSpellchecker(GuiView & lv);
4361 Dialog * createGuiSymbols(GuiView & lv);
4362 Dialog * createGuiTabularCreate(GuiView & lv);
4363 Dialog * createGuiTexInfo(GuiView & lv);
4364 Dialog * createGuiToc(GuiView & lv);
4365 Dialog * createGuiThesaurus(GuiView & lv);
4366 Dialog * createGuiViewSource(GuiView & lv);
4367 Dialog * createGuiWrap(GuiView & lv);
4368 Dialog * createGuiProgressView(GuiView & lv);
4372 Dialog * GuiView::build(string const & name)
4374 LASSERT(isValidName(name), return 0);
4376 Dialog * dialog = createDialog(*this, name);
4380 if (name == "aboutlyx")
4381 return createGuiAbout(*this);
4382 if (name == "bibtex")
4383 return createGuiBibtex(*this);
4384 if (name == "changes")
4385 return createGuiChanges(*this);
4386 if (name == "character")
4387 return createGuiCharacter(*this);
4388 if (name == "citation")
4389 return createGuiCitation(*this);
4390 if (name == "compare")
4391 return createGuiCompare(*this);
4392 if (name == "comparehistory")
4393 return createGuiCompareHistory(*this);
4394 if (name == "document")
4395 return createGuiDocument(*this);
4396 if (name == "errorlist")
4397 return createGuiErrorList(*this);
4398 if (name == "external")
4399 return createGuiExternal(*this);
4401 return createGuiShowFile(*this);
4402 if (name == "findreplace")
4403 return createGuiSearch(*this);
4404 if (name == "findreplaceadv")
4405 return createGuiSearchAdv(*this);
4406 if (name == "graphics")
4407 return createGuiGraphics(*this);
4408 if (name == "include")
4409 return createGuiInclude(*this);
4410 if (name == "index")
4411 return createGuiIndex(*this);
4412 if (name == "index_print")
4413 return createGuiPrintindex(*this);
4414 if (name == "listings")
4415 return createGuiListings(*this);
4417 return createGuiLog(*this);
4418 if (name == "mathdelimiter")
4419 return createGuiDelimiter(*this);
4420 if (name == "mathmatrix")
4421 return createGuiMathMatrix(*this);
4423 return createGuiNote(*this);
4424 if (name == "paragraph")
4425 return createGuiParagraph(*this);
4426 if (name == "phantom")
4427 return createGuiPhantom(*this);
4428 if (name == "prefs")
4429 return createGuiPreferences(*this);
4431 return createGuiRef(*this);
4432 if (name == "sendto")
4433 return createGuiSendTo(*this);
4434 if (name == "spellchecker")
4435 return createGuiSpellchecker(*this);
4436 if (name == "symbols")
4437 return createGuiSymbols(*this);
4438 if (name == "tabularcreate")
4439 return createGuiTabularCreate(*this);
4440 if (name == "texinfo")
4441 return createGuiTexInfo(*this);
4442 if (name == "thesaurus")
4443 return createGuiThesaurus(*this);
4445 return createGuiToc(*this);
4446 if (name == "view-source")
4447 return createGuiViewSource(*this);
4449 return createGuiWrap(*this);
4450 if (name == "progress")
4451 return createGuiProgressView(*this);
4457 } // namespace frontend
4460 #include "moc_GuiView.cpp"