3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #include <QSvgRenderer>
114 #include <QtConcurrentRun>
122 // sync with GuiAlert.cpp
123 #define EXPORT_in_THREAD 1
126 #include "support/bind.h"
130 #ifdef HAVE_SYS_TIME_H
131 # include <sys/time.h>
139 using namespace lyx::support;
143 using support::addExtension;
144 using support::changeExtension;
145 using support::removeExtension;
151 class BackgroundWidget : public QWidget
154 BackgroundWidget(int width, int height)
155 : width_(width), height_(height)
157 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
158 if (!lyxrc.show_banner)
160 /// The text to be written on top of the pixmap
161 QString const text = lyx_version ?
162 qt_("version ") + lyx_version : qt_("unknown version");
163 #if QT_VERSION >= 0x050000
164 QString imagedir = "images/";
165 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
166 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
167 if (svgRenderer.isValid()) {
168 splash_ = QPixmap(splashSize());
169 QPainter painter(&splash_);
170 svgRenderer.render(&painter);
171 splash_.setDevicePixelRatio(pixelRatio());
173 splash_ = getPixmap("images/", "banner", "png");
176 splash_ = getPixmap("images/", "banner", "svgz,png");
179 QPainter pain(&splash_);
180 pain.setPen(QColor(0, 0, 0));
181 qreal const fsize = fontSize();
182 QPointF const position = textPosition();
184 "widget pixel ratio: " << pixelRatio() <<
185 " splash pixel ratio: " << splashPixelRatio() <<
186 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
188 // The font used to display the version info
189 font.setStyleHint(QFont::SansSerif);
190 font.setWeight(QFont::Bold);
191 font.setPointSizeF(fsize);
193 pain.drawText(position, text);
194 setFocusPolicy(Qt::StrongFocus);
197 void paintEvent(QPaintEvent *)
199 int const w = width_;
200 int const h = height_;
201 int const x = (width() - w) / 2;
202 int const y = (height() - h) / 2;
204 "widget pixel ratio: " << pixelRatio() <<
205 " splash pixel ratio: " << splashPixelRatio() <<
206 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
208 pain.drawPixmap(x, y, w, h, splash_);
211 void keyPressEvent(QKeyEvent * ev)
214 setKeySymbol(&sym, ev);
216 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
228 /// Current ratio between physical pixels and device-independent pixels
229 double pixelRatio() const {
230 #if QT_VERSION >= 0x050000
231 return devicePixelRatio();
237 qreal fontSize() const {
238 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
241 QPointF textPosition() const {
242 return QPointF(width_/2 - 18, height_/2 + 45);
245 QSize splashSize() const {
247 static_cast<unsigned int>(width_ * pixelRatio()),
248 static_cast<unsigned int>(height_ * pixelRatio()));
251 /// Ratio between physical pixels and device-independent pixels of splash image
252 double splashPixelRatio() const {
253 #if QT_VERSION >= 0x050000
254 return splash_.devicePixelRatio();
262 /// Toolbar store providing access to individual toolbars by name.
263 typedef map<string, GuiToolbar *> ToolbarMap;
265 typedef shared_ptr<Dialog> DialogPtr;
270 class GuiView::GuiViewPrivate
273 GuiViewPrivate(GuiViewPrivate const &);
274 void operator=(GuiViewPrivate const &);
276 GuiViewPrivate(GuiView * gv)
277 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
278 layout_(0), autosave_timeout_(5000),
281 // hardcode here the platform specific icon size
282 smallIconSize = 16; // scaling problems
283 normalIconSize = 20; // ok, default if iconsize.png is missing
284 bigIconSize = 26; // better for some math icons
285 hugeIconSize = 32; // better for hires displays
288 // if it exists, use width of iconsize.png as normal size
289 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
290 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
292 QImage image(toqstr(fn.absFileName()));
293 if (image.width() < int(smallIconSize))
294 normalIconSize = smallIconSize;
295 else if (image.width() > int(giantIconSize))
296 normalIconSize = giantIconSize;
298 normalIconSize = image.width();
301 splitter_ = new QSplitter;
302 bg_widget_ = new BackgroundWidget(400, 250);
303 stack_widget_ = new QStackedWidget;
304 stack_widget_->addWidget(bg_widget_);
305 stack_widget_->addWidget(splitter_);
308 // TODO cleanup, remove the singleton, handle multiple Windows?
309 progress_ = ProgressInterface::instance();
310 if (!dynamic_cast<GuiProgress*>(progress_)) {
311 progress_ = new GuiProgress; // TODO who deletes it
312 ProgressInterface::setInstance(progress_);
315 dynamic_cast<GuiProgress*>(progress_),
316 SIGNAL(updateStatusBarMessage(QString const&)),
317 gv, SLOT(updateStatusBarMessage(QString const&)));
319 dynamic_cast<GuiProgress*>(progress_),
320 SIGNAL(clearMessageText()),
321 gv, SLOT(clearMessageText()));
328 delete stack_widget_;
333 stack_widget_->setCurrentWidget(bg_widget_);
334 bg_widget_->setUpdatesEnabled(true);
335 bg_widget_->setFocus();
338 int tabWorkAreaCount()
340 return splitter_->count();
343 TabWorkArea * tabWorkArea(int i)
345 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
348 TabWorkArea * currentTabWorkArea()
350 int areas = tabWorkAreaCount();
352 // The first TabWorkArea is always the first one, if any.
353 return tabWorkArea(0);
355 for (int i = 0; i != areas; ++i) {
356 TabWorkArea * twa = tabWorkArea(i);
357 if (current_main_work_area_ == twa->currentWorkArea())
361 // None has the focus so we just take the first one.
362 return tabWorkArea(0);
365 int countWorkAreasOf(Buffer & buf)
367 int areas = tabWorkAreaCount();
369 for (int i = 0; i != areas; ++i) {
370 TabWorkArea * twa = tabWorkArea(i);
371 if (twa->workArea(buf))
377 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
379 if (processing_thread_watcher_.isRunning()) {
380 // we prefer to cancel this preview in order to keep a snappy
384 processing_thread_watcher_.setFuture(f);
387 QSize iconSize(docstring const & icon_size)
390 if (icon_size == "small")
391 size = smallIconSize;
392 else if (icon_size == "normal")
393 size = normalIconSize;
394 else if (icon_size == "big")
396 else if (icon_size == "huge")
398 else if (icon_size == "giant")
399 size = giantIconSize;
401 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
403 if (size < smallIconSize)
404 size = smallIconSize;
406 return QSize(size, size);
409 QSize iconSize(QString const & icon_size)
411 return iconSize(qstring_to_ucs4(icon_size));
414 string & iconSize(QSize const & qsize)
416 LATTEST(qsize.width() == qsize.height());
418 static string icon_size;
420 unsigned int size = qsize.width();
422 if (size < smallIconSize)
423 size = smallIconSize;
425 if (size == smallIconSize)
427 else if (size == normalIconSize)
428 icon_size = "normal";
429 else if (size == bigIconSize)
431 else if (size == hugeIconSize)
433 else if (size == giantIconSize)
436 icon_size = convert<string>(size);
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),
512 command_execute_(false), minibuffer_focus_(false)
514 // GuiToolbars *must* be initialised before the menu bar.
515 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
518 // set ourself as the current view. This is needed for the menu bar
519 // filling, at least for the static special menu item on Mac. Otherwise
520 // they are greyed out.
521 guiApp->setCurrentView(this);
523 // Fill up the menu bar.
524 guiApp->menus().fillMenuBar(menuBar(), this, true);
526 setCentralWidget(d.stack_widget_);
528 // Start autosave timer
529 if (lyxrc.autosave) {
530 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
531 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
532 d.autosave_timeout_.start();
534 connect(&d.statusbar_timer_, SIGNAL(timeout()),
535 this, SLOT(clearMessage()));
537 // We don't want to keep the window in memory if it is closed.
538 setAttribute(Qt::WA_DeleteOnClose, true);
540 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
541 // QIcon::fromTheme was introduced in Qt 4.6
542 #if (QT_VERSION >= 0x040600)
543 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
544 // since the icon is provided in the application bundle. We use a themed
545 // version when available and use the bundled one as fallback.
546 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
548 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
554 // use tabbed dock area for multiple docks
555 // (such as "source" and "messages")
556 setDockOptions(QMainWindow::ForceTabbedDocks);
559 setAcceptDrops(true);
561 // add busy indicator to statusbar
562 QLabel * busylabel = new QLabel(statusBar());
563 statusBar()->addPermanentWidget(busylabel);
564 search_mode mode = theGuiApp()->imageSearchMode();
565 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
566 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
567 busylabel->setMovie(busyanim);
571 connect(&d.processing_thread_watcher_, SIGNAL(started()),
572 busylabel, SLOT(show()));
573 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
574 busylabel, SLOT(hide()));
576 QFontMetrics const fm(statusBar()->fontMetrics());
577 int const roheight = max(int(d.normalIconSize), fm.height());
578 QSize const rosize(roheight, roheight);
579 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
580 read_only_ = new QLabel(statusBar());
581 read_only_->setPixmap(readonly);
582 read_only_->setScaledContents(true);
583 read_only_->setAlignment(Qt::AlignCenter);
585 statusBar()->addPermanentWidget(read_only_);
587 version_control_ = new QLabel(statusBar());
588 version_control_->setAlignment(Qt::AlignCenter);
589 version_control_->setFrameStyle(QFrame::StyledPanel);
590 version_control_->hide();
591 statusBar()->addPermanentWidget(version_control_);
593 statusBar()->setSizeGripEnabled(true);
596 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
597 SLOT(autoSaveThreadFinished()));
599 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
600 SLOT(processingThreadStarted()));
601 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
602 SLOT(processingThreadFinished()));
604 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
605 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
607 // set custom application bars context menu, e.g. tool bar and menu bar
608 setContextMenuPolicy(Qt::CustomContextMenu);
609 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
610 SLOT(toolBarPopup(const QPoint &)));
612 // Forbid too small unresizable window because it can happen
613 // with some window manager under X11.
614 setMinimumSize(300, 200);
616 if (lyxrc.allow_geometry_session) {
617 // Now take care of session management.
622 // no session handling, default to a sane size.
623 setGeometry(50, 50, 690, 510);
626 // clear session data if any.
628 settings.remove("views");
638 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
640 QVector<GuiWorkArea*> areas;
641 for (int i = 0; i < tabWorkAreaCount(); i++) {
642 TabWorkArea* ta = tabWorkArea(i);
643 for (int u = 0; u < ta->count(); u++) {
644 areas << ta->workArea(u);
650 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
651 string const & format)
653 docstring const fmt = formats.prettyName(format);
656 case Buffer::ExportSuccess:
657 msg = bformat(_("Successful export to format: %1$s"), fmt);
659 case Buffer::ExportCancel:
660 msg = _("Document export cancelled.");
662 case Buffer::ExportError:
663 case Buffer::ExportNoPathToFormat:
664 case Buffer::ExportTexPathHasSpaces:
665 case Buffer::ExportConverterError:
666 msg = bformat(_("Error while exporting format: %1$s"), fmt);
668 case Buffer::PreviewSuccess:
669 msg = bformat(_("Successful preview of format: %1$s"), fmt);
671 case Buffer::PreviewError:
672 msg = bformat(_("Error while previewing format: %1$s"), fmt);
679 void GuiView::processingThreadStarted()
684 void GuiView::processingThreadFinished()
686 QFutureWatcher<Buffer::ExportStatus> const * watcher =
687 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
689 Buffer::ExportStatus const status = watcher->result();
690 handleExportStatus(this, status, d.processing_format);
693 BufferView const * const bv = currentBufferView();
694 if (bv && !bv->buffer().errorList("Export").empty()) {
698 errors(d.last_export_format);
702 void GuiView::autoSaveThreadFinished()
704 QFutureWatcher<docstring> const * watcher =
705 static_cast<QFutureWatcher<docstring> const *>(sender());
706 message(watcher->result());
711 void GuiView::saveLayout() const
714 settings.beginGroup("views");
715 settings.beginGroup(QString::number(id_));
716 #if defined(Q_WS_X11) || defined(QPA_XCB)
717 settings.setValue("pos", pos());
718 settings.setValue("size", size());
720 settings.setValue("geometry", saveGeometry());
722 settings.setValue("layout", saveState(0));
723 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
727 void GuiView::saveUISettings() const
731 // Save the toolbar private states
732 ToolbarMap::iterator end = d.toolbars_.end();
733 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
734 it->second->saveSession(settings);
735 // Now take care of all other dialogs
736 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
737 for (; it!= d.dialogs_.end(); ++it)
738 it->second->saveSession(settings);
742 bool GuiView::restoreLayout()
745 settings.beginGroup("views");
746 settings.beginGroup(QString::number(id_));
747 QString const icon_key = "icon_size";
748 if (!settings.contains(icon_key))
751 //code below is skipped when when ~/.config/LyX is (re)created
752 setIconSize(d.iconSize(settings.value(icon_key).toString()));
754 #if defined(Q_WS_X11) || defined(QPA_XCB)
755 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
756 QSize size = settings.value("size", QSize(690, 510)).toSize();
760 // Work-around for bug #6034: the window ends up in an undetermined
761 // state when trying to restore a maximized window when it is
762 // already maximized.
763 if (!(windowState() & Qt::WindowMaximized))
764 if (!restoreGeometry(settings.value("geometry").toByteArray()))
765 setGeometry(50, 50, 690, 510);
767 // Make sure layout is correctly oriented.
768 setLayoutDirection(qApp->layoutDirection());
770 // Allow the toc and view-source dock widget to be restored if needed.
772 if ((dialog = findOrBuild("toc", true)))
773 // see bug 5082. At least setup title and enabled state.
774 // Visibility will be adjusted by restoreState below.
775 dialog->prepareView();
776 if ((dialog = findOrBuild("view-source", true)))
777 dialog->prepareView();
778 if ((dialog = findOrBuild("progress", true)))
779 dialog->prepareView();
781 if (!restoreState(settings.value("layout").toByteArray(), 0))
784 // init the toolbars that have not been restored
785 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
786 Toolbars::Infos::iterator end = guiApp->toolbars().end();
787 for (; cit != end; ++cit) {
788 GuiToolbar * tb = toolbar(cit->name);
789 if (tb && !tb->isRestored())
790 initToolbar(cit->name);
798 GuiToolbar * GuiView::toolbar(string const & name)
800 ToolbarMap::iterator it = d.toolbars_.find(name);
801 if (it != d.toolbars_.end())
804 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
809 void GuiView::constructToolbars()
811 ToolbarMap::iterator it = d.toolbars_.begin();
812 for (; it != d.toolbars_.end(); ++it)
816 // I don't like doing this here, but the standard toolbar
817 // destroys this object when it's destroyed itself (vfr)
818 d.layout_ = new LayoutBox(*this);
819 d.stack_widget_->addWidget(d.layout_);
820 d.layout_->move(0,0);
822 // extracts the toolbars from the backend
823 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
824 Toolbars::Infos::iterator end = guiApp->toolbars().end();
825 for (; cit != end; ++cit)
826 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
830 void GuiView::initToolbars()
832 // extracts the toolbars from the backend
833 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
834 Toolbars::Infos::iterator end = guiApp->toolbars().end();
835 for (; cit != end; ++cit)
836 initToolbar(cit->name);
840 void GuiView::initToolbar(string const & name)
842 GuiToolbar * tb = toolbar(name);
845 int const visibility = guiApp->toolbars().defaultVisibility(name);
846 bool newline = !(visibility & Toolbars::SAMEROW);
847 tb->setVisible(false);
848 tb->setVisibility(visibility);
850 if (visibility & Toolbars::TOP) {
852 addToolBarBreak(Qt::TopToolBarArea);
853 addToolBar(Qt::TopToolBarArea, tb);
856 if (visibility & Toolbars::BOTTOM) {
858 addToolBarBreak(Qt::BottomToolBarArea);
859 addToolBar(Qt::BottomToolBarArea, tb);
862 if (visibility & Toolbars::LEFT) {
864 addToolBarBreak(Qt::LeftToolBarArea);
865 addToolBar(Qt::LeftToolBarArea, tb);
868 if (visibility & Toolbars::RIGHT) {
870 addToolBarBreak(Qt::RightToolBarArea);
871 addToolBar(Qt::RightToolBarArea, tb);
874 if (visibility & Toolbars::ON)
875 tb->setVisible(true);
879 TocModels & GuiView::tocModels()
881 return d.toc_models_;
885 void GuiView::setFocus()
887 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
888 QMainWindow::setFocus();
892 bool GuiView::hasFocus() const
894 if (currentWorkArea())
895 return currentWorkArea()->hasFocus();
896 if (currentMainWorkArea())
897 return currentMainWorkArea()->hasFocus();
898 return d.bg_widget_->hasFocus();
902 void GuiView::focusInEvent(QFocusEvent * e)
904 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
905 QMainWindow::focusInEvent(e);
906 // Make sure guiApp points to the correct view.
907 guiApp->setCurrentView(this);
908 if (currentWorkArea())
909 currentWorkArea()->setFocus();
910 else if (currentMainWorkArea())
911 currentMainWorkArea()->setFocus();
913 d.bg_widget_->setFocus();
917 void GuiView::showEvent(QShowEvent * e)
919 LYXERR(Debug::GUI, "Passed Geometry "
920 << size().height() << "x" << size().width()
921 << "+" << pos().x() << "+" << pos().y());
923 if (d.splitter_->count() == 0)
924 // No work area, switch to the background widget.
928 QMainWindow::showEvent(e);
932 bool GuiView::closeScheduled()
939 bool GuiView::prepareAllBuffersForLogout()
941 Buffer * first = theBufferList().first();
945 // First, iterate over all buffers and ask the users if unsaved
946 // changes should be saved.
947 // We cannot use a for loop as the buffer list cycles.
950 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
952 b = theBufferList().next(b);
953 } while (b != first);
955 // Next, save session state
956 // When a view/window was closed before without quitting LyX, there
957 // are already entries in the lastOpened list.
958 theSession().lastOpened().clear();
965 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
966 ** is responsibility of the container (e.g., dialog)
968 void GuiView::closeEvent(QCloseEvent * close_event)
970 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
972 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
973 Alert::warning(_("Exit LyX"),
974 _("LyX could not be closed because documents are being processed by LyX."));
975 close_event->setAccepted(false);
979 // If the user pressed the x (so we didn't call closeView
980 // programmatically), we want to clear all existing entries.
982 theSession().lastOpened().clear();
987 // it can happen that this event arrives without selecting the view,
988 // e.g. when clicking the close button on a background window.
990 if (!closeWorkAreaAll()) {
992 close_event->ignore();
996 // Make sure that nothing will use this to be closed View.
997 guiApp->unregisterView(this);
999 if (isFullScreen()) {
1000 // Switch off fullscreen before closing.
1005 // Make sure the timer time out will not trigger a statusbar update.
1006 d.statusbar_timer_.stop();
1008 // Saving fullscreen requires additional tweaks in the toolbar code.
1009 // It wouldn't also work under linux natively.
1010 if (lyxrc.allow_geometry_session) {
1015 close_event->accept();
1019 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1021 if (event->mimeData()->hasUrls())
1023 /// \todo Ask lyx-devel is this is enough:
1024 /// if (event->mimeData()->hasFormat("text/plain"))
1025 /// event->acceptProposedAction();
1029 void GuiView::dropEvent(QDropEvent * event)
1031 QList<QUrl> files = event->mimeData()->urls();
1032 if (files.isEmpty())
1035 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1036 for (int i = 0; i != files.size(); ++i) {
1037 string const file = os::internal_path(fromqstr(
1038 files.at(i).toLocalFile()));
1042 string const ext = support::getExtension(file);
1043 vector<const Format *> found_formats;
1045 // Find all formats that have the correct extension.
1046 vector<const Format *> const & import_formats
1047 = theConverters().importableFormats();
1048 vector<const Format *>::const_iterator it = import_formats.begin();
1049 for (; it != import_formats.end(); ++it)
1050 if ((*it)->hasExtension(ext))
1051 found_formats.push_back(*it);
1054 if (found_formats.size() >= 1) {
1055 if (found_formats.size() > 1) {
1056 //FIXME: show a dialog to choose the correct importable format
1057 LYXERR(Debug::FILES,
1058 "Multiple importable formats found, selecting first");
1060 string const arg = found_formats[0]->name() + " " + file;
1061 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1064 //FIXME: do we have to explicitly check whether it's a lyx file?
1065 LYXERR(Debug::FILES,
1066 "No formats found, trying to open it as a lyx file");
1067 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1069 // add the functions to the queue
1070 guiApp->addToFuncRequestQueue(cmd);
1073 // now process the collected functions. We perform the events
1074 // asynchronously. This prevents potential problems in case the
1075 // BufferView is closed within an event.
1076 guiApp->processFuncRequestQueueAsync();
1080 void GuiView::message(docstring const & str)
1082 if (ForkedProcess::iAmAChild())
1085 // call is moved to GUI-thread by GuiProgress
1086 d.progress_->appendMessage(toqstr(str));
1090 void GuiView::clearMessageText()
1092 message(docstring());
1096 void GuiView::updateStatusBarMessage(QString const & str)
1098 statusBar()->showMessage(str);
1099 d.statusbar_timer_.stop();
1100 d.statusbar_timer_.start(3000);
1104 void GuiView::clearMessage()
1106 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1107 // the hasFocus function mostly returns false, even if the focus is on
1108 // a workarea in this view.
1112 d.statusbar_timer_.stop();
1116 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1118 if (wa != d.current_work_area_
1119 || wa->bufferView().buffer().isInternal())
1121 Buffer const & buf = wa->bufferView().buffer();
1122 // Set the windows title
1123 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1125 title += from_ascii(" - LyX");
1127 setWindowTitle(toqstr(title));
1128 // Sets the path for the window: this is used by OSX to
1129 // allow a context click on the title bar showing a menu
1130 // with the path up to the file
1131 setWindowFilePath(toqstr(buf.absFileName()));
1132 // Tell Qt whether the current document is changed
1133 setWindowModified(!buf.isClean());
1135 if (buf.isReadonly())
1140 if (buf.lyxvc().inUse()) {
1141 version_control_->show();
1142 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1144 version_control_->hide();
1148 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1150 if (d.current_work_area_)
1151 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1152 this, SLOT(setBusy(bool)));
1154 disconnectBufferView();
1155 connectBufferView(wa->bufferView());
1156 connectBuffer(wa->bufferView().buffer());
1157 d.current_work_area_ = wa;
1158 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1159 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1160 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1161 updateWindowTitle(wa);
1165 // The document settings needs to be reinitialised.
1166 updateDialog("document", "");
1168 // Buffer-dependent dialogs must be updated. This is done here because
1169 // some dialogs require buffer()->text.
1174 void GuiView::on_lastWorkAreaRemoved()
1177 // We already are in a close event. Nothing more to do.
1180 if (d.splitter_->count() > 1)
1181 // We have a splitter so don't close anything.
1184 // Reset and updates the dialogs.
1185 d.toc_models_.reset(0);
1186 updateDialog("document", "");
1192 if (lyxrc.open_buffers_in_tabs)
1193 // Nothing more to do, the window should stay open.
1196 if (guiApp->viewIds().size() > 1) {
1202 // On Mac we also close the last window because the application stay
1203 // resident in memory. On other platforms we don't close the last
1204 // window because this would quit the application.
1210 void GuiView::updateStatusBar()
1212 // let the user see the explicit message
1213 if (d.statusbar_timer_.isActive())
1220 void GuiView::showMessage()
1224 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1225 if (msg.isEmpty()) {
1226 BufferView const * bv = currentBufferView();
1228 msg = toqstr(bv->cursor().currentState());
1230 msg = qt_("Welcome to LyX!");
1232 statusBar()->showMessage(msg);
1236 bool GuiView::event(QEvent * e)
1240 // Useful debug code:
1241 //case QEvent::ActivationChange:
1242 //case QEvent::WindowDeactivate:
1243 //case QEvent::Paint:
1244 //case QEvent::Enter:
1245 //case QEvent::Leave:
1246 //case QEvent::HoverEnter:
1247 //case QEvent::HoverLeave:
1248 //case QEvent::HoverMove:
1249 //case QEvent::StatusTip:
1250 //case QEvent::DragEnter:
1251 //case QEvent::DragLeave:
1252 //case QEvent::Drop:
1255 case QEvent::WindowActivate: {
1256 GuiView * old_view = guiApp->currentView();
1257 if (this == old_view) {
1259 return QMainWindow::event(e);
1261 if (old_view && old_view->currentBufferView()) {
1262 // save current selection to the selection buffer to allow
1263 // middle-button paste in this window.
1264 cap::saveSelection(old_view->currentBufferView()->cursor());
1266 guiApp->setCurrentView(this);
1267 if (d.current_work_area_) {
1268 BufferView & bv = d.current_work_area_->bufferView();
1269 connectBufferView(bv);
1270 connectBuffer(bv.buffer());
1271 // The document structure, name and dialogs might have
1272 // changed in another view.
1274 // The document settings needs to be reinitialised.
1275 updateDialog("document", "");
1281 return QMainWindow::event(e);
1284 case QEvent::ShortcutOverride: {
1286 if (isFullScreen() && menuBar()->isHidden()) {
1287 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1288 // FIXME: we should also try to detect special LyX shortcut such as
1289 // Alt-P and Alt-M. Right now there is a hack in
1290 // GuiWorkArea::processKeySym() that hides again the menubar for
1292 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1294 return QMainWindow::event(e);
1297 return QMainWindow::event(e);
1301 return QMainWindow::event(e);
1305 void GuiView::resetWindowTitle()
1307 setWindowTitle(qt_("LyX"));
1310 bool GuiView::focusNextPrevChild(bool /*next*/)
1317 bool GuiView::busy() const
1323 void GuiView::setBusy(bool busy)
1325 bool const busy_before = busy_ > 0;
1326 busy ? ++busy_ : --busy_;
1327 if ((busy_ > 0) == busy_before)
1328 // busy state didn't change
1332 QApplication::setOverrideCursor(Qt::WaitCursor);
1335 QApplication::restoreOverrideCursor();
1340 void GuiView::resetCommandExecute()
1342 command_execute_ = false;
1347 double GuiView::pixelRatio() const
1349 #if QT_VERSION >= 0x050000
1350 return devicePixelRatio();
1357 GuiWorkArea * GuiView::workArea(int index)
1359 if (TabWorkArea * twa = d.currentTabWorkArea())
1360 if (index < twa->count())
1361 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1366 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1368 if (currentWorkArea()
1369 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1370 return currentWorkArea();
1371 if (TabWorkArea * twa = d.currentTabWorkArea())
1372 return twa->workArea(buffer);
1377 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1379 // Automatically create a TabWorkArea if there are none yet.
1380 TabWorkArea * tab_widget = d.splitter_->count()
1381 ? d.currentTabWorkArea() : addTabWorkArea();
1382 return tab_widget->addWorkArea(buffer, *this);
1386 TabWorkArea * GuiView::addTabWorkArea()
1388 TabWorkArea * twa = new TabWorkArea;
1389 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1390 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1391 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1392 this, SLOT(on_lastWorkAreaRemoved()));
1394 d.splitter_->addWidget(twa);
1395 d.stack_widget_->setCurrentWidget(d.splitter_);
1400 GuiWorkArea const * GuiView::currentWorkArea() const
1402 return d.current_work_area_;
1406 GuiWorkArea * GuiView::currentWorkArea()
1408 return d.current_work_area_;
1412 GuiWorkArea const * GuiView::currentMainWorkArea() const
1414 if (!d.currentTabWorkArea())
1416 return d.currentTabWorkArea()->currentWorkArea();
1420 GuiWorkArea * GuiView::currentMainWorkArea()
1422 if (!d.currentTabWorkArea())
1424 return d.currentTabWorkArea()->currentWorkArea();
1428 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1430 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1432 d.current_work_area_ = 0;
1437 // FIXME: I've no clue why this is here and why it accesses
1438 // theGuiApp()->currentView, which might be 0 (bug 6464).
1439 // See also 27525 (vfr).
1440 if (theGuiApp()->currentView() == this
1441 && theGuiApp()->currentView()->currentWorkArea() == wa)
1444 if (currentBufferView())
1445 cap::saveSelection(currentBufferView()->cursor());
1447 theGuiApp()->setCurrentView(this);
1448 d.current_work_area_ = wa;
1450 // We need to reset this now, because it will need to be
1451 // right if the tabWorkArea gets reset in the for loop. We
1452 // will change it back if we aren't in that case.
1453 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1454 d.current_main_work_area_ = wa;
1456 for (int i = 0; i != d.splitter_->count(); ++i) {
1457 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1458 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1459 << ", Current main wa: " << currentMainWorkArea());
1464 d.current_main_work_area_ = old_cmwa;
1466 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1467 on_currentWorkAreaChanged(wa);
1468 BufferView & bv = wa->bufferView();
1469 bv.cursor().fixIfBroken();
1471 wa->setUpdatesEnabled(true);
1472 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1476 void GuiView::removeWorkArea(GuiWorkArea * wa)
1478 LASSERT(wa, return);
1479 if (wa == d.current_work_area_) {
1481 disconnectBufferView();
1482 d.current_work_area_ = 0;
1483 d.current_main_work_area_ = 0;
1486 bool found_twa = false;
1487 for (int i = 0; i != d.splitter_->count(); ++i) {
1488 TabWorkArea * twa = d.tabWorkArea(i);
1489 if (twa->removeWorkArea(wa)) {
1490 // Found in this tab group, and deleted the GuiWorkArea.
1492 if (twa->count() != 0) {
1493 if (d.current_work_area_ == 0)
1494 // This means that we are closing the current GuiWorkArea, so
1495 // switch to the next GuiWorkArea in the found TabWorkArea.
1496 setCurrentWorkArea(twa->currentWorkArea());
1498 // No more WorkAreas in this tab group, so delete it.
1505 // It is not a tabbed work area (i.e., the search work area), so it
1506 // should be deleted by other means.
1507 LASSERT(found_twa, return);
1509 if (d.current_work_area_ == 0) {
1510 if (d.splitter_->count() != 0) {
1511 TabWorkArea * twa = d.currentTabWorkArea();
1512 setCurrentWorkArea(twa->currentWorkArea());
1514 // No more work areas, switch to the background widget.
1515 setCurrentWorkArea(0);
1521 LayoutBox * GuiView::getLayoutDialog() const
1527 void GuiView::updateLayoutList()
1530 d.layout_->updateContents(false);
1534 void GuiView::updateToolbars()
1536 ToolbarMap::iterator end = d.toolbars_.end();
1537 if (d.current_work_area_) {
1539 if (d.current_work_area_->bufferView().cursor().inMathed()
1540 && !d.current_work_area_->bufferView().cursor().inRegexped())
1541 context |= Toolbars::MATH;
1542 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1543 context |= Toolbars::TABLE;
1544 if (currentBufferView()->buffer().areChangesPresent()
1545 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1546 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1547 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1548 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1549 context |= Toolbars::REVIEW;
1550 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1551 context |= Toolbars::MATHMACROTEMPLATE;
1552 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1553 context |= Toolbars::IPA;
1554 if (command_execute_)
1555 context |= Toolbars::MINIBUFFER;
1556 if (minibuffer_focus_) {
1557 context |= Toolbars::MINIBUFFER_FOCUS;
1558 minibuffer_focus_ = false;
1561 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1562 it->second->update(context);
1564 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1565 it->second->update();
1569 void GuiView::setBuffer(Buffer * newBuffer)
1571 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1572 LASSERT(newBuffer, return);
1574 GuiWorkArea * wa = workArea(*newBuffer);
1577 newBuffer->masterBuffer()->updateBuffer();
1579 wa = addWorkArea(*newBuffer);
1580 // scroll to the position when the BufferView was last closed
1581 if (lyxrc.use_lastfilepos) {
1582 LastFilePosSection::FilePos filepos =
1583 theSession().lastFilePos().load(newBuffer->fileName());
1584 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1587 //Disconnect the old buffer...there's no new one.
1590 connectBuffer(*newBuffer);
1591 connectBufferView(wa->bufferView());
1592 setCurrentWorkArea(wa);
1596 void GuiView::connectBuffer(Buffer & buf)
1598 buf.setGuiDelegate(this);
1602 void GuiView::disconnectBuffer()
1604 if (d.current_work_area_)
1605 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1609 void GuiView::connectBufferView(BufferView & bv)
1611 bv.setGuiDelegate(this);
1615 void GuiView::disconnectBufferView()
1617 if (d.current_work_area_)
1618 d.current_work_area_->bufferView().setGuiDelegate(0);
1622 void GuiView::errors(string const & error_type, bool from_master)
1624 BufferView const * const bv = currentBufferView();
1628 #if EXPORT_in_THREAD
1629 // We are called with from_master == false by default, so we
1630 // have to figure out whether that is the case or not.
1631 ErrorList & el = bv->buffer().errorList(error_type);
1633 el = bv->buffer().masterBuffer()->errorList(error_type);
1637 ErrorList const & el = from_master ?
1638 bv->buffer().masterBuffer()->errorList(error_type) :
1639 bv->buffer().errorList(error_type);
1645 string data = error_type;
1647 data = "from_master|" + error_type;
1648 showDialog("errorlist", data);
1652 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1654 d.toc_models_.updateItem(toqstr(type), dit);
1658 void GuiView::structureChanged()
1660 d.toc_models_.reset(documentBufferView());
1661 // Navigator needs more than a simple update in this case. It needs to be
1663 updateDialog("toc", "");
1667 void GuiView::updateDialog(string const & name, string const & data)
1669 if (!isDialogVisible(name))
1672 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1673 if (it == d.dialogs_.end())
1676 Dialog * const dialog = it->second.get();
1677 if (dialog->isVisibleView())
1678 dialog->initialiseParams(data);
1682 BufferView * GuiView::documentBufferView()
1684 return currentMainWorkArea()
1685 ? ¤tMainWorkArea()->bufferView()
1690 BufferView const * GuiView::documentBufferView() const
1692 return currentMainWorkArea()
1693 ? ¤tMainWorkArea()->bufferView()
1698 BufferView * GuiView::currentBufferView()
1700 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1704 BufferView const * GuiView::currentBufferView() const
1706 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1710 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1711 Buffer const * orig, Buffer * clone)
1713 bool const success = clone->autoSave();
1715 busyBuffers.remove(orig);
1717 ? _("Automatic save done.")
1718 : _("Automatic save failed!");
1722 void GuiView::autoSave()
1724 LYXERR(Debug::INFO, "Running autoSave()");
1726 Buffer * buffer = documentBufferView()
1727 ? &documentBufferView()->buffer() : 0;
1729 resetAutosaveTimers();
1733 GuiViewPrivate::busyBuffers.insert(buffer);
1734 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1735 buffer, buffer->cloneBufferOnly());
1736 d.autosave_watcher_.setFuture(f);
1737 resetAutosaveTimers();
1741 void GuiView::resetAutosaveTimers()
1744 d.autosave_timeout_.restart();
1748 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1751 Buffer * buf = currentBufferView()
1752 ? ¤tBufferView()->buffer() : 0;
1753 Buffer * doc_buffer = documentBufferView()
1754 ? &(documentBufferView()->buffer()) : 0;
1757 /* In LyX/Mac, when a dialog is open, the menus of the
1758 application can still be accessed without giving focus to
1759 the main window. In this case, we want to disable the menu
1760 entries that are buffer-related.
1761 This code must not be used on Linux and Windows, since it
1762 would disable buffer-related entries when hovering over the
1763 menu (see bug #9574).
1765 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1771 // Check whether we need a buffer
1772 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1773 // no, exit directly
1774 flag.message(from_utf8(N_("Command not allowed with"
1775 "out any document open")));
1776 flag.setEnabled(false);
1780 if (cmd.origin() == FuncRequest::TOC) {
1781 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1782 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1783 flag.setEnabled(false);
1787 switch(cmd.action()) {
1788 case LFUN_BUFFER_IMPORT:
1791 case LFUN_MASTER_BUFFER_UPDATE:
1792 case LFUN_MASTER_BUFFER_VIEW:
1794 && (doc_buffer->parent() != 0
1795 || doc_buffer->hasChildren())
1796 && !d.processing_thread_watcher_.isRunning();
1799 case LFUN_BUFFER_UPDATE:
1800 case LFUN_BUFFER_VIEW: {
1801 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1805 string format = to_utf8(cmd.argument());
1806 if (cmd.argument().empty())
1807 format = doc_buffer->params().getDefaultOutputFormat();
1808 enable = doc_buffer->params().isExportable(format, true);
1812 case LFUN_BUFFER_RELOAD:
1813 enable = doc_buffer && !doc_buffer->isUnnamed()
1814 && doc_buffer->fileName().exists()
1815 && (!doc_buffer->isClean()
1816 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1819 case LFUN_BUFFER_CHILD_OPEN:
1820 enable = doc_buffer != 0;
1823 case LFUN_BUFFER_WRITE:
1824 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1827 //FIXME: This LFUN should be moved to GuiApplication.
1828 case LFUN_BUFFER_WRITE_ALL: {
1829 // We enable the command only if there are some modified buffers
1830 Buffer * first = theBufferList().first();
1835 // We cannot use a for loop as the buffer list is a cycle.
1837 if (!b->isClean()) {
1841 b = theBufferList().next(b);
1842 } while (b != first);
1846 case LFUN_BUFFER_WRITE_AS:
1847 case LFUN_BUFFER_EXPORT_AS:
1848 enable = doc_buffer != 0;
1851 case LFUN_BUFFER_CLOSE:
1852 case LFUN_VIEW_CLOSE:
1853 enable = doc_buffer != 0;
1856 case LFUN_BUFFER_CLOSE_ALL:
1857 enable = theBufferList().last() != theBufferList().first();
1860 case LFUN_VIEW_SPLIT:
1861 if (cmd.getArg(0) == "vertical")
1862 enable = doc_buffer && (d.splitter_->count() == 1 ||
1863 d.splitter_->orientation() == Qt::Vertical);
1865 enable = doc_buffer && (d.splitter_->count() == 1 ||
1866 d.splitter_->orientation() == Qt::Horizontal);
1869 case LFUN_TAB_GROUP_CLOSE:
1870 enable = d.tabWorkAreaCount() > 1;
1873 case LFUN_TOOLBAR_TOGGLE: {
1874 string const name = cmd.getArg(0);
1875 if (GuiToolbar * t = toolbar(name))
1876 flag.setOnOff(t->isVisible());
1879 docstring const msg =
1880 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1886 case LFUN_ICON_SIZE:
1887 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1890 case LFUN_DROP_LAYOUTS_CHOICE:
1894 case LFUN_UI_TOGGLE:
1895 flag.setOnOff(isFullScreen());
1898 case LFUN_DIALOG_DISCONNECT_INSET:
1901 case LFUN_DIALOG_HIDE:
1902 // FIXME: should we check if the dialog is shown?
1905 case LFUN_DIALOG_TOGGLE:
1906 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1907 // fall through to set "enable"
1908 case LFUN_DIALOG_SHOW: {
1909 string const name = cmd.getArg(0);
1911 enable = name == "aboutlyx"
1912 || name == "file" //FIXME: should be removed.
1914 || name == "texinfo"
1915 || name == "progress"
1916 || name == "compare";
1917 else if (name == "character" || name == "symbols"
1918 || name == "mathdelimiter" || name == "mathmatrix") {
1919 if (!buf || buf->isReadonly())
1922 Cursor const & cur = currentBufferView()->cursor();
1923 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1926 else if (name == "latexlog")
1927 enable = FileName(doc_buffer->logName()).isReadableFile();
1928 else if (name == "spellchecker")
1929 enable = theSpellChecker()
1930 && !doc_buffer->isReadonly()
1931 && !doc_buffer->text().empty();
1932 else if (name == "vclog")
1933 enable = doc_buffer->lyxvc().inUse();
1937 case LFUN_DIALOG_UPDATE: {
1938 string const name = cmd.getArg(0);
1940 enable = name == "prefs";
1944 case LFUN_COMMAND_EXECUTE:
1946 case LFUN_MENU_OPEN:
1947 // Nothing to check.
1950 case LFUN_COMPLETION_INLINE:
1951 if (!d.current_work_area_
1952 || !d.current_work_area_->completer().inlinePossible(
1953 currentBufferView()->cursor()))
1957 case LFUN_COMPLETION_POPUP:
1958 if (!d.current_work_area_
1959 || !d.current_work_area_->completer().popupPossible(
1960 currentBufferView()->cursor()))
1965 if (!d.current_work_area_
1966 || !d.current_work_area_->completer().inlinePossible(
1967 currentBufferView()->cursor()))
1971 case LFUN_COMPLETION_ACCEPT:
1972 if (!d.current_work_area_
1973 || (!d.current_work_area_->completer().popupVisible()
1974 && !d.current_work_area_->completer().inlineVisible()
1975 && !d.current_work_area_->completer().completionAvailable()))
1979 case LFUN_COMPLETION_CANCEL:
1980 if (!d.current_work_area_
1981 || (!d.current_work_area_->completer().popupVisible()
1982 && !d.current_work_area_->completer().inlineVisible()))
1986 case LFUN_BUFFER_ZOOM_OUT:
1987 enable = doc_buffer && lyxrc.zoom > 10;
1988 if (lyxrc.zoom <= 10)
1989 flag.message(_("Zoom level cannot be less than 10%."));
1992 case LFUN_BUFFER_ZOOM_IN:
1993 enable = doc_buffer != 0;
1996 case LFUN_BUFFER_MOVE_NEXT:
1997 case LFUN_BUFFER_MOVE_PREVIOUS:
1998 // we do not cycle when moving
1999 case LFUN_BUFFER_NEXT:
2000 case LFUN_BUFFER_PREVIOUS:
2001 // because we cycle, it doesn't matter whether on first or last
2002 enable = (d.currentTabWorkArea()->count() > 1);
2004 case LFUN_BUFFER_SWITCH:
2005 // toggle on the current buffer, but do not toggle off
2006 // the other ones (is that a good idea?)
2008 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2009 flag.setOnOff(true);
2012 case LFUN_VC_REGISTER:
2013 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2015 case LFUN_VC_RENAME:
2016 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2019 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2021 case LFUN_VC_CHECK_IN:
2022 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2024 case LFUN_VC_CHECK_OUT:
2025 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2027 case LFUN_VC_LOCKING_TOGGLE:
2028 enable = doc_buffer && !doc_buffer->isReadonly()
2029 && doc_buffer->lyxvc().lockingToggleEnabled();
2030 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2032 case LFUN_VC_REVERT:
2033 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2035 case LFUN_VC_UNDO_LAST:
2036 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2038 case LFUN_VC_REPO_UPDATE:
2039 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2041 case LFUN_VC_COMMAND: {
2042 if (cmd.argument().empty())
2044 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2048 case LFUN_VC_COMPARE:
2049 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2052 case LFUN_SERVER_GOTO_FILE_ROW:
2054 case LFUN_FORWARD_SEARCH:
2055 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2058 case LFUN_FILE_INSERT_PLAINTEXT:
2059 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2060 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2063 case LFUN_SPELLING_CONTINUOUSLY:
2064 flag.setOnOff(lyxrc.spellcheck_continuously);
2072 flag.setEnabled(false);
2078 static FileName selectTemplateFile()
2080 FileDialog dlg(qt_("Select template file"));
2081 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2082 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2084 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2085 QStringList(qt_("LyX Documents (*.lyx)")));
2087 if (result.first == FileDialog::Later)
2089 if (result.second.isEmpty())
2091 return FileName(fromqstr(result.second));
2095 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2099 Buffer * newBuffer = 0;
2101 newBuffer = checkAndLoadLyXFile(filename);
2102 } catch (ExceptionMessage const & e) {
2109 message(_("Document not loaded."));
2113 setBuffer(newBuffer);
2114 newBuffer->errors("Parse");
2117 theSession().lastFiles().add(filename);
2123 void GuiView::openDocument(string const & fname)
2125 string initpath = lyxrc.document_path;
2127 if (documentBufferView()) {
2128 string const trypath = documentBufferView()->buffer().filePath();
2129 // If directory is writeable, use this as default.
2130 if (FileName(trypath).isDirWritable())
2136 if (fname.empty()) {
2137 FileDialog dlg(qt_("Select document to open"));
2138 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2139 dlg.setButton2(qt_("Examples|#E#e"),
2140 toqstr(addPath(package().system_support().absFileName(), "examples")));
2142 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2143 FileDialog::Result result =
2144 dlg.open(toqstr(initpath), filter);
2146 if (result.first == FileDialog::Later)
2149 filename = fromqstr(result.second);
2151 // check selected filename
2152 if (filename.empty()) {
2153 message(_("Canceled."));
2159 // get absolute path of file and add ".lyx" to the filename if
2161 FileName const fullname =
2162 fileSearch(string(), filename, "lyx", support::may_not_exist);
2163 if (!fullname.empty())
2164 filename = fullname.absFileName();
2166 if (!fullname.onlyPath().isDirectory()) {
2167 Alert::warning(_("Invalid filename"),
2168 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2169 from_utf8(fullname.absFileName())));
2173 // if the file doesn't exist and isn't already open (bug 6645),
2174 // let the user create one
2175 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2176 !LyXVC::file_not_found_hook(fullname)) {
2177 // the user specifically chose this name. Believe him.
2178 Buffer * const b = newFile(filename, string(), true);
2184 docstring const disp_fn = makeDisplayPath(filename);
2185 message(bformat(_("Opening document %1$s..."), disp_fn));
2188 Buffer * buf = loadDocument(fullname);
2190 str2 = bformat(_("Document %1$s opened."), disp_fn);
2191 if (buf->lyxvc().inUse())
2192 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2193 " " + _("Version control detected.");
2195 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2200 // FIXME: clean that
2201 static bool import(GuiView * lv, FileName const & filename,
2202 string const & format, ErrorList & errorList)
2204 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2206 string loader_format;
2207 vector<string> loaders = theConverters().loaders();
2208 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2209 vector<string>::const_iterator it = loaders.begin();
2210 vector<string>::const_iterator en = loaders.end();
2211 for (; it != en; ++it) {
2212 if (!theConverters().isReachable(format, *it))
2215 string const tofile =
2216 support::changeExtension(filename.absFileName(),
2217 formats.extension(*it));
2218 if (!theConverters().convert(0, filename, FileName(tofile),
2219 filename, format, *it, errorList))
2221 loader_format = *it;
2224 if (loader_format.empty()) {
2225 frontend::Alert::error(_("Couldn't import file"),
2226 bformat(_("No information for importing the format %1$s."),
2227 formats.prettyName(format)));
2231 loader_format = format;
2233 if (loader_format == "lyx") {
2234 Buffer * buf = lv->loadDocument(lyxfile);
2238 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2242 bool as_paragraphs = loader_format == "textparagraph";
2243 string filename2 = (loader_format == format) ? filename.absFileName()
2244 : support::changeExtension(filename.absFileName(),
2245 formats.extension(loader_format));
2246 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2248 guiApp->setCurrentView(lv);
2249 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2256 void GuiView::importDocument(string const & argument)
2259 string filename = split(argument, format, ' ');
2261 LYXERR(Debug::INFO, format << " file: " << filename);
2263 // need user interaction
2264 if (filename.empty()) {
2265 string initpath = lyxrc.document_path;
2266 if (documentBufferView()) {
2267 string const trypath = documentBufferView()->buffer().filePath();
2268 // If directory is writeable, use this as default.
2269 if (FileName(trypath).isDirWritable())
2273 docstring const text = bformat(_("Select %1$s file to import"),
2274 formats.prettyName(format));
2276 FileDialog dlg(toqstr(text));
2277 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2278 dlg.setButton2(qt_("Examples|#E#e"),
2279 toqstr(addPath(package().system_support().absFileName(), "examples")));
2281 docstring filter = formats.prettyName(format);
2284 filter += from_utf8(formats.extensions(format));
2287 FileDialog::Result result =
2288 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2290 if (result.first == FileDialog::Later)
2293 filename = fromqstr(result.second);
2295 // check selected filename
2296 if (filename.empty())
2297 message(_("Canceled."));
2300 if (filename.empty())
2303 // get absolute path of file
2304 FileName const fullname(support::makeAbsPath(filename));
2306 // Can happen if the user entered a path into the dialog
2308 if (fullname.onlyFileName().empty()) {
2309 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2310 "Aborting import."),
2311 from_utf8(fullname.absFileName()));
2312 frontend::Alert::error(_("File name error"), msg);
2313 message(_("Canceled."));
2318 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2320 // Check if the document already is open
2321 Buffer * buf = theBufferList().getBuffer(lyxfile);
2324 if (!closeBuffer()) {
2325 message(_("Canceled."));
2330 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2332 // if the file exists already, and we didn't do
2333 // -i lyx thefile.lyx, warn
2334 if (lyxfile.exists() && fullname != lyxfile) {
2336 docstring text = bformat(_("The document %1$s already exists.\n\n"
2337 "Do you want to overwrite that document?"), displaypath);
2338 int const ret = Alert::prompt(_("Overwrite document?"),
2339 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2342 message(_("Canceled."));
2347 message(bformat(_("Importing %1$s..."), displaypath));
2348 ErrorList errorList;
2349 if (import(this, fullname, format, errorList))
2350 message(_("imported."));
2352 message(_("file not imported!"));
2354 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2358 void GuiView::newDocument(string const & filename, bool from_template)
2360 FileName initpath(lyxrc.document_path);
2361 if (documentBufferView()) {
2362 FileName const trypath(documentBufferView()->buffer().filePath());
2363 // If directory is writeable, use this as default.
2364 if (trypath.isDirWritable())
2368 string templatefile;
2369 if (from_template) {
2370 templatefile = selectTemplateFile().absFileName();
2371 if (templatefile.empty())
2376 if (filename.empty())
2377 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2379 b = newFile(filename, templatefile, true);
2384 // If no new document could be created, it is unsure
2385 // whether there is a valid BufferView.
2386 if (currentBufferView())
2387 // Ensure the cursor is correctly positioned on screen.
2388 currentBufferView()->showCursor();
2392 void GuiView::insertLyXFile(docstring const & fname)
2394 BufferView * bv = documentBufferView();
2399 FileName filename(to_utf8(fname));
2400 if (filename.empty()) {
2401 // Launch a file browser
2403 string initpath = lyxrc.document_path;
2404 string const trypath = bv->buffer().filePath();
2405 // If directory is writeable, use this as default.
2406 if (FileName(trypath).isDirWritable())
2410 FileDialog dlg(qt_("Select LyX document to insert"));
2411 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2412 dlg.setButton2(qt_("Examples|#E#e"),
2413 toqstr(addPath(package().system_support().absFileName(),
2416 FileDialog::Result result = dlg.open(toqstr(initpath),
2417 QStringList(qt_("LyX Documents (*.lyx)")));
2419 if (result.first == FileDialog::Later)
2423 filename.set(fromqstr(result.second));
2425 // check selected filename
2426 if (filename.empty()) {
2427 // emit message signal.
2428 message(_("Canceled."));
2433 bv->insertLyXFile(filename);
2434 bv->buffer().errors("Parse");
2438 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2440 FileName fname = b.fileName();
2441 FileName const oldname = fname;
2443 if (!newname.empty()) {
2445 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2447 // Switch to this Buffer.
2450 // No argument? Ask user through dialog.
2452 FileDialog dlg(qt_("Choose a filename to save document as"));
2453 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2454 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2456 if (!isLyXFileName(fname.absFileName()))
2457 fname.changeExtension(".lyx");
2459 FileDialog::Result result =
2460 dlg.save(toqstr(fname.onlyPath().absFileName()),
2461 QStringList(qt_("LyX Documents (*.lyx)")),
2462 toqstr(fname.onlyFileName()));
2464 if (result.first == FileDialog::Later)
2467 fname.set(fromqstr(result.second));
2472 if (!isLyXFileName(fname.absFileName()))
2473 fname.changeExtension(".lyx");
2476 // fname is now the new Buffer location.
2478 // if there is already a Buffer open with this name, we do not want
2479 // to have another one. (the second test makes sure we're not just
2480 // trying to overwrite ourselves, which is fine.)
2481 if (theBufferList().exists(fname) && fname != oldname
2482 && theBufferList().getBuffer(fname) != &b) {
2483 docstring const text =
2484 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2485 "Please close it before attempting to overwrite it.\n"
2486 "Do you want to choose a new filename?"),
2487 from_utf8(fname.absFileName()));
2488 int const ret = Alert::prompt(_("Chosen File Already Open"),
2489 text, 0, 1, _("&Rename"), _("&Cancel"));
2491 case 0: return renameBuffer(b, docstring(), kind);
2492 case 1: return false;
2497 bool const existsLocal = fname.exists();
2498 bool const existsInVC = LyXVC::fileInVC(fname);
2499 if (existsLocal || existsInVC) {
2500 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2501 if (kind != LV_WRITE_AS && existsInVC) {
2502 // renaming to a name that is already in VC
2504 docstring text = bformat(_("The document %1$s "
2505 "is already registered.\n\n"
2506 "Do you want to choose a new name?"),
2508 docstring const title = (kind == LV_VC_RENAME) ?
2509 _("Rename document?") : _("Copy document?");
2510 docstring const button = (kind == LV_VC_RENAME) ?
2511 _("&Rename") : _("&Copy");
2512 int const ret = Alert::prompt(title, text, 0, 1,
2513 button, _("&Cancel"));
2515 case 0: return renameBuffer(b, docstring(), kind);
2516 case 1: return false;
2521 docstring text = bformat(_("The document %1$s "
2522 "already exists.\n\n"
2523 "Do you want to overwrite that document?"),
2525 int const ret = Alert::prompt(_("Overwrite document?"),
2526 text, 0, 2, _("&Overwrite"),
2527 _("&Rename"), _("&Cancel"));
2530 case 1: return renameBuffer(b, docstring(), kind);
2531 case 2: return false;
2537 case LV_VC_RENAME: {
2538 string msg = b.lyxvc().rename(fname);
2541 message(from_utf8(msg));
2545 string msg = b.lyxvc().copy(fname);
2548 message(from_utf8(msg));
2554 // LyXVC created the file already in case of LV_VC_RENAME or
2555 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2556 // relative paths of included stuff right if we moved e.g. from
2557 // /a/b.lyx to /a/c/b.lyx.
2559 bool const saved = saveBuffer(b, fname);
2566 struct PrettyNameComparator
2568 bool operator()(Format const *first, Format const *second) const {
2569 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2570 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2575 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2577 FileName fname = b.fileName();
2579 FileDialog dlg(qt_("Choose a filename to export the document as"));
2580 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2583 QString const anyformat = qt_("Guess from extension (*.*)");
2585 Formats::const_iterator it = formats.begin();
2586 vector<Format const *> export_formats;
2587 for (; it != formats.end(); ++it)
2588 if (it->documentFormat())
2589 export_formats.push_back(&(*it));
2590 PrettyNameComparator cmp;
2591 sort(export_formats.begin(), export_formats.end(), cmp);
2592 vector<Format const *>::const_iterator fit = export_formats.begin();
2593 map<QString, string> fmap;
2596 for (; fit != export_formats.end(); ++fit) {
2597 docstring const loc_prettyname =
2598 translateIfPossible(from_utf8((*fit)->prettyname()));
2599 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2601 from_ascii((*fit)->extension())));
2602 types << loc_filter;
2603 fmap[loc_filter] = (*fit)->name();
2604 if (from_ascii((*fit)->name()) == iformat) {
2605 filter = loc_filter;
2606 ext = (*fit)->extension();
2609 string ofname = fname.onlyFileName();
2611 ofname = support::changeExtension(ofname, ext);
2612 FileDialog::Result result =
2613 dlg.save(toqstr(fname.onlyPath().absFileName()),
2617 if (result.first != FileDialog::Chosen)
2621 fname.set(fromqstr(result.second));
2622 if (filter == anyformat)
2623 fmt_name = formats.getFormatFromExtension(fname.extension());
2625 fmt_name = fmap[filter];
2626 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2627 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2629 if (fmt_name.empty() || fname.empty())
2632 // fname is now the new Buffer location.
2633 if (FileName(fname).exists()) {
2634 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2635 docstring text = bformat(_("The document %1$s already "
2636 "exists.\n\nDo you want to "
2637 "overwrite that document?"),
2639 int const ret = Alert::prompt(_("Overwrite document?"),
2640 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2643 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2644 case 2: return false;
2648 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2651 return dr.dispatched();
2655 bool GuiView::saveBuffer(Buffer & b)
2657 return saveBuffer(b, FileName());
2661 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2663 if (workArea(b) && workArea(b)->inDialogMode())
2666 if (fn.empty() && b.isUnnamed())
2667 return renameBuffer(b, docstring());
2669 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2671 theSession().lastFiles().add(b.fileName());
2675 // Switch to this Buffer.
2678 // FIXME: we don't tell the user *WHY* the save failed !!
2679 docstring const file = makeDisplayPath(b.absFileName(), 30);
2680 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2681 "Do you want to rename the document and "
2682 "try again?"), file);
2683 int const ret = Alert::prompt(_("Rename and save?"),
2684 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2687 if (!renameBuffer(b, docstring()))
2696 return saveBuffer(b, fn);
2700 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2702 return closeWorkArea(wa, false);
2706 // We only want to close the buffer if it is not visible in other workareas
2707 // of the same view, nor in other views, and if this is not a child
2708 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2710 Buffer & buf = wa->bufferView().buffer();
2712 bool last_wa = d.countWorkAreasOf(buf) == 1
2713 && !inOtherView(buf) && !buf.parent();
2715 bool close_buffer = last_wa;
2718 if (lyxrc.close_buffer_with_last_view == "yes")
2720 else if (lyxrc.close_buffer_with_last_view == "no")
2721 close_buffer = false;
2724 if (buf.isUnnamed())
2725 file = from_utf8(buf.fileName().onlyFileName());
2727 file = buf.fileName().displayName(30);
2728 docstring const text = bformat(
2729 _("Last view on document %1$s is being closed.\n"
2730 "Would you like to close or hide the document?\n"
2732 "Hidden documents can be displayed back through\n"
2733 "the menu: View->Hidden->...\n"
2735 "To remove this question, set your preference in:\n"
2736 " Tools->Preferences->Look&Feel->UserInterface\n"
2738 int ret = Alert::prompt(_("Close or hide document?"),
2739 text, 0, 1, _("&Close"), _("&Hide"));
2740 close_buffer = (ret == 0);
2744 return closeWorkArea(wa, close_buffer);
2748 bool GuiView::closeBuffer()
2750 GuiWorkArea * wa = currentMainWorkArea();
2751 setCurrentWorkArea(wa);
2752 Buffer & buf = wa->bufferView().buffer();
2753 return wa && closeWorkArea(wa, !buf.parent());
2757 void GuiView::writeSession() const {
2758 GuiWorkArea const * active_wa = currentMainWorkArea();
2759 for (int i = 0; i < d.splitter_->count(); ++i) {
2760 TabWorkArea * twa = d.tabWorkArea(i);
2761 for (int j = 0; j < twa->count(); ++j) {
2762 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2763 Buffer & buf = wa->bufferView().buffer();
2764 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2770 bool GuiView::closeBufferAll()
2772 // Close the workareas in all other views
2773 QList<int> const ids = guiApp->viewIds();
2774 for (int i = 0; i != ids.size(); ++i) {
2775 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2779 // Close our own workareas
2780 if (!closeWorkAreaAll())
2783 // Now close the hidden buffers. We prevent hidden buffers from being
2784 // dirty, so we can just close them.
2785 theBufferList().closeAll();
2790 bool GuiView::closeWorkAreaAll()
2792 setCurrentWorkArea(currentMainWorkArea());
2794 // We might be in a situation that there is still a tabWorkArea, but
2795 // there are no tabs anymore. This can happen when we get here after a
2796 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2797 // many TabWorkArea's have no documents anymore.
2800 // We have to call count() each time, because it can happen that
2801 // more than one splitter will disappear in one iteration (bug 5998).
2802 while (d.splitter_->count() > empty_twa) {
2803 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2805 if (twa->count() == 0)
2808 setCurrentWorkArea(twa->currentWorkArea());
2809 if (!closeTabWorkArea(twa))
2817 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2822 Buffer & buf = wa->bufferView().buffer();
2824 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2825 Alert::warning(_("Close document"),
2826 _("Document could not be closed because it is being processed by LyX."));
2831 return closeBuffer(buf);
2833 if (!inMultiTabs(wa))
2834 if (!saveBufferIfNeeded(buf, true))
2842 bool GuiView::closeBuffer(Buffer & buf)
2844 // If we are in a close_event all children will be closed in some time,
2845 // so no need to do it here. This will ensure that the children end up
2846 // in the session file in the correct order. If we close the master
2847 // buffer, we can close or release the child buffers here too.
2848 bool success = true;
2850 ListOfBuffers clist = buf.getChildren();
2851 ListOfBuffers::const_iterator it = clist.begin();
2852 ListOfBuffers::const_iterator const bend = clist.end();
2853 for (; it != bend; ++it) {
2854 Buffer * child_buf = *it;
2855 if (theBufferList().isOthersChild(&buf, child_buf)) {
2856 child_buf->setParent(0);
2860 // FIXME: should we look in other tabworkareas?
2861 // ANSWER: I don't think so. I've tested, and if the child is
2862 // open in some other window, it closes without a problem.
2863 GuiWorkArea * child_wa = workArea(*child_buf);
2865 success = closeWorkArea(child_wa, true);
2869 // In this case the child buffer is open but hidden.
2870 // It therefore should not (MUST NOT) be dirty!
2871 LATTEST(child_buf->isClean());
2872 theBufferList().release(child_buf);
2877 // goto bookmark to update bookmark pit.
2878 // FIXME: we should update only the bookmarks related to this buffer!
2879 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2880 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2881 guiApp->gotoBookmark(i+1, false, false);
2883 if (saveBufferIfNeeded(buf, false)) {
2884 buf.removeAutosaveFile();
2885 theBufferList().release(&buf);
2889 // open all children again to avoid a crash because of dangling
2890 // pointers (bug 6603)
2896 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2898 while (twa == d.currentTabWorkArea()) {
2899 twa->setCurrentIndex(twa->count() - 1);
2901 GuiWorkArea * wa = twa->currentWorkArea();
2902 Buffer & b = wa->bufferView().buffer();
2904 // We only want to close the buffer if the same buffer is not visible
2905 // in another view, and if this is not a child and if we are closing
2906 // a view (not a tabgroup).
2907 bool const close_buffer =
2908 !inOtherView(b) && !b.parent() && closing_;
2910 if (!closeWorkArea(wa, close_buffer))
2917 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2919 if (buf.isClean() || buf.paragraphs().empty())
2922 // Switch to this Buffer.
2927 if (buf.isUnnamed())
2928 file = from_utf8(buf.fileName().onlyFileName());
2930 file = buf.fileName().displayName(30);
2932 // Bring this window to top before asking questions.
2937 if (hiding && buf.isUnnamed()) {
2938 docstring const text = bformat(_("The document %1$s has not been "
2939 "saved yet.\n\nDo you want to save "
2940 "the document?"), file);
2941 ret = Alert::prompt(_("Save new document?"),
2942 text, 0, 1, _("&Save"), _("&Cancel"));
2946 docstring const text = bformat(_("The document %1$s has unsaved changes."
2947 "\n\nDo you want to save the document or discard the changes?"), file);
2948 ret = Alert::prompt(_("Save changed document?"),
2949 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2954 if (!saveBuffer(buf))
2958 // If we crash after this we could have no autosave file
2959 // but I guess this is really improbable (Jug).
2960 // Sometimes improbable things happen:
2961 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2962 // buf.removeAutosaveFile();
2964 // revert all changes
2975 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2977 Buffer & buf = wa->bufferView().buffer();
2979 for (int i = 0; i != d.splitter_->count(); ++i) {
2980 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2981 if (wa_ && wa_ != wa)
2984 return inOtherView(buf);
2988 bool GuiView::inOtherView(Buffer & buf)
2990 QList<int> const ids = guiApp->viewIds();
2992 for (int i = 0; i != ids.size(); ++i) {
2996 if (guiApp->view(ids[i]).workArea(buf))
3003 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3005 if (!documentBufferView())
3008 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3009 Buffer * const curbuf = &documentBufferView()->buffer();
3010 int nwa = twa->count();
3011 for (int i = 0; i < nwa; ++i) {
3012 if (&workArea(i)->bufferView().buffer() == curbuf) {
3014 if (np == NEXTBUFFER)
3015 next_index = (i == nwa - 1 ? 0 : i + 1);
3017 next_index = (i == 0 ? nwa - 1 : i - 1);
3019 twa->moveTab(i, next_index);
3021 setBuffer(&workArea(next_index)->bufferView().buffer());
3029 /// make sure the document is saved
3030 static bool ensureBufferClean(Buffer * buffer)
3032 LASSERT(buffer, return false);
3033 if (buffer->isClean() && !buffer->isUnnamed())
3036 docstring const file = buffer->fileName().displayName(30);
3039 if (!buffer->isUnnamed()) {
3040 text = bformat(_("The document %1$s has unsaved "
3041 "changes.\n\nDo you want to save "
3042 "the document?"), file);
3043 title = _("Save changed document?");
3046 text = bformat(_("The document %1$s has not been "
3047 "saved yet.\n\nDo you want to save "
3048 "the document?"), file);
3049 title = _("Save new document?");
3051 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3054 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3056 return buffer->isClean() && !buffer->isUnnamed();
3060 bool GuiView::reloadBuffer(Buffer & buf)
3062 currentBufferView()->cursor().reset();
3063 Buffer::ReadStatus status = buf.reload();
3064 return status == Buffer::ReadSuccess;
3068 void GuiView::checkExternallyModifiedBuffers()
3070 BufferList::iterator bit = theBufferList().begin();
3071 BufferList::iterator const bend = theBufferList().end();
3072 for (; bit != bend; ++bit) {
3073 Buffer * buf = *bit;
3074 if (buf->fileName().exists()
3075 && buf->isExternallyModified(Buffer::checksum_method)) {
3076 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3077 " Reload now? Any local changes will be lost."),
3078 from_utf8(buf->absFileName()));
3079 int const ret = Alert::prompt(_("Reload externally changed document?"),
3080 text, 0, 1, _("&Reload"), _("&Cancel"));
3088 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3090 Buffer * buffer = documentBufferView()
3091 ? &(documentBufferView()->buffer()) : 0;
3093 switch (cmd.action()) {
3094 case LFUN_VC_REGISTER:
3095 if (!buffer || !ensureBufferClean(buffer))
3097 if (!buffer->lyxvc().inUse()) {
3098 if (buffer->lyxvc().registrer()) {
3099 reloadBuffer(*buffer);
3100 dr.clearMessageUpdate();
3105 case LFUN_VC_RENAME:
3106 case LFUN_VC_COPY: {
3107 if (!buffer || !ensureBufferClean(buffer))
3109 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3110 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3111 // Some changes are not yet committed.
3112 // We test here and not in getStatus(), since
3113 // this test is expensive.
3115 LyXVC::CommandResult ret =
3116 buffer->lyxvc().checkIn(log);
3118 if (ret == LyXVC::ErrorCommand ||
3119 ret == LyXVC::VCSuccess)
3120 reloadBuffer(*buffer);
3121 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3122 frontend::Alert::error(
3123 _("Revision control error."),
3124 _("Document could not be checked in."));
3128 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3129 LV_VC_RENAME : LV_VC_COPY;
3130 renameBuffer(*buffer, cmd.argument(), kind);
3135 case LFUN_VC_CHECK_IN:
3136 if (!buffer || !ensureBufferClean(buffer))
3138 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3140 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3142 // Only skip reloading if the checkin was cancelled or
3143 // an error occurred before the real checkin VCS command
3144 // was executed, since the VCS might have changed the
3145 // file even if it could not checkin successfully.
3146 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3147 reloadBuffer(*buffer);
3151 case LFUN_VC_CHECK_OUT:
3152 if (!buffer || !ensureBufferClean(buffer))
3154 if (buffer->lyxvc().inUse()) {
3155 dr.setMessage(buffer->lyxvc().checkOut());
3156 reloadBuffer(*buffer);
3160 case LFUN_VC_LOCKING_TOGGLE:
3161 LASSERT(buffer, return);
3162 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3164 if (buffer->lyxvc().inUse()) {
3165 string res = buffer->lyxvc().lockingToggle();
3167 frontend::Alert::error(_("Revision control error."),
3168 _("Error when setting the locking property."));
3171 reloadBuffer(*buffer);
3176 case LFUN_VC_REVERT:
3177 LASSERT(buffer, return);
3178 if (buffer->lyxvc().revert()) {
3179 reloadBuffer(*buffer);
3180 dr.clearMessageUpdate();
3184 case LFUN_VC_UNDO_LAST:
3185 LASSERT(buffer, return);
3186 buffer->lyxvc().undoLast();
3187 reloadBuffer(*buffer);
3188 dr.clearMessageUpdate();
3191 case LFUN_VC_REPO_UPDATE:
3192 LASSERT(buffer, return);
3193 if (ensureBufferClean(buffer)) {
3194 dr.setMessage(buffer->lyxvc().repoUpdate());
3195 checkExternallyModifiedBuffers();
3199 case LFUN_VC_COMMAND: {
3200 string flag = cmd.getArg(0);
3201 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3204 if (contains(flag, 'M')) {
3205 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3208 string path = cmd.getArg(1);
3209 if (contains(path, "$$p") && buffer)
3210 path = subst(path, "$$p", buffer->filePath());
3211 LYXERR(Debug::LYXVC, "Directory: " << path);
3213 if (!pp.isReadableDirectory()) {
3214 lyxerr << _("Directory is not accessible.") << endl;
3217 support::PathChanger p(pp);
3219 string command = cmd.getArg(2);
3220 if (command.empty())
3223 command = subst(command, "$$i", buffer->absFileName());
3224 command = subst(command, "$$p", buffer->filePath());
3226 command = subst(command, "$$m", to_utf8(message));
3227 LYXERR(Debug::LYXVC, "Command: " << command);
3229 one.startscript(Systemcall::Wait, command);
3233 if (contains(flag, 'I'))
3234 buffer->markDirty();
3235 if (contains(flag, 'R'))
3236 reloadBuffer(*buffer);
3241 case LFUN_VC_COMPARE: {
3243 if (cmd.argument().empty()) {
3244 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3248 string rev1 = cmd.getArg(0);
3252 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3255 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3256 f2 = buffer->absFileName();
3258 string rev2 = cmd.getArg(1);
3262 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3266 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3267 f1 << "\n" << f2 << "\n" );
3268 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3269 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3279 void GuiView::openChildDocument(string const & fname)
3281 LASSERT(documentBufferView(), return);
3282 Buffer & buffer = documentBufferView()->buffer();
3283 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3284 documentBufferView()->saveBookmark(false);
3286 if (theBufferList().exists(filename)) {
3287 child = theBufferList().getBuffer(filename);
3290 message(bformat(_("Opening child document %1$s..."),
3291 makeDisplayPath(filename.absFileName())));
3292 child = loadDocument(filename, false);
3294 // Set the parent name of the child document.
3295 // This makes insertion of citations and references in the child work,
3296 // when the target is in the parent or another child document.
3298 child->setParent(&buffer);
3302 bool GuiView::goToFileRow(string const & argument)
3306 size_t i = argument.find_last_of(' ');
3307 if (i != string::npos) {
3308 file_name = os::internal_path(trim(argument.substr(0, i)));
3309 istringstream is(argument.substr(i + 1));
3314 if (i == string::npos) {
3315 LYXERR0("Wrong argument: " << argument);
3319 string const abstmp = package().temp_dir().absFileName();
3320 string const realtmp = package().temp_dir().realPath();
3321 // We have to use os::path_prefix_is() here, instead of
3322 // simply prefixIs(), because the file name comes from
3323 // an external application and may need case adjustment.
3324 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3325 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3326 // Needed by inverse dvi search. If it is a file
3327 // in tmpdir, call the apropriated function.
3328 // If tmpdir is a symlink, we may have the real
3329 // path passed back, so we correct for that.
3330 if (!prefixIs(file_name, abstmp))
3331 file_name = subst(file_name, realtmp, abstmp);
3332 buf = theBufferList().getBufferFromTmp(file_name);
3334 // Must replace extension of the file to be .lyx
3335 // and get full path
3336 FileName const s = fileSearch(string(),
3337 support::changeExtension(file_name, ".lyx"), "lyx");
3338 // Either change buffer or load the file
3339 if (theBufferList().exists(s))
3340 buf = theBufferList().getBuffer(s);
3341 else if (s.exists()) {
3342 buf = loadDocument(s);
3347 _("File does not exist: %1$s"),
3348 makeDisplayPath(file_name)));
3354 _("No buffer for file: %1$s."),
3355 makeDisplayPath(file_name))
3360 documentBufferView()->setCursorFromRow(row);
3365 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3367 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3368 menu->exec(QCursor::pos());
3373 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3375 Buffer::ExportStatus const status = func(format);
3377 // the cloning operation will have produced a clone of the entire set of
3378 // documents, starting from the master. so we must delete those.
3379 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3381 busyBuffers.remove(orig);
3386 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3388 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3389 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3393 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3395 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3396 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3400 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3402 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3403 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3407 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3408 string const & argument,
3409 Buffer const * used_buffer,
3410 docstring const & msg,
3411 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3412 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3413 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3418 string format = argument;
3420 format = used_buffer->params().getDefaultOutputFormat();
3421 processing_format = format;
3423 progress_->clearMessages();
3426 #if EXPORT_in_THREAD
3427 GuiViewPrivate::busyBuffers.insert(used_buffer);
3428 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3429 if (!cloned_buffer) {
3430 Alert::error(_("Export Error"),
3431 _("Error cloning the Buffer."));
3434 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3439 setPreviewFuture(f);
3440 last_export_format = used_buffer->params().bufferFormat();
3443 // We are asynchronous, so we don't know here anything about the success
3446 Buffer::ExportStatus status;
3448 // TODO check here if it breaks exporting with Qt < 4.4
3449 status = (used_buffer->*syncFunc)(format, true);
3450 } else if (previewFunc) {
3451 status = (used_buffer->*previewFunc)(format);
3454 handleExportStatus(gv_, status, format);
3456 return (status == Buffer::ExportSuccess
3457 || status == Buffer::PreviewSuccess);
3461 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3463 BufferView * bv = currentBufferView();
3464 LASSERT(bv, return);
3466 // Let the current BufferView dispatch its own actions.
3467 bv->dispatch(cmd, dr);
3468 if (dr.dispatched())
3471 // Try with the document BufferView dispatch if any.
3472 BufferView * doc_bv = documentBufferView();
3473 if (doc_bv && doc_bv != bv) {
3474 doc_bv->dispatch(cmd, dr);
3475 if (dr.dispatched())
3479 // Then let the current Cursor dispatch its own actions.
3480 bv->cursor().dispatch(cmd);
3482 // update completion. We do it here and not in
3483 // processKeySym to avoid another redraw just for a
3484 // changed inline completion
3485 if (cmd.origin() == FuncRequest::KEYBOARD) {
3486 if (cmd.action() == LFUN_SELF_INSERT
3487 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3488 updateCompletion(bv->cursor(), true, true);
3489 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3490 updateCompletion(bv->cursor(), false, true);
3492 updateCompletion(bv->cursor(), false, false);
3495 dr = bv->cursor().result();
3499 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3501 BufferView * bv = currentBufferView();
3502 // By default we won't need any update.
3503 dr.screenUpdate(Update::None);
3504 // assume cmd will be dispatched
3505 dr.dispatched(true);
3507 Buffer * doc_buffer = documentBufferView()
3508 ? &(documentBufferView()->buffer()) : 0;
3510 if (cmd.origin() == FuncRequest::TOC) {
3511 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3512 // FIXME: do we need to pass a DispatchResult object here?
3513 toc->doDispatch(bv->cursor(), cmd);
3517 string const argument = to_utf8(cmd.argument());
3519 switch(cmd.action()) {
3520 case LFUN_BUFFER_CHILD_OPEN:
3521 openChildDocument(to_utf8(cmd.argument()));
3524 case LFUN_BUFFER_IMPORT:
3525 importDocument(to_utf8(cmd.argument()));
3528 case LFUN_BUFFER_EXPORT: {
3531 FileName target_dir = doc_buffer->fileName().onlyPath();
3532 string const dest = cmd.getArg(1);
3533 if (!dest.empty() && FileName::isAbsolute(dest))
3534 target_dir = FileName(support::onlyPath(dest));
3535 // GCC only sees strfwd.h when building merged
3536 if (::lyx::operator==(cmd.argument(), "custom")) {
3537 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3540 if ((dest.empty() && doc_buffer->isUnnamed())
3541 || !target_dir.isDirWritable()) {
3542 exportBufferAs(*doc_buffer, cmd.argument());
3545 /* TODO/Review: Is it a problem to also export the children?
3546 See the update_unincluded flag */
3547 d.asyncBufferProcessing(argument,
3550 &GuiViewPrivate::exportAndDestroy,
3553 // TODO Inform user about success
3557 case LFUN_BUFFER_EXPORT_AS: {
3558 LASSERT(doc_buffer, break);
3559 docstring f = cmd.argument();
3561 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3562 exportBufferAs(*doc_buffer, f);
3566 case LFUN_BUFFER_UPDATE: {
3567 d.asyncBufferProcessing(argument,
3570 &GuiViewPrivate::compileAndDestroy,
3575 case LFUN_BUFFER_VIEW: {
3576 d.asyncBufferProcessing(argument,
3578 _("Previewing ..."),
3579 &GuiViewPrivate::previewAndDestroy,
3584 case LFUN_MASTER_BUFFER_UPDATE: {
3585 d.asyncBufferProcessing(argument,
3586 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3588 &GuiViewPrivate::compileAndDestroy,
3593 case LFUN_MASTER_BUFFER_VIEW: {
3594 d.asyncBufferProcessing(argument,
3595 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3597 &GuiViewPrivate::previewAndDestroy,
3598 0, &Buffer::preview);
3601 case LFUN_BUFFER_SWITCH: {
3602 string const file_name = to_utf8(cmd.argument());
3603 if (!FileName::isAbsolute(file_name)) {
3605 dr.setMessage(_("Absolute filename expected."));
3609 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3612 dr.setMessage(_("Document not loaded"));
3616 // Do we open or switch to the buffer in this view ?
3617 if (workArea(*buffer)
3618 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3623 // Look for the buffer in other views
3624 QList<int> const ids = guiApp->viewIds();
3626 for (; i != ids.size(); ++i) {
3627 GuiView & gv = guiApp->view(ids[i]);
3628 if (gv.workArea(*buffer)) {
3630 gv.activateWindow();
3632 gv.setBuffer(buffer);
3637 // If necessary, open a new window as a last resort
3638 if (i == ids.size()) {
3639 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3645 case LFUN_BUFFER_NEXT:
3646 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3649 case LFUN_BUFFER_MOVE_NEXT:
3650 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3653 case LFUN_BUFFER_PREVIOUS:
3654 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3657 case LFUN_BUFFER_MOVE_PREVIOUS:
3658 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3661 case LFUN_COMMAND_EXECUTE: {
3662 command_execute_ = true;
3663 minibuffer_focus_ = true;
3666 case LFUN_DROP_LAYOUTS_CHOICE:
3667 d.layout_->showPopup();
3670 case LFUN_MENU_OPEN:
3671 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3672 menu->exec(QCursor::pos());
3675 case LFUN_FILE_INSERT:
3676 insertLyXFile(cmd.argument());
3679 case LFUN_FILE_INSERT_PLAINTEXT:
3680 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3681 string const fname = to_utf8(cmd.argument());
3682 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3683 dr.setMessage(_("Absolute filename expected."));
3687 FileName filename(fname);
3688 if (fname.empty()) {
3689 FileDialog dlg(qt_("Select file to insert"));
3691 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3692 QStringList(qt_("All Files (*)")));
3694 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3695 dr.setMessage(_("Canceled."));
3699 filename.set(fromqstr(result.second));
3703 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3704 bv->dispatch(new_cmd, dr);
3709 case LFUN_BUFFER_RELOAD: {
3710 LASSERT(doc_buffer, break);
3713 if (!doc_buffer->isClean()) {
3714 docstring const file =
3715 makeDisplayPath(doc_buffer->absFileName(), 20);
3716 docstring text = bformat(_("Any changes will be lost. "
3717 "Are you sure you want to revert to the saved version "
3718 "of the document %1$s?"), file);
3719 ret = Alert::prompt(_("Revert to saved document?"),
3720 text, 1, 1, _("&Revert"), _("&Cancel"));
3724 doc_buffer->markClean();
3725 reloadBuffer(*doc_buffer);
3726 dr.forceBufferUpdate();
3731 case LFUN_BUFFER_WRITE:
3732 LASSERT(doc_buffer, break);
3733 saveBuffer(*doc_buffer);
3736 case LFUN_BUFFER_WRITE_AS:
3737 LASSERT(doc_buffer, break);
3738 renameBuffer(*doc_buffer, cmd.argument());
3741 case LFUN_BUFFER_WRITE_ALL: {
3742 Buffer * first = theBufferList().first();
3745 message(_("Saving all documents..."));
3746 // We cannot use a for loop as the buffer list cycles.
3749 if (!b->isClean()) {
3751 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3753 b = theBufferList().next(b);
3754 } while (b != first);
3755 dr.setMessage(_("All documents saved."));
3759 case LFUN_BUFFER_CLOSE:
3763 case LFUN_BUFFER_CLOSE_ALL:
3767 case LFUN_TOOLBAR_TOGGLE: {
3768 string const name = cmd.getArg(0);
3769 if (GuiToolbar * t = toolbar(name))
3774 case LFUN_ICON_SIZE: {
3775 QSize size = d.iconSize(cmd.argument());
3777 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3778 size.width(), size.height()));
3782 case LFUN_DIALOG_UPDATE: {
3783 string const name = to_utf8(cmd.argument());
3784 if (name == "prefs" || name == "document")
3785 updateDialog(name, string());
3786 else if (name == "paragraph")
3787 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3788 else if (currentBufferView()) {
3789 Inset * inset = currentBufferView()->editedInset(name);
3790 // Can only update a dialog connected to an existing inset
3792 // FIXME: get rid of this indirection; GuiView ask the inset
3793 // if he is kind enough to update itself...
3794 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3795 //FIXME: pass DispatchResult here?
3796 inset->dispatch(currentBufferView()->cursor(), fr);
3802 case LFUN_DIALOG_TOGGLE: {
3803 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3804 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3805 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3809 case LFUN_DIALOG_DISCONNECT_INSET:
3810 disconnectDialog(to_utf8(cmd.argument()));
3813 case LFUN_DIALOG_HIDE: {
3814 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3818 case LFUN_DIALOG_SHOW: {
3819 string const name = cmd.getArg(0);
3820 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3822 if (name == "character") {
3823 data = freefont2string();
3825 showDialog("character", data);
3826 } else if (name == "latexlog") {
3827 Buffer::LogType type;
3828 string const logfile = doc_buffer->logName(&type);
3830 case Buffer::latexlog:
3833 case Buffer::buildlog:
3837 data += Lexer::quoteString(logfile);
3838 showDialog("log", data);
3839 } else if (name == "vclog") {
3840 string const data = "vc " +
3841 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3842 showDialog("log", data);
3843 } else if (name == "symbols") {
3844 data = bv->cursor().getEncoding()->name();
3846 showDialog("symbols", data);
3848 } else if (name == "prefs" && isFullScreen()) {
3849 lfunUiToggle("fullscreen");
3850 showDialog("prefs", data);
3852 showDialog(name, data);
3857 dr.setMessage(cmd.argument());
3860 case LFUN_UI_TOGGLE: {
3861 string arg = cmd.getArg(0);
3862 if (!lfunUiToggle(arg)) {
3863 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3864 dr.setMessage(bformat(msg, from_utf8(arg)));
3866 // Make sure the keyboard focus stays in the work area.
3871 case LFUN_VIEW_SPLIT: {
3872 LASSERT(doc_buffer, break);
3873 string const orientation = cmd.getArg(0);
3874 d.splitter_->setOrientation(orientation == "vertical"
3875 ? Qt::Vertical : Qt::Horizontal);
3876 TabWorkArea * twa = addTabWorkArea();
3877 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3878 setCurrentWorkArea(wa);
3881 case LFUN_TAB_GROUP_CLOSE:
3882 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3883 closeTabWorkArea(twa);
3884 d.current_work_area_ = 0;
3885 twa = d.currentTabWorkArea();
3886 // Switch to the next GuiWorkArea in the found TabWorkArea.
3888 // Make sure the work area is up to date.
3889 setCurrentWorkArea(twa->currentWorkArea());
3891 setCurrentWorkArea(0);
3896 case LFUN_VIEW_CLOSE:
3897 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3898 closeWorkArea(twa->currentWorkArea());
3899 d.current_work_area_ = 0;
3900 twa = d.currentTabWorkArea();
3901 // Switch to the next GuiWorkArea in the found TabWorkArea.
3903 // Make sure the work area is up to date.
3904 setCurrentWorkArea(twa->currentWorkArea());
3906 setCurrentWorkArea(0);
3911 case LFUN_COMPLETION_INLINE:
3912 if (d.current_work_area_)
3913 d.current_work_area_->completer().showInline();
3916 case LFUN_COMPLETION_POPUP:
3917 if (d.current_work_area_)
3918 d.current_work_area_->completer().showPopup();
3923 if (d.current_work_area_)
3924 d.current_work_area_->completer().tab();
3927 case LFUN_COMPLETION_CANCEL:
3928 if (d.current_work_area_) {
3929 if (d.current_work_area_->completer().popupVisible())
3930 d.current_work_area_->completer().hidePopup();
3932 d.current_work_area_->completer().hideInline();
3936 case LFUN_COMPLETION_ACCEPT:
3937 if (d.current_work_area_)
3938 d.current_work_area_->completer().activate();
3941 case LFUN_BUFFER_ZOOM_IN:
3942 case LFUN_BUFFER_ZOOM_OUT: {
3943 // use a signed temp to avoid overflow
3944 int zoom = lyxrc.zoom;
3945 if (cmd.argument().empty()) {
3946 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3951 zoom += convert<int>(cmd.argument());
3957 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3959 // The global QPixmapCache is used in GuiPainter to cache text
3960 // painting so we must reset it.
3961 QPixmapCache::clear();
3962 guiApp->fontLoader().update();
3963 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3967 case LFUN_VC_REGISTER:
3968 case LFUN_VC_RENAME:
3970 case LFUN_VC_CHECK_IN:
3971 case LFUN_VC_CHECK_OUT:
3972 case LFUN_VC_REPO_UPDATE:
3973 case LFUN_VC_LOCKING_TOGGLE:
3974 case LFUN_VC_REVERT:
3975 case LFUN_VC_UNDO_LAST:
3976 case LFUN_VC_COMMAND:
3977 case LFUN_VC_COMPARE:
3978 dispatchVC(cmd, dr);
3981 case LFUN_SERVER_GOTO_FILE_ROW:
3982 goToFileRow(to_utf8(cmd.argument()));
3985 case LFUN_FORWARD_SEARCH: {
3986 Buffer const * doc_master = doc_buffer->masterBuffer();
3987 FileName const path(doc_master->temppath());
3988 string const texname = doc_master->isChild(doc_buffer)
3989 ? DocFileName(changeExtension(
3990 doc_buffer->absFileName(),
3991 "tex")).mangledFileName()
3992 : doc_buffer->latexName();
3993 string const fulltexname =
3994 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3995 string const mastername =
3996 removeExtension(doc_master->latexName());
3997 FileName const dviname(addName(path.absFileName(),
3998 addExtension(mastername, "dvi")));
3999 FileName const pdfname(addName(path.absFileName(),
4000 addExtension(mastername, "pdf")));
4001 bool const have_dvi = dviname.exists();
4002 bool const have_pdf = pdfname.exists();
4003 if (!have_dvi && !have_pdf) {
4004 dr.setMessage(_("Please, preview the document first."));
4007 string outname = dviname.onlyFileName();
4008 string command = lyxrc.forward_search_dvi;
4009 if (!have_dvi || (have_pdf &&
4010 pdfname.lastModified() > dviname.lastModified())) {
4011 outname = pdfname.onlyFileName();
4012 command = lyxrc.forward_search_pdf;
4015 DocIterator cur = bv->cursor();
4016 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4017 LYXERR(Debug::ACTION, "Forward search: row:" << row
4019 if (row == -1 || command.empty()) {
4020 dr.setMessage(_("Couldn't proceed."));
4023 string texrow = convert<string>(row);
4025 command = subst(command, "$$n", texrow);
4026 command = subst(command, "$$f", fulltexname);
4027 command = subst(command, "$$t", texname);
4028 command = subst(command, "$$o", outname);
4030 PathChanger p(path);
4032 one.startscript(Systemcall::DontWait, command);
4036 case LFUN_SPELLING_CONTINUOUSLY:
4037 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4038 dr.screenUpdate(Update::Force | Update::FitCursor);
4042 // The LFUN must be for one of BufferView, Buffer or Cursor;
4044 dispatchToBufferView(cmd, dr);
4048 // Part of automatic menu appearance feature.
4049 if (isFullScreen()) {
4050 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4054 // Need to update bv because many LFUNs here might have destroyed it
4055 bv = currentBufferView();
4057 // Clear non-empty selections
4058 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4060 Cursor & cur = bv->cursor();
4061 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4062 cur.clearSelection();
4068 bool GuiView::lfunUiToggle(string const & ui_component)
4070 if (ui_component == "scrollbar") {
4071 // hide() is of no help
4072 if (d.current_work_area_->verticalScrollBarPolicy() ==
4073 Qt::ScrollBarAlwaysOff)
4075 d.current_work_area_->setVerticalScrollBarPolicy(
4076 Qt::ScrollBarAsNeeded);
4078 d.current_work_area_->setVerticalScrollBarPolicy(
4079 Qt::ScrollBarAlwaysOff);
4080 } else if (ui_component == "statusbar") {
4081 statusBar()->setVisible(!statusBar()->isVisible());
4082 } else if (ui_component == "menubar") {
4083 menuBar()->setVisible(!menuBar()->isVisible());
4085 if (ui_component == "frame") {
4087 getContentsMargins(&l, &t, &r, &b);
4088 //are the frames in default state?
4089 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4091 setContentsMargins(-2, -2, -2, -2);
4093 setContentsMargins(0, 0, 0, 0);
4096 if (ui_component == "fullscreen") {
4104 void GuiView::toggleFullScreen()
4106 if (isFullScreen()) {
4107 for (int i = 0; i != d.splitter_->count(); ++i)
4108 d.tabWorkArea(i)->setFullScreen(false);
4109 setContentsMargins(0, 0, 0, 0);
4110 setWindowState(windowState() ^ Qt::WindowFullScreen);
4113 statusBar()->show();
4116 hideDialogs("prefs", 0);
4117 for (int i = 0; i != d.splitter_->count(); ++i)
4118 d.tabWorkArea(i)->setFullScreen(true);
4119 setContentsMargins(-2, -2, -2, -2);
4121 setWindowState(windowState() ^ Qt::WindowFullScreen);
4122 if (lyxrc.full_screen_statusbar)
4123 statusBar()->hide();
4124 if (lyxrc.full_screen_menubar)
4126 if (lyxrc.full_screen_toolbars) {
4127 ToolbarMap::iterator end = d.toolbars_.end();
4128 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4133 // give dialogs like the TOC a chance to adapt
4138 Buffer const * GuiView::updateInset(Inset const * inset)
4143 Buffer const * inset_buffer = &(inset->buffer());
4145 for (int i = 0; i != d.splitter_->count(); ++i) {
4146 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4149 Buffer const * buffer = &(wa->bufferView().buffer());
4150 if (inset_buffer == buffer)
4151 wa->scheduleRedraw();
4153 return inset_buffer;
4157 void GuiView::restartCursor()
4159 /* When we move around, or type, it's nice to be able to see
4160 * the cursor immediately after the keypress.
4162 if (d.current_work_area_)
4163 d.current_work_area_->startBlinkingCursor();
4165 // Take this occasion to update the other GUI elements.
4171 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4173 if (d.current_work_area_)
4174 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4179 // This list should be kept in sync with the list of insets in
4180 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4181 // dialog should have the same name as the inset.
4182 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4183 // docs in LyXAction.cpp.
4185 char const * const dialognames[] = {
4187 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4188 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4189 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4190 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4191 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4192 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4193 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4194 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4196 char const * const * const end_dialognames =
4197 dialognames + (sizeof(dialognames) / sizeof(char *));
4201 cmpCStr(char const * name) : name_(name) {}
4202 bool operator()(char const * other) {
4203 return strcmp(other, name_) == 0;
4210 bool isValidName(string const & name)
4212 return find_if(dialognames, end_dialognames,
4213 cmpCStr(name.c_str())) != end_dialognames;
4219 void GuiView::resetDialogs()
4221 // Make sure that no LFUN uses any GuiView.
4222 guiApp->setCurrentView(0);
4226 constructToolbars();
4227 guiApp->menus().fillMenuBar(menuBar(), this, false);
4228 d.layout_->updateContents(true);
4229 // Now update controls with current buffer.
4230 guiApp->setCurrentView(this);
4236 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4238 if (!isValidName(name))
4241 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4243 if (it != d.dialogs_.end()) {
4245 it->second->hideView();
4246 return it->second.get();
4249 Dialog * dialog = build(name);
4250 d.dialogs_[name].reset(dialog);
4251 if (lyxrc.allow_geometry_session)
4252 dialog->restoreSession();
4259 void GuiView::showDialog(string const & name, string const & data,
4262 triggerShowDialog(toqstr(name), toqstr(data), inset);
4266 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4272 const string name = fromqstr(qname);
4273 const string data = fromqstr(qdata);
4277 Dialog * dialog = findOrBuild(name, false);
4279 bool const visible = dialog->isVisibleView();
4280 dialog->showData(data);
4281 if (inset && currentBufferView())
4282 currentBufferView()->editInset(name, inset);
4283 // We only set the focus to the new dialog if it was not yet
4284 // visible in order not to change the existing previous behaviour
4286 // activateWindow is needed for floating dockviews
4287 dialog->asQWidget()->raise();
4288 dialog->asQWidget()->activateWindow();
4289 dialog->asQWidget()->setFocus();
4293 catch (ExceptionMessage const & ex) {
4301 bool GuiView::isDialogVisible(string const & name) const
4303 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4304 if (it == d.dialogs_.end())
4306 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4310 void GuiView::hideDialog(string const & name, Inset * inset)
4312 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4313 if (it == d.dialogs_.end())
4317 if (!currentBufferView())
4319 if (inset != currentBufferView()->editedInset(name))
4323 Dialog * const dialog = it->second.get();
4324 if (dialog->isVisibleView())
4326 if (currentBufferView())
4327 currentBufferView()->editInset(name, 0);
4331 void GuiView::disconnectDialog(string const & name)
4333 if (!isValidName(name))
4335 if (currentBufferView())
4336 currentBufferView()->editInset(name, 0);
4340 void GuiView::hideAll() const
4342 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4343 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4345 for(; it != end; ++it)
4346 it->second->hideView();
4350 void GuiView::updateDialogs()
4352 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4353 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4355 for(; it != end; ++it) {
4356 Dialog * dialog = it->second.get();
4358 if (dialog->needBufferOpen() && !documentBufferView())
4359 hideDialog(fromqstr(dialog->name()), 0);
4360 else if (dialog->isVisibleView())
4361 dialog->checkStatus();
4368 Dialog * createDialog(GuiView & lv, string const & name);
4370 // will be replaced by a proper factory...
4371 Dialog * createGuiAbout(GuiView & lv);
4372 Dialog * createGuiBibtex(GuiView & lv);
4373 Dialog * createGuiChanges(GuiView & lv);
4374 Dialog * createGuiCharacter(GuiView & lv);
4375 Dialog * createGuiCitation(GuiView & lv);
4376 Dialog * createGuiCompare(GuiView & lv);
4377 Dialog * createGuiCompareHistory(GuiView & lv);
4378 Dialog * createGuiDelimiter(GuiView & lv);
4379 Dialog * createGuiDocument(GuiView & lv);
4380 Dialog * createGuiErrorList(GuiView & lv);
4381 Dialog * createGuiExternal(GuiView & lv);
4382 Dialog * createGuiGraphics(GuiView & lv);
4383 Dialog * createGuiInclude(GuiView & lv);
4384 Dialog * createGuiIndex(GuiView & lv);
4385 Dialog * createGuiListings(GuiView & lv);
4386 Dialog * createGuiLog(GuiView & lv);
4387 Dialog * createGuiMathMatrix(GuiView & lv);
4388 Dialog * createGuiNote(GuiView & lv);
4389 Dialog * createGuiParagraph(GuiView & lv);
4390 Dialog * createGuiPhantom(GuiView & lv);
4391 Dialog * createGuiPreferences(GuiView & lv);
4392 Dialog * createGuiPrint(GuiView & lv);
4393 Dialog * createGuiPrintindex(GuiView & lv);
4394 Dialog * createGuiRef(GuiView & lv);
4395 Dialog * createGuiSearch(GuiView & lv);
4396 Dialog * createGuiSearchAdv(GuiView & lv);
4397 Dialog * createGuiSendTo(GuiView & lv);
4398 Dialog * createGuiShowFile(GuiView & lv);
4399 Dialog * createGuiSpellchecker(GuiView & lv);
4400 Dialog * createGuiSymbols(GuiView & lv);
4401 Dialog * createGuiTabularCreate(GuiView & lv);
4402 Dialog * createGuiTexInfo(GuiView & lv);
4403 Dialog * createGuiToc(GuiView & lv);
4404 Dialog * createGuiThesaurus(GuiView & lv);
4405 Dialog * createGuiViewSource(GuiView & lv);
4406 Dialog * createGuiWrap(GuiView & lv);
4407 Dialog * createGuiProgressView(GuiView & lv);
4411 Dialog * GuiView::build(string const & name)
4413 LASSERT(isValidName(name), return 0);
4415 Dialog * dialog = createDialog(*this, name);
4419 if (name == "aboutlyx")
4420 return createGuiAbout(*this);
4421 if (name == "bibtex")
4422 return createGuiBibtex(*this);
4423 if (name == "changes")
4424 return createGuiChanges(*this);
4425 if (name == "character")
4426 return createGuiCharacter(*this);
4427 if (name == "citation")
4428 return createGuiCitation(*this);
4429 if (name == "compare")
4430 return createGuiCompare(*this);
4431 if (name == "comparehistory")
4432 return createGuiCompareHistory(*this);
4433 if (name == "document")
4434 return createGuiDocument(*this);
4435 if (name == "errorlist")
4436 return createGuiErrorList(*this);
4437 if (name == "external")
4438 return createGuiExternal(*this);
4440 return createGuiShowFile(*this);
4441 if (name == "findreplace")
4442 return createGuiSearch(*this);
4443 if (name == "findreplaceadv")
4444 return createGuiSearchAdv(*this);
4445 if (name == "graphics")
4446 return createGuiGraphics(*this);
4447 if (name == "include")
4448 return createGuiInclude(*this);
4449 if (name == "index")
4450 return createGuiIndex(*this);
4451 if (name == "index_print")
4452 return createGuiPrintindex(*this);
4453 if (name == "listings")
4454 return createGuiListings(*this);
4456 return createGuiLog(*this);
4457 if (name == "mathdelimiter")
4458 return createGuiDelimiter(*this);
4459 if (name == "mathmatrix")
4460 return createGuiMathMatrix(*this);
4462 return createGuiNote(*this);
4463 if (name == "paragraph")
4464 return createGuiParagraph(*this);
4465 if (name == "phantom")
4466 return createGuiPhantom(*this);
4467 if (name == "prefs")
4468 return createGuiPreferences(*this);
4470 return createGuiRef(*this);
4471 if (name == "sendto")
4472 return createGuiSendTo(*this);
4473 if (name == "spellchecker")
4474 return createGuiSpellchecker(*this);
4475 if (name == "symbols")
4476 return createGuiSymbols(*this);
4477 if (name == "tabularcreate")
4478 return createGuiTabularCreate(*this);
4479 if (name == "texinfo")
4480 return createGuiTexInfo(*this);
4481 if (name == "thesaurus")
4482 return createGuiThesaurus(*this);
4484 return createGuiToc(*this);
4485 if (name == "view-source")
4486 return createGuiViewSource(*this);
4488 return createGuiWrap(*this);
4489 if (name == "progress")
4490 return createGuiProgressView(*this);
4496 } // namespace frontend
4499 #include "moc_GuiView.cpp"