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.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
388 GuiWorkArea * current_work_area_;
389 GuiWorkArea * current_main_work_area_;
390 QSplitter * splitter_;
391 QStackedWidget * stack_widget_;
392 BackgroundWidget * bg_widget_;
394 ToolbarMap toolbars_;
395 ProgressInterface* progress_;
396 /// The main layout box.
398 * \warning Don't Delete! The layout box is actually owned by
399 * whichever toolbar contains it. All the GuiView class needs is a
400 * means of accessing it.
402 * FIXME: replace that with a proper model so that we are not limited
403 * to only one dialog.
408 map<string, DialogPtr> dialogs_;
410 unsigned int smallIconSize;
411 unsigned int normalIconSize;
412 unsigned int bigIconSize;
413 unsigned int hugeIconSize;
414 unsigned int giantIconSize;
416 QTimer statusbar_timer_;
417 /// auto-saving of buffers
418 Timeout autosave_timeout_;
419 /// flag against a race condition due to multiclicks, see bug #1119
423 TocModels toc_models_;
426 QFutureWatcher<docstring> autosave_watcher_;
427 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
429 string last_export_format;
430 string processing_format;
432 static QSet<Buffer const *> busyBuffers;
433 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
434 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
435 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
436 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
439 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
441 // TODO syncFunc/previewFunc: use bind
442 bool asyncBufferProcessing(string const & argument,
443 Buffer const * used_buffer,
444 docstring const & msg,
445 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
446 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
447 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
449 QVector<GuiWorkArea*> guiWorkAreas();
452 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
455 GuiView::GuiView(int id)
456 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
457 command_execute_(false), minibuffer_focus_(false)
459 connect(this, SIGNAL(bufferViewChanged()),
460 this, SLOT(onBufferViewChanged()));
462 // GuiToolbars *must* be initialised before the menu bar.
463 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
466 // set ourself as the current view. This is needed for the menu bar
467 // filling, at least for the static special menu item on Mac. Otherwise
468 // they are greyed out.
469 guiApp->setCurrentView(this);
471 // Fill up the menu bar.
472 guiApp->menus().fillMenuBar(menuBar(), this, true);
474 setCentralWidget(d.stack_widget_);
476 // Start autosave timer
477 if (lyxrc.autosave) {
478 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
479 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
480 d.autosave_timeout_.start();
482 connect(&d.statusbar_timer_, SIGNAL(timeout()),
483 this, SLOT(clearMessage()));
485 // We don't want to keep the window in memory if it is closed.
486 setAttribute(Qt::WA_DeleteOnClose, true);
488 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
489 // QIcon::fromTheme was introduced in Qt 4.6
490 #if (QT_VERSION >= 0x040600)
491 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
492 // since the icon is provided in the application bundle. We use a themed
493 // version when available and use the bundled one as fallback.
494 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
496 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
502 // use tabbed dock area for multiple docks
503 // (such as "source" and "messages")
504 setDockOptions(QMainWindow::ForceTabbedDocks);
507 setAcceptDrops(true);
509 // add busy indicator to statusbar
510 QLabel * busylabel = new QLabel(statusBar());
511 statusBar()->addPermanentWidget(busylabel);
512 search_mode mode = theGuiApp()->imageSearchMode();
513 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
514 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
515 busylabel->setMovie(busyanim);
519 connect(&d.processing_thread_watcher_, SIGNAL(started()),
520 busylabel, SLOT(show()));
521 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
522 busylabel, SLOT(hide()));
524 QFontMetrics const fm(statusBar()->fontMetrics());
525 int const roheight = max(int(d.normalIconSize), fm.height());
526 QSize const rosize(roheight, roheight);
527 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
528 read_only_ = new QLabel(statusBar());
529 read_only_->setPixmap(readonly);
530 read_only_->setScaledContents(true);
531 read_only_->setAlignment(Qt::AlignCenter);
533 statusBar()->addPermanentWidget(read_only_);
535 version_control_ = new QLabel(statusBar());
536 version_control_->setAlignment(Qt::AlignCenter);
537 version_control_->setFrameStyle(QFrame::StyledPanel);
538 version_control_->hide();
539 statusBar()->addPermanentWidget(version_control_);
541 statusBar()->setSizeGripEnabled(true);
544 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
545 SLOT(autoSaveThreadFinished()));
547 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
548 SLOT(processingThreadStarted()));
549 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
550 SLOT(processingThreadFinished()));
552 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
553 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
555 // set custom application bars context menu, e.g. tool bar and menu bar
556 setContextMenuPolicy(Qt::CustomContextMenu);
557 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
558 SLOT(toolBarPopup(const QPoint &)));
560 // Forbid too small unresizable window because it can happen
561 // with some window manager under X11.
562 setMinimumSize(300, 200);
564 if (lyxrc.allow_geometry_session) {
565 // Now take care of session management.
570 // no session handling, default to a sane size.
571 setGeometry(50, 50, 690, 510);
574 // clear session data if any.
576 settings.remove("views");
586 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
588 QVector<GuiWorkArea*> areas;
589 for (int i = 0; i < tabWorkAreaCount(); i++) {
590 TabWorkArea* ta = tabWorkArea(i);
591 for (int u = 0; u < ta->count(); u++) {
592 areas << ta->workArea(u);
598 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
599 string const & format)
601 docstring const fmt = formats.prettyName(format);
604 case Buffer::ExportSuccess:
605 msg = bformat(_("Successful export to format: %1$s"), fmt);
607 case Buffer::ExportCancel:
608 msg = _("Document export cancelled.");
610 case Buffer::ExportError:
611 case Buffer::ExportNoPathToFormat:
612 case Buffer::ExportTexPathHasSpaces:
613 case Buffer::ExportConverterError:
614 msg = bformat(_("Error while exporting format: %1$s"), fmt);
616 case Buffer::PreviewSuccess:
617 msg = bformat(_("Successful preview of format: %1$s"), fmt);
619 case Buffer::PreviewError:
620 msg = bformat(_("Error while previewing format: %1$s"), fmt);
627 void GuiView::processingThreadStarted()
632 void GuiView::processingThreadFinished()
634 QFutureWatcher<Buffer::ExportStatus> const * watcher =
635 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
637 Buffer::ExportStatus const status = watcher->result();
638 handleExportStatus(this, status, d.processing_format);
641 BufferView const * const bv = currentBufferView();
642 if (bv && !bv->buffer().errorList("Export").empty()) {
646 errors(d.last_export_format);
650 void GuiView::autoSaveThreadFinished()
652 QFutureWatcher<docstring> const * watcher =
653 static_cast<QFutureWatcher<docstring> const *>(sender());
654 message(watcher->result());
659 void GuiView::saveLayout() const
662 settings.beginGroup("views");
663 settings.beginGroup(QString::number(id_));
664 #if defined(Q_WS_X11) || defined(QPA_XCB)
665 settings.setValue("pos", pos());
666 settings.setValue("size", size());
668 settings.setValue("geometry", saveGeometry());
670 settings.setValue("layout", saveState(0));
671 settings.setValue("icon_size", iconSize());
675 void GuiView::saveUISettings() const
677 // Save the toolbar private states
678 ToolbarMap::iterator end = d.toolbars_.end();
679 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
680 it->second->saveSession();
681 // Now take care of all other dialogs
682 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
683 for (; it!= d.dialogs_.end(); ++it)
684 it->second->saveSession();
688 bool GuiView::restoreLayout()
691 settings.beginGroup("views");
692 settings.beginGroup(QString::number(id_));
693 QString const icon_key = "icon_size";
694 if (!settings.contains(icon_key))
697 //code below is skipped when when ~/.config/LyX is (re)created
698 QSize icon_size = settings.value(icon_key).toSize();
699 // Check whether session size changed.
700 setIconSize(icon_size);
702 #if defined(Q_WS_X11) || defined(QPA_XCB)
703 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
704 QSize size = settings.value("size", QSize(690, 510)).toSize();
708 // Work-around for bug #6034: the window ends up in an undetermined
709 // state when trying to restore a maximized window when it is
710 // already maximized.
711 if (!(windowState() & Qt::WindowMaximized))
712 if (!restoreGeometry(settings.value("geometry").toByteArray()))
713 setGeometry(50, 50, 690, 510);
715 // Make sure layout is correctly oriented.
716 setLayoutDirection(qApp->layoutDirection());
718 // Allow the toc and view-source dock widget to be restored if needed.
720 if ((dialog = findOrBuild("toc", true)))
721 // see bug 5082. At least setup title and enabled state.
722 // Visibility will be adjusted by restoreState below.
723 dialog->prepareView();
724 if ((dialog = findOrBuild("view-source", true)))
725 dialog->prepareView();
726 if ((dialog = findOrBuild("progress", true)))
727 dialog->prepareView();
729 if (!restoreState(settings.value("layout").toByteArray(), 0))
732 // init the toolbars that have not been restored
733 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
734 Toolbars::Infos::iterator end = guiApp->toolbars().end();
735 for (; cit != end; ++cit) {
736 GuiToolbar * tb = toolbar(cit->name);
737 if (tb && !tb->isRestored())
738 initToolbar(cit->name);
746 GuiToolbar * GuiView::toolbar(string const & name)
748 ToolbarMap::iterator it = d.toolbars_.find(name);
749 if (it != d.toolbars_.end())
752 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
757 void GuiView::constructToolbars()
759 ToolbarMap::iterator it = d.toolbars_.begin();
760 for (; it != d.toolbars_.end(); ++it)
764 // I don't like doing this here, but the standard toolbar
765 // destroys this object when it's destroyed itself (vfr)
766 d.layout_ = new LayoutBox(*this);
767 d.stack_widget_->addWidget(d.layout_);
768 d.layout_->move(0,0);
770 // extracts the toolbars from the backend
771 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
772 Toolbars::Infos::iterator end = guiApp->toolbars().end();
773 for (; cit != end; ++cit)
774 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
778 void GuiView::initToolbars()
780 // extracts the toolbars from the backend
781 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
782 Toolbars::Infos::iterator end = guiApp->toolbars().end();
783 for (; cit != end; ++cit)
784 initToolbar(cit->name);
788 void GuiView::initToolbar(string const & name)
790 GuiToolbar * tb = toolbar(name);
793 int const visibility = guiApp->toolbars().defaultVisibility(name);
794 bool newline = !(visibility & Toolbars::SAMEROW);
795 tb->setVisible(false);
796 tb->setVisibility(visibility);
798 if (visibility & Toolbars::TOP) {
800 addToolBarBreak(Qt::TopToolBarArea);
801 addToolBar(Qt::TopToolBarArea, tb);
804 if (visibility & Toolbars::BOTTOM) {
806 addToolBarBreak(Qt::BottomToolBarArea);
807 addToolBar(Qt::BottomToolBarArea, tb);
810 if (visibility & Toolbars::LEFT) {
812 addToolBarBreak(Qt::LeftToolBarArea);
813 addToolBar(Qt::LeftToolBarArea, tb);
816 if (visibility & Toolbars::RIGHT) {
818 addToolBarBreak(Qt::RightToolBarArea);
819 addToolBar(Qt::RightToolBarArea, tb);
822 if (visibility & Toolbars::ON)
823 tb->setVisible(true);
827 TocModels & GuiView::tocModels()
829 return d.toc_models_;
833 void GuiView::setFocus()
835 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
836 QMainWindow::setFocus();
840 bool GuiView::hasFocus() const
842 if (currentWorkArea())
843 return currentWorkArea()->hasFocus();
844 if (currentMainWorkArea())
845 return currentMainWorkArea()->hasFocus();
846 return d.bg_widget_->hasFocus();
850 void GuiView::focusInEvent(QFocusEvent * e)
852 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
853 QMainWindow::focusInEvent(e);
854 // Make sure guiApp points to the correct view.
855 guiApp->setCurrentView(this);
856 if (currentWorkArea())
857 currentWorkArea()->setFocus();
858 else if (currentMainWorkArea())
859 currentMainWorkArea()->setFocus();
861 d.bg_widget_->setFocus();
865 void GuiView::showEvent(QShowEvent * e)
867 LYXERR(Debug::GUI, "Passed Geometry "
868 << size().height() << "x" << size().width()
869 << "+" << pos().x() << "+" << pos().y());
871 if (d.splitter_->count() == 0)
872 // No work area, switch to the background widget.
876 QMainWindow::showEvent(e);
880 bool GuiView::closeScheduled()
887 bool GuiView::prepareAllBuffersForLogout()
889 Buffer * first = theBufferList().first();
893 // First, iterate over all buffers and ask the users if unsaved
894 // changes should be saved.
895 // We cannot use a for loop as the buffer list cycles.
898 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
900 b = theBufferList().next(b);
901 } while (b != first);
903 // Next, save session state
904 // When a view/window was closed before without quitting LyX, there
905 // are already entries in the lastOpened list.
906 theSession().lastOpened().clear();
913 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
914 ** is responsibility of the container (e.g., dialog)
916 void GuiView::closeEvent(QCloseEvent * close_event)
918 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
920 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
921 Alert::warning(_("Exit LyX"),
922 _("LyX could not be closed because documents are being processed by LyX."));
923 close_event->setAccepted(false);
927 // If the user pressed the x (so we didn't call closeView
928 // programmatically), we want to clear all existing entries.
930 theSession().lastOpened().clear();
935 // it can happen that this event arrives without selecting the view,
936 // e.g. when clicking the close button on a background window.
938 if (!closeWorkAreaAll()) {
940 close_event->ignore();
944 // Make sure that nothing will use this to be closed View.
945 guiApp->unregisterView(this);
947 if (isFullScreen()) {
948 // Switch off fullscreen before closing.
953 // Make sure the timer time out will not trigger a statusbar update.
954 d.statusbar_timer_.stop();
956 // Saving fullscreen requires additional tweaks in the toolbar code.
957 // It wouldn't also work under linux natively.
958 if (lyxrc.allow_geometry_session) {
963 close_event->accept();
967 void GuiView::dragEnterEvent(QDragEnterEvent * event)
969 if (event->mimeData()->hasUrls())
971 /// \todo Ask lyx-devel is this is enough:
972 /// if (event->mimeData()->hasFormat("text/plain"))
973 /// event->acceptProposedAction();
977 void GuiView::dropEvent(QDropEvent * event)
979 QList<QUrl> files = event->mimeData()->urls();
983 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
984 for (int i = 0; i != files.size(); ++i) {
985 string const file = os::internal_path(fromqstr(
986 files.at(i).toLocalFile()));
990 string const ext = support::getExtension(file);
991 vector<const Format *> found_formats;
993 // Find all formats that have the correct extension.
994 vector<const Format *> const & import_formats
995 = theConverters().importableFormats();
996 vector<const Format *>::const_iterator it = import_formats.begin();
997 for (; it != import_formats.end(); ++it)
998 if ((*it)->hasExtension(ext))
999 found_formats.push_back(*it);
1002 if (found_formats.size() >= 1) {
1003 if (found_formats.size() > 1) {
1004 //FIXME: show a dialog to choose the correct importable format
1005 LYXERR(Debug::FILES,
1006 "Multiple importable formats found, selecting first");
1008 string const arg = found_formats[0]->name() + " " + file;
1009 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1012 //FIXME: do we have to explicitly check whether it's a lyx file?
1013 LYXERR(Debug::FILES,
1014 "No formats found, trying to open it as a lyx file");
1015 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1017 // add the functions to the queue
1018 guiApp->addToFuncRequestQueue(cmd);
1021 // now process the collected functions. We perform the events
1022 // asynchronously. This prevents potential problems in case the
1023 // BufferView is closed within an event.
1024 guiApp->processFuncRequestQueueAsync();
1028 void GuiView::message(docstring const & str)
1030 if (ForkedProcess::iAmAChild())
1033 // call is moved to GUI-thread by GuiProgress
1034 d.progress_->appendMessage(toqstr(str));
1038 void GuiView::clearMessageText()
1040 message(docstring());
1044 void GuiView::updateStatusBarMessage(QString const & str)
1046 statusBar()->showMessage(str);
1047 d.statusbar_timer_.stop();
1048 d.statusbar_timer_.start(3000);
1052 void GuiView::clearMessage()
1054 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1055 // the hasFocus function mostly returns false, even if the focus is on
1056 // a workarea in this view.
1060 d.statusbar_timer_.stop();
1064 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1066 if (wa != d.current_work_area_
1067 || wa->bufferView().buffer().isInternal())
1069 Buffer const & buf = wa->bufferView().buffer();
1070 // Set the windows title
1071 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1073 title += from_ascii(" - LyX");
1075 setWindowTitle(toqstr(title));
1076 // Sets the path for the window: this is used by OSX to
1077 // allow a context click on the title bar showing a menu
1078 // with the path up to the file
1079 setWindowFilePath(toqstr(buf.absFileName()));
1080 // Tell Qt whether the current document is changed
1081 setWindowModified(!buf.isClean());
1083 if (buf.isReadonly())
1088 if (buf.lyxvc().inUse()) {
1089 version_control_->show();
1090 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1092 version_control_->hide();
1096 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1098 if (d.current_work_area_)
1099 // disconnect the current work area from all slots
1100 QObject::disconnect(d.current_work_area_, 0, this, 0);
1102 disconnectBufferView();
1103 connectBufferView(wa->bufferView());
1104 connectBuffer(wa->bufferView().buffer());
1105 d.current_work_area_ = wa;
1106 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1107 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1108 QObject::connect(wa, SIGNAL(busy(bool)),
1109 this, SLOT(setBusy(bool)));
1110 // connection of a signal to a signal
1111 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1112 this, SIGNAL(bufferViewChanged()));
1113 Q_EMIT updateWindowTitle(wa);
1114 Q_EMIT bufferViewChanged();
1118 void GuiView::onBufferViewChanged()
1121 // Buffer-dependent dialogs must be updated. This is done here because
1122 // some dialogs require buffer()->text.
1127 void GuiView::on_lastWorkAreaRemoved()
1130 // We already are in a close event. Nothing more to do.
1133 if (d.splitter_->count() > 1)
1134 // We have a splitter so don't close anything.
1137 // Reset and updates the dialogs.
1138 Q_EMIT bufferViewChanged();
1143 if (lyxrc.open_buffers_in_tabs)
1144 // Nothing more to do, the window should stay open.
1147 if (guiApp->viewIds().size() > 1) {
1153 // On Mac we also close the last window because the application stay
1154 // resident in memory. On other platforms we don't close the last
1155 // window because this would quit the application.
1161 void GuiView::updateStatusBar()
1163 // let the user see the explicit message
1164 if (d.statusbar_timer_.isActive())
1171 void GuiView::showMessage()
1175 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1176 if (msg.isEmpty()) {
1177 BufferView const * bv = currentBufferView();
1179 msg = toqstr(bv->cursor().currentState());
1181 msg = qt_("Welcome to LyX!");
1183 statusBar()->showMessage(msg);
1187 bool GuiView::event(QEvent * e)
1191 // Useful debug code:
1192 //case QEvent::ActivationChange:
1193 //case QEvent::WindowDeactivate:
1194 //case QEvent::Paint:
1195 //case QEvent::Enter:
1196 //case QEvent::Leave:
1197 //case QEvent::HoverEnter:
1198 //case QEvent::HoverLeave:
1199 //case QEvent::HoverMove:
1200 //case QEvent::StatusTip:
1201 //case QEvent::DragEnter:
1202 //case QEvent::DragLeave:
1203 //case QEvent::Drop:
1206 case QEvent::WindowActivate: {
1207 GuiView * old_view = guiApp->currentView();
1208 if (this == old_view) {
1210 return QMainWindow::event(e);
1212 if (old_view && old_view->currentBufferView()) {
1213 // save current selection to the selection buffer to allow
1214 // middle-button paste in this window.
1215 cap::saveSelection(old_view->currentBufferView()->cursor());
1217 guiApp->setCurrentView(this);
1218 if (d.current_work_area_)
1219 on_currentWorkAreaChanged(d.current_work_area_);
1223 return QMainWindow::event(e);
1226 case QEvent::ShortcutOverride: {
1228 if (isFullScreen() && menuBar()->isHidden()) {
1229 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1230 // FIXME: we should also try to detect special LyX shortcut such as
1231 // Alt-P and Alt-M. Right now there is a hack in
1232 // GuiWorkArea::processKeySym() that hides again the menubar for
1234 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1236 return QMainWindow::event(e);
1239 return QMainWindow::event(e);
1243 return QMainWindow::event(e);
1247 void GuiView::resetWindowTitle()
1249 setWindowTitle(qt_("LyX"));
1252 bool GuiView::focusNextPrevChild(bool /*next*/)
1259 bool GuiView::busy() const
1265 void GuiView::setBusy(bool busy)
1267 bool const busy_before = busy_ > 0;
1268 busy ? ++busy_ : --busy_;
1269 if ((busy_ > 0) == busy_before)
1270 // busy state didn't change
1274 QApplication::setOverrideCursor(Qt::WaitCursor);
1277 QApplication::restoreOverrideCursor();
1282 void GuiView::resetCommandExecute()
1284 command_execute_ = false;
1289 double GuiView::pixelRatio() const
1291 #if QT_VERSION >= 0x050000
1292 return devicePixelRatio();
1299 GuiWorkArea * GuiView::workArea(int index)
1301 if (TabWorkArea * twa = d.currentTabWorkArea())
1302 if (index < twa->count())
1303 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1308 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1310 if (currentWorkArea()
1311 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1312 return currentWorkArea();
1313 if (TabWorkArea * twa = d.currentTabWorkArea())
1314 return twa->workArea(buffer);
1319 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1321 // Automatically create a TabWorkArea if there are none yet.
1322 TabWorkArea * tab_widget = d.splitter_->count()
1323 ? d.currentTabWorkArea() : addTabWorkArea();
1324 return tab_widget->addWorkArea(buffer, *this);
1328 TabWorkArea * GuiView::addTabWorkArea()
1330 TabWorkArea * twa = new TabWorkArea;
1331 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1332 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1333 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1334 this, SLOT(on_lastWorkAreaRemoved()));
1336 d.splitter_->addWidget(twa);
1337 d.stack_widget_->setCurrentWidget(d.splitter_);
1342 GuiWorkArea const * GuiView::currentWorkArea() const
1344 return d.current_work_area_;
1348 GuiWorkArea * GuiView::currentWorkArea()
1350 return d.current_work_area_;
1354 GuiWorkArea const * GuiView::currentMainWorkArea() const
1356 if (!d.currentTabWorkArea())
1358 return d.currentTabWorkArea()->currentWorkArea();
1362 GuiWorkArea * GuiView::currentMainWorkArea()
1364 if (!d.currentTabWorkArea())
1366 return d.currentTabWorkArea()->currentWorkArea();
1370 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1372 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1374 d.current_work_area_ = 0;
1376 Q_EMIT bufferViewChanged();
1380 // FIXME: I've no clue why this is here and why it accesses
1381 // theGuiApp()->currentView, which might be 0 (bug 6464).
1382 // See also 27525 (vfr).
1383 if (theGuiApp()->currentView() == this
1384 && theGuiApp()->currentView()->currentWorkArea() == wa)
1387 if (currentBufferView())
1388 cap::saveSelection(currentBufferView()->cursor());
1390 theGuiApp()->setCurrentView(this);
1391 d.current_work_area_ = wa;
1393 // We need to reset this now, because it will need to be
1394 // right if the tabWorkArea gets reset in the for loop. We
1395 // will change it back if we aren't in that case.
1396 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1397 d.current_main_work_area_ = wa;
1399 for (int i = 0; i != d.splitter_->count(); ++i) {
1400 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1401 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1402 << ", Current main wa: " << currentMainWorkArea());
1407 d.current_main_work_area_ = old_cmwa;
1409 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1410 on_currentWorkAreaChanged(wa);
1411 BufferView & bv = wa->bufferView();
1412 bv.cursor().fixIfBroken();
1414 wa->setUpdatesEnabled(true);
1415 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1419 void GuiView::removeWorkArea(GuiWorkArea * wa)
1421 LASSERT(wa, return);
1422 if (wa == d.current_work_area_) {
1424 disconnectBufferView();
1425 d.current_work_area_ = 0;
1426 d.current_main_work_area_ = 0;
1429 bool found_twa = false;
1430 for (int i = 0; i != d.splitter_->count(); ++i) {
1431 TabWorkArea * twa = d.tabWorkArea(i);
1432 if (twa->removeWorkArea(wa)) {
1433 // Found in this tab group, and deleted the GuiWorkArea.
1435 if (twa->count() != 0) {
1436 if (d.current_work_area_ == 0)
1437 // This means that we are closing the current GuiWorkArea, so
1438 // switch to the next GuiWorkArea in the found TabWorkArea.
1439 setCurrentWorkArea(twa->currentWorkArea());
1441 // No more WorkAreas in this tab group, so delete it.
1448 // It is not a tabbed work area (i.e., the search work area), so it
1449 // should be deleted by other means.
1450 LASSERT(found_twa, return);
1452 if (d.current_work_area_ == 0) {
1453 if (d.splitter_->count() != 0) {
1454 TabWorkArea * twa = d.currentTabWorkArea();
1455 setCurrentWorkArea(twa->currentWorkArea());
1457 // No more work areas, switch to the background widget.
1458 setCurrentWorkArea(0);
1464 LayoutBox * GuiView::getLayoutDialog() const
1470 void GuiView::updateLayoutList()
1473 d.layout_->updateContents(false);
1477 void GuiView::updateToolbars()
1479 ToolbarMap::iterator end = d.toolbars_.end();
1480 if (d.current_work_area_) {
1482 if (d.current_work_area_->bufferView().cursor().inMathed()
1483 && !d.current_work_area_->bufferView().cursor().inRegexped())
1484 context |= Toolbars::MATH;
1485 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1486 context |= Toolbars::TABLE;
1487 if (currentBufferView()->buffer().areChangesPresent()
1488 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1489 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1490 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1491 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1492 context |= Toolbars::REVIEW;
1493 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1494 context |= Toolbars::MATHMACROTEMPLATE;
1495 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1496 context |= Toolbars::IPA;
1497 if (command_execute_)
1498 context |= Toolbars::MINIBUFFER;
1499 if (minibuffer_focus_) {
1500 context |= Toolbars::MINIBUFFER_FOCUS;
1501 minibuffer_focus_ = false;
1504 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1505 it->second->update(context);
1507 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1508 it->second->update();
1512 void GuiView::setBuffer(Buffer * newBuffer)
1514 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1515 LASSERT(newBuffer, return);
1517 GuiWorkArea * wa = workArea(*newBuffer);
1520 newBuffer->masterBuffer()->updateBuffer();
1522 wa = addWorkArea(*newBuffer);
1523 // scroll to the position when the BufferView was last closed
1524 if (lyxrc.use_lastfilepos) {
1525 LastFilePosSection::FilePos filepos =
1526 theSession().lastFilePos().load(newBuffer->fileName());
1527 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1530 //Disconnect the old buffer...there's no new one.
1533 connectBuffer(*newBuffer);
1534 connectBufferView(wa->bufferView());
1535 setCurrentWorkArea(wa);
1539 void GuiView::connectBuffer(Buffer & buf)
1541 buf.setGuiDelegate(this);
1545 void GuiView::disconnectBuffer()
1547 if (d.current_work_area_)
1548 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1552 void GuiView::connectBufferView(BufferView & bv)
1554 bv.setGuiDelegate(this);
1558 void GuiView::disconnectBufferView()
1560 if (d.current_work_area_)
1561 d.current_work_area_->bufferView().setGuiDelegate(0);
1565 void GuiView::errors(string const & error_type, bool from_master)
1567 BufferView const * const bv = currentBufferView();
1571 #if EXPORT_in_THREAD
1572 // We are called with from_master == false by default, so we
1573 // have to figure out whether that is the case or not.
1574 ErrorList & el = bv->buffer().errorList(error_type);
1576 el = bv->buffer().masterBuffer()->errorList(error_type);
1580 ErrorList const & el = from_master ?
1581 bv->buffer().masterBuffer()->errorList(error_type) :
1582 bv->buffer().errorList(error_type);
1588 string data = error_type;
1590 data = "from_master|" + error_type;
1591 showDialog("errorlist", data);
1595 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1597 d.toc_models_.updateItem(toqstr(type), dit);
1601 void GuiView::structureChanged()
1603 // FIXME: This is slightly expensive, though less than the tocBackend update
1604 // (#9880). This also resets the view in the Toc Widget (#6675).
1605 d.toc_models_.reset(documentBufferView());
1606 // Navigator needs more than a simple update in this case. It needs to be
1608 updateDialog("toc", "");
1612 void GuiView::updateDialog(string const & name, string const & data)
1614 if (!isDialogVisible(name))
1617 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1618 if (it == d.dialogs_.end())
1621 Dialog * const dialog = it->second.get();
1622 if (dialog->isVisibleView())
1623 dialog->initialiseParams(data);
1627 BufferView * GuiView::documentBufferView()
1629 return currentMainWorkArea()
1630 ? ¤tMainWorkArea()->bufferView()
1635 BufferView const * GuiView::documentBufferView() const
1637 return currentMainWorkArea()
1638 ? ¤tMainWorkArea()->bufferView()
1643 BufferView * GuiView::currentBufferView()
1645 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1649 BufferView const * GuiView::currentBufferView() const
1651 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1655 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1656 Buffer const * orig, Buffer * clone)
1658 bool const success = clone->autoSave();
1660 busyBuffers.remove(orig);
1662 ? _("Automatic save done.")
1663 : _("Automatic save failed!");
1667 void GuiView::autoSave()
1669 LYXERR(Debug::INFO, "Running autoSave()");
1671 Buffer * buffer = documentBufferView()
1672 ? &documentBufferView()->buffer() : 0;
1674 resetAutosaveTimers();
1678 GuiViewPrivate::busyBuffers.insert(buffer);
1679 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1680 buffer, buffer->cloneBufferOnly());
1681 d.autosave_watcher_.setFuture(f);
1682 resetAutosaveTimers();
1686 void GuiView::resetAutosaveTimers()
1689 d.autosave_timeout_.restart();
1693 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1696 Buffer * buf = currentBufferView()
1697 ? ¤tBufferView()->buffer() : 0;
1698 Buffer * doc_buffer = documentBufferView()
1699 ? &(documentBufferView()->buffer()) : 0;
1702 /* In LyX/Mac, when a dialog is open, the menus of the
1703 application can still be accessed without giving focus to
1704 the main window. In this case, we want to disable the menu
1705 entries that are buffer-related.
1706 This code must not be used on Linux and Windows, since it
1707 would disable buffer-related entries when hovering over the
1708 menu (see bug #9574).
1710 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1716 // Check whether we need a buffer
1717 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1718 // no, exit directly
1719 flag.message(from_utf8(N_("Command not allowed with"
1720 "out any document open")));
1721 flag.setEnabled(false);
1725 if (cmd.origin() == FuncRequest::TOC) {
1726 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1727 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1728 flag.setEnabled(false);
1732 switch(cmd.action()) {
1733 case LFUN_BUFFER_IMPORT:
1736 case LFUN_MASTER_BUFFER_UPDATE:
1737 case LFUN_MASTER_BUFFER_VIEW:
1739 && (doc_buffer->parent() != 0
1740 || doc_buffer->hasChildren())
1741 && !d.processing_thread_watcher_.isRunning();
1744 case LFUN_BUFFER_UPDATE:
1745 case LFUN_BUFFER_VIEW: {
1746 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1750 string format = to_utf8(cmd.argument());
1751 if (cmd.argument().empty())
1752 format = doc_buffer->params().getDefaultOutputFormat();
1753 enable = doc_buffer->params().isExportable(format, true);
1757 case LFUN_BUFFER_RELOAD:
1758 enable = doc_buffer && !doc_buffer->isUnnamed()
1759 && doc_buffer->fileName().exists()
1760 && (!doc_buffer->isClean()
1761 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1764 case LFUN_BUFFER_CHILD_OPEN:
1765 enable = doc_buffer != 0;
1768 case LFUN_BUFFER_WRITE:
1769 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1772 //FIXME: This LFUN should be moved to GuiApplication.
1773 case LFUN_BUFFER_WRITE_ALL: {
1774 // We enable the command only if there are some modified buffers
1775 Buffer * first = theBufferList().first();
1780 // We cannot use a for loop as the buffer list is a cycle.
1782 if (!b->isClean()) {
1786 b = theBufferList().next(b);
1787 } while (b != first);
1791 case LFUN_BUFFER_WRITE_AS:
1792 case LFUN_BUFFER_EXPORT_AS:
1793 enable = doc_buffer != 0;
1796 case LFUN_BUFFER_CLOSE:
1797 case LFUN_VIEW_CLOSE:
1798 enable = doc_buffer != 0;
1801 case LFUN_BUFFER_CLOSE_ALL:
1802 enable = theBufferList().last() != theBufferList().first();
1805 case LFUN_VIEW_SPLIT:
1806 if (cmd.getArg(0) == "vertical")
1807 enable = doc_buffer && (d.splitter_->count() == 1 ||
1808 d.splitter_->orientation() == Qt::Vertical);
1810 enable = doc_buffer && (d.splitter_->count() == 1 ||
1811 d.splitter_->orientation() == Qt::Horizontal);
1814 case LFUN_TAB_GROUP_CLOSE:
1815 enable = d.tabWorkAreaCount() > 1;
1818 case LFUN_TOOLBAR_TOGGLE: {
1819 string const name = cmd.getArg(0);
1820 if (GuiToolbar * t = toolbar(name))
1821 flag.setOnOff(t->isVisible());
1824 docstring const msg =
1825 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1831 case LFUN_ICON_SIZE: {
1832 int const size = cmd.argument().empty() ? d.normalIconSize : convert<int>(cmd.argument());
1833 flag.setOnOff(QSize(size, size) == iconSize());
1837 case LFUN_DROP_LAYOUTS_CHOICE:
1841 case LFUN_UI_TOGGLE:
1842 flag.setOnOff(isFullScreen());
1845 case LFUN_DIALOG_DISCONNECT_INSET:
1848 case LFUN_DIALOG_HIDE:
1849 // FIXME: should we check if the dialog is shown?
1852 case LFUN_DIALOG_TOGGLE:
1853 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1854 // fall through to set "enable"
1855 case LFUN_DIALOG_SHOW: {
1856 string const name = cmd.getArg(0);
1858 enable = name == "aboutlyx"
1859 || name == "file" //FIXME: should be removed.
1861 || name == "texinfo"
1862 || name == "progress"
1863 || name == "compare";
1864 else if (name == "character" || name == "symbols"
1865 || name == "mathdelimiter" || name == "mathmatrix") {
1866 if (!buf || buf->isReadonly())
1869 Cursor const & cur = currentBufferView()->cursor();
1870 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1873 else if (name == "latexlog")
1874 enable = FileName(doc_buffer->logName()).isReadableFile();
1875 else if (name == "spellchecker")
1876 enable = theSpellChecker()
1877 && !doc_buffer->isReadonly()
1878 && !doc_buffer->text().empty();
1879 else if (name == "vclog")
1880 enable = doc_buffer->lyxvc().inUse();
1884 case LFUN_DIALOG_UPDATE: {
1885 string const name = cmd.getArg(0);
1887 enable = name == "prefs";
1891 case LFUN_COMMAND_EXECUTE:
1893 case LFUN_MENU_OPEN:
1894 // Nothing to check.
1897 case LFUN_COMPLETION_INLINE:
1898 if (!d.current_work_area_
1899 || !d.current_work_area_->completer().inlinePossible(
1900 currentBufferView()->cursor()))
1904 case LFUN_COMPLETION_POPUP:
1905 if (!d.current_work_area_
1906 || !d.current_work_area_->completer().popupPossible(
1907 currentBufferView()->cursor()))
1912 if (!d.current_work_area_
1913 || !d.current_work_area_->completer().inlinePossible(
1914 currentBufferView()->cursor()))
1918 case LFUN_COMPLETION_ACCEPT:
1919 if (!d.current_work_area_
1920 || (!d.current_work_area_->completer().popupVisible()
1921 && !d.current_work_area_->completer().inlineVisible()
1922 && !d.current_work_area_->completer().completionAvailable()))
1926 case LFUN_COMPLETION_CANCEL:
1927 if (!d.current_work_area_
1928 || (!d.current_work_area_->completer().popupVisible()
1929 && !d.current_work_area_->completer().inlineVisible()))
1933 case LFUN_BUFFER_ZOOM_OUT:
1934 case LFUN_BUFFER_ZOOM_IN: {
1935 // only diff between these two is that the default for ZOOM_OUT
1937 bool const neg_zoom =
1938 convert<int>(cmd.argument()) < 0 ||
1939 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
1940 if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
1941 docstring const msg =
1942 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
1946 enable = doc_buffer;
1949 case LFUN_BUFFER_MOVE_NEXT:
1950 case LFUN_BUFFER_MOVE_PREVIOUS:
1951 // we do not cycle when moving
1952 case LFUN_BUFFER_NEXT:
1953 case LFUN_BUFFER_PREVIOUS:
1954 // because we cycle, it doesn't matter whether on first or last
1955 enable = (d.currentTabWorkArea()->count() > 1);
1957 case LFUN_BUFFER_SWITCH:
1958 // toggle on the current buffer, but do not toggle off
1959 // the other ones (is that a good idea?)
1961 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1962 flag.setOnOff(true);
1965 case LFUN_VC_REGISTER:
1966 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1968 case LFUN_VC_RENAME:
1969 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
1972 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
1974 case LFUN_VC_CHECK_IN:
1975 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
1977 case LFUN_VC_CHECK_OUT:
1978 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
1980 case LFUN_VC_LOCKING_TOGGLE:
1981 enable = doc_buffer && !doc_buffer->isReadonly()
1982 && doc_buffer->lyxvc().lockingToggleEnabled();
1983 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
1985 case LFUN_VC_REVERT:
1986 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
1988 case LFUN_VC_UNDO_LAST:
1989 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
1991 case LFUN_VC_REPO_UPDATE:
1992 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
1994 case LFUN_VC_COMMAND: {
1995 if (cmd.argument().empty())
1997 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2001 case LFUN_VC_COMPARE:
2002 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2005 case LFUN_SERVER_GOTO_FILE_ROW:
2006 case LFUN_LYX_ACTIVATE:
2008 case LFUN_FORWARD_SEARCH:
2009 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2012 case LFUN_FILE_INSERT_PLAINTEXT:
2013 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2014 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2017 case LFUN_SPELLING_CONTINUOUSLY:
2018 flag.setOnOff(lyxrc.spellcheck_continuously);
2026 flag.setEnabled(false);
2032 static FileName selectTemplateFile()
2034 FileDialog dlg(qt_("Select template file"));
2035 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2036 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2038 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2039 QStringList(qt_("LyX Documents (*.lyx)")));
2041 if (result.first == FileDialog::Later)
2043 if (result.second.isEmpty())
2045 return FileName(fromqstr(result.second));
2049 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2053 Buffer * newBuffer = 0;
2055 newBuffer = checkAndLoadLyXFile(filename);
2056 } catch (ExceptionMessage const & e) {
2063 message(_("Document not loaded."));
2067 setBuffer(newBuffer);
2068 newBuffer->errors("Parse");
2071 theSession().lastFiles().add(filename);
2077 void GuiView::openDocument(string const & fname)
2079 string initpath = lyxrc.document_path;
2081 if (documentBufferView()) {
2082 string const trypath = documentBufferView()->buffer().filePath();
2083 // If directory is writeable, use this as default.
2084 if (FileName(trypath).isDirWritable())
2090 if (fname.empty()) {
2091 FileDialog dlg(qt_("Select document to open"));
2092 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2093 dlg.setButton2(qt_("Examples|#E#e"),
2094 toqstr(addPath(package().system_support().absFileName(), "examples")));
2096 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2097 FileDialog::Result result =
2098 dlg.open(toqstr(initpath), filter);
2100 if (result.first == FileDialog::Later)
2103 filename = fromqstr(result.second);
2105 // check selected filename
2106 if (filename.empty()) {
2107 message(_("Canceled."));
2113 // get absolute path of file and add ".lyx" to the filename if
2115 FileName const fullname =
2116 fileSearch(string(), filename, "lyx", support::may_not_exist);
2117 if (!fullname.empty())
2118 filename = fullname.absFileName();
2120 if (!fullname.onlyPath().isDirectory()) {
2121 Alert::warning(_("Invalid filename"),
2122 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2123 from_utf8(fullname.absFileName())));
2127 // if the file doesn't exist and isn't already open (bug 6645),
2128 // let the user create one
2129 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2130 !LyXVC::file_not_found_hook(fullname)) {
2131 // the user specifically chose this name. Believe him.
2132 Buffer * const b = newFile(filename, string(), true);
2138 docstring const disp_fn = makeDisplayPath(filename);
2139 message(bformat(_("Opening document %1$s..."), disp_fn));
2142 Buffer * buf = loadDocument(fullname);
2144 str2 = bformat(_("Document %1$s opened."), disp_fn);
2145 if (buf->lyxvc().inUse())
2146 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2147 " " + _("Version control detected.");
2149 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2154 // FIXME: clean that
2155 static bool import(GuiView * lv, FileName const & filename,
2156 string const & format, ErrorList & errorList)
2158 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2160 string loader_format;
2161 vector<string> loaders = theConverters().loaders();
2162 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2163 vector<string>::const_iterator it = loaders.begin();
2164 vector<string>::const_iterator en = loaders.end();
2165 for (; it != en; ++it) {
2166 if (!theConverters().isReachable(format, *it))
2169 string const tofile =
2170 support::changeExtension(filename.absFileName(),
2171 formats.extension(*it));
2172 if (!theConverters().convert(0, filename, FileName(tofile),
2173 filename, format, *it, errorList))
2175 loader_format = *it;
2178 if (loader_format.empty()) {
2179 frontend::Alert::error(_("Couldn't import file"),
2180 bformat(_("No information for importing the format %1$s."),
2181 formats.prettyName(format)));
2185 loader_format = format;
2187 if (loader_format == "lyx") {
2188 Buffer * buf = lv->loadDocument(lyxfile);
2192 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2196 bool as_paragraphs = loader_format == "textparagraph";
2197 string filename2 = (loader_format == format) ? filename.absFileName()
2198 : support::changeExtension(filename.absFileName(),
2199 formats.extension(loader_format));
2200 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2202 guiApp->setCurrentView(lv);
2203 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2210 void GuiView::importDocument(string const & argument)
2213 string filename = split(argument, format, ' ');
2215 LYXERR(Debug::INFO, format << " file: " << filename);
2217 // need user interaction
2218 if (filename.empty()) {
2219 string initpath = lyxrc.document_path;
2220 if (documentBufferView()) {
2221 string const trypath = documentBufferView()->buffer().filePath();
2222 // If directory is writeable, use this as default.
2223 if (FileName(trypath).isDirWritable())
2227 docstring const text = bformat(_("Select %1$s file to import"),
2228 formats.prettyName(format));
2230 FileDialog dlg(toqstr(text));
2231 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2232 dlg.setButton2(qt_("Examples|#E#e"),
2233 toqstr(addPath(package().system_support().absFileName(), "examples")));
2235 docstring filter = formats.prettyName(format);
2238 filter += from_utf8(formats.extensions(format));
2241 FileDialog::Result result =
2242 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2244 if (result.first == FileDialog::Later)
2247 filename = fromqstr(result.second);
2249 // check selected filename
2250 if (filename.empty())
2251 message(_("Canceled."));
2254 if (filename.empty())
2257 // get absolute path of file
2258 FileName const fullname(support::makeAbsPath(filename));
2260 // Can happen if the user entered a path into the dialog
2262 if (fullname.onlyFileName().empty()) {
2263 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2264 "Aborting import."),
2265 from_utf8(fullname.absFileName()));
2266 frontend::Alert::error(_("File name error"), msg);
2267 message(_("Canceled."));
2272 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2274 // Check if the document already is open
2275 Buffer * buf = theBufferList().getBuffer(lyxfile);
2278 if (!closeBuffer()) {
2279 message(_("Canceled."));
2284 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2286 // if the file exists already, and we didn't do
2287 // -i lyx thefile.lyx, warn
2288 if (lyxfile.exists() && fullname != lyxfile) {
2290 docstring text = bformat(_("The document %1$s already exists.\n\n"
2291 "Do you want to overwrite that document?"), displaypath);
2292 int const ret = Alert::prompt(_("Overwrite document?"),
2293 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2296 message(_("Canceled."));
2301 message(bformat(_("Importing %1$s..."), displaypath));
2302 ErrorList errorList;
2303 if (import(this, fullname, format, errorList))
2304 message(_("imported."));
2306 message(_("file not imported!"));
2308 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2312 void GuiView::newDocument(string const & filename, bool from_template)
2314 FileName initpath(lyxrc.document_path);
2315 if (documentBufferView()) {
2316 FileName const trypath(documentBufferView()->buffer().filePath());
2317 // If directory is writeable, use this as default.
2318 if (trypath.isDirWritable())
2322 string templatefile;
2323 if (from_template) {
2324 templatefile = selectTemplateFile().absFileName();
2325 if (templatefile.empty())
2330 if (filename.empty())
2331 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2333 b = newFile(filename, templatefile, true);
2338 // If no new document could be created, it is unsure
2339 // whether there is a valid BufferView.
2340 if (currentBufferView())
2341 // Ensure the cursor is correctly positioned on screen.
2342 currentBufferView()->showCursor();
2346 void GuiView::insertLyXFile(docstring const & fname)
2348 BufferView * bv = documentBufferView();
2353 FileName filename(to_utf8(fname));
2354 if (filename.empty()) {
2355 // Launch a file browser
2357 string initpath = lyxrc.document_path;
2358 string const trypath = bv->buffer().filePath();
2359 // If directory is writeable, use this as default.
2360 if (FileName(trypath).isDirWritable())
2364 FileDialog dlg(qt_("Select LyX document to insert"));
2365 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2366 dlg.setButton2(qt_("Examples|#E#e"),
2367 toqstr(addPath(package().system_support().absFileName(),
2370 FileDialog::Result result = dlg.open(toqstr(initpath),
2371 QStringList(qt_("LyX Documents (*.lyx)")));
2373 if (result.first == FileDialog::Later)
2377 filename.set(fromqstr(result.second));
2379 // check selected filename
2380 if (filename.empty()) {
2381 // emit message signal.
2382 message(_("Canceled."));
2387 bv->insertLyXFile(filename);
2388 bv->buffer().errors("Parse");
2392 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2394 FileName fname = b.fileName();
2395 FileName const oldname = fname;
2397 if (!newname.empty()) {
2399 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2401 // Switch to this Buffer.
2404 // No argument? Ask user through dialog.
2406 FileDialog dlg(qt_("Choose a filename to save document as"));
2407 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2408 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2410 if (!isLyXFileName(fname.absFileName()))
2411 fname.changeExtension(".lyx");
2413 FileDialog::Result result =
2414 dlg.save(toqstr(fname.onlyPath().absFileName()),
2415 QStringList(qt_("LyX Documents (*.lyx)")),
2416 toqstr(fname.onlyFileName()));
2418 if (result.first == FileDialog::Later)
2421 fname.set(fromqstr(result.second));
2426 if (!isLyXFileName(fname.absFileName()))
2427 fname.changeExtension(".lyx");
2430 // fname is now the new Buffer location.
2432 // if there is already a Buffer open with this name, we do not want
2433 // to have another one. (the second test makes sure we're not just
2434 // trying to overwrite ourselves, which is fine.)
2435 if (theBufferList().exists(fname) && fname != oldname
2436 && theBufferList().getBuffer(fname) != &b) {
2437 docstring const text =
2438 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2439 "Please close it before attempting to overwrite it.\n"
2440 "Do you want to choose a new filename?"),
2441 from_utf8(fname.absFileName()));
2442 int const ret = Alert::prompt(_("Chosen File Already Open"),
2443 text, 0, 1, _("&Rename"), _("&Cancel"));
2445 case 0: return renameBuffer(b, docstring(), kind);
2446 case 1: return false;
2451 bool const existsLocal = fname.exists();
2452 bool const existsInVC = LyXVC::fileInVC(fname);
2453 if (existsLocal || existsInVC) {
2454 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2455 if (kind != LV_WRITE_AS && existsInVC) {
2456 // renaming to a name that is already in VC
2458 docstring text = bformat(_("The document %1$s "
2459 "is already registered.\n\n"
2460 "Do you want to choose a new name?"),
2462 docstring const title = (kind == LV_VC_RENAME) ?
2463 _("Rename document?") : _("Copy document?");
2464 docstring const button = (kind == LV_VC_RENAME) ?
2465 _("&Rename") : _("&Copy");
2466 int const ret = Alert::prompt(title, text, 0, 1,
2467 button, _("&Cancel"));
2469 case 0: return renameBuffer(b, docstring(), kind);
2470 case 1: return false;
2475 docstring text = bformat(_("The document %1$s "
2476 "already exists.\n\n"
2477 "Do you want to overwrite that document?"),
2479 int const ret = Alert::prompt(_("Overwrite document?"),
2480 text, 0, 2, _("&Overwrite"),
2481 _("&Rename"), _("&Cancel"));
2484 case 1: return renameBuffer(b, docstring(), kind);
2485 case 2: return false;
2491 case LV_VC_RENAME: {
2492 string msg = b.lyxvc().rename(fname);
2495 message(from_utf8(msg));
2499 string msg = b.lyxvc().copy(fname);
2502 message(from_utf8(msg));
2508 // LyXVC created the file already in case of LV_VC_RENAME or
2509 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2510 // relative paths of included stuff right if we moved e.g. from
2511 // /a/b.lyx to /a/c/b.lyx.
2513 bool const saved = saveBuffer(b, fname);
2520 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2522 FileName fname = b.fileName();
2524 FileDialog dlg(qt_("Choose a filename to export the document as"));
2525 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2528 QString const anyformat = qt_("Guess from extension (*.*)");
2531 vector<Format const *> export_formats;
2532 for (Format const & f : formats)
2533 if (f.documentFormat())
2534 export_formats.push_back(&f);
2535 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2536 map<QString, string> fmap;
2539 for (Format const * f : export_formats) {
2540 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2541 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2543 from_ascii(f->extension())));
2544 types << loc_filter;
2545 fmap[loc_filter] = f->name();
2546 if (from_ascii(f->name()) == iformat) {
2547 filter = loc_filter;
2548 ext = f->extension();
2551 string ofname = fname.onlyFileName();
2553 ofname = support::changeExtension(ofname, ext);
2554 FileDialog::Result result =
2555 dlg.save(toqstr(fname.onlyPath().absFileName()),
2559 if (result.first != FileDialog::Chosen)
2563 fname.set(fromqstr(result.second));
2564 if (filter == anyformat)
2565 fmt_name = formats.getFormatFromExtension(fname.extension());
2567 fmt_name = fmap[filter];
2568 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2569 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2571 if (fmt_name.empty() || fname.empty())
2574 // fname is now the new Buffer location.
2575 if (FileName(fname).exists()) {
2576 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2577 docstring text = bformat(_("The document %1$s already "
2578 "exists.\n\nDo you want to "
2579 "overwrite that document?"),
2581 int const ret = Alert::prompt(_("Overwrite document?"),
2582 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2585 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2586 case 2: return false;
2590 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2593 return dr.dispatched();
2597 bool GuiView::saveBuffer(Buffer & b)
2599 return saveBuffer(b, FileName());
2603 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2605 if (workArea(b) && workArea(b)->inDialogMode())
2608 if (fn.empty() && b.isUnnamed())
2609 return renameBuffer(b, docstring());
2611 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2613 theSession().lastFiles().add(b.fileName());
2617 // Switch to this Buffer.
2620 // FIXME: we don't tell the user *WHY* the save failed !!
2621 docstring const file = makeDisplayPath(b.absFileName(), 30);
2622 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2623 "Do you want to rename the document and "
2624 "try again?"), file);
2625 int const ret = Alert::prompt(_("Rename and save?"),
2626 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2629 if (!renameBuffer(b, docstring()))
2638 return saveBuffer(b, fn);
2642 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2644 return closeWorkArea(wa, false);
2648 // We only want to close the buffer if it is not visible in other workareas
2649 // of the same view, nor in other views, and if this is not a child
2650 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2652 Buffer & buf = wa->bufferView().buffer();
2654 bool last_wa = d.countWorkAreasOf(buf) == 1
2655 && !inOtherView(buf) && !buf.parent();
2657 bool close_buffer = last_wa;
2660 if (lyxrc.close_buffer_with_last_view == "yes")
2662 else if (lyxrc.close_buffer_with_last_view == "no")
2663 close_buffer = false;
2666 if (buf.isUnnamed())
2667 file = from_utf8(buf.fileName().onlyFileName());
2669 file = buf.fileName().displayName(30);
2670 docstring const text = bformat(
2671 _("Last view on document %1$s is being closed.\n"
2672 "Would you like to close or hide the document?\n"
2674 "Hidden documents can be displayed back through\n"
2675 "the menu: View->Hidden->...\n"
2677 "To remove this question, set your preference in:\n"
2678 " Tools->Preferences->Look&Feel->UserInterface\n"
2680 int ret = Alert::prompt(_("Close or hide document?"),
2681 text, 0, 1, _("&Close"), _("&Hide"));
2682 close_buffer = (ret == 0);
2686 return closeWorkArea(wa, close_buffer);
2690 bool GuiView::closeBuffer()
2692 GuiWorkArea * wa = currentMainWorkArea();
2693 // coverity complained about this
2694 // it seems unnecessary, but perhaps is worth the check
2695 LASSERT(wa, return false);
2697 setCurrentWorkArea(wa);
2698 Buffer & buf = wa->bufferView().buffer();
2699 return closeWorkArea(wa, !buf.parent());
2703 void GuiView::writeSession() const {
2704 GuiWorkArea const * active_wa = currentMainWorkArea();
2705 for (int i = 0; i < d.splitter_->count(); ++i) {
2706 TabWorkArea * twa = d.tabWorkArea(i);
2707 for (int j = 0; j < twa->count(); ++j) {
2708 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2709 Buffer & buf = wa->bufferView().buffer();
2710 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2716 bool GuiView::closeBufferAll()
2718 // Close the workareas in all other views
2719 QList<int> const ids = guiApp->viewIds();
2720 for (int i = 0; i != ids.size(); ++i) {
2721 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2725 // Close our own workareas
2726 if (!closeWorkAreaAll())
2729 // Now close the hidden buffers. We prevent hidden buffers from being
2730 // dirty, so we can just close them.
2731 theBufferList().closeAll();
2736 bool GuiView::closeWorkAreaAll()
2738 setCurrentWorkArea(currentMainWorkArea());
2740 // We might be in a situation that there is still a tabWorkArea, but
2741 // there are no tabs anymore. This can happen when we get here after a
2742 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2743 // many TabWorkArea's have no documents anymore.
2746 // We have to call count() each time, because it can happen that
2747 // more than one splitter will disappear in one iteration (bug 5998).
2748 while (d.splitter_->count() > empty_twa) {
2749 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2751 if (twa->count() == 0)
2754 setCurrentWorkArea(twa->currentWorkArea());
2755 if (!closeTabWorkArea(twa))
2763 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2768 Buffer & buf = wa->bufferView().buffer();
2770 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2771 Alert::warning(_("Close document"),
2772 _("Document could not be closed because it is being processed by LyX."));
2777 return closeBuffer(buf);
2779 if (!inMultiTabs(wa))
2780 if (!saveBufferIfNeeded(buf, true))
2788 bool GuiView::closeBuffer(Buffer & buf)
2790 // If we are in a close_event all children will be closed in some time,
2791 // so no need to do it here. This will ensure that the children end up
2792 // in the session file in the correct order. If we close the master
2793 // buffer, we can close or release the child buffers here too.
2794 bool success = true;
2796 ListOfBuffers clist = buf.getChildren();
2797 ListOfBuffers::const_iterator it = clist.begin();
2798 ListOfBuffers::const_iterator const bend = clist.end();
2799 for (; it != bend; ++it) {
2800 Buffer * child_buf = *it;
2801 if (theBufferList().isOthersChild(&buf, child_buf)) {
2802 child_buf->setParent(0);
2806 // FIXME: should we look in other tabworkareas?
2807 // ANSWER: I don't think so. I've tested, and if the child is
2808 // open in some other window, it closes without a problem.
2809 GuiWorkArea * child_wa = workArea(*child_buf);
2811 success = closeWorkArea(child_wa, true);
2815 // In this case the child buffer is open but hidden.
2816 // It therefore should not (MUST NOT) be dirty!
2817 LATTEST(child_buf->isClean());
2818 theBufferList().release(child_buf);
2823 // goto bookmark to update bookmark pit.
2824 // FIXME: we should update only the bookmarks related to this buffer!
2825 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2826 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2827 guiApp->gotoBookmark(i+1, false, false);
2829 if (saveBufferIfNeeded(buf, false)) {
2830 buf.removeAutosaveFile();
2831 theBufferList().release(&buf);
2835 // open all children again to avoid a crash because of dangling
2836 // pointers (bug 6603)
2842 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2844 while (twa == d.currentTabWorkArea()) {
2845 twa->setCurrentIndex(twa->count() - 1);
2847 GuiWorkArea * wa = twa->currentWorkArea();
2848 Buffer & b = wa->bufferView().buffer();
2850 // We only want to close the buffer if the same buffer is not visible
2851 // in another view, and if this is not a child and if we are closing
2852 // a view (not a tabgroup).
2853 bool const close_buffer =
2854 !inOtherView(b) && !b.parent() && closing_;
2856 if (!closeWorkArea(wa, close_buffer))
2863 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2865 if (buf.isClean() || buf.paragraphs().empty())
2868 // Switch to this Buffer.
2873 if (buf.isUnnamed())
2874 file = from_utf8(buf.fileName().onlyFileName());
2876 file = buf.fileName().displayName(30);
2878 // Bring this window to top before asking questions.
2883 if (hiding && buf.isUnnamed()) {
2884 docstring const text = bformat(_("The document %1$s has not been "
2885 "saved yet.\n\nDo you want to save "
2886 "the document?"), file);
2887 ret = Alert::prompt(_("Save new document?"),
2888 text, 0, 1, _("&Save"), _("&Cancel"));
2892 docstring const text = bformat(_("The document %1$s has unsaved changes."
2893 "\n\nDo you want to save the document or discard the changes?"), file);
2894 ret = Alert::prompt(_("Save changed document?"),
2895 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2900 if (!saveBuffer(buf))
2904 // If we crash after this we could have no autosave file
2905 // but I guess this is really improbable (Jug).
2906 // Sometimes improbable things happen:
2907 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2908 // buf.removeAutosaveFile();
2910 // revert all changes
2921 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2923 Buffer & buf = wa->bufferView().buffer();
2925 for (int i = 0; i != d.splitter_->count(); ++i) {
2926 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2927 if (wa_ && wa_ != wa)
2930 return inOtherView(buf);
2934 bool GuiView::inOtherView(Buffer & buf)
2936 QList<int> const ids = guiApp->viewIds();
2938 for (int i = 0; i != ids.size(); ++i) {
2942 if (guiApp->view(ids[i]).workArea(buf))
2949 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2951 if (!documentBufferView())
2954 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2955 Buffer * const curbuf = &documentBufferView()->buffer();
2956 int nwa = twa->count();
2957 for (int i = 0; i < nwa; ++i) {
2958 if (&workArea(i)->bufferView().buffer() == curbuf) {
2960 if (np == NEXTBUFFER)
2961 next_index = (i == nwa - 1 ? 0 : i + 1);
2963 next_index = (i == 0 ? nwa - 1 : i - 1);
2965 twa->moveTab(i, next_index);
2967 setBuffer(&workArea(next_index)->bufferView().buffer());
2975 /// make sure the document is saved
2976 static bool ensureBufferClean(Buffer * buffer)
2978 LASSERT(buffer, return false);
2979 if (buffer->isClean() && !buffer->isUnnamed())
2982 docstring const file = buffer->fileName().displayName(30);
2985 if (!buffer->isUnnamed()) {
2986 text = bformat(_("The document %1$s has unsaved "
2987 "changes.\n\nDo you want to save "
2988 "the document?"), file);
2989 title = _("Save changed document?");
2992 text = bformat(_("The document %1$s has not been "
2993 "saved yet.\n\nDo you want to save "
2994 "the document?"), file);
2995 title = _("Save new document?");
2997 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3000 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3002 return buffer->isClean() && !buffer->isUnnamed();
3006 bool GuiView::reloadBuffer(Buffer & buf)
3008 Buffer::ReadStatus status = buf.reload();
3009 return status == Buffer::ReadSuccess;
3013 void GuiView::checkExternallyModifiedBuffers()
3015 BufferList::iterator bit = theBufferList().begin();
3016 BufferList::iterator const bend = theBufferList().end();
3017 for (; bit != bend; ++bit) {
3018 Buffer * buf = *bit;
3019 if (buf->fileName().exists()
3020 && buf->isExternallyModified(Buffer::checksum_method)) {
3021 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3022 " Reload now? Any local changes will be lost."),
3023 from_utf8(buf->absFileName()));
3024 int const ret = Alert::prompt(_("Reload externally changed document?"),
3025 text, 0, 1, _("&Reload"), _("&Cancel"));
3033 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3035 Buffer * buffer = documentBufferView()
3036 ? &(documentBufferView()->buffer()) : 0;
3038 switch (cmd.action()) {
3039 case LFUN_VC_REGISTER:
3040 if (!buffer || !ensureBufferClean(buffer))
3042 if (!buffer->lyxvc().inUse()) {
3043 if (buffer->lyxvc().registrer()) {
3044 reloadBuffer(*buffer);
3045 dr.clearMessageUpdate();
3050 case LFUN_VC_RENAME:
3051 case LFUN_VC_COPY: {
3052 if (!buffer || !ensureBufferClean(buffer))
3054 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3055 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3056 // Some changes are not yet committed.
3057 // We test here and not in getStatus(), since
3058 // this test is expensive.
3060 LyXVC::CommandResult ret =
3061 buffer->lyxvc().checkIn(log);
3063 if (ret == LyXVC::ErrorCommand ||
3064 ret == LyXVC::VCSuccess)
3065 reloadBuffer(*buffer);
3066 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3067 frontend::Alert::error(
3068 _("Revision control error."),
3069 _("Document could not be checked in."));
3073 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3074 LV_VC_RENAME : LV_VC_COPY;
3075 renameBuffer(*buffer, cmd.argument(), kind);
3080 case LFUN_VC_CHECK_IN:
3081 if (!buffer || !ensureBufferClean(buffer))
3083 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3085 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3087 // Only skip reloading if the checkin was cancelled or
3088 // an error occurred before the real checkin VCS command
3089 // was executed, since the VCS might have changed the
3090 // file even if it could not checkin successfully.
3091 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3092 reloadBuffer(*buffer);
3096 case LFUN_VC_CHECK_OUT:
3097 if (!buffer || !ensureBufferClean(buffer))
3099 if (buffer->lyxvc().inUse()) {
3100 dr.setMessage(buffer->lyxvc().checkOut());
3101 reloadBuffer(*buffer);
3105 case LFUN_VC_LOCKING_TOGGLE:
3106 LASSERT(buffer, return);
3107 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3109 if (buffer->lyxvc().inUse()) {
3110 string res = buffer->lyxvc().lockingToggle();
3112 frontend::Alert::error(_("Revision control error."),
3113 _("Error when setting the locking property."));
3116 reloadBuffer(*buffer);
3121 case LFUN_VC_REVERT:
3122 LASSERT(buffer, return);
3123 if (buffer->lyxvc().revert()) {
3124 reloadBuffer(*buffer);
3125 dr.clearMessageUpdate();
3129 case LFUN_VC_UNDO_LAST:
3130 LASSERT(buffer, return);
3131 buffer->lyxvc().undoLast();
3132 reloadBuffer(*buffer);
3133 dr.clearMessageUpdate();
3136 case LFUN_VC_REPO_UPDATE:
3137 LASSERT(buffer, return);
3138 if (ensureBufferClean(buffer)) {
3139 dr.setMessage(buffer->lyxvc().repoUpdate());
3140 checkExternallyModifiedBuffers();
3144 case LFUN_VC_COMMAND: {
3145 string flag = cmd.getArg(0);
3146 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3149 if (contains(flag, 'M')) {
3150 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3153 string path = cmd.getArg(1);
3154 if (contains(path, "$$p") && buffer)
3155 path = subst(path, "$$p", buffer->filePath());
3156 LYXERR(Debug::LYXVC, "Directory: " << path);
3158 if (!pp.isReadableDirectory()) {
3159 lyxerr << _("Directory is not accessible.") << endl;
3162 support::PathChanger p(pp);
3164 string command = cmd.getArg(2);
3165 if (command.empty())
3168 command = subst(command, "$$i", buffer->absFileName());
3169 command = subst(command, "$$p", buffer->filePath());
3171 command = subst(command, "$$m", to_utf8(message));
3172 LYXERR(Debug::LYXVC, "Command: " << command);
3174 one.startscript(Systemcall::Wait, command);
3178 if (contains(flag, 'I'))
3179 buffer->markDirty();
3180 if (contains(flag, 'R'))
3181 reloadBuffer(*buffer);
3186 case LFUN_VC_COMPARE: {
3187 if (cmd.argument().empty()) {
3188 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3192 string rev1 = cmd.getArg(0);
3196 // it seems safe to assume we have a buffer
3197 // coverity[FORWARD_NULL]
3198 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3201 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3202 f2 = buffer->absFileName();
3204 string rev2 = cmd.getArg(1);
3208 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3212 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3213 f1 << "\n" << f2 << "\n" );
3214 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3215 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3225 void GuiView::openChildDocument(string const & fname)
3227 LASSERT(documentBufferView(), return);
3228 Buffer & buffer = documentBufferView()->buffer();
3229 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3230 documentBufferView()->saveBookmark(false);
3232 if (theBufferList().exists(filename)) {
3233 child = theBufferList().getBuffer(filename);
3236 message(bformat(_("Opening child document %1$s..."),
3237 makeDisplayPath(filename.absFileName())));
3238 child = loadDocument(filename, false);
3240 // Set the parent name of the child document.
3241 // This makes insertion of citations and references in the child work,
3242 // when the target is in the parent or another child document.
3244 child->setParent(&buffer);
3248 bool GuiView::goToFileRow(string const & argument)
3252 size_t i = argument.find_last_of(' ');
3253 if (i != string::npos) {
3254 file_name = os::internal_path(trim(argument.substr(0, i)));
3255 istringstream is(argument.substr(i + 1));
3260 if (i == string::npos) {
3261 LYXERR0("Wrong argument: " << argument);
3265 string const abstmp = package().temp_dir().absFileName();
3266 string const realtmp = package().temp_dir().realPath();
3267 // We have to use os::path_prefix_is() here, instead of
3268 // simply prefixIs(), because the file name comes from
3269 // an external application and may need case adjustment.
3270 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3271 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3272 // Needed by inverse dvi search. If it is a file
3273 // in tmpdir, call the apropriated function.
3274 // If tmpdir is a symlink, we may have the real
3275 // path passed back, so we correct for that.
3276 if (!prefixIs(file_name, abstmp))
3277 file_name = subst(file_name, realtmp, abstmp);
3278 buf = theBufferList().getBufferFromTmp(file_name);
3280 // Must replace extension of the file to be .lyx
3281 // and get full path
3282 FileName const s = fileSearch(string(),
3283 support::changeExtension(file_name, ".lyx"), "lyx");
3284 // Either change buffer or load the file
3285 if (theBufferList().exists(s))
3286 buf = theBufferList().getBuffer(s);
3287 else if (s.exists()) {
3288 buf = loadDocument(s);
3293 _("File does not exist: %1$s"),
3294 makeDisplayPath(file_name)));
3300 _("No buffer for file: %1$s."),
3301 makeDisplayPath(file_name))
3306 bool success = documentBufferView()->setCursorFromRow(row);
3308 LYXERR(Debug::LATEX,
3309 "setCursorFromRow: invalid position for row " << row);
3310 frontend::Alert::error(_("Inverse Search Failed"),
3311 _("Invalid position requested by inverse search.\n"
3312 "You may need to update the viewed document."));
3318 void GuiView::toolBarPopup(const QPoint & pos)
3320 QMenu * menu = new QMenu;
3321 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3322 menu->exec(QCursor::pos());
3327 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3329 Buffer::ExportStatus const status = func(format);
3331 // the cloning operation will have produced a clone of the entire set of
3332 // documents, starting from the master. so we must delete those.
3333 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3335 busyBuffers.remove(orig);
3340 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3342 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3343 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3347 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3349 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3350 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3354 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3356 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3357 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3361 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3362 string const & argument,
3363 Buffer const * used_buffer,
3364 docstring const & msg,
3365 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3366 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3367 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3372 string format = argument;
3374 format = used_buffer->params().getDefaultOutputFormat();
3375 processing_format = format;
3377 progress_->clearMessages();
3380 #if EXPORT_in_THREAD
3381 GuiViewPrivate::busyBuffers.insert(used_buffer);
3382 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3383 if (!cloned_buffer) {
3384 Alert::error(_("Export Error"),
3385 _("Error cloning the Buffer."));
3388 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3393 setPreviewFuture(f);
3394 last_export_format = used_buffer->params().bufferFormat();
3397 // We are asynchronous, so we don't know here anything about the success
3400 Buffer::ExportStatus status;
3402 status = (used_buffer->*syncFunc)(format, true);
3403 } else if (previewFunc) {
3404 status = (used_buffer->*previewFunc)(format);
3407 handleExportStatus(gv_, status, format);
3409 return (status == Buffer::ExportSuccess
3410 || status == Buffer::PreviewSuccess);
3414 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3416 BufferView * bv = currentBufferView();
3417 LASSERT(bv, return);
3419 // Let the current BufferView dispatch its own actions.
3420 bv->dispatch(cmd, dr);
3421 if (dr.dispatched())
3424 // Try with the document BufferView dispatch if any.
3425 BufferView * doc_bv = documentBufferView();
3426 if (doc_bv && doc_bv != bv) {
3427 doc_bv->dispatch(cmd, dr);
3428 if (dr.dispatched())
3432 // Then let the current Cursor dispatch its own actions.
3433 bv->cursor().dispatch(cmd);
3435 // update completion. We do it here and not in
3436 // processKeySym to avoid another redraw just for a
3437 // changed inline completion
3438 if (cmd.origin() == FuncRequest::KEYBOARD) {
3439 if (cmd.action() == LFUN_SELF_INSERT
3440 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3441 updateCompletion(bv->cursor(), true, true);
3442 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3443 updateCompletion(bv->cursor(), false, true);
3445 updateCompletion(bv->cursor(), false, false);
3448 dr = bv->cursor().result();
3452 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3454 BufferView * bv = currentBufferView();
3455 // By default we won't need any update.
3456 dr.screenUpdate(Update::None);
3457 // assume cmd will be dispatched
3458 dr.dispatched(true);
3460 Buffer * doc_buffer = documentBufferView()
3461 ? &(documentBufferView()->buffer()) : 0;
3463 if (cmd.origin() == FuncRequest::TOC) {
3464 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3465 // FIXME: do we need to pass a DispatchResult object here?
3466 toc->doDispatch(bv->cursor(), cmd);
3470 string const argument = to_utf8(cmd.argument());
3472 switch(cmd.action()) {
3473 case LFUN_BUFFER_CHILD_OPEN:
3474 openChildDocument(to_utf8(cmd.argument()));
3477 case LFUN_BUFFER_IMPORT:
3478 importDocument(to_utf8(cmd.argument()));
3481 case LFUN_BUFFER_EXPORT: {
3484 // GCC only sees strfwd.h when building merged
3485 if (::lyx::operator==(cmd.argument(), "custom")) {
3486 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3490 string const dest = cmd.getArg(1);
3491 FileName target_dir;
3492 if (!dest.empty() && FileName::isAbsolute(dest))
3493 target_dir = FileName(support::onlyPath(dest));
3495 target_dir = doc_buffer->fileName().onlyPath();
3497 if ((dest.empty() && doc_buffer->isUnnamed())
3498 || !target_dir.isDirWritable()) {
3499 exportBufferAs(*doc_buffer, cmd.argument());
3502 /* TODO/Review: Is it a problem to also export the children?
3503 See the update_unincluded flag */
3504 d.asyncBufferProcessing(argument,
3507 &GuiViewPrivate::exportAndDestroy,
3510 // TODO Inform user about success
3514 case LFUN_BUFFER_EXPORT_AS: {
3515 LASSERT(doc_buffer, break);
3516 docstring f = cmd.argument();
3518 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3519 exportBufferAs(*doc_buffer, f);
3523 case LFUN_BUFFER_UPDATE: {
3524 d.asyncBufferProcessing(argument,
3527 &GuiViewPrivate::compileAndDestroy,
3532 case LFUN_BUFFER_VIEW: {
3533 d.asyncBufferProcessing(argument,
3535 _("Previewing ..."),
3536 &GuiViewPrivate::previewAndDestroy,
3541 case LFUN_MASTER_BUFFER_UPDATE: {
3542 d.asyncBufferProcessing(argument,
3543 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3545 &GuiViewPrivate::compileAndDestroy,
3550 case LFUN_MASTER_BUFFER_VIEW: {
3551 d.asyncBufferProcessing(argument,
3552 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3554 &GuiViewPrivate::previewAndDestroy,
3555 0, &Buffer::preview);
3558 case LFUN_BUFFER_SWITCH: {
3559 string const file_name = to_utf8(cmd.argument());
3560 if (!FileName::isAbsolute(file_name)) {
3562 dr.setMessage(_("Absolute filename expected."));
3566 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3569 dr.setMessage(_("Document not loaded"));
3573 // Do we open or switch to the buffer in this view ?
3574 if (workArea(*buffer)
3575 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3580 // Look for the buffer in other views
3581 QList<int> const ids = guiApp->viewIds();
3583 for (; i != ids.size(); ++i) {
3584 GuiView & gv = guiApp->view(ids[i]);
3585 if (gv.workArea(*buffer)) {
3587 gv.activateWindow();
3589 gv.setBuffer(buffer);
3594 // If necessary, open a new window as a last resort
3595 if (i == ids.size()) {
3596 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3602 case LFUN_BUFFER_NEXT:
3603 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3606 case LFUN_BUFFER_MOVE_NEXT:
3607 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3610 case LFUN_BUFFER_PREVIOUS:
3611 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3614 case LFUN_BUFFER_MOVE_PREVIOUS:
3615 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3618 case LFUN_COMMAND_EXECUTE: {
3619 command_execute_ = true;
3620 minibuffer_focus_ = true;
3623 case LFUN_DROP_LAYOUTS_CHOICE:
3624 d.layout_->showPopup();
3627 case LFUN_MENU_OPEN:
3628 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3629 menu->exec(QCursor::pos());
3632 case LFUN_FILE_INSERT:
3633 insertLyXFile(cmd.argument());
3636 case LFUN_FILE_INSERT_PLAINTEXT:
3637 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3638 string const fname = to_utf8(cmd.argument());
3639 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3640 dr.setMessage(_("Absolute filename expected."));
3644 FileName filename(fname);
3645 if (fname.empty()) {
3646 FileDialog dlg(qt_("Select file to insert"));
3648 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3649 QStringList(qt_("All Files (*)")));
3651 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3652 dr.setMessage(_("Canceled."));
3656 filename.set(fromqstr(result.second));
3660 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3661 bv->dispatch(new_cmd, dr);
3666 case LFUN_BUFFER_RELOAD: {
3667 LASSERT(doc_buffer, break);
3670 if (!doc_buffer->isClean()) {
3671 docstring const file =
3672 makeDisplayPath(doc_buffer->absFileName(), 20);
3673 docstring text = bformat(_("Any changes will be lost. "
3674 "Are you sure you want to revert to the saved version "
3675 "of the document %1$s?"), file);
3676 ret = Alert::prompt(_("Revert to saved document?"),
3677 text, 1, 1, _("&Revert"), _("&Cancel"));
3681 doc_buffer->markClean();
3682 reloadBuffer(*doc_buffer);
3683 dr.forceBufferUpdate();
3688 case LFUN_BUFFER_WRITE:
3689 LASSERT(doc_buffer, break);
3690 saveBuffer(*doc_buffer);
3693 case LFUN_BUFFER_WRITE_AS:
3694 LASSERT(doc_buffer, break);
3695 renameBuffer(*doc_buffer, cmd.argument());
3698 case LFUN_BUFFER_WRITE_ALL: {
3699 Buffer * first = theBufferList().first();
3702 message(_("Saving all documents..."));
3703 // We cannot use a for loop as the buffer list cycles.
3706 if (!b->isClean()) {
3708 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3710 b = theBufferList().next(b);
3711 } while (b != first);
3712 dr.setMessage(_("All documents saved."));
3716 case LFUN_BUFFER_CLOSE:
3720 case LFUN_BUFFER_CLOSE_ALL:
3724 case LFUN_TOOLBAR_TOGGLE: {
3725 string const name = cmd.getArg(0);
3726 if (GuiToolbar * t = toolbar(name))
3731 case LFUN_ICON_SIZE: {
3732 int const size = cmd.argument().empty() ? d.normalIconSize : convert<int>(cmd.argument());
3733 setIconSize(QSize(size, size));
3734 if (size == d.smallIconSize)
3735 dr.setMessage("Icon size set to small");
3736 else if (size == d.normalIconSize)
3737 dr.setMessage("Icon size set to normal");
3738 else if (size == d.bigIconSize)
3739 dr.setMessage("Icon size set to big");
3740 else if (size == d.hugeIconSize)
3741 dr.setMessage("Icon size set to huge");
3742 else if (size == d.giantIconSize)
3743 dr.setMessage("Icon size set to giant");
3745 dr.setMessage(bformat(_("Icon size set to %1$d"), size));
3749 case LFUN_DIALOG_UPDATE: {
3750 string const name = to_utf8(cmd.argument());
3751 if (name == "prefs" || name == "document")
3752 updateDialog(name, string());
3753 else if (name == "paragraph")
3754 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3755 else if (currentBufferView()) {
3756 Inset * inset = currentBufferView()->editedInset(name);
3757 // Can only update a dialog connected to an existing inset
3759 // FIXME: get rid of this indirection; GuiView ask the inset
3760 // if he is kind enough to update itself...
3761 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3762 //FIXME: pass DispatchResult here?
3763 inset->dispatch(currentBufferView()->cursor(), fr);
3769 case LFUN_DIALOG_TOGGLE: {
3770 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3771 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3772 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3776 case LFUN_DIALOG_DISCONNECT_INSET:
3777 disconnectDialog(to_utf8(cmd.argument()));
3780 case LFUN_DIALOG_HIDE: {
3781 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3785 case LFUN_DIALOG_SHOW: {
3786 string const name = cmd.getArg(0);
3787 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3789 if (name == "character") {
3790 data = freefont2string();
3792 showDialog("character", data);
3793 } else if (name == "latexlog") {
3794 Buffer::LogType type;
3795 string const logfile = doc_buffer->logName(&type);
3797 case Buffer::latexlog:
3800 case Buffer::buildlog:
3804 data += Lexer::quoteString(logfile);
3805 showDialog("log", data);
3806 } else if (name == "vclog") {
3807 string const data = "vc " +
3808 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3809 showDialog("log", data);
3810 } else if (name == "symbols") {
3811 data = bv->cursor().getEncoding()->name();
3813 showDialog("symbols", data);
3815 } else if (name == "prefs" && isFullScreen()) {
3816 lfunUiToggle("fullscreen");
3817 showDialog("prefs", data);
3819 showDialog(name, data);
3824 dr.setMessage(cmd.argument());
3827 case LFUN_UI_TOGGLE: {
3828 string arg = cmd.getArg(0);
3829 if (!lfunUiToggle(arg)) {
3830 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3831 dr.setMessage(bformat(msg, from_utf8(arg)));
3833 // Make sure the keyboard focus stays in the work area.
3838 case LFUN_VIEW_SPLIT: {
3839 LASSERT(doc_buffer, break);
3840 string const orientation = cmd.getArg(0);
3841 d.splitter_->setOrientation(orientation == "vertical"
3842 ? Qt::Vertical : Qt::Horizontal);
3843 TabWorkArea * twa = addTabWorkArea();
3844 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3845 setCurrentWorkArea(wa);
3848 case LFUN_TAB_GROUP_CLOSE:
3849 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3850 closeTabWorkArea(twa);
3851 d.current_work_area_ = 0;
3852 twa = d.currentTabWorkArea();
3853 // Switch to the next GuiWorkArea in the found TabWorkArea.
3855 // Make sure the work area is up to date.
3856 setCurrentWorkArea(twa->currentWorkArea());
3858 setCurrentWorkArea(0);
3863 case LFUN_VIEW_CLOSE:
3864 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3865 closeWorkArea(twa->currentWorkArea());
3866 d.current_work_area_ = 0;
3867 twa = d.currentTabWorkArea();
3868 // Switch to the next GuiWorkArea in the found TabWorkArea.
3870 // Make sure the work area is up to date.
3871 setCurrentWorkArea(twa->currentWorkArea());
3873 setCurrentWorkArea(0);
3878 case LFUN_COMPLETION_INLINE:
3879 if (d.current_work_area_)
3880 d.current_work_area_->completer().showInline();
3883 case LFUN_COMPLETION_POPUP:
3884 if (d.current_work_area_)
3885 d.current_work_area_->completer().showPopup();
3890 if (d.current_work_area_)
3891 d.current_work_area_->completer().tab();
3894 case LFUN_COMPLETION_CANCEL:
3895 if (d.current_work_area_) {
3896 if (d.current_work_area_->completer().popupVisible())
3897 d.current_work_area_->completer().hidePopup();
3899 d.current_work_area_->completer().hideInline();
3903 case LFUN_COMPLETION_ACCEPT:
3904 if (d.current_work_area_)
3905 d.current_work_area_->completer().activate();
3908 case LFUN_BUFFER_ZOOM_IN:
3909 case LFUN_BUFFER_ZOOM_OUT: {
3910 // use a signed temp to avoid overflow
3911 int zoom = lyxrc.zoom;
3912 if (cmd.argument().empty()) {
3913 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3918 zoom += convert<int>(cmd.argument());
3920 if (zoom < static_cast<int>(zoom_min_))
3924 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3926 // The global QPixmapCache is used in GuiPainter to cache text
3927 // painting so we must reset it.
3928 QPixmapCache::clear();
3929 guiApp->fontLoader().update();
3930 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3934 case LFUN_VC_REGISTER:
3935 case LFUN_VC_RENAME:
3937 case LFUN_VC_CHECK_IN:
3938 case LFUN_VC_CHECK_OUT:
3939 case LFUN_VC_REPO_UPDATE:
3940 case LFUN_VC_LOCKING_TOGGLE:
3941 case LFUN_VC_REVERT:
3942 case LFUN_VC_UNDO_LAST:
3943 case LFUN_VC_COMMAND:
3944 case LFUN_VC_COMPARE:
3945 dispatchVC(cmd, dr);
3948 case LFUN_SERVER_GOTO_FILE_ROW:
3949 if(goToFileRow(to_utf8(cmd.argument())))
3950 dr.screenUpdate(Update::Force | Update::FitCursor);
3953 case LFUN_LYX_ACTIVATE:
3957 case LFUN_FORWARD_SEARCH: {
3958 // it seems safe to assume we have a document buffer, since
3959 // getStatus wants one.
3960 // coverity[FORWARD_NULL]
3961 Buffer const * doc_master = doc_buffer->masterBuffer();
3962 FileName const path(doc_master->temppath());
3963 string const texname = doc_master->isChild(doc_buffer)
3964 ? DocFileName(changeExtension(
3965 doc_buffer->absFileName(),
3966 "tex")).mangledFileName()
3967 : doc_buffer->latexName();
3968 string const fulltexname =
3969 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3970 string const mastername =
3971 removeExtension(doc_master->latexName());
3972 FileName const dviname(addName(path.absFileName(),
3973 addExtension(mastername, "dvi")));
3974 FileName const pdfname(addName(path.absFileName(),
3975 addExtension(mastername, "pdf")));
3976 bool const have_dvi = dviname.exists();
3977 bool const have_pdf = pdfname.exists();
3978 if (!have_dvi && !have_pdf) {
3979 dr.setMessage(_("Please, preview the document first."));
3982 string outname = dviname.onlyFileName();
3983 string command = lyxrc.forward_search_dvi;
3984 if (!have_dvi || (have_pdf &&
3985 pdfname.lastModified() > dviname.lastModified())) {
3986 outname = pdfname.onlyFileName();
3987 command = lyxrc.forward_search_pdf;
3990 DocIterator cur = bv->cursor();
3991 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
3992 LYXERR(Debug::ACTION, "Forward search: row:" << row
3994 if (row == -1 || command.empty()) {
3995 dr.setMessage(_("Couldn't proceed."));
3998 string texrow = convert<string>(row);
4000 command = subst(command, "$$n", texrow);
4001 command = subst(command, "$$f", fulltexname);
4002 command = subst(command, "$$t", texname);
4003 command = subst(command, "$$o", outname);
4005 PathChanger p(path);
4007 one.startscript(Systemcall::DontWait, command);
4011 case LFUN_SPELLING_CONTINUOUSLY:
4012 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4013 dr.screenUpdate(Update::Force);
4017 // The LFUN must be for one of BufferView, Buffer or Cursor;
4019 dispatchToBufferView(cmd, dr);
4023 // Part of automatic menu appearance feature.
4024 if (isFullScreen()) {
4025 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4029 // Need to update bv because many LFUNs here might have destroyed it
4030 bv = currentBufferView();
4032 // Clear non-empty selections
4033 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4035 Cursor & cur = bv->cursor();
4036 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4037 cur.clearSelection();
4043 bool GuiView::lfunUiToggle(string const & ui_component)
4045 if (ui_component == "scrollbar") {
4046 // hide() is of no help
4047 if (d.current_work_area_->verticalScrollBarPolicy() ==
4048 Qt::ScrollBarAlwaysOff)
4050 d.current_work_area_->setVerticalScrollBarPolicy(
4051 Qt::ScrollBarAsNeeded);
4053 d.current_work_area_->setVerticalScrollBarPolicy(
4054 Qt::ScrollBarAlwaysOff);
4055 } else if (ui_component == "statusbar") {
4056 statusBar()->setVisible(!statusBar()->isVisible());
4057 } else if (ui_component == "menubar") {
4058 menuBar()->setVisible(!menuBar()->isVisible());
4060 if (ui_component == "frame") {
4062 getContentsMargins(&l, &t, &r, &b);
4063 //are the frames in default state?
4064 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4066 setContentsMargins(-2, -2, -2, -2);
4068 setContentsMargins(0, 0, 0, 0);
4071 if (ui_component == "fullscreen") {
4079 void GuiView::toggleFullScreen()
4081 if (isFullScreen()) {
4082 for (int i = 0; i != d.splitter_->count(); ++i)
4083 d.tabWorkArea(i)->setFullScreen(false);
4084 setContentsMargins(0, 0, 0, 0);
4085 setWindowState(windowState() ^ Qt::WindowFullScreen);
4088 statusBar()->show();
4091 hideDialogs("prefs", 0);
4092 for (int i = 0; i != d.splitter_->count(); ++i)
4093 d.tabWorkArea(i)->setFullScreen(true);
4094 setContentsMargins(-2, -2, -2, -2);
4096 setWindowState(windowState() ^ Qt::WindowFullScreen);
4097 if (lyxrc.full_screen_statusbar)
4098 statusBar()->hide();
4099 if (lyxrc.full_screen_menubar)
4101 if (lyxrc.full_screen_toolbars) {
4102 ToolbarMap::iterator end = d.toolbars_.end();
4103 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4108 // give dialogs like the TOC a chance to adapt
4113 Buffer const * GuiView::updateInset(Inset const * inset)
4118 Buffer const * inset_buffer = &(inset->buffer());
4120 for (int i = 0; i != d.splitter_->count(); ++i) {
4121 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4124 Buffer const * buffer = &(wa->bufferView().buffer());
4125 if (inset_buffer == buffer)
4126 wa->scheduleRedraw();
4128 return inset_buffer;
4132 void GuiView::restartCursor()
4134 /* When we move around, or type, it's nice to be able to see
4135 * the cursor immediately after the keypress.
4137 if (d.current_work_area_)
4138 d.current_work_area_->startBlinkingCursor();
4140 // Take this occasion to update the other GUI elements.
4146 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4148 if (d.current_work_area_)
4149 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4154 // This list should be kept in sync with the list of insets in
4155 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4156 // dialog should have the same name as the inset.
4157 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4158 // docs in LyXAction.cpp.
4160 char const * const dialognames[] = {
4162 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4163 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4164 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4165 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4166 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4167 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4168 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4169 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4171 char const * const * const end_dialognames =
4172 dialognames + (sizeof(dialognames) / sizeof(char *));
4176 cmpCStr(char const * name) : name_(name) {}
4177 bool operator()(char const * other) {
4178 return strcmp(other, name_) == 0;
4185 bool isValidName(string const & name)
4187 return find_if(dialognames, end_dialognames,
4188 cmpCStr(name.c_str())) != end_dialognames;
4194 void GuiView::resetDialogs()
4196 // Make sure that no LFUN uses any GuiView.
4197 guiApp->setCurrentView(0);
4201 constructToolbars();
4202 guiApp->menus().fillMenuBar(menuBar(), this, false);
4203 d.layout_->updateContents(true);
4204 // Now update controls with current buffer.
4205 guiApp->setCurrentView(this);
4211 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4213 if (!isValidName(name))
4216 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4218 if (it != d.dialogs_.end()) {
4220 it->second->hideView();
4221 return it->second.get();
4224 Dialog * dialog = build(name);
4225 d.dialogs_[name].reset(dialog);
4226 if (lyxrc.allow_geometry_session)
4227 dialog->restoreSession();
4234 void GuiView::showDialog(string const & name, string const & data,
4237 triggerShowDialog(toqstr(name), toqstr(data), inset);
4241 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4247 const string name = fromqstr(qname);
4248 const string data = fromqstr(qdata);
4252 Dialog * dialog = findOrBuild(name, false);
4254 bool const visible = dialog->isVisibleView();
4255 dialog->showData(data);
4256 if (inset && currentBufferView())
4257 currentBufferView()->editInset(name, inset);
4258 // We only set the focus to the new dialog if it was not yet
4259 // visible in order not to change the existing previous behaviour
4261 // activateWindow is needed for floating dockviews
4262 dialog->asQWidget()->raise();
4263 dialog->asQWidget()->activateWindow();
4264 dialog->asQWidget()->setFocus();
4268 catch (ExceptionMessage const & ex) {
4276 bool GuiView::isDialogVisible(string const & name) const
4278 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4279 if (it == d.dialogs_.end())
4281 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4285 void GuiView::hideDialog(string const & name, Inset * inset)
4287 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4288 if (it == d.dialogs_.end())
4292 if (!currentBufferView())
4294 if (inset != currentBufferView()->editedInset(name))
4298 Dialog * const dialog = it->second.get();
4299 if (dialog->isVisibleView())
4301 if (currentBufferView())
4302 currentBufferView()->editInset(name, 0);
4306 void GuiView::disconnectDialog(string const & name)
4308 if (!isValidName(name))
4310 if (currentBufferView())
4311 currentBufferView()->editInset(name, 0);
4315 void GuiView::hideAll() const
4317 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4318 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4320 for(; it != end; ++it)
4321 it->second->hideView();
4325 void GuiView::updateDialogs()
4327 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4328 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4330 for(; it != end; ++it) {
4331 Dialog * dialog = it->second.get();
4333 if (dialog->needBufferOpen() && !documentBufferView())
4334 hideDialog(fromqstr(dialog->name()), 0);
4335 else if (dialog->isVisibleView())
4336 dialog->checkStatus();
4343 Dialog * createDialog(GuiView & lv, string const & name);
4345 // will be replaced by a proper factory...
4346 Dialog * createGuiAbout(GuiView & lv);
4347 Dialog * createGuiBibtex(GuiView & lv);
4348 Dialog * createGuiChanges(GuiView & lv);
4349 Dialog * createGuiCharacter(GuiView & lv);
4350 Dialog * createGuiCitation(GuiView & lv);
4351 Dialog * createGuiCompare(GuiView & lv);
4352 Dialog * createGuiCompareHistory(GuiView & lv);
4353 Dialog * createGuiDelimiter(GuiView & lv);
4354 Dialog * createGuiDocument(GuiView & lv);
4355 Dialog * createGuiErrorList(GuiView & lv);
4356 Dialog * createGuiExternal(GuiView & lv);
4357 Dialog * createGuiGraphics(GuiView & lv);
4358 Dialog * createGuiInclude(GuiView & lv);
4359 Dialog * createGuiIndex(GuiView & lv);
4360 Dialog * createGuiListings(GuiView & lv);
4361 Dialog * createGuiLog(GuiView & lv);
4362 Dialog * createGuiMathMatrix(GuiView & lv);
4363 Dialog * createGuiNote(GuiView & lv);
4364 Dialog * createGuiParagraph(GuiView & lv);
4365 Dialog * createGuiPhantom(GuiView & lv);
4366 Dialog * createGuiPreferences(GuiView & lv);
4367 Dialog * createGuiPrint(GuiView & lv);
4368 Dialog * createGuiPrintindex(GuiView & lv);
4369 Dialog * createGuiRef(GuiView & lv);
4370 Dialog * createGuiSearch(GuiView & lv);
4371 Dialog * createGuiSearchAdv(GuiView & lv);
4372 Dialog * createGuiSendTo(GuiView & lv);
4373 Dialog * createGuiShowFile(GuiView & lv);
4374 Dialog * createGuiSpellchecker(GuiView & lv);
4375 Dialog * createGuiSymbols(GuiView & lv);
4376 Dialog * createGuiTabularCreate(GuiView & lv);
4377 Dialog * createGuiTexInfo(GuiView & lv);
4378 Dialog * createGuiToc(GuiView & lv);
4379 Dialog * createGuiThesaurus(GuiView & lv);
4380 Dialog * createGuiViewSource(GuiView & lv);
4381 Dialog * createGuiWrap(GuiView & lv);
4382 Dialog * createGuiProgressView(GuiView & lv);
4386 Dialog * GuiView::build(string const & name)
4388 LASSERT(isValidName(name), return 0);
4390 Dialog * dialog = createDialog(*this, name);
4394 if (name == "aboutlyx")
4395 return createGuiAbout(*this);
4396 if (name == "bibtex")
4397 return createGuiBibtex(*this);
4398 if (name == "changes")
4399 return createGuiChanges(*this);
4400 if (name == "character")
4401 return createGuiCharacter(*this);
4402 if (name == "citation")
4403 return createGuiCitation(*this);
4404 if (name == "compare")
4405 return createGuiCompare(*this);
4406 if (name == "comparehistory")
4407 return createGuiCompareHistory(*this);
4408 if (name == "document")
4409 return createGuiDocument(*this);
4410 if (name == "errorlist")
4411 return createGuiErrorList(*this);
4412 if (name == "external")
4413 return createGuiExternal(*this);
4415 return createGuiShowFile(*this);
4416 if (name == "findreplace")
4417 return createGuiSearch(*this);
4418 if (name == "findreplaceadv")
4419 return createGuiSearchAdv(*this);
4420 if (name == "graphics")
4421 return createGuiGraphics(*this);
4422 if (name == "include")
4423 return createGuiInclude(*this);
4424 if (name == "index")
4425 return createGuiIndex(*this);
4426 if (name == "index_print")
4427 return createGuiPrintindex(*this);
4428 if (name == "listings")
4429 return createGuiListings(*this);
4431 return createGuiLog(*this);
4432 if (name == "mathdelimiter")
4433 return createGuiDelimiter(*this);
4434 if (name == "mathmatrix")
4435 return createGuiMathMatrix(*this);
4437 return createGuiNote(*this);
4438 if (name == "paragraph")
4439 return createGuiParagraph(*this);
4440 if (name == "phantom")
4441 return createGuiPhantom(*this);
4442 if (name == "prefs")
4443 return createGuiPreferences(*this);
4445 return createGuiRef(*this);
4446 if (name == "sendto")
4447 return createGuiSendTo(*this);
4448 if (name == "spellchecker")
4449 return createGuiSpellchecker(*this);
4450 if (name == "symbols")
4451 return createGuiSymbols(*this);
4452 if (name == "tabularcreate")
4453 return createGuiTabularCreate(*this);
4454 if (name == "texinfo")
4455 return createGuiTexInfo(*this);
4456 if (name == "thesaurus")
4457 return createGuiThesaurus(*this);
4459 return createGuiToc(*this);
4460 if (name == "view-source")
4461 return createGuiViewSource(*this);
4463 return createGuiWrap(*this);
4464 if (name == "progress")
4465 return createGuiProgressView(*this);
4471 } // namespace frontend
4474 #include "moc_GuiView.cpp"