3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #if QT_VERSION >= 0x050000
114 #include <QSvgRenderer>
116 #include <QtConcurrentRun>
124 // sync with GuiAlert.cpp
125 #define EXPORT_in_THREAD 1
128 #include "support/bind.h"
132 #ifdef HAVE_SYS_TIME_H
133 # include <sys/time.h>
141 using namespace lyx::support;
145 using support::addExtension;
146 using support::changeExtension;
147 using support::removeExtension;
153 class BackgroundWidget : public QWidget
156 BackgroundWidget(int width, int height)
157 : width_(width), height_(height)
159 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
160 if (!lyxrc.show_banner)
162 /// The text to be written on top of the pixmap
163 QString const text = lyx_version ?
164 qt_("version ") + lyx_version : qt_("unknown version");
165 #if QT_VERSION >= 0x050000
166 QString imagedir = "images/";
167 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
168 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
169 if (svgRenderer.isValid()) {
170 splash_ = QPixmap(splashSize());
171 QPainter painter(&splash_);
172 svgRenderer.render(&painter);
173 splash_.setDevicePixelRatio(pixelRatio());
175 splash_ = getPixmap("images/", "banner", "png");
178 splash_ = getPixmap("images/", "banner", "svgz,png");
181 QPainter pain(&splash_);
182 pain.setPen(QColor(0, 0, 0));
183 qreal const fsize = fontSize();
184 QPointF const position = textPosition();
186 "widget pixel ratio: " << pixelRatio() <<
187 " splash pixel ratio: " << splashPixelRatio() <<
188 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
190 // The font used to display the version info
191 font.setStyleHint(QFont::SansSerif);
192 font.setWeight(QFont::Bold);
193 font.setPointSizeF(fsize);
195 pain.drawText(position, text);
196 setFocusPolicy(Qt::StrongFocus);
199 void paintEvent(QPaintEvent *)
201 int const w = width_;
202 int const h = height_;
203 int const x = (width() - w) / 2;
204 int const y = (height() - h) / 2;
206 "widget pixel ratio: " << pixelRatio() <<
207 " splash pixel ratio: " << splashPixelRatio() <<
208 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
210 pain.drawPixmap(x, y, w, h, splash_);
213 void keyPressEvent(QKeyEvent * ev)
216 setKeySymbol(&sym, ev);
218 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
230 /// Current ratio between physical pixels and device-independent pixels
231 double pixelRatio() const {
232 #if QT_VERSION >= 0x050000
233 return devicePixelRatio();
239 qreal fontSize() const {
240 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
243 QPointF textPosition() const {
244 return QPointF(width_/2 - 18, height_/2 + 45);
247 QSize splashSize() const {
249 static_cast<unsigned int>(width_ * pixelRatio()),
250 static_cast<unsigned int>(height_ * pixelRatio()));
253 /// Ratio between physical pixels and device-independent pixels of splash image
254 double splashPixelRatio() const {
255 #if QT_VERSION >= 0x050000
256 return splash_.devicePixelRatio();
264 /// Toolbar store providing access to individual toolbars by name.
265 typedef map<string, GuiToolbar *> ToolbarMap;
267 typedef shared_ptr<Dialog> DialogPtr;
272 struct GuiView::GuiViewPrivate
274 GuiViewPrivate(GuiView * gv)
275 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
276 layout_(0), autosave_timeout_(5000),
279 // hardcode here the platform specific icon size
280 smallIconSize = 16; // scaling problems
281 normalIconSize = 20; // ok, default if iconsize.png is missing
282 bigIconSize = 26; // better for some math icons
283 hugeIconSize = 32; // better for hires displays
286 // if it exists, use width of iconsize.png as normal size
287 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
288 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290 QImage image(toqstr(fn.absFileName()));
291 if (image.width() < int(smallIconSize))
292 normalIconSize = smallIconSize;
293 else if (image.width() > int(giantIconSize))
294 normalIconSize = giantIconSize;
296 normalIconSize = image.width();
299 splitter_ = new QSplitter;
300 bg_widget_ = new BackgroundWidget(400, 250);
301 stack_widget_ = new QStackedWidget;
302 stack_widget_->addWidget(bg_widget_);
303 stack_widget_->addWidget(splitter_);
306 // TODO cleanup, remove the singleton, handle multiple Windows?
307 progress_ = ProgressInterface::instance();
308 if (!dynamic_cast<GuiProgress*>(progress_)) {
309 progress_ = new GuiProgress; // TODO who deletes it
310 ProgressInterface::setInstance(progress_);
313 dynamic_cast<GuiProgress*>(progress_),
314 SIGNAL(updateStatusBarMessage(QString const&)),
315 gv, SLOT(updateStatusBarMessage(QString const&)));
317 dynamic_cast<GuiProgress*>(progress_),
318 SIGNAL(clearMessageText()),
319 gv, SLOT(clearMessageText()));
326 delete stack_widget_;
329 QMenu * toolBarPopup(GuiView * parent)
331 // FIXME: translation
332 QMenu * menu = new QMenu(parent);
333 QActionGroup * iconSizeGroup = new QActionGroup(parent);
335 QAction * smallIcons = new QAction(iconSizeGroup);
336 smallIcons->setText(qt_("Small-sized icons"));
337 smallIcons->setCheckable(true);
338 QObject::connect(smallIcons, SIGNAL(triggered()),
339 parent, SLOT(smallSizedIcons()));
340 menu->addAction(smallIcons);
342 QAction * normalIcons = new QAction(iconSizeGroup);
343 normalIcons->setText(qt_("Normal-sized icons"));
344 normalIcons->setCheckable(true);
345 QObject::connect(normalIcons, SIGNAL(triggered()),
346 parent, SLOT(normalSizedIcons()));
347 menu->addAction(normalIcons);
349 QAction * bigIcons = new QAction(iconSizeGroup);
350 bigIcons->setText(qt_("Big-sized icons"));
351 bigIcons->setCheckable(true);
352 QObject::connect(bigIcons, SIGNAL(triggered()),
353 parent, SLOT(bigSizedIcons()));
354 menu->addAction(bigIcons);
356 QAction * hugeIcons = new QAction(iconSizeGroup);
357 hugeIcons->setText(qt_("Huge-sized icons"));
358 hugeIcons->setCheckable(true);
359 QObject::connect(hugeIcons, SIGNAL(triggered()),
360 parent, SLOT(hugeSizedIcons()));
361 menu->addAction(hugeIcons);
363 QAction * giantIcons = new QAction(iconSizeGroup);
364 giantIcons->setText(qt_("Giant-sized icons"));
365 giantIcons->setCheckable(true);
366 QObject::connect(giantIcons, SIGNAL(triggered()),
367 parent, SLOT(giantSizedIcons()));
368 menu->addAction(giantIcons);
370 unsigned int cur = parent->iconSize().width();
371 if ( cur == parent->d.smallIconSize)
372 smallIcons->setChecked(true);
373 else if (cur == parent->d.normalIconSize)
374 normalIcons->setChecked(true);
375 else if (cur == parent->d.bigIconSize)
376 bigIcons->setChecked(true);
377 else if (cur == parent->d.hugeIconSize)
378 hugeIcons->setChecked(true);
379 else if (cur == parent->d.giantIconSize)
380 giantIcons->setChecked(true);
387 stack_widget_->setCurrentWidget(bg_widget_);
388 bg_widget_->setUpdatesEnabled(true);
389 bg_widget_->setFocus();
392 int tabWorkAreaCount()
394 return splitter_->count();
397 TabWorkArea * tabWorkArea(int i)
399 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
402 TabWorkArea * currentTabWorkArea()
404 int areas = tabWorkAreaCount();
406 // The first TabWorkArea is always the first one, if any.
407 return tabWorkArea(0);
409 for (int i = 0; i != areas; ++i) {
410 TabWorkArea * twa = tabWorkArea(i);
411 if (current_main_work_area_ == twa->currentWorkArea())
415 // None has the focus so we just take the first one.
416 return tabWorkArea(0);
419 int countWorkAreasOf(Buffer & buf)
421 int areas = tabWorkAreaCount();
423 for (int i = 0; i != areas; ++i) {
424 TabWorkArea * twa = tabWorkArea(i);
425 if (twa->workArea(buf))
431 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
433 if (processing_thread_watcher_.isRunning()) {
434 // we prefer to cancel this preview in order to keep a snappy
438 processing_thread_watcher_.setFuture(f);
443 GuiWorkArea * current_work_area_;
444 GuiWorkArea * current_main_work_area_;
445 QSplitter * splitter_;
446 QStackedWidget * stack_widget_;
447 BackgroundWidget * bg_widget_;
449 ToolbarMap toolbars_;
450 ProgressInterface* progress_;
451 /// The main layout box.
453 * \warning Don't Delete! The layout box is actually owned by
454 * whichever toolbar contains it. All the GuiView class needs is a
455 * means of accessing it.
457 * FIXME: replace that with a proper model so that we are not limited
458 * to only one dialog.
463 map<string, DialogPtr> dialogs_;
465 unsigned int smallIconSize;
466 unsigned int normalIconSize;
467 unsigned int bigIconSize;
468 unsigned int hugeIconSize;
469 unsigned int giantIconSize;
471 QTimer statusbar_timer_;
472 /// auto-saving of buffers
473 Timeout autosave_timeout_;
474 /// flag against a race condition due to multiclicks, see bug #1119
478 TocModels toc_models_;
481 QFutureWatcher<docstring> autosave_watcher_;
482 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
484 string last_export_format;
485 string processing_format;
487 static QSet<Buffer const *> busyBuffers;
488 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
494 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
496 // TODO syncFunc/previewFunc: use bind
497 bool asyncBufferProcessing(string const & argument,
498 Buffer const * used_buffer,
499 docstring const & msg,
500 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
501 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
502 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
504 QVector<GuiWorkArea*> guiWorkAreas();
507 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
510 GuiView::GuiView(int id)
511 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
513 // GuiToolbars *must* be initialised before the menu bar.
514 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
517 // set ourself as the current view. This is needed for the menu bar
518 // filling, at least for the static special menu item on Mac. Otherwise
519 // they are greyed out.
520 guiApp->setCurrentView(this);
522 // Fill up the menu bar.
523 guiApp->menus().fillMenuBar(menuBar(), this, true);
525 setCentralWidget(d.stack_widget_);
527 // Start autosave timer
528 if (lyxrc.autosave) {
529 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
530 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
531 d.autosave_timeout_.start();
533 connect(&d.statusbar_timer_, SIGNAL(timeout()),
534 this, SLOT(clearMessage()));
536 // We don't want to keep the window in memory if it is closed.
537 setAttribute(Qt::WA_DeleteOnClose, true);
539 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
540 // QIcon::fromTheme was introduced in Qt 4.6
541 #if (QT_VERSION >= 0x040600)
542 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
543 // since the icon is provided in the application bundle. We use a themed
544 // version when available and use the bundled one as fallback.
545 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
547 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
551 resetWindowTitleAndIconText();
553 // use tabbed dock area for multiple docks
554 // (such as "source" and "messages")
555 setDockOptions(QMainWindow::ForceTabbedDocks);
558 setAcceptDrops(true);
560 // add busy indicator to statusbar
561 QLabel * busylabel = new QLabel(statusBar());
562 statusBar()->addPermanentWidget(busylabel);
563 search_mode mode = theGuiApp()->imageSearchMode();
564 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
565 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
566 busylabel->setMovie(busyanim);
570 connect(&d.processing_thread_watcher_, SIGNAL(started()),
571 busylabel, SLOT(show()));
572 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
573 busylabel, SLOT(hide()));
575 statusBar()->setSizeGripEnabled(true);
578 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
579 SLOT(autoSaveThreadFinished()));
581 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
582 SLOT(processingThreadStarted()));
583 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
584 SLOT(processingThreadFinished()));
586 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
587 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
589 // Forbid too small unresizable window because it can happen
590 // with some window manager under X11.
591 setMinimumSize(300, 200);
593 if (lyxrc.allow_geometry_session) {
594 // Now take care of session management.
599 // no session handling, default to a sane size.
600 setGeometry(50, 50, 690, 510);
603 // clear session data if any.
605 settings.remove("views");
615 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
617 QVector<GuiWorkArea*> areas;
618 for (int i = 0; i < tabWorkAreaCount(); i++) {
619 TabWorkArea* ta = tabWorkArea(i);
620 for (int u = 0; u < ta->count(); u++) {
621 areas << ta->workArea(u);
627 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
628 string const & format)
630 docstring const fmt = formats.prettyName(format);
633 case Buffer::ExportSuccess:
634 msg = bformat(_("Successful export to format: %1$s"), fmt);
636 case Buffer::ExportCancel:
637 msg = _("Document export cancelled.");
639 case Buffer::ExportError:
640 case Buffer::ExportNoPathToFormat:
641 case Buffer::ExportTexPathHasSpaces:
642 case Buffer::ExportConverterError:
643 msg = bformat(_("Error while exporting format: %1$s"), fmt);
645 case Buffer::PreviewSuccess:
646 msg = bformat(_("Successful preview of format: %1$s"), fmt);
648 case Buffer::PreviewError:
649 msg = bformat(_("Error while previewing format: %1$s"), fmt);
656 void GuiView::processingThreadStarted()
661 void GuiView::processingThreadFinished()
663 QFutureWatcher<Buffer::ExportStatus> const * watcher =
664 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
666 Buffer::ExportStatus const status = watcher->result();
667 handleExportStatus(this, status, d.processing_format);
670 BufferView const * const bv = currentBufferView();
671 if (bv && !bv->buffer().errorList("Export").empty()) {
675 errors(d.last_export_format);
679 void GuiView::autoSaveThreadFinished()
681 QFutureWatcher<docstring> const * watcher =
682 static_cast<QFutureWatcher<docstring> const *>(sender());
683 message(watcher->result());
688 void GuiView::saveLayout() const
691 settings.beginGroup("views");
692 settings.beginGroup(QString::number(id_));
693 #if defined(Q_WS_X11) || defined(QPA_XCB)
694 settings.setValue("pos", pos());
695 settings.setValue("size", size());
697 settings.setValue("geometry", saveGeometry());
699 settings.setValue("layout", saveState(0));
700 settings.setValue("icon_size", iconSize());
704 void GuiView::saveUISettings() const
706 // Save the toolbar private states
707 ToolbarMap::iterator end = d.toolbars_.end();
708 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
709 it->second->saveSession();
710 // Now take care of all other dialogs
711 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
712 for (; it!= d.dialogs_.end(); ++it)
713 it->second->saveSession();
717 bool GuiView::restoreLayout()
720 settings.beginGroup("views");
721 settings.beginGroup(QString::number(id_));
722 QString const icon_key = "icon_size";
723 if (!settings.contains(icon_key))
726 //code below is skipped when when ~/.config/LyX is (re)created
727 QSize icon_size = settings.value(icon_key).toSize();
728 // Check whether session size changed.
729 if (icon_size.width() != int(d.smallIconSize) &&
730 icon_size.width() != int(d.normalIconSize) &&
731 icon_size.width() != int(d.bigIconSize) &&
732 icon_size.width() != int(d.hugeIconSize) &&
733 icon_size.width() != int(d.giantIconSize)) {
734 icon_size.setWidth(d.normalIconSize);
735 icon_size.setHeight(d.normalIconSize);
737 setIconSize(icon_size);
739 #if defined(Q_WS_X11) || defined(QPA_XCB)
740 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
741 QSize size = settings.value("size", QSize(690, 510)).toSize();
745 // Work-around for bug #6034: the window ends up in an undetermined
746 // state when trying to restore a maximized window when it is
747 // already maximized.
748 if (!(windowState() & Qt::WindowMaximized))
749 if (!restoreGeometry(settings.value("geometry").toByteArray()))
750 setGeometry(50, 50, 690, 510);
752 // Make sure layout is correctly oriented.
753 setLayoutDirection(qApp->layoutDirection());
755 // Allow the toc and view-source dock widget to be restored if needed.
757 if ((dialog = findOrBuild("toc", true)))
758 // see bug 5082. At least setup title and enabled state.
759 // Visibility will be adjusted by restoreState below.
760 dialog->prepareView();
761 if ((dialog = findOrBuild("view-source", true)))
762 dialog->prepareView();
763 if ((dialog = findOrBuild("progress", true)))
764 dialog->prepareView();
766 if (!restoreState(settings.value("layout").toByteArray(), 0))
769 // init the toolbars that have not been restored
770 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
771 Toolbars::Infos::iterator end = guiApp->toolbars().end();
772 for (; cit != end; ++cit) {
773 GuiToolbar * tb = toolbar(cit->name);
774 if (tb && !tb->isRestored())
775 initToolbar(cit->name);
783 GuiToolbar * GuiView::toolbar(string const & name)
785 ToolbarMap::iterator it = d.toolbars_.find(name);
786 if (it != d.toolbars_.end())
789 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
794 void GuiView::constructToolbars()
796 ToolbarMap::iterator it = d.toolbars_.begin();
797 for (; it != d.toolbars_.end(); ++it)
801 // I don't like doing this here, but the standard toolbar
802 // destroys this object when it's destroyed itself (vfr)
803 d.layout_ = new LayoutBox(*this);
804 d.stack_widget_->addWidget(d.layout_);
805 d.layout_->move(0,0);
807 // extracts the toolbars from the backend
808 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
809 Toolbars::Infos::iterator end = guiApp->toolbars().end();
810 for (; cit != end; ++cit)
811 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
815 void GuiView::initToolbars()
817 // extracts the toolbars from the backend
818 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
819 Toolbars::Infos::iterator end = guiApp->toolbars().end();
820 for (; cit != end; ++cit)
821 initToolbar(cit->name);
825 void GuiView::initToolbar(string const & name)
827 GuiToolbar * tb = toolbar(name);
830 int const visibility = guiApp->toolbars().defaultVisibility(name);
831 bool newline = !(visibility & Toolbars::SAMEROW);
832 tb->setVisible(false);
833 tb->setVisibility(visibility);
835 if (visibility & Toolbars::TOP) {
837 addToolBarBreak(Qt::TopToolBarArea);
838 addToolBar(Qt::TopToolBarArea, tb);
841 if (visibility & Toolbars::BOTTOM) {
843 addToolBarBreak(Qt::BottomToolBarArea);
844 addToolBar(Qt::BottomToolBarArea, tb);
847 if (visibility & Toolbars::LEFT) {
849 addToolBarBreak(Qt::LeftToolBarArea);
850 addToolBar(Qt::LeftToolBarArea, tb);
853 if (visibility & Toolbars::RIGHT) {
855 addToolBarBreak(Qt::RightToolBarArea);
856 addToolBar(Qt::RightToolBarArea, tb);
859 if (visibility & Toolbars::ON)
860 tb->setVisible(true);
864 TocModels & GuiView::tocModels()
866 return d.toc_models_;
870 void GuiView::setFocus()
872 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
873 QMainWindow::setFocus();
877 bool GuiView::hasFocus() const
879 if (currentWorkArea())
880 return currentWorkArea()->hasFocus();
881 if (currentMainWorkArea())
882 return currentMainWorkArea()->hasFocus();
883 return d.bg_widget_->hasFocus();
887 void GuiView::focusInEvent(QFocusEvent * e)
889 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
890 QMainWindow::focusInEvent(e);
891 // Make sure guiApp points to the correct view.
892 guiApp->setCurrentView(this);
893 if (currentWorkArea())
894 currentWorkArea()->setFocus();
895 else if (currentMainWorkArea())
896 currentMainWorkArea()->setFocus();
898 d.bg_widget_->setFocus();
902 QMenu * GuiView::createPopupMenu()
904 return d.toolBarPopup(this);
908 void GuiView::showEvent(QShowEvent * e)
910 LYXERR(Debug::GUI, "Passed Geometry "
911 << size().height() << "x" << size().width()
912 << "+" << pos().x() << "+" << pos().y());
914 if (d.splitter_->count() == 0)
915 // No work area, switch to the background widget.
919 QMainWindow::showEvent(e);
923 bool GuiView::closeScheduled()
930 bool GuiView::prepareAllBuffersForLogout()
932 Buffer * first = theBufferList().first();
936 // First, iterate over all buffers and ask the users if unsaved
937 // changes should be saved.
938 // We cannot use a for loop as the buffer list cycles.
941 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
943 b = theBufferList().next(b);
944 } while (b != first);
946 // Next, save session state
947 // When a view/window was closed before without quitting LyX, there
948 // are already entries in the lastOpened list.
949 theSession().lastOpened().clear();
956 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
957 ** is responsibility of the container (e.g., dialog)
959 void GuiView::closeEvent(QCloseEvent * close_event)
961 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
963 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
964 Alert::warning(_("Exit LyX"),
965 _("LyX could not be closed because documents are being processed by LyX."));
966 close_event->setAccepted(false);
970 // If the user pressed the x (so we didn't call closeView
971 // programmatically), we want to clear all existing entries.
973 theSession().lastOpened().clear();
978 // it can happen that this event arrives without selecting the view,
979 // e.g. when clicking the close button on a background window.
981 if (!closeWorkAreaAll()) {
983 close_event->ignore();
987 // Make sure that nothing will use this to be closed View.
988 guiApp->unregisterView(this);
990 if (isFullScreen()) {
991 // Switch off fullscreen before closing.
996 // Make sure the timer time out will not trigger a statusbar update.
997 d.statusbar_timer_.stop();
999 // Saving fullscreen requires additional tweaks in the toolbar code.
1000 // It wouldn't also work under linux natively.
1001 if (lyxrc.allow_geometry_session) {
1006 close_event->accept();
1010 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1012 if (event->mimeData()->hasUrls())
1014 /// \todo Ask lyx-devel is this is enough:
1015 /// if (event->mimeData()->hasFormat("text/plain"))
1016 /// event->acceptProposedAction();
1020 void GuiView::dropEvent(QDropEvent * event)
1022 QList<QUrl> files = event->mimeData()->urls();
1023 if (files.isEmpty())
1026 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1027 for (int i = 0; i != files.size(); ++i) {
1028 string const file = os::internal_path(fromqstr(
1029 files.at(i).toLocalFile()));
1033 string const ext = support::getExtension(file);
1034 vector<const Format *> found_formats;
1036 // Find all formats that have the correct extension.
1037 vector<const Format *> const & import_formats
1038 = theConverters().importableFormats();
1039 vector<const Format *>::const_iterator it = import_formats.begin();
1040 for (; it != import_formats.end(); ++it)
1041 if ((*it)->hasExtension(ext))
1042 found_formats.push_back(*it);
1045 if (found_formats.size() >= 1) {
1046 if (found_formats.size() > 1) {
1047 //FIXME: show a dialog to choose the correct importable format
1048 LYXERR(Debug::FILES,
1049 "Multiple importable formats found, selecting first");
1051 string const arg = found_formats[0]->name() + " " + file;
1052 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1055 //FIXME: do we have to explicitly check whether it's a lyx file?
1056 LYXERR(Debug::FILES,
1057 "No formats found, trying to open it as a lyx file");
1058 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1060 // add the functions to the queue
1061 guiApp->addToFuncRequestQueue(cmd);
1064 // now process the collected functions. We perform the events
1065 // asynchronously. This prevents potential problems in case the
1066 // BufferView is closed within an event.
1067 guiApp->processFuncRequestQueueAsync();
1071 void GuiView::message(docstring const & str)
1073 if (ForkedProcess::iAmAChild())
1076 // call is moved to GUI-thread by GuiProgress
1077 d.progress_->appendMessage(toqstr(str));
1081 void GuiView::clearMessageText()
1083 message(docstring());
1087 void GuiView::updateStatusBarMessage(QString const & str)
1089 statusBar()->showMessage(str);
1090 d.statusbar_timer_.stop();
1091 d.statusbar_timer_.start(3000);
1095 void GuiView::smallSizedIcons()
1097 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1101 void GuiView::normalSizedIcons()
1103 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1107 void GuiView::bigSizedIcons()
1109 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1113 void GuiView::hugeSizedIcons()
1115 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1119 void GuiView::giantSizedIcons()
1121 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1125 void GuiView::clearMessage()
1127 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1128 // the hasFocus function mostly returns false, even if the focus is on
1129 // a workarea in this view.
1133 d.statusbar_timer_.stop();
1137 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1139 if (wa != d.current_work_area_
1140 || wa->bufferView().buffer().isInternal())
1142 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1143 setWindowIconText(wa->windowIconText());
1144 #if (QT_VERSION >= 0x040400)
1145 // Sets the path for the window: this is used by OSX to
1146 // allow a context click on the title bar showing a menu
1147 // with the path up to the file
1148 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1153 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1155 if (d.current_work_area_)
1156 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1157 this, SLOT(setBusy(bool)));
1159 disconnectBufferView();
1160 connectBufferView(wa->bufferView());
1161 connectBuffer(wa->bufferView().buffer());
1162 d.current_work_area_ = wa;
1163 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1164 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1165 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1166 updateWindowTitle(wa);
1170 // The document settings needs to be reinitialised.
1171 updateDialog("document", "");
1173 // Buffer-dependent dialogs must be updated. This is done here because
1174 // some dialogs require buffer()->text.
1179 void GuiView::on_lastWorkAreaRemoved()
1182 // We already are in a close event. Nothing more to do.
1185 if (d.splitter_->count() > 1)
1186 // We have a splitter so don't close anything.
1189 // Reset and updates the dialogs.
1190 d.toc_models_.reset(0);
1191 updateDialog("document", "");
1194 resetWindowTitleAndIconText();
1197 if (lyxrc.open_buffers_in_tabs)
1198 // Nothing more to do, the window should stay open.
1201 if (guiApp->viewIds().size() > 1) {
1207 // On Mac we also close the last window because the application stay
1208 // resident in memory. On other platforms we don't close the last
1209 // window because this would quit the application.
1215 void GuiView::updateStatusBar()
1217 // let the user see the explicit message
1218 if (d.statusbar_timer_.isActive())
1225 void GuiView::showMessage()
1229 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1230 if (msg.isEmpty()) {
1231 BufferView const * bv = currentBufferView();
1233 msg = toqstr(bv->cursor().currentState());
1235 msg = qt_("Welcome to LyX!");
1237 statusBar()->showMessage(msg);
1241 bool GuiView::event(QEvent * e)
1245 // Useful debug code:
1246 //case QEvent::ActivationChange:
1247 //case QEvent::WindowDeactivate:
1248 //case QEvent::Paint:
1249 //case QEvent::Enter:
1250 //case QEvent::Leave:
1251 //case QEvent::HoverEnter:
1252 //case QEvent::HoverLeave:
1253 //case QEvent::HoverMove:
1254 //case QEvent::StatusTip:
1255 //case QEvent::DragEnter:
1256 //case QEvent::DragLeave:
1257 //case QEvent::Drop:
1260 case QEvent::WindowActivate: {
1261 GuiView * old_view = guiApp->currentView();
1262 if (this == old_view) {
1264 return QMainWindow::event(e);
1266 if (old_view && old_view->currentBufferView()) {
1267 // save current selection to the selection buffer to allow
1268 // middle-button paste in this window.
1269 cap::saveSelection(old_view->currentBufferView()->cursor());
1271 guiApp->setCurrentView(this);
1272 if (d.current_work_area_) {
1273 BufferView & bv = d.current_work_area_->bufferView();
1274 connectBufferView(bv);
1275 connectBuffer(bv.buffer());
1276 // The document structure, name and dialogs might have
1277 // changed in another view.
1279 // The document settings needs to be reinitialised.
1280 updateDialog("document", "");
1283 resetWindowTitleAndIconText();
1286 return QMainWindow::event(e);
1289 case QEvent::ShortcutOverride: {
1291 if (isFullScreen() && menuBar()->isHidden()) {
1292 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1293 // FIXME: we should also try to detect special LyX shortcut such as
1294 // Alt-P and Alt-M. Right now there is a hack in
1295 // GuiWorkArea::processKeySym() that hides again the menubar for
1297 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1299 return QMainWindow::event(e);
1302 return QMainWindow::event(e);
1306 return QMainWindow::event(e);
1310 void GuiView::resetWindowTitleAndIconText()
1312 setWindowTitle(qt_("LyX"));
1313 setWindowIconText(qt_("LyX"));
1316 bool GuiView::focusNextPrevChild(bool /*next*/)
1323 bool GuiView::busy() const
1329 void GuiView::setBusy(bool busy)
1331 bool const busy_before = busy_ > 0;
1332 busy ? ++busy_ : --busy_;
1333 if ((busy_ > 0) == busy_before)
1334 // busy state didn't change
1338 QApplication::setOverrideCursor(Qt::WaitCursor);
1341 QApplication::restoreOverrideCursor();
1346 double GuiView::pixelRatio() const
1348 #if QT_VERSION >= 0x050000
1349 return devicePixelRatio();
1356 GuiWorkArea * GuiView::workArea(int index)
1358 if (TabWorkArea * twa = d.currentTabWorkArea())
1359 if (index < twa->count())
1360 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1365 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1367 if (currentWorkArea()
1368 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1369 return (GuiWorkArea *) currentWorkArea();
1370 if (TabWorkArea * twa = d.currentTabWorkArea())
1371 return twa->workArea(buffer);
1376 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1378 // Automatically create a TabWorkArea if there are none yet.
1379 TabWorkArea * tab_widget = d.splitter_->count()
1380 ? d.currentTabWorkArea() : addTabWorkArea();
1381 return tab_widget->addWorkArea(buffer, *this);
1385 TabWorkArea * GuiView::addTabWorkArea()
1387 TabWorkArea * twa = new TabWorkArea;
1388 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1389 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1390 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1391 this, SLOT(on_lastWorkAreaRemoved()));
1393 d.splitter_->addWidget(twa);
1394 d.stack_widget_->setCurrentWidget(d.splitter_);
1399 GuiWorkArea const * GuiView::currentWorkArea() const
1401 return d.current_work_area_;
1405 GuiWorkArea * GuiView::currentWorkArea()
1407 return d.current_work_area_;
1411 GuiWorkArea const * GuiView::currentMainWorkArea() const
1413 if (!d.currentTabWorkArea())
1415 return d.currentTabWorkArea()->currentWorkArea();
1419 GuiWorkArea * GuiView::currentMainWorkArea()
1421 if (!d.currentTabWorkArea())
1423 return d.currentTabWorkArea()->currentWorkArea();
1427 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1429 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1431 d.current_work_area_ = 0;
1436 // FIXME: I've no clue why this is here and why it accesses
1437 // theGuiApp()->currentView, which might be 0 (bug 6464).
1438 // See also 27525 (vfr).
1439 if (theGuiApp()->currentView() == this
1440 && theGuiApp()->currentView()->currentWorkArea() == wa)
1443 if (currentBufferView())
1444 cap::saveSelection(currentBufferView()->cursor());
1446 theGuiApp()->setCurrentView(this);
1447 d.current_work_area_ = wa;
1449 // We need to reset this now, because it will need to be
1450 // right if the tabWorkArea gets reset in the for loop. We
1451 // will change it back if we aren't in that case.
1452 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1453 d.current_main_work_area_ = wa;
1455 for (int i = 0; i != d.splitter_->count(); ++i) {
1456 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1457 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1458 << ", Current main wa: " << currentMainWorkArea());
1463 d.current_main_work_area_ = old_cmwa;
1465 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1466 on_currentWorkAreaChanged(wa);
1467 BufferView & bv = wa->bufferView();
1468 bv.cursor().fixIfBroken();
1470 wa->setUpdatesEnabled(true);
1471 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1475 void GuiView::removeWorkArea(GuiWorkArea * wa)
1477 LASSERT(wa, return);
1478 if (wa == d.current_work_area_) {
1480 disconnectBufferView();
1481 d.current_work_area_ = 0;
1482 d.current_main_work_area_ = 0;
1485 bool found_twa = false;
1486 for (int i = 0; i != d.splitter_->count(); ++i) {
1487 TabWorkArea * twa = d.tabWorkArea(i);
1488 if (twa->removeWorkArea(wa)) {
1489 // Found in this tab group, and deleted the GuiWorkArea.
1491 if (twa->count() != 0) {
1492 if (d.current_work_area_ == 0)
1493 // This means that we are closing the current GuiWorkArea, so
1494 // switch to the next GuiWorkArea in the found TabWorkArea.
1495 setCurrentWorkArea(twa->currentWorkArea());
1497 // No more WorkAreas in this tab group, so delete it.
1504 // It is not a tabbed work area (i.e., the search work area), so it
1505 // should be deleted by other means.
1506 LASSERT(found_twa, return);
1508 if (d.current_work_area_ == 0) {
1509 if (d.splitter_->count() != 0) {
1510 TabWorkArea * twa = d.currentTabWorkArea();
1511 setCurrentWorkArea(twa->currentWorkArea());
1513 // No more work areas, switch to the background widget.
1514 setCurrentWorkArea(0);
1520 LayoutBox * GuiView::getLayoutDialog() const
1526 void GuiView::updateLayoutList()
1529 d.layout_->updateContents(false);
1533 void GuiView::updateToolbars()
1535 ToolbarMap::iterator end = d.toolbars_.end();
1536 if (d.current_work_area_) {
1538 d.current_work_area_->bufferView().cursor().inMathed()
1539 && !d.current_work_area_->bufferView().cursor().inRegexped();
1541 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1543 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1544 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
1545 bool const mathmacrotemplate =
1546 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1548 lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
1550 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1551 it->second->update(math, table, review, mathmacrotemplate, ipa);
1553 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1554 it->second->update(false, false, false, false, false);
1558 void GuiView::setBuffer(Buffer * newBuffer)
1560 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1561 LASSERT(newBuffer, return);
1563 GuiWorkArea * wa = workArea(*newBuffer);
1566 newBuffer->masterBuffer()->updateBuffer();
1568 wa = addWorkArea(*newBuffer);
1569 // scroll to the position when the BufferView was last closed
1570 if (lyxrc.use_lastfilepos) {
1571 LastFilePosSection::FilePos filepos =
1572 theSession().lastFilePos().load(newBuffer->fileName());
1573 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1576 //Disconnect the old buffer...there's no new one.
1579 connectBuffer(*newBuffer);
1580 connectBufferView(wa->bufferView());
1581 setCurrentWorkArea(wa);
1585 void GuiView::connectBuffer(Buffer & buf)
1587 buf.setGuiDelegate(this);
1591 void GuiView::disconnectBuffer()
1593 if (d.current_work_area_)
1594 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1598 void GuiView::connectBufferView(BufferView & bv)
1600 bv.setGuiDelegate(this);
1604 void GuiView::disconnectBufferView()
1606 if (d.current_work_area_)
1607 d.current_work_area_->bufferView().setGuiDelegate(0);
1611 void GuiView::errors(string const & error_type, bool from_master)
1613 BufferView const * const bv = currentBufferView();
1617 #if EXPORT_in_THREAD
1618 // We are called with from_master == false by default, so we
1619 // have to figure out whether that is the case or not.
1620 ErrorList & el = bv->buffer().errorList(error_type);
1622 el = bv->buffer().masterBuffer()->errorList(error_type);
1626 ErrorList const & el = from_master ?
1627 bv->buffer().masterBuffer()->errorList(error_type) :
1628 bv->buffer().errorList(error_type);
1634 string data = error_type;
1636 data = "from_master|" + error_type;
1637 showDialog("errorlist", data);
1641 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1643 d.toc_models_.updateItem(toqstr(type), dit);
1647 void GuiView::structureChanged()
1649 d.toc_models_.reset(documentBufferView());
1650 // Navigator needs more than a simple update in this case. It needs to be
1652 updateDialog("toc", "");
1656 void GuiView::updateDialog(string const & name, string const & data)
1658 if (!isDialogVisible(name))
1661 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1662 if (it == d.dialogs_.end())
1665 Dialog * const dialog = it->second.get();
1666 if (dialog->isVisibleView())
1667 dialog->initialiseParams(data);
1671 BufferView * GuiView::documentBufferView()
1673 return currentMainWorkArea()
1674 ? ¤tMainWorkArea()->bufferView()
1679 BufferView const * GuiView::documentBufferView() const
1681 return currentMainWorkArea()
1682 ? ¤tMainWorkArea()->bufferView()
1687 BufferView * GuiView::currentBufferView()
1689 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1693 BufferView const * GuiView::currentBufferView() const
1695 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1699 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1700 Buffer const * orig, Buffer * clone)
1702 bool const success = clone->autoSave();
1704 busyBuffers.remove(orig);
1706 ? _("Automatic save done.")
1707 : _("Automatic save failed!");
1711 void GuiView::autoSave()
1713 LYXERR(Debug::INFO, "Running autoSave()");
1715 Buffer * buffer = documentBufferView()
1716 ? &documentBufferView()->buffer() : 0;
1718 resetAutosaveTimers();
1722 GuiViewPrivate::busyBuffers.insert(buffer);
1723 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1724 buffer, buffer->cloneBufferOnly());
1725 d.autosave_watcher_.setFuture(f);
1726 resetAutosaveTimers();
1730 void GuiView::resetAutosaveTimers()
1733 d.autosave_timeout_.restart();
1737 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1740 Buffer * buf = currentBufferView()
1741 ? ¤tBufferView()->buffer() : 0;
1742 Buffer * doc_buffer = documentBufferView()
1743 ? &(documentBufferView()->buffer()) : 0;
1746 /* In LyX/Mac, when a dialog is open, the menus of the
1747 application can still be accessed without giving focus to
1748 the main window. In this case, we want to disable the menu
1749 entries that are buffer-related.
1750 This code must not be used on Linux and Windows, since it
1751 would disable buffer-related entries when hovering over the
1752 menu (see bug #9574).
1754 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1760 // Check whether we need a buffer
1761 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1762 // no, exit directly
1763 flag.message(from_utf8(N_("Command not allowed with"
1764 "out any document open")));
1765 flag.setEnabled(false);
1769 if (cmd.origin() == FuncRequest::TOC) {
1770 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1771 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1772 flag.setEnabled(false);
1776 switch(cmd.action()) {
1777 case LFUN_BUFFER_IMPORT:
1780 case LFUN_MASTER_BUFFER_UPDATE:
1781 case LFUN_MASTER_BUFFER_VIEW:
1783 && (doc_buffer->parent() != 0
1784 || doc_buffer->hasChildren())
1785 && !d.processing_thread_watcher_.isRunning();
1788 case LFUN_BUFFER_UPDATE:
1789 case LFUN_BUFFER_VIEW: {
1790 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1794 string format = to_utf8(cmd.argument());
1795 if (cmd.argument().empty())
1796 format = doc_buffer->params().getDefaultOutputFormat();
1797 enable = doc_buffer->params().isExportableFormat(format);
1801 case LFUN_BUFFER_RELOAD:
1802 enable = doc_buffer && !doc_buffer->isUnnamed()
1803 && doc_buffer->fileName().exists()
1804 && (!doc_buffer->isClean()
1805 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1808 case LFUN_BUFFER_CHILD_OPEN:
1809 enable = doc_buffer;
1812 case LFUN_BUFFER_WRITE:
1813 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1816 //FIXME: This LFUN should be moved to GuiApplication.
1817 case LFUN_BUFFER_WRITE_ALL: {
1818 // We enable the command only if there are some modified buffers
1819 Buffer * first = theBufferList().first();
1824 // We cannot use a for loop as the buffer list is a cycle.
1826 if (!b->isClean()) {
1830 b = theBufferList().next(b);
1831 } while (b != first);
1835 case LFUN_BUFFER_WRITE_AS:
1836 case LFUN_BUFFER_EXPORT_AS:
1837 enable = doc_buffer;
1840 case LFUN_BUFFER_CLOSE:
1841 case LFUN_VIEW_CLOSE:
1842 enable = doc_buffer;
1845 case LFUN_BUFFER_CLOSE_ALL:
1846 enable = theBufferList().last() != theBufferList().first();
1849 case LFUN_VIEW_SPLIT:
1850 if (cmd.getArg(0) == "vertical")
1851 enable = doc_buffer && (d.splitter_->count() == 1 ||
1852 d.splitter_->orientation() == Qt::Vertical);
1854 enable = doc_buffer && (d.splitter_->count() == 1 ||
1855 d.splitter_->orientation() == Qt::Horizontal);
1858 case LFUN_TAB_GROUP_CLOSE:
1859 enable = d.tabWorkAreaCount() > 1;
1862 case LFUN_TOOLBAR_TOGGLE: {
1863 string const name = cmd.getArg(0);
1864 if (GuiToolbar * t = toolbar(name))
1865 flag.setOnOff(t->isVisible());
1868 docstring const msg =
1869 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1875 case LFUN_DROP_LAYOUTS_CHOICE:
1879 case LFUN_UI_TOGGLE:
1880 flag.setOnOff(isFullScreen());
1883 case LFUN_DIALOG_DISCONNECT_INSET:
1886 case LFUN_DIALOG_HIDE:
1887 // FIXME: should we check if the dialog is shown?
1890 case LFUN_DIALOG_TOGGLE:
1891 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1892 // fall through to set "enable"
1893 case LFUN_DIALOG_SHOW: {
1894 string const name = cmd.getArg(0);
1896 enable = name == "aboutlyx"
1897 || name == "file" //FIXME: should be removed.
1899 || name == "texinfo"
1900 || name == "progress"
1901 || name == "compare";
1902 else if (name == "character" || name == "symbols"
1903 || name == "mathdelimiter" || name == "mathmatrix") {
1904 if (!buf || buf->isReadonly())
1907 Cursor const & cur = currentBufferView()->cursor();
1908 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1911 else if (name == "latexlog")
1912 enable = FileName(doc_buffer->logName()).isReadableFile();
1913 else if (name == "spellchecker")
1914 enable = theSpellChecker()
1915 && !doc_buffer->isReadonly()
1916 && !doc_buffer->text().empty();
1917 else if (name == "vclog")
1918 enable = doc_buffer->lyxvc().inUse();
1922 case LFUN_DIALOG_UPDATE: {
1923 string const name = cmd.getArg(0);
1925 enable = name == "prefs";
1929 case LFUN_COMMAND_EXECUTE:
1931 case LFUN_MENU_OPEN:
1932 // Nothing to check.
1935 case LFUN_COMPLETION_INLINE:
1936 if (!d.current_work_area_
1937 || !d.current_work_area_->completer().inlinePossible(
1938 currentBufferView()->cursor()))
1942 case LFUN_COMPLETION_POPUP:
1943 if (!d.current_work_area_
1944 || !d.current_work_area_->completer().popupPossible(
1945 currentBufferView()->cursor()))
1950 if (!d.current_work_area_
1951 || !d.current_work_area_->completer().inlinePossible(
1952 currentBufferView()->cursor()))
1956 case LFUN_COMPLETION_ACCEPT:
1957 if (!d.current_work_area_
1958 || (!d.current_work_area_->completer().popupVisible()
1959 && !d.current_work_area_->completer().inlineVisible()
1960 && !d.current_work_area_->completer().completionAvailable()))
1964 case LFUN_COMPLETION_CANCEL:
1965 if (!d.current_work_area_
1966 || (!d.current_work_area_->completer().popupVisible()
1967 && !d.current_work_area_->completer().inlineVisible()))
1971 case LFUN_BUFFER_ZOOM_OUT:
1972 enable = doc_buffer && lyxrc.zoom > 10;
1975 case LFUN_BUFFER_ZOOM_IN:
1976 enable = doc_buffer;
1979 case LFUN_BUFFER_MOVE_NEXT:
1980 case LFUN_BUFFER_MOVE_PREVIOUS:
1981 // we do not cycle when moving
1982 case LFUN_BUFFER_NEXT:
1983 case LFUN_BUFFER_PREVIOUS:
1984 // because we cycle, it doesn't matter whether on first or last
1985 enable = (d.currentTabWorkArea()->count() > 1);
1987 case LFUN_BUFFER_SWITCH:
1988 // toggle on the current buffer, but do not toggle off
1989 // the other ones (is that a good idea?)
1991 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1992 flag.setOnOff(true);
1995 case LFUN_VC_REGISTER:
1996 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1998 case LFUN_VC_RENAME:
1999 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2002 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2004 case LFUN_VC_CHECK_IN:
2005 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2007 case LFUN_VC_CHECK_OUT:
2008 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2010 case LFUN_VC_LOCKING_TOGGLE:
2011 enable = doc_buffer && !doc_buffer->isReadonly()
2012 && doc_buffer->lyxvc().lockingToggleEnabled();
2013 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2015 case LFUN_VC_REVERT:
2016 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2018 case LFUN_VC_UNDO_LAST:
2019 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2021 case LFUN_VC_REPO_UPDATE:
2022 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2024 case LFUN_VC_COMMAND: {
2025 if (cmd.argument().empty())
2027 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2031 case LFUN_VC_COMPARE:
2032 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2035 case LFUN_SERVER_GOTO_FILE_ROW:
2037 case LFUN_FORWARD_SEARCH:
2038 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2041 case LFUN_FILE_INSERT_PLAINTEXT:
2042 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2043 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2046 case LFUN_SPELLING_CONTINUOUSLY:
2047 flag.setOnOff(lyxrc.spellcheck_continuously);
2055 flag.setEnabled(false);
2061 static FileName selectTemplateFile()
2063 FileDialog dlg(qt_("Select template file"));
2064 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2065 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2067 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2068 QStringList(qt_("LyX Documents (*.lyx)")));
2070 if (result.first == FileDialog::Later)
2072 if (result.second.isEmpty())
2074 return FileName(fromqstr(result.second));
2078 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2082 Buffer * newBuffer = 0;
2084 newBuffer = checkAndLoadLyXFile(filename);
2085 } catch (ExceptionMessage const & e) {
2092 message(_("Document not loaded."));
2096 setBuffer(newBuffer);
2097 newBuffer->errors("Parse");
2100 theSession().lastFiles().add(filename);
2106 void GuiView::openDocument(string const & fname)
2108 string initpath = lyxrc.document_path;
2110 if (documentBufferView()) {
2111 string const trypath = documentBufferView()->buffer().filePath();
2112 // If directory is writeable, use this as default.
2113 if (FileName(trypath).isDirWritable())
2119 if (fname.empty()) {
2120 FileDialog dlg(qt_("Select document to open"));
2121 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2122 dlg.setButton2(qt_("Examples|#E#e"),
2123 toqstr(addPath(package().system_support().absFileName(), "examples")));
2125 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2126 FileDialog::Result result =
2127 dlg.open(toqstr(initpath), filter);
2129 if (result.first == FileDialog::Later)
2132 filename = fromqstr(result.second);
2134 // check selected filename
2135 if (filename.empty()) {
2136 message(_("Canceled."));
2142 // get absolute path of file and add ".lyx" to the filename if
2144 FileName const fullname =
2145 fileSearch(string(), filename, "lyx", support::may_not_exist);
2146 if (!fullname.empty())
2147 filename = fullname.absFileName();
2149 if (!fullname.onlyPath().isDirectory()) {
2150 Alert::warning(_("Invalid filename"),
2151 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2152 from_utf8(fullname.absFileName())));
2156 // if the file doesn't exist and isn't already open (bug 6645),
2157 // let the user create one
2158 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2159 !LyXVC::file_not_found_hook(fullname)) {
2160 // the user specifically chose this name. Believe him.
2161 Buffer * const b = newFile(filename, string(), true);
2167 docstring const disp_fn = makeDisplayPath(filename);
2168 message(bformat(_("Opening document %1$s..."), disp_fn));
2171 Buffer * buf = loadDocument(fullname);
2173 str2 = bformat(_("Document %1$s opened."), disp_fn);
2174 if (buf->lyxvc().inUse())
2175 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2176 " " + _("Version control detected.");
2178 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2183 // FIXME: clean that
2184 static bool import(GuiView * lv, FileName const & filename,
2185 string const & format, ErrorList & errorList)
2187 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2189 string loader_format;
2190 vector<string> loaders = theConverters().loaders();
2191 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2192 vector<string>::const_iterator it = loaders.begin();
2193 vector<string>::const_iterator en = loaders.end();
2194 for (; it != en; ++it) {
2195 if (!theConverters().isReachable(format, *it))
2198 string const tofile =
2199 support::changeExtension(filename.absFileName(),
2200 formats.extension(*it));
2201 if (!theConverters().convert(0, filename, FileName(tofile),
2202 filename, format, *it, errorList))
2204 loader_format = *it;
2207 if (loader_format.empty()) {
2208 frontend::Alert::error(_("Couldn't import file"),
2209 bformat(_("No information for importing the format %1$s."),
2210 formats.prettyName(format)));
2214 loader_format = format;
2216 if (loader_format == "lyx") {
2217 Buffer * buf = lv->loadDocument(lyxfile);
2221 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2225 bool as_paragraphs = loader_format == "textparagraph";
2226 string filename2 = (loader_format == format) ? filename.absFileName()
2227 : support::changeExtension(filename.absFileName(),
2228 formats.extension(loader_format));
2229 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2231 guiApp->setCurrentView(lv);
2232 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2239 void GuiView::importDocument(string const & argument)
2242 string filename = split(argument, format, ' ');
2244 LYXERR(Debug::INFO, format << " file: " << filename);
2246 // need user interaction
2247 if (filename.empty()) {
2248 string initpath = lyxrc.document_path;
2249 if (documentBufferView()) {
2250 string const trypath = documentBufferView()->buffer().filePath();
2251 // If directory is writeable, use this as default.
2252 if (FileName(trypath).isDirWritable())
2256 docstring const text = bformat(_("Select %1$s file to import"),
2257 formats.prettyName(format));
2259 FileDialog dlg(toqstr(text));
2260 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2261 dlg.setButton2(qt_("Examples|#E#e"),
2262 toqstr(addPath(package().system_support().absFileName(), "examples")));
2264 docstring filter = formats.prettyName(format);
2267 filter += from_utf8(formats.extensions(format));
2270 FileDialog::Result result =
2271 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2273 if (result.first == FileDialog::Later)
2276 filename = fromqstr(result.second);
2278 // check selected filename
2279 if (filename.empty())
2280 message(_("Canceled."));
2283 if (filename.empty())
2286 // get absolute path of file
2287 FileName const fullname(support::makeAbsPath(filename));
2289 // Can happen if the user entered a path into the dialog
2291 if (fullname.onlyFileName().empty()) {
2292 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2293 "Aborting import."),
2294 from_utf8(fullname.absFileName()));
2295 frontend::Alert::error(_("File name error"), msg);
2296 message(_("Canceled."));
2301 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2303 // Check if the document already is open
2304 Buffer * buf = theBufferList().getBuffer(lyxfile);
2307 if (!closeBuffer()) {
2308 message(_("Canceled."));
2313 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2315 // if the file exists already, and we didn't do
2316 // -i lyx thefile.lyx, warn
2317 if (lyxfile.exists() && fullname != lyxfile) {
2319 docstring text = bformat(_("The document %1$s already exists.\n\n"
2320 "Do you want to overwrite that document?"), displaypath);
2321 int const ret = Alert::prompt(_("Overwrite document?"),
2322 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2325 message(_("Canceled."));
2330 message(bformat(_("Importing %1$s..."), displaypath));
2331 ErrorList errorList;
2332 if (import(this, fullname, format, errorList))
2333 message(_("imported."));
2335 message(_("file not imported!"));
2337 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2341 void GuiView::newDocument(string const & filename, bool from_template)
2343 FileName initpath(lyxrc.document_path);
2344 if (documentBufferView()) {
2345 FileName const trypath(documentBufferView()->buffer().filePath());
2346 // If directory is writeable, use this as default.
2347 if (trypath.isDirWritable())
2351 string templatefile;
2352 if (from_template) {
2353 templatefile = selectTemplateFile().absFileName();
2354 if (templatefile.empty())
2359 if (filename.empty())
2360 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2362 b = newFile(filename, templatefile, true);
2367 // If no new document could be created, it is unsure
2368 // whether there is a valid BufferView.
2369 if (currentBufferView())
2370 // Ensure the cursor is correctly positioned on screen.
2371 currentBufferView()->showCursor();
2375 void GuiView::insertLyXFile(docstring const & fname)
2377 BufferView * bv = documentBufferView();
2382 FileName filename(to_utf8(fname));
2383 if (filename.empty()) {
2384 // Launch a file browser
2386 string initpath = lyxrc.document_path;
2387 string const trypath = bv->buffer().filePath();
2388 // If directory is writeable, use this as default.
2389 if (FileName(trypath).isDirWritable())
2393 FileDialog dlg(qt_("Select LyX document to insert"));
2394 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2395 dlg.setButton2(qt_("Examples|#E#e"),
2396 toqstr(addPath(package().system_support().absFileName(),
2399 FileDialog::Result result = dlg.open(toqstr(initpath),
2400 QStringList(qt_("LyX Documents (*.lyx)")));
2402 if (result.first == FileDialog::Later)
2406 filename.set(fromqstr(result.second));
2408 // check selected filename
2409 if (filename.empty()) {
2410 // emit message signal.
2411 message(_("Canceled."));
2416 bv->insertLyXFile(filename);
2417 bv->buffer().errors("Parse");
2421 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2423 FileName fname = b.fileName();
2424 FileName const oldname = fname;
2426 if (!newname.empty()) {
2428 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2430 // Switch to this Buffer.
2433 // No argument? Ask user through dialog.
2435 FileDialog dlg(qt_("Choose a filename to save document as"));
2436 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2437 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2439 if (!isLyXFileName(fname.absFileName()))
2440 fname.changeExtension(".lyx");
2442 FileDialog::Result result =
2443 dlg.save(toqstr(fname.onlyPath().absFileName()),
2444 QStringList(qt_("LyX Documents (*.lyx)")),
2445 toqstr(fname.onlyFileName()));
2447 if (result.first == FileDialog::Later)
2450 fname.set(fromqstr(result.second));
2455 if (!isLyXFileName(fname.absFileName()))
2456 fname.changeExtension(".lyx");
2459 // fname is now the new Buffer location.
2461 // if there is already a Buffer open with this name, we do not want
2462 // to have another one. (the second test makes sure we're not just
2463 // trying to overwrite ourselves, which is fine.)
2464 if (theBufferList().exists(fname) && fname != oldname
2465 && theBufferList().getBuffer(fname) != &b) {
2466 docstring const text =
2467 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2468 "Please close it before attempting to overwrite it.\n"
2469 "Do you want to choose a new filename?"),
2470 from_utf8(fname.absFileName()));
2471 int const ret = Alert::prompt(_("Chosen File Already Open"),
2472 text, 0, 1, _("&Rename"), _("&Cancel"));
2474 case 0: return renameBuffer(b, docstring(), kind);
2475 case 1: return false;
2480 bool const existsLocal = fname.exists();
2481 bool const existsInVC = LyXVC::fileInVC(fname);
2482 if (existsLocal || existsInVC) {
2483 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2484 if (kind != LV_WRITE_AS && existsInVC) {
2485 // renaming to a name that is already in VC
2487 docstring text = bformat(_("The document %1$s "
2488 "is already registered.\n\n"
2489 "Do you want to choose a new name?"),
2491 docstring const title = (kind == LV_VC_RENAME) ?
2492 _("Rename document?") : _("Copy document?");
2493 docstring const button = (kind == LV_VC_RENAME) ?
2494 _("&Rename") : _("&Copy");
2495 int const ret = Alert::prompt(title, text, 0, 1,
2496 button, _("&Cancel"));
2498 case 0: return renameBuffer(b, docstring(), kind);
2499 case 1: return false;
2504 docstring text = bformat(_("The document %1$s "
2505 "already exists.\n\n"
2506 "Do you want to overwrite that document?"),
2508 int const ret = Alert::prompt(_("Overwrite document?"),
2509 text, 0, 2, _("&Overwrite"),
2510 _("&Rename"), _("&Cancel"));
2513 case 1: return renameBuffer(b, docstring(), kind);
2514 case 2: return false;
2520 case LV_VC_RENAME: {
2521 string msg = b.lyxvc().rename(fname);
2524 message(from_utf8(msg));
2528 string msg = b.lyxvc().copy(fname);
2531 message(from_utf8(msg));
2537 // LyXVC created the file already in case of LV_VC_RENAME or
2538 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2539 // relative paths of included stuff right if we moved e.g. from
2540 // /a/b.lyx to /a/c/b.lyx.
2542 bool const saved = saveBuffer(b, fname);
2549 struct PrettyNameComparator
2551 bool operator()(Format const *first, Format const *second) const {
2552 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2553 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2558 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2560 FileName fname = b.fileName();
2562 FileDialog dlg(qt_("Choose a filename to export the document as"));
2563 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2566 QString const anyformat = qt_("Guess from extension (*.*)");
2568 Formats::const_iterator it = formats.begin();
2569 vector<Format const *> export_formats;
2570 for (; it != formats.end(); ++it)
2571 if (it->documentFormat())
2572 export_formats.push_back(&(*it));
2573 PrettyNameComparator cmp;
2574 sort(export_formats.begin(), export_formats.end(), cmp);
2575 vector<Format const *>::const_iterator fit = export_formats.begin();
2576 map<QString, string> fmap;
2579 for (; fit != export_formats.end(); ++fit) {
2580 docstring const loc_prettyname =
2581 translateIfPossible(from_utf8((*fit)->prettyname()));
2582 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2584 from_ascii((*fit)->extension())));
2585 types << loc_filter;
2586 fmap[loc_filter] = (*fit)->name();
2587 if (from_ascii((*fit)->name()) == iformat) {
2588 filter = loc_filter;
2589 ext = (*fit)->extension();
2592 string ofname = fname.onlyFileName();
2594 ofname = support::changeExtension(ofname, ext);
2595 FileDialog::Result result =
2596 dlg.save(toqstr(fname.onlyPath().absFileName()),
2600 if (result.first != FileDialog::Chosen)
2604 fname.set(fromqstr(result.second));
2605 if (filter == anyformat)
2606 fmt_name = formats.getFormatFromExtension(fname.extension());
2608 fmt_name = fmap[filter];
2609 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2610 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2612 if (fmt_name.empty() || fname.empty())
2615 // fname is now the new Buffer location.
2616 if (FileName(fname).exists()) {
2617 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2618 docstring text = bformat(_("The document %1$s already "
2619 "exists.\n\nDo you want to "
2620 "overwrite that document?"),
2622 int const ret = Alert::prompt(_("Overwrite document?"),
2623 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2626 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2627 case 2: return false;
2631 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2634 return dr.dispatched();
2638 bool GuiView::saveBuffer(Buffer & b)
2640 return saveBuffer(b, FileName());
2644 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2646 if (workArea(b) && workArea(b)->inDialogMode())
2649 if (fn.empty() && b.isUnnamed())
2650 return renameBuffer(b, docstring());
2652 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2654 theSession().lastFiles().add(b.fileName());
2658 // Switch to this Buffer.
2661 // FIXME: we don't tell the user *WHY* the save failed !!
2662 docstring const file = makeDisplayPath(b.absFileName(), 30);
2663 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2664 "Do you want to rename the document and "
2665 "try again?"), file);
2666 int const ret = Alert::prompt(_("Rename and save?"),
2667 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2670 if (!renameBuffer(b, docstring()))
2679 return saveBuffer(b, fn);
2683 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2685 return closeWorkArea(wa, false);
2689 // We only want to close the buffer if it is not visible in other workareas
2690 // of the same view, nor in other views, and if this is not a child
2691 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2693 Buffer & buf = wa->bufferView().buffer();
2695 bool last_wa = d.countWorkAreasOf(buf) == 1
2696 && !inOtherView(buf) && !buf.parent();
2698 bool close_buffer = last_wa;
2701 if (lyxrc.close_buffer_with_last_view == "yes")
2703 else if (lyxrc.close_buffer_with_last_view == "no")
2704 close_buffer = false;
2707 if (buf.isUnnamed())
2708 file = from_utf8(buf.fileName().onlyFileName());
2710 file = buf.fileName().displayName(30);
2711 docstring const text = bformat(
2712 _("Last view on document %1$s is being closed.\n"
2713 "Would you like to close or hide the document?\n"
2715 "Hidden documents can be displayed back through\n"
2716 "the menu: View->Hidden->...\n"
2718 "To remove this question, set your preference in:\n"
2719 " Tools->Preferences->Look&Feel->UserInterface\n"
2721 int ret = Alert::prompt(_("Close or hide document?"),
2722 text, 0, 1, _("&Close"), _("&Hide"));
2723 close_buffer = (ret == 0);
2727 return closeWorkArea(wa, close_buffer);
2731 bool GuiView::closeBuffer()
2733 GuiWorkArea * wa = currentMainWorkArea();
2734 setCurrentWorkArea(wa);
2735 Buffer & buf = wa->bufferView().buffer();
2736 return wa && closeWorkArea(wa, !buf.parent());
2740 void GuiView::writeSession() const {
2741 GuiWorkArea const * active_wa = currentMainWorkArea();
2742 for (int i = 0; i < d.splitter_->count(); ++i) {
2743 TabWorkArea * twa = d.tabWorkArea(i);
2744 for (int j = 0; j < twa->count(); ++j) {
2745 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2746 Buffer & buf = wa->bufferView().buffer();
2747 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2753 bool GuiView::closeBufferAll()
2755 // Close the workareas in all other views
2756 QList<int> const ids = guiApp->viewIds();
2757 for (int i = 0; i != ids.size(); ++i) {
2758 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2762 // Close our own workareas
2763 if (!closeWorkAreaAll())
2766 // Now close the hidden buffers. We prevent hidden buffers from being
2767 // dirty, so we can just close them.
2768 theBufferList().closeAll();
2773 bool GuiView::closeWorkAreaAll()
2775 setCurrentWorkArea(currentMainWorkArea());
2777 // We might be in a situation that there is still a tabWorkArea, but
2778 // there are no tabs anymore. This can happen when we get here after a
2779 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2780 // many TabWorkArea's have no documents anymore.
2783 // We have to call count() each time, because it can happen that
2784 // more than one splitter will disappear in one iteration (bug 5998).
2785 for (; d.splitter_->count() > empty_twa; ) {
2786 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2788 if (twa->count() == 0)
2791 setCurrentWorkArea(twa->currentWorkArea());
2792 if (!closeTabWorkArea(twa))
2800 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2805 Buffer & buf = wa->bufferView().buffer();
2807 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2808 Alert::warning(_("Close document"),
2809 _("Document could not be closed because it is being processed by LyX."));
2814 return closeBuffer(buf);
2816 if (!inMultiTabs(wa))
2817 if (!saveBufferIfNeeded(buf, true))
2825 bool GuiView::closeBuffer(Buffer & buf)
2827 // If we are in a close_event all children will be closed in some time,
2828 // so no need to do it here. This will ensure that the children end up
2829 // in the session file in the correct order. If we close the master
2830 // buffer, we can close or release the child buffers here too.
2831 bool success = true;
2833 ListOfBuffers clist = buf.getChildren();
2834 ListOfBuffers::const_iterator it = clist.begin();
2835 ListOfBuffers::const_iterator const bend = clist.end();
2836 for (; it != bend; ++it) {
2837 // If a child is dirty, do not close
2838 // without user intervention
2839 //FIXME: should we look in other tabworkareas?
2840 Buffer * child_buf = *it;
2841 GuiWorkArea * child_wa = workArea(*child_buf);
2843 if (!closeWorkArea(child_wa, true)) {
2848 theBufferList().releaseChild(&buf, child_buf);
2852 // goto bookmark to update bookmark pit.
2853 //FIXME: we should update only the bookmarks related to this buffer!
2854 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2855 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2856 guiApp->gotoBookmark(i+1, false, false);
2858 if (saveBufferIfNeeded(buf, false)) {
2859 buf.removeAutosaveFile();
2860 theBufferList().release(&buf);
2864 // open all children again to avoid a crash because of dangling
2865 // pointers (bug 6603)
2871 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2873 while (twa == d.currentTabWorkArea()) {
2874 twa->setCurrentIndex(twa->count()-1);
2876 GuiWorkArea * wa = twa->currentWorkArea();
2877 Buffer & b = wa->bufferView().buffer();
2879 // We only want to close the buffer if the same buffer is not visible
2880 // in another view, and if this is not a child and if we are closing
2881 // a view (not a tabgroup).
2882 bool const close_buffer =
2883 !inOtherView(b) && !b.parent() && closing_;
2885 if (!closeWorkArea(wa, close_buffer))
2892 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2894 if (buf.isClean() || buf.paragraphs().empty())
2897 // Switch to this Buffer.
2902 if (buf.isUnnamed())
2903 file = from_utf8(buf.fileName().onlyFileName());
2905 file = buf.fileName().displayName(30);
2907 // Bring this window to top before asking questions.
2912 if (hiding && buf.isUnnamed()) {
2913 docstring const text = bformat(_("The document %1$s has not been "
2914 "saved yet.\n\nDo you want to save "
2915 "the document?"), file);
2916 ret = Alert::prompt(_("Save new document?"),
2917 text, 0, 1, _("&Save"), _("&Cancel"));
2921 docstring const text = bformat(_("The document %1$s has unsaved changes."
2922 "\n\nDo you want to save the document or discard the changes?"), file);
2923 ret = Alert::prompt(_("Save changed document?"),
2924 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2929 if (!saveBuffer(buf))
2933 // If we crash after this we could have no autosave file
2934 // but I guess this is really improbable (Jug).
2935 // Sometimes improbable things happen:
2936 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2937 // buf.removeAutosaveFile();
2939 // revert all changes
2950 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2952 Buffer & buf = wa->bufferView().buffer();
2954 for (int i = 0; i != d.splitter_->count(); ++i) {
2955 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2956 if (wa_ && wa_ != wa)
2959 return inOtherView(buf);
2963 bool GuiView::inOtherView(Buffer & buf)
2965 QList<int> const ids = guiApp->viewIds();
2967 for (int i = 0; i != ids.size(); ++i) {
2971 if (guiApp->view(ids[i]).workArea(buf))
2978 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2980 if (!documentBufferView())
2983 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2984 Buffer * const curbuf = &documentBufferView()->buffer();
2985 int nwa = twa->count();
2986 for (int i = 0; i < nwa; ++i) {
2987 if (&workArea(i)->bufferView().buffer() == curbuf) {
2989 if (np == NEXTBUFFER)
2990 next_index = (i == nwa - 1 ? 0 : i + 1);
2992 next_index = (i == 0 ? nwa - 1 : i - 1);
2994 twa->moveTab(i, next_index);
2996 setBuffer(&workArea(next_index)->bufferView().buffer());
3004 /// make sure the document is saved
3005 static bool ensureBufferClean(Buffer * buffer)
3007 LASSERT(buffer, return false);
3008 if (buffer->isClean() && !buffer->isUnnamed())
3011 docstring const file = buffer->fileName().displayName(30);
3014 if (!buffer->isUnnamed()) {
3015 text = bformat(_("The document %1$s has unsaved "
3016 "changes.\n\nDo you want to save "
3017 "the document?"), file);
3018 title = _("Save changed document?");
3021 text = bformat(_("The document %1$s has not been "
3022 "saved yet.\n\nDo you want to save "
3023 "the document?"), file);
3024 title = _("Save new document?");
3026 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3029 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3031 return buffer->isClean() && !buffer->isUnnamed();
3035 bool GuiView::reloadBuffer(Buffer & buf)
3037 Buffer::ReadStatus status = buf.reload();
3038 return status == Buffer::ReadSuccess;
3042 void GuiView::checkExternallyModifiedBuffers()
3044 BufferList::iterator bit = theBufferList().begin();
3045 BufferList::iterator const bend = theBufferList().end();
3046 for (; bit != bend; ++bit) {
3047 Buffer * buf = *bit;
3048 if (buf->fileName().exists()
3049 && buf->isExternallyModified(Buffer::checksum_method)) {
3050 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3051 " Reload now? Any local changes will be lost."),
3052 from_utf8(buf->absFileName()));
3053 int const ret = Alert::prompt(_("Reload externally changed document?"),
3054 text, 0, 1, _("&Reload"), _("&Cancel"));
3062 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3064 Buffer * buffer = documentBufferView()
3065 ? &(documentBufferView()->buffer()) : 0;
3067 switch (cmd.action()) {
3068 case LFUN_VC_REGISTER:
3069 if (!buffer || !ensureBufferClean(buffer))
3071 if (!buffer->lyxvc().inUse()) {
3072 if (buffer->lyxvc().registrer()) {
3073 reloadBuffer(*buffer);
3074 dr.clearMessageUpdate();
3079 case LFUN_VC_RENAME:
3080 case LFUN_VC_COPY: {
3081 if (!buffer || !ensureBufferClean(buffer))
3083 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3084 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3085 // Some changes are not yet committed.
3086 // We test here and not in getStatus(), since
3087 // this test is expensive.
3089 LyXVC::CommandResult ret =
3090 buffer->lyxvc().checkIn(log);
3092 if (ret == LyXVC::ErrorCommand ||
3093 ret == LyXVC::VCSuccess)
3094 reloadBuffer(*buffer);
3095 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3096 frontend::Alert::error(
3097 _("Revision control error."),
3098 _("Document could not be checked in."));
3102 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3103 LV_VC_RENAME : LV_VC_COPY;
3104 renameBuffer(*buffer, cmd.argument(), kind);
3109 case LFUN_VC_CHECK_IN:
3110 if (!buffer || !ensureBufferClean(buffer))
3112 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3114 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3116 // Only skip reloading if the checkin was cancelled or
3117 // an error occurred before the real checkin VCS command
3118 // was executed, since the VCS might have changed the
3119 // file even if it could not checkin successfully.
3120 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3121 reloadBuffer(*buffer);
3125 case LFUN_VC_CHECK_OUT:
3126 if (!buffer || !ensureBufferClean(buffer))
3128 if (buffer->lyxvc().inUse()) {
3129 dr.setMessage(buffer->lyxvc().checkOut());
3130 reloadBuffer(*buffer);
3134 case LFUN_VC_LOCKING_TOGGLE:
3135 LASSERT(buffer, return);
3136 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3138 if (buffer->lyxvc().inUse()) {
3139 string res = buffer->lyxvc().lockingToggle();
3141 frontend::Alert::error(_("Revision control error."),
3142 _("Error when setting the locking property."));
3145 reloadBuffer(*buffer);
3150 case LFUN_VC_REVERT:
3151 LASSERT(buffer, return);
3152 if (buffer->lyxvc().revert()) {
3153 reloadBuffer(*buffer);
3154 dr.clearMessageUpdate();
3158 case LFUN_VC_UNDO_LAST:
3159 LASSERT(buffer, return);
3160 buffer->lyxvc().undoLast();
3161 reloadBuffer(*buffer);
3162 dr.clearMessageUpdate();
3165 case LFUN_VC_REPO_UPDATE:
3166 LASSERT(buffer, return);
3167 if (ensureBufferClean(buffer)) {
3168 dr.setMessage(buffer->lyxvc().repoUpdate());
3169 checkExternallyModifiedBuffers();
3173 case LFUN_VC_COMMAND: {
3174 string flag = cmd.getArg(0);
3175 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3178 if (contains(flag, 'M')) {
3179 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3182 string path = cmd.getArg(1);
3183 if (contains(path, "$$p") && buffer)
3184 path = subst(path, "$$p", buffer->filePath());
3185 LYXERR(Debug::LYXVC, "Directory: " << path);
3187 if (!pp.isReadableDirectory()) {
3188 lyxerr << _("Directory is not accessible.") << endl;
3191 support::PathChanger p(pp);
3193 string command = cmd.getArg(2);
3194 if (command.empty())
3197 command = subst(command, "$$i", buffer->absFileName());
3198 command = subst(command, "$$p", buffer->filePath());
3200 command = subst(command, "$$m", to_utf8(message));
3201 LYXERR(Debug::LYXVC, "Command: " << command);
3203 one.startscript(Systemcall::Wait, command);
3207 if (contains(flag, 'I'))
3208 buffer->markDirty();
3209 if (contains(flag, 'R'))
3210 reloadBuffer(*buffer);
3215 case LFUN_VC_COMPARE: {
3217 if (cmd.argument().empty()) {
3218 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3222 string rev1 = cmd.getArg(0);
3226 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3229 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3230 f2 = buffer->absFileName();
3232 string rev2 = cmd.getArg(1);
3236 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3240 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3241 f1 << "\n" << f2 << "\n" );
3242 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3243 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3253 void GuiView::openChildDocument(string const & fname)
3255 LASSERT(documentBufferView(), return);
3256 Buffer & buffer = documentBufferView()->buffer();
3257 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3258 documentBufferView()->saveBookmark(false);
3260 if (theBufferList().exists(filename)) {
3261 child = theBufferList().getBuffer(filename);
3264 message(bformat(_("Opening child document %1$s..."),
3265 makeDisplayPath(filename.absFileName())));
3266 child = loadDocument(filename, false);
3268 // Set the parent name of the child document.
3269 // This makes insertion of citations and references in the child work,
3270 // when the target is in the parent or another child document.
3272 child->setParent(&buffer);
3276 bool GuiView::goToFileRow(string const & argument)
3280 size_t i = argument.find_last_of(' ');
3281 if (i != string::npos) {
3282 file_name = os::internal_path(trim(argument.substr(0, i)));
3283 istringstream is(argument.substr(i + 1));
3288 if (i == string::npos) {
3289 LYXERR0("Wrong argument: " << argument);
3293 string const abstmp = package().temp_dir().absFileName();
3294 string const realtmp = package().temp_dir().realPath();
3295 // We have to use os::path_prefix_is() here, instead of
3296 // simply prefixIs(), because the file name comes from
3297 // an external application and may need case adjustment.
3298 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3299 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3300 // Needed by inverse dvi search. If it is a file
3301 // in tmpdir, call the apropriated function.
3302 // If tmpdir is a symlink, we may have the real
3303 // path passed back, so we correct for that.
3304 if (!prefixIs(file_name, abstmp))
3305 file_name = subst(file_name, realtmp, abstmp);
3306 buf = theBufferList().getBufferFromTmp(file_name);
3308 // Must replace extension of the file to be .lyx
3309 // and get full path
3310 FileName const s = fileSearch(string(),
3311 support::changeExtension(file_name, ".lyx"), "lyx");
3312 // Either change buffer or load the file
3313 if (theBufferList().exists(s))
3314 buf = theBufferList().getBuffer(s);
3315 else if (s.exists()) {
3316 buf = loadDocument(s);
3321 _("File does not exist: %1$s"),
3322 makeDisplayPath(file_name)));
3328 _("No buffer for file: %1$s."),
3329 makeDisplayPath(file_name))
3334 documentBufferView()->setCursorFromRow(row);
3340 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3342 Buffer::ExportStatus const status = func(format);
3344 // the cloning operation will have produced a clone of the entire set of
3345 // documents, starting from the master. so we must delete those.
3346 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3348 busyBuffers.remove(orig);
3353 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3355 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3356 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3360 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3362 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3363 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3367 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3369 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3370 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3374 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3375 string const & argument,
3376 Buffer const * used_buffer,
3377 docstring const & msg,
3378 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3379 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3380 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3385 string format = argument;
3387 format = used_buffer->params().getDefaultOutputFormat();
3388 processing_format = format;
3390 progress_->clearMessages();
3393 #if EXPORT_in_THREAD
3394 GuiViewPrivate::busyBuffers.insert(used_buffer);
3395 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3396 if (!cloned_buffer) {
3397 Alert::error(_("Export Error"),
3398 _("Error cloning the Buffer."));
3401 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3406 setPreviewFuture(f);
3407 last_export_format = used_buffer->params().bufferFormat();
3410 // We are asynchronous, so we don't know here anything about the success
3413 Buffer::ExportStatus status;
3415 // TODO check here if it breaks exporting with Qt < 4.4
3416 status = (used_buffer->*syncFunc)(format, true);
3417 } else if (previewFunc) {
3418 status = (used_buffer->*previewFunc)(format);
3421 handleExportStatus(gv_, status, format);
3423 return (status == Buffer::ExportSuccess
3424 || status == Buffer::PreviewSuccess);
3428 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3430 BufferView * bv = currentBufferView();
3431 LASSERT(bv, return);
3433 // Let the current BufferView dispatch its own actions.
3434 bv->dispatch(cmd, dr);
3435 if (dr.dispatched())
3438 // Try with the document BufferView dispatch if any.
3439 BufferView * doc_bv = documentBufferView();
3440 if (doc_bv && doc_bv != bv) {
3441 doc_bv->dispatch(cmd, dr);
3442 if (dr.dispatched())
3446 // Then let the current Cursor dispatch its own actions.
3447 bv->cursor().dispatch(cmd);
3449 // update completion. We do it here and not in
3450 // processKeySym to avoid another redraw just for a
3451 // changed inline completion
3452 if (cmd.origin() == FuncRequest::KEYBOARD) {
3453 if (cmd.action() == LFUN_SELF_INSERT
3454 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3455 updateCompletion(bv->cursor(), true, true);
3456 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3457 updateCompletion(bv->cursor(), false, true);
3459 updateCompletion(bv->cursor(), false, false);
3462 dr = bv->cursor().result();
3466 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3468 BufferView * bv = currentBufferView();
3469 // By default we won't need any update.
3470 dr.screenUpdate(Update::None);
3471 // assume cmd will be dispatched
3472 dr.dispatched(true);
3474 Buffer * doc_buffer = documentBufferView()
3475 ? &(documentBufferView()->buffer()) : 0;
3477 if (cmd.origin() == FuncRequest::TOC) {
3478 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3479 // FIXME: do we need to pass a DispatchResult object here?
3480 toc->doDispatch(bv->cursor(), cmd);
3484 string const argument = to_utf8(cmd.argument());
3486 switch(cmd.action()) {
3487 case LFUN_BUFFER_CHILD_OPEN:
3488 openChildDocument(to_utf8(cmd.argument()));
3491 case LFUN_BUFFER_IMPORT:
3492 importDocument(to_utf8(cmd.argument()));
3495 case LFUN_BUFFER_EXPORT: {
3498 FileName target_dir = doc_buffer->fileName().onlyPath();
3499 string const dest = cmd.getArg(1);
3500 if (!dest.empty() && FileName::isAbsolute(dest))
3501 target_dir = FileName(support::onlyPath(dest));
3502 // GCC only sees strfwd.h when building merged
3503 if (::lyx::operator==(cmd.argument(), "custom")) {
3504 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3507 if (!target_dir.isDirWritable()) {
3508 exportBufferAs(*doc_buffer, cmd.argument());
3511 /* TODO/Review: Is it a problem to also export the children?
3512 See the update_unincluded flag */
3513 d.asyncBufferProcessing(argument,
3516 &GuiViewPrivate::exportAndDestroy,
3519 // TODO Inform user about success
3523 case LFUN_BUFFER_EXPORT_AS: {
3524 LASSERT(doc_buffer, break);
3525 docstring f = cmd.argument();
3527 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3528 exportBufferAs(*doc_buffer, f);
3532 case LFUN_BUFFER_UPDATE: {
3533 d.asyncBufferProcessing(argument,
3536 &GuiViewPrivate::compileAndDestroy,
3541 case LFUN_BUFFER_VIEW: {
3542 d.asyncBufferProcessing(argument,
3544 _("Previewing ..."),
3545 &GuiViewPrivate::previewAndDestroy,
3550 case LFUN_MASTER_BUFFER_UPDATE: {
3551 d.asyncBufferProcessing(argument,
3552 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3554 &GuiViewPrivate::compileAndDestroy,
3559 case LFUN_MASTER_BUFFER_VIEW: {
3560 d.asyncBufferProcessing(argument,
3561 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3563 &GuiViewPrivate::previewAndDestroy,
3564 0, &Buffer::preview);
3567 case LFUN_BUFFER_SWITCH: {
3568 string const file_name = to_utf8(cmd.argument());
3569 if (!FileName::isAbsolute(file_name)) {
3571 dr.setMessage(_("Absolute filename expected."));
3575 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3578 dr.setMessage(_("Document not loaded"));
3582 // Do we open or switch to the buffer in this view ?
3583 if (workArea(*buffer)
3584 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3589 // Look for the buffer in other views
3590 QList<int> const ids = guiApp->viewIds();
3592 for (; i != ids.size(); ++i) {
3593 GuiView & gv = guiApp->view(ids[i]);
3594 if (gv.workArea(*buffer)) {
3595 gv.activateWindow();
3596 gv.setBuffer(buffer);
3601 // If necessary, open a new window as a last resort
3602 if (i == ids.size()) {
3603 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3609 case LFUN_BUFFER_NEXT:
3610 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3613 case LFUN_BUFFER_MOVE_NEXT:
3614 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3617 case LFUN_BUFFER_PREVIOUS:
3618 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3621 case LFUN_BUFFER_MOVE_PREVIOUS:
3622 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3625 case LFUN_COMMAND_EXECUTE: {
3626 bool const show_it = cmd.argument() != "off";
3627 // FIXME: this is a hack, "minibuffer" should not be
3629 if (GuiToolbar * t = toolbar("minibuffer")) {
3630 t->setVisible(show_it);
3631 if (show_it && t->commandBuffer())
3632 t->commandBuffer()->setFocus();
3636 case LFUN_DROP_LAYOUTS_CHOICE:
3637 d.layout_->showPopup();
3640 case LFUN_MENU_OPEN:
3641 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3642 menu->exec(QCursor::pos());
3645 case LFUN_FILE_INSERT:
3646 insertLyXFile(cmd.argument());
3649 case LFUN_FILE_INSERT_PLAINTEXT:
3650 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3651 string const fname = to_utf8(cmd.argument());
3652 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3653 dr.setMessage(_("Absolute filename expected."));
3657 FileName filename(fname);
3658 if (fname.empty()) {
3659 FileDialog dlg(qt_("Select file to insert"));
3661 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3662 QStringList(qt_("All Files (*)")));
3664 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3665 dr.setMessage(_("Canceled."));
3669 filename.set(fromqstr(result.second));
3673 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3674 bv->dispatch(new_cmd, dr);
3679 case LFUN_BUFFER_RELOAD: {
3680 LASSERT(doc_buffer, break);
3683 if (!doc_buffer->isClean()) {
3684 docstring const file =
3685 makeDisplayPath(doc_buffer->absFileName(), 20);
3686 docstring text = bformat(_("Any changes will be lost. "
3687 "Are you sure you want to revert to the saved version "
3688 "of the document %1$s?"), file);
3689 ret = Alert::prompt(_("Revert to saved document?"),
3690 text, 1, 1, _("&Revert"), _("&Cancel"));
3694 doc_buffer->markClean();
3695 reloadBuffer(*doc_buffer);
3696 dr.forceBufferUpdate();
3701 case LFUN_BUFFER_WRITE:
3702 LASSERT(doc_buffer, break);
3703 saveBuffer(*doc_buffer);
3706 case LFUN_BUFFER_WRITE_AS:
3707 LASSERT(doc_buffer, break);
3708 renameBuffer(*doc_buffer, cmd.argument());
3711 case LFUN_BUFFER_WRITE_ALL: {
3712 Buffer * first = theBufferList().first();
3715 message(_("Saving all documents..."));
3716 // We cannot use a for loop as the buffer list cycles.
3719 if (!b->isClean()) {
3721 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3723 b = theBufferList().next(b);
3724 } while (b != first);
3725 dr.setMessage(_("All documents saved."));
3729 case LFUN_BUFFER_CLOSE:
3733 case LFUN_BUFFER_CLOSE_ALL:
3737 case LFUN_TOOLBAR_TOGGLE: {
3738 string const name = cmd.getArg(0);
3739 if (GuiToolbar * t = toolbar(name))
3744 case LFUN_DIALOG_UPDATE: {
3745 string const name = to_utf8(cmd.argument());
3746 if (name == "prefs" || name == "document")
3747 updateDialog(name, string());
3748 else if (name == "paragraph")
3749 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3750 else if (currentBufferView()) {
3751 Inset * inset = currentBufferView()->editedInset(name);
3752 // Can only update a dialog connected to an existing inset
3754 // FIXME: get rid of this indirection; GuiView ask the inset
3755 // if he is kind enough to update itself...
3756 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3757 //FIXME: pass DispatchResult here?
3758 inset->dispatch(currentBufferView()->cursor(), fr);
3764 case LFUN_DIALOG_TOGGLE: {
3765 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3766 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3767 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3771 case LFUN_DIALOG_DISCONNECT_INSET:
3772 disconnectDialog(to_utf8(cmd.argument()));
3775 case LFUN_DIALOG_HIDE: {
3776 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3780 case LFUN_DIALOG_SHOW: {
3781 string const name = cmd.getArg(0);
3782 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3784 if (name == "character") {
3785 data = freefont2string();
3787 showDialog("character", data);
3788 } else if (name == "latexlog") {
3789 Buffer::LogType type;
3790 string const logfile = doc_buffer->logName(&type);
3792 case Buffer::latexlog:
3795 case Buffer::buildlog:
3799 data += Lexer::quoteString(logfile);
3800 showDialog("log", data);
3801 } else if (name == "vclog") {
3802 string const data = "vc " +
3803 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3804 showDialog("log", data);
3805 } else if (name == "symbols") {
3806 data = bv->cursor().getEncoding()->name();
3808 showDialog("symbols", data);
3810 } else if (name == "prefs" && isFullScreen()) {
3811 lfunUiToggle("fullscreen");
3812 showDialog("prefs", data);
3814 showDialog(name, data);
3819 dr.setMessage(cmd.argument());
3822 case LFUN_UI_TOGGLE: {
3823 string arg = cmd.getArg(0);
3824 if (!lfunUiToggle(arg)) {
3825 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3826 dr.setMessage(bformat(msg, from_utf8(arg)));
3828 // Make sure the keyboard focus stays in the work area.
3833 case LFUN_VIEW_SPLIT: {
3834 LASSERT(doc_buffer, break);
3835 string const orientation = cmd.getArg(0);
3836 d.splitter_->setOrientation(orientation == "vertical"
3837 ? Qt::Vertical : Qt::Horizontal);
3838 TabWorkArea * twa = addTabWorkArea();
3839 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3840 setCurrentWorkArea(wa);
3843 case LFUN_TAB_GROUP_CLOSE:
3844 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3845 closeTabWorkArea(twa);
3846 d.current_work_area_ = 0;
3847 twa = d.currentTabWorkArea();
3848 // Switch to the next GuiWorkArea in the found TabWorkArea.
3850 // Make sure the work area is up to date.
3851 setCurrentWorkArea(twa->currentWorkArea());
3853 setCurrentWorkArea(0);
3858 case LFUN_VIEW_CLOSE:
3859 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3860 closeWorkArea(twa->currentWorkArea());
3861 d.current_work_area_ = 0;
3862 twa = d.currentTabWorkArea();
3863 // Switch to the next GuiWorkArea in the found TabWorkArea.
3865 // Make sure the work area is up to date.
3866 setCurrentWorkArea(twa->currentWorkArea());
3868 setCurrentWorkArea(0);
3873 case LFUN_COMPLETION_INLINE:
3874 if (d.current_work_area_)
3875 d.current_work_area_->completer().showInline();
3878 case LFUN_COMPLETION_POPUP:
3879 if (d.current_work_area_)
3880 d.current_work_area_->completer().showPopup();
3885 if (d.current_work_area_)
3886 d.current_work_area_->completer().tab();
3889 case LFUN_COMPLETION_CANCEL:
3890 if (d.current_work_area_) {
3891 if (d.current_work_area_->completer().popupVisible())
3892 d.current_work_area_->completer().hidePopup();
3894 d.current_work_area_->completer().hideInline();
3898 case LFUN_COMPLETION_ACCEPT:
3899 if (d.current_work_area_)
3900 d.current_work_area_->completer().activate();
3903 case LFUN_BUFFER_ZOOM_IN:
3904 case LFUN_BUFFER_ZOOM_OUT:
3905 if (cmd.argument().empty()) {
3906 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3911 lyxrc.zoom += convert<int>(cmd.argument());
3913 if (lyxrc.zoom < 10)
3916 // The global QPixmapCache is used in GuiPainter to cache text
3917 // painting so we must reset it.
3918 QPixmapCache::clear();
3919 guiApp->fontLoader().update();
3920 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3923 case LFUN_VC_REGISTER:
3924 case LFUN_VC_RENAME:
3926 case LFUN_VC_CHECK_IN:
3927 case LFUN_VC_CHECK_OUT:
3928 case LFUN_VC_REPO_UPDATE:
3929 case LFUN_VC_LOCKING_TOGGLE:
3930 case LFUN_VC_REVERT:
3931 case LFUN_VC_UNDO_LAST:
3932 case LFUN_VC_COMMAND:
3933 case LFUN_VC_COMPARE:
3934 dispatchVC(cmd, dr);
3937 case LFUN_SERVER_GOTO_FILE_ROW:
3938 goToFileRow(to_utf8(cmd.argument()));
3941 case LFUN_FORWARD_SEARCH: {
3942 Buffer const * doc_master = doc_buffer->masterBuffer();
3943 FileName const path(doc_master->temppath());
3944 string const texname = doc_master->isChild(doc_buffer)
3945 ? DocFileName(changeExtension(
3946 doc_buffer->absFileName(),
3947 "tex")).mangledFileName()
3948 : doc_buffer->latexName();
3949 string const fulltexname =
3950 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3951 string const mastername =
3952 removeExtension(doc_master->latexName());
3953 FileName const dviname(addName(path.absFileName(),
3954 addExtension(mastername, "dvi")));
3955 FileName const pdfname(addName(path.absFileName(),
3956 addExtension(mastername, "pdf")));
3957 bool const have_dvi = dviname.exists();
3958 bool const have_pdf = pdfname.exists();
3959 if (!have_dvi && !have_pdf) {
3960 dr.setMessage(_("Please, preview the document first."));
3963 string outname = dviname.onlyFileName();
3964 string command = lyxrc.forward_search_dvi;
3965 if (!have_dvi || (have_pdf &&
3966 pdfname.lastModified() > dviname.lastModified())) {
3967 outname = pdfname.onlyFileName();
3968 command = lyxrc.forward_search_pdf;
3971 DocIterator tmpcur = bv->cursor();
3973 while (tmpcur.inMathed())
3975 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3976 tmpcur.paragraph().id(), tmpcur.pos());
3977 LYXERR(Debug::ACTION, "Forward search: row:" << row
3978 << " id:" << tmpcur.paragraph().id());
3979 if (!row || command.empty()) {
3980 dr.setMessage(_("Couldn't proceed."));
3983 string texrow = convert<string>(row);
3985 command = subst(command, "$$n", texrow);
3986 command = subst(command, "$$f", fulltexname);
3987 command = subst(command, "$$t", texname);
3988 command = subst(command, "$$o", outname);
3990 PathChanger p(path);
3992 one.startscript(Systemcall::DontWait, command);
3996 case LFUN_SPELLING_CONTINUOUSLY:
3997 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3998 dr.screenUpdate(Update::Force | Update::FitCursor);
4002 // The LFUN must be for one of BufferView, Buffer or Cursor;
4004 dispatchToBufferView(cmd, dr);
4008 // Part of automatic menu appearance feature.
4009 if (isFullScreen()) {
4010 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4014 // Need to update bv because many LFUNs here might have destroyed it
4015 bv = currentBufferView();
4017 // Clear non-empty selections
4018 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4020 Cursor & cur = bv->cursor();
4021 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4022 cur.clearSelection();
4028 bool GuiView::lfunUiToggle(string const & ui_component)
4030 if (ui_component == "scrollbar") {
4031 // hide() is of no help
4032 if (d.current_work_area_->verticalScrollBarPolicy() ==
4033 Qt::ScrollBarAlwaysOff)
4035 d.current_work_area_->setVerticalScrollBarPolicy(
4036 Qt::ScrollBarAsNeeded);
4038 d.current_work_area_->setVerticalScrollBarPolicy(
4039 Qt::ScrollBarAlwaysOff);
4040 } else if (ui_component == "statusbar") {
4041 statusBar()->setVisible(!statusBar()->isVisible());
4042 } else if (ui_component == "menubar") {
4043 menuBar()->setVisible(!menuBar()->isVisible());
4045 if (ui_component == "frame") {
4047 getContentsMargins(&l, &t, &r, &b);
4048 //are the frames in default state?
4049 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4051 setContentsMargins(-2, -2, -2, -2);
4053 setContentsMargins(0, 0, 0, 0);
4056 if (ui_component == "fullscreen") {
4064 void GuiView::toggleFullScreen()
4066 if (isFullScreen()) {
4067 for (int i = 0; i != d.splitter_->count(); ++i)
4068 d.tabWorkArea(i)->setFullScreen(false);
4069 setContentsMargins(0, 0, 0, 0);
4070 setWindowState(windowState() ^ Qt::WindowFullScreen);
4073 statusBar()->show();
4076 hideDialogs("prefs", 0);
4077 for (int i = 0; i != d.splitter_->count(); ++i)
4078 d.tabWorkArea(i)->setFullScreen(true);
4079 setContentsMargins(-2, -2, -2, -2);
4081 setWindowState(windowState() ^ Qt::WindowFullScreen);
4082 if (lyxrc.full_screen_statusbar)
4083 statusBar()->hide();
4084 if (lyxrc.full_screen_menubar)
4086 if (lyxrc.full_screen_toolbars) {
4087 ToolbarMap::iterator end = d.toolbars_.end();
4088 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4093 // give dialogs like the TOC a chance to adapt
4098 Buffer const * GuiView::updateInset(Inset const * inset)
4103 Buffer const * inset_buffer = &(inset->buffer());
4105 for (int i = 0; i != d.splitter_->count(); ++i) {
4106 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4109 Buffer const * buffer = &(wa->bufferView().buffer());
4110 if (inset_buffer == buffer)
4111 wa->scheduleRedraw();
4113 return inset_buffer;
4117 void GuiView::restartCursor()
4119 /* When we move around, or type, it's nice to be able to see
4120 * the cursor immediately after the keypress.
4122 if (d.current_work_area_)
4123 d.current_work_area_->startBlinkingCursor();
4125 // Take this occasion to update the other GUI elements.
4131 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4133 if (d.current_work_area_)
4134 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4139 // This list should be kept in sync with the list of insets in
4140 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4141 // dialog should have the same name as the inset.
4142 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4143 // docs in LyXAction.cpp.
4145 char const * const dialognames[] = {
4147 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4148 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4149 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4150 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4151 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4152 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4153 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4154 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4156 char const * const * const end_dialognames =
4157 dialognames + (sizeof(dialognames) / sizeof(char *));
4161 cmpCStr(char const * name) : name_(name) {}
4162 bool operator()(char const * other) {
4163 return strcmp(other, name_) == 0;
4170 bool isValidName(string const & name)
4172 return find_if(dialognames, end_dialognames,
4173 cmpCStr(name.c_str())) != end_dialognames;
4179 void GuiView::resetDialogs()
4181 // Make sure that no LFUN uses any GuiView.
4182 guiApp->setCurrentView(0);
4186 constructToolbars();
4187 guiApp->menus().fillMenuBar(menuBar(), this, false);
4188 d.layout_->updateContents(true);
4189 // Now update controls with current buffer.
4190 guiApp->setCurrentView(this);
4196 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4198 if (!isValidName(name))
4201 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4203 if (it != d.dialogs_.end()) {
4205 it->second->hideView();
4206 return it->second.get();
4209 Dialog * dialog = build(name);
4210 d.dialogs_[name].reset(dialog);
4211 if (lyxrc.allow_geometry_session)
4212 dialog->restoreSession();
4219 void GuiView::showDialog(string const & name, string const & data,
4222 triggerShowDialog(toqstr(name), toqstr(data), inset);
4226 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4232 const string name = fromqstr(qname);
4233 const string data = fromqstr(qdata);
4237 Dialog * dialog = findOrBuild(name, false);
4239 bool const visible = dialog->isVisibleView();
4240 dialog->showData(data);
4241 if (inset && currentBufferView())
4242 currentBufferView()->editInset(name, inset);
4243 // We only set the focus to the new dialog if it was not yet
4244 // visible in order not to change the existing previous behaviour
4246 // activateWindow is needed for floating dockviews
4247 dialog->asQWidget()->raise();
4248 dialog->asQWidget()->activateWindow();
4249 dialog->asQWidget()->setFocus();
4253 catch (ExceptionMessage const & ex) {
4261 bool GuiView::isDialogVisible(string const & name) const
4263 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4264 if (it == d.dialogs_.end())
4266 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4270 void GuiView::hideDialog(string const & name, Inset * inset)
4272 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4273 if (it == d.dialogs_.end())
4277 if (!currentBufferView())
4279 if (inset != currentBufferView()->editedInset(name))
4283 Dialog * const dialog = it->second.get();
4284 if (dialog->isVisibleView())
4286 if (currentBufferView())
4287 currentBufferView()->editInset(name, 0);
4291 void GuiView::disconnectDialog(string const & name)
4293 if (!isValidName(name))
4295 if (currentBufferView())
4296 currentBufferView()->editInset(name, 0);
4300 void GuiView::hideAll() const
4302 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4303 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4305 for(; it != end; ++it)
4306 it->second->hideView();
4310 void GuiView::updateDialogs()
4312 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4313 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4315 for(; it != end; ++it) {
4316 Dialog * dialog = it->second.get();
4318 if (dialog->needBufferOpen() && !documentBufferView())
4319 hideDialog(fromqstr(dialog->name()), 0);
4320 else if (dialog->isVisibleView())
4321 dialog->checkStatus();
4328 Dialog * createDialog(GuiView & lv, string const & name);
4330 // will be replaced by a proper factory...
4331 Dialog * createGuiAbout(GuiView & lv);
4332 Dialog * createGuiBibtex(GuiView & lv);
4333 Dialog * createGuiChanges(GuiView & lv);
4334 Dialog * createGuiCharacter(GuiView & lv);
4335 Dialog * createGuiCitation(GuiView & lv);
4336 Dialog * createGuiCompare(GuiView & lv);
4337 Dialog * createGuiCompareHistory(GuiView & lv);
4338 Dialog * createGuiDelimiter(GuiView & lv);
4339 Dialog * createGuiDocument(GuiView & lv);
4340 Dialog * createGuiErrorList(GuiView & lv);
4341 Dialog * createGuiExternal(GuiView & lv);
4342 Dialog * createGuiGraphics(GuiView & lv);
4343 Dialog * createGuiInclude(GuiView & lv);
4344 Dialog * createGuiIndex(GuiView & lv);
4345 Dialog * createGuiListings(GuiView & lv);
4346 Dialog * createGuiLog(GuiView & lv);
4347 Dialog * createGuiMathMatrix(GuiView & lv);
4348 Dialog * createGuiNote(GuiView & lv);
4349 Dialog * createGuiParagraph(GuiView & lv);
4350 Dialog * createGuiPhantom(GuiView & lv);
4351 Dialog * createGuiPreferences(GuiView & lv);
4352 Dialog * createGuiPrint(GuiView & lv);
4353 Dialog * createGuiPrintindex(GuiView & lv);
4354 Dialog * createGuiRef(GuiView & lv);
4355 Dialog * createGuiSearch(GuiView & lv);
4356 Dialog * createGuiSearchAdv(GuiView & lv);
4357 Dialog * createGuiSendTo(GuiView & lv);
4358 Dialog * createGuiShowFile(GuiView & lv);
4359 Dialog * createGuiSpellchecker(GuiView & lv);
4360 Dialog * createGuiSymbols(GuiView & lv);
4361 Dialog * createGuiTabularCreate(GuiView & lv);
4362 Dialog * createGuiTexInfo(GuiView & lv);
4363 Dialog * createGuiToc(GuiView & lv);
4364 Dialog * createGuiThesaurus(GuiView & lv);
4365 Dialog * createGuiViewSource(GuiView & lv);
4366 Dialog * createGuiWrap(GuiView & lv);
4367 Dialog * createGuiProgressView(GuiView & lv);
4371 Dialog * GuiView::build(string const & name)
4373 LASSERT(isValidName(name), return 0);
4375 Dialog * dialog = createDialog(*this, name);
4379 if (name == "aboutlyx")
4380 return createGuiAbout(*this);
4381 if (name == "bibtex")
4382 return createGuiBibtex(*this);
4383 if (name == "changes")
4384 return createGuiChanges(*this);
4385 if (name == "character")
4386 return createGuiCharacter(*this);
4387 if (name == "citation")
4388 return createGuiCitation(*this);
4389 if (name == "compare")
4390 return createGuiCompare(*this);
4391 if (name == "comparehistory")
4392 return createGuiCompareHistory(*this);
4393 if (name == "document")
4394 return createGuiDocument(*this);
4395 if (name == "errorlist")
4396 return createGuiErrorList(*this);
4397 if (name == "external")
4398 return createGuiExternal(*this);
4400 return createGuiShowFile(*this);
4401 if (name == "findreplace")
4402 return createGuiSearch(*this);
4403 if (name == "findreplaceadv")
4404 return createGuiSearchAdv(*this);
4405 if (name == "graphics")
4406 return createGuiGraphics(*this);
4407 if (name == "include")
4408 return createGuiInclude(*this);
4409 if (name == "index")
4410 return createGuiIndex(*this);
4411 if (name == "index_print")
4412 return createGuiPrintindex(*this);
4413 if (name == "listings")
4414 return createGuiListings(*this);
4416 return createGuiLog(*this);
4417 if (name == "mathdelimiter")
4418 return createGuiDelimiter(*this);
4419 if (name == "mathmatrix")
4420 return createGuiMathMatrix(*this);
4422 return createGuiNote(*this);
4423 if (name == "paragraph")
4424 return createGuiParagraph(*this);
4425 if (name == "phantom")
4426 return createGuiPhantom(*this);
4427 if (name == "prefs")
4428 return createGuiPreferences(*this);
4430 return createGuiRef(*this);
4431 if (name == "sendto")
4432 return createGuiSendTo(*this);
4433 if (name == "spellchecker")
4434 return createGuiSpellchecker(*this);
4435 if (name == "symbols")
4436 return createGuiSymbols(*this);
4437 if (name == "tabularcreate")
4438 return createGuiTabularCreate(*this);
4439 if (name == "texinfo")
4440 return createGuiTexInfo(*this);
4441 if (name == "thesaurus")
4442 return createGuiThesaurus(*this);
4444 return createGuiToc(*this);
4445 if (name == "view-source")
4446 return createGuiViewSource(*this);
4448 return createGuiWrap(*this);
4449 if (name == "progress")
4450 return createGuiProgressView(*this);
4456 } // namespace frontend
4459 #include "moc_GuiView.cpp"