3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #if QT_VERSION >= 0x050000
114 #include <QSvgRenderer>
116 #include <QtConcurrentRun>
124 // sync with GuiAlert.cpp
125 #define EXPORT_in_THREAD 1
128 #include "support/bind.h"
132 #ifdef HAVE_SYS_TIME_H
133 # include <sys/time.h>
141 using namespace lyx::support;
145 using support::addExtension;
146 using support::changeExtension;
147 using support::removeExtension;
153 class BackgroundWidget : public QWidget
156 BackgroundWidget(int width, int height)
157 : width_(width), height_(height)
159 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
160 if (!lyxrc.show_banner)
162 /// The text to be written on top of the pixmap
163 QString const text = lyx_version ?
164 qt_("version ") + lyx_version : qt_("unknown version");
165 #if QT_VERSION >= 0x050000
166 QString imagedir = "images/";
167 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
168 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
169 if (svgRenderer.isValid()) {
170 splash_ = QPixmap(splashSize());
171 QPainter painter(&splash_);
172 svgRenderer.render(&painter);
173 splash_.setDevicePixelRatio(pixelRatio());
175 splash_ = getPixmap("images/", "banner", "png");
178 splash_ = getPixmap("images/", "banner", "svgz,png");
181 QPainter pain(&splash_);
182 pain.setPen(QColor(0, 0, 0));
183 qreal const fsize = fontSize();
184 QPointF const position = textPosition();
186 "widget pixel ratio: " << pixelRatio() <<
187 " splash pixel ratio: " << splashPixelRatio() <<
188 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
190 // The font used to display the version info
191 font.setStyleHint(QFont::SansSerif);
192 font.setWeight(QFont::Bold);
193 font.setPointSizeF(fsize);
195 pain.drawText(position, text);
196 setFocusPolicy(Qt::StrongFocus);
199 void paintEvent(QPaintEvent *)
201 int const w = width_;
202 int const h = height_;
203 int const x = (width() - w) / 2;
204 int const y = (height() - h) / 2;
206 "widget pixel ratio: " << pixelRatio() <<
207 " splash pixel ratio: " << splashPixelRatio() <<
208 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
210 pain.drawPixmap(x, y, w, h, splash_);
213 void keyPressEvent(QKeyEvent * ev)
216 setKeySymbol(&sym, ev);
218 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
230 /// Current ratio between physical pixels and device-independent pixels
231 double pixelRatio() const {
232 #if QT_VERSION >= 0x050000
233 return devicePixelRatio();
239 qreal fontSize() const {
240 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
243 QPointF textPosition() const {
244 return QPointF(width_/2 - 18, height_/2 + 45);
247 QSize splashSize() const {
249 static_cast<unsigned int>(width_ * pixelRatio()),
250 static_cast<unsigned int>(height_ * pixelRatio()));
253 /// Ratio between physical pixels and device-independent pixels of splash image
254 double splashPixelRatio() const {
255 #if QT_VERSION >= 0x050000
256 return splash_.devicePixelRatio();
264 /// Toolbar store providing access to individual toolbars by name.
265 typedef map<string, GuiToolbar *> ToolbarMap;
267 typedef shared_ptr<Dialog> DialogPtr;
272 struct GuiView::GuiViewPrivate
274 GuiViewPrivate(GuiView * gv)
275 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
276 layout_(0), autosave_timeout_(5000),
279 // hardcode here the platform specific icon size
280 smallIconSize = 16; // scaling problems
281 normalIconSize = 20; // ok, default if iconsize.png is missing
282 bigIconSize = 26; // better for some math icons
283 hugeIconSize = 32; // better for hires displays
286 // if it exists, use width of iconsize.png as normal size
287 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
288 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
290 QImage image(toqstr(fn.absFileName()));
291 if (image.width() < int(smallIconSize))
292 normalIconSize = smallIconSize;
293 else if (image.width() > int(giantIconSize))
294 normalIconSize = giantIconSize;
296 normalIconSize = image.width();
299 splitter_ = new QSplitter;
300 bg_widget_ = new BackgroundWidget(400, 250);
301 stack_widget_ = new QStackedWidget;
302 stack_widget_->addWidget(bg_widget_);
303 stack_widget_->addWidget(splitter_);
306 // TODO cleanup, remove the singleton, handle multiple Windows?
307 progress_ = ProgressInterface::instance();
308 if (!dynamic_cast<GuiProgress*>(progress_)) {
309 progress_ = new GuiProgress; // TODO who deletes it
310 ProgressInterface::setInstance(progress_);
313 dynamic_cast<GuiProgress*>(progress_),
314 SIGNAL(updateStatusBarMessage(QString const&)),
315 gv, SLOT(updateStatusBarMessage(QString const&)));
317 dynamic_cast<GuiProgress*>(progress_),
318 SIGNAL(clearMessageText()),
319 gv, SLOT(clearMessageText()));
326 delete stack_widget_;
329 QMenu * toolBarPopup(GuiView * parent)
331 // FIXME: translation
332 QMenu * menu = new QMenu(parent);
333 QActionGroup * iconSizeGroup = new QActionGroup(parent);
335 QAction * smallIcons = new QAction(iconSizeGroup);
336 smallIcons->setText(qt_("Small-sized icons"));
337 smallIcons->setCheckable(true);
338 QObject::connect(smallIcons, SIGNAL(triggered()),
339 parent, SLOT(smallSizedIcons()));
340 menu->addAction(smallIcons);
342 QAction * normalIcons = new QAction(iconSizeGroup);
343 normalIcons->setText(qt_("Normal-sized icons"));
344 normalIcons->setCheckable(true);
345 QObject::connect(normalIcons, SIGNAL(triggered()),
346 parent, SLOT(normalSizedIcons()));
347 menu->addAction(normalIcons);
349 QAction * bigIcons = new QAction(iconSizeGroup);
350 bigIcons->setText(qt_("Big-sized icons"));
351 bigIcons->setCheckable(true);
352 QObject::connect(bigIcons, SIGNAL(triggered()),
353 parent, SLOT(bigSizedIcons()));
354 menu->addAction(bigIcons);
356 QAction * hugeIcons = new QAction(iconSizeGroup);
357 hugeIcons->setText(qt_("Huge-sized icons"));
358 hugeIcons->setCheckable(true);
359 QObject::connect(hugeIcons, SIGNAL(triggered()),
360 parent, SLOT(hugeSizedIcons()));
361 menu->addAction(hugeIcons);
363 QAction * giantIcons = new QAction(iconSizeGroup);
364 giantIcons->setText(qt_("Giant-sized icons"));
365 giantIcons->setCheckable(true);
366 QObject::connect(giantIcons, SIGNAL(triggered()),
367 parent, SLOT(giantSizedIcons()));
368 menu->addAction(giantIcons);
370 unsigned int cur = parent->iconSize().width();
371 if ( cur == parent->d.smallIconSize)
372 smallIcons->setChecked(true);
373 else if (cur == parent->d.normalIconSize)
374 normalIcons->setChecked(true);
375 else if (cur == parent->d.bigIconSize)
376 bigIcons->setChecked(true);
377 else if (cur == parent->d.hugeIconSize)
378 hugeIcons->setChecked(true);
379 else if (cur == parent->d.giantIconSize)
380 giantIcons->setChecked(true);
387 stack_widget_->setCurrentWidget(bg_widget_);
388 bg_widget_->setUpdatesEnabled(true);
389 bg_widget_->setFocus();
392 int tabWorkAreaCount()
394 return splitter_->count();
397 TabWorkArea * tabWorkArea(int i)
399 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
402 TabWorkArea * currentTabWorkArea()
404 int areas = tabWorkAreaCount();
406 // The first TabWorkArea is always the first one, if any.
407 return tabWorkArea(0);
409 for (int i = 0; i != areas; ++i) {
410 TabWorkArea * twa = tabWorkArea(i);
411 if (current_main_work_area_ == twa->currentWorkArea())
415 // None has the focus so we just take the first one.
416 return tabWorkArea(0);
419 int countWorkAreasOf(Buffer & buf)
421 int areas = tabWorkAreaCount();
423 for (int i = 0; i != areas; ++i) {
424 TabWorkArea * twa = tabWorkArea(i);
425 if (twa->workArea(buf))
431 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
433 if (processing_thread_watcher_.isRunning()) {
434 // we prefer to cancel this preview in order to keep a snappy
438 processing_thread_watcher_.setFuture(f);
443 GuiWorkArea * current_work_area_;
444 GuiWorkArea * current_main_work_area_;
445 QSplitter * splitter_;
446 QStackedWidget * stack_widget_;
447 BackgroundWidget * bg_widget_;
449 ToolbarMap toolbars_;
450 ProgressInterface* progress_;
451 /// The main layout box.
453 * \warning Don't Delete! The layout box is actually owned by
454 * whichever toolbar contains it. All the GuiView class needs is a
455 * means of accessing it.
457 * FIXME: replace that with a proper model so that we are not limited
458 * to only one dialog.
463 map<string, DialogPtr> dialogs_;
465 unsigned int smallIconSize;
466 unsigned int normalIconSize;
467 unsigned int bigIconSize;
468 unsigned int hugeIconSize;
469 unsigned int giantIconSize;
471 QTimer statusbar_timer_;
472 /// auto-saving of buffers
473 Timeout autosave_timeout_;
474 /// flag against a race condition due to multiclicks, see bug #1119
478 TocModels toc_models_;
481 QFutureWatcher<docstring> autosave_watcher_;
482 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
484 string last_export_format;
485 string processing_format;
487 static QSet<Buffer const *> busyBuffers;
488 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
494 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
496 // TODO syncFunc/previewFunc: use bind
497 bool asyncBufferProcessing(string const & argument,
498 Buffer const * used_buffer,
499 docstring const & msg,
500 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
501 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
502 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
504 QVector<GuiWorkArea*> guiWorkAreas();
507 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
510 GuiView::GuiView(int id)
511 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
512 command_execute_(false)
514 // GuiToolbars *must* be initialised before the menu bar.
515 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
518 // set ourself as the current view. This is needed for the menu bar
519 // filling, at least for the static special menu item on Mac. Otherwise
520 // they are greyed out.
521 guiApp->setCurrentView(this);
523 // Fill up the menu bar.
524 guiApp->menus().fillMenuBar(menuBar(), this, true);
526 setCentralWidget(d.stack_widget_);
528 // Start autosave timer
529 if (lyxrc.autosave) {
530 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
531 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
532 d.autosave_timeout_.start();
534 connect(&d.statusbar_timer_, SIGNAL(timeout()),
535 this, SLOT(clearMessage()));
537 // We don't want to keep the window in memory if it is closed.
538 setAttribute(Qt::WA_DeleteOnClose, true);
540 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
541 // QIcon::fromTheme was introduced in Qt 4.6
542 #if (QT_VERSION >= 0x040600)
543 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
544 // since the icon is provided in the application bundle. We use a themed
545 // version when available and use the bundled one as fallback.
546 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
548 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
552 resetWindowTitleAndIconText();
554 // use tabbed dock area for multiple docks
555 // (such as "source" and "messages")
556 setDockOptions(QMainWindow::ForceTabbedDocks);
559 setAcceptDrops(true);
561 // add busy indicator to statusbar
562 QLabel * busylabel = new QLabel(statusBar());
563 statusBar()->addPermanentWidget(busylabel);
564 search_mode mode = theGuiApp()->imageSearchMode();
565 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
566 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
567 busylabel->setMovie(busyanim);
571 connect(&d.processing_thread_watcher_, SIGNAL(started()),
572 busylabel, SLOT(show()));
573 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
574 busylabel, SLOT(hide()));
576 statusBar()->setSizeGripEnabled(true);
579 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
580 SLOT(autoSaveThreadFinished()));
582 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
583 SLOT(processingThreadStarted()));
584 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
585 SLOT(processingThreadFinished()));
587 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
588 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
590 // Forbid too small unresizable window because it can happen
591 // with some window manager under X11.
592 setMinimumSize(300, 200);
594 if (lyxrc.allow_geometry_session) {
595 // Now take care of session management.
600 // no session handling, default to a sane size.
601 setGeometry(50, 50, 690, 510);
604 // clear session data if any.
606 settings.remove("views");
616 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
618 QVector<GuiWorkArea*> areas;
619 for (int i = 0; i < tabWorkAreaCount(); i++) {
620 TabWorkArea* ta = tabWorkArea(i);
621 for (int u = 0; u < ta->count(); u++) {
622 areas << ta->workArea(u);
628 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
629 string const & format)
631 docstring const fmt = formats.prettyName(format);
634 case Buffer::ExportSuccess:
635 msg = bformat(_("Successful export to format: %1$s"), fmt);
637 case Buffer::ExportCancel:
638 msg = _("Document export cancelled.");
640 case Buffer::ExportError:
641 case Buffer::ExportNoPathToFormat:
642 case Buffer::ExportTexPathHasSpaces:
643 case Buffer::ExportConverterError:
644 msg = bformat(_("Error while exporting format: %1$s"), fmt);
646 case Buffer::PreviewSuccess:
647 msg = bformat(_("Successful preview of format: %1$s"), fmt);
649 case Buffer::PreviewError:
650 msg = bformat(_("Error while previewing format: %1$s"), fmt);
657 void GuiView::processingThreadStarted()
662 void GuiView::processingThreadFinished()
664 QFutureWatcher<Buffer::ExportStatus> const * watcher =
665 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
667 Buffer::ExportStatus const status = watcher->result();
668 handleExportStatus(this, status, d.processing_format);
671 BufferView const * const bv = currentBufferView();
672 if (bv && !bv->buffer().errorList("Export").empty()) {
676 errors(d.last_export_format);
680 void GuiView::autoSaveThreadFinished()
682 QFutureWatcher<docstring> const * watcher =
683 static_cast<QFutureWatcher<docstring> const *>(sender());
684 message(watcher->result());
689 void GuiView::saveLayout() const
692 settings.beginGroup("views");
693 settings.beginGroup(QString::number(id_));
694 #if defined(Q_WS_X11) || defined(QPA_XCB)
695 settings.setValue("pos", pos());
696 settings.setValue("size", size());
698 settings.setValue("geometry", saveGeometry());
700 settings.setValue("layout", saveState(0));
701 settings.setValue("icon_size", iconSize());
705 void GuiView::saveUISettings() const
707 // Save the toolbar private states
708 ToolbarMap::iterator end = d.toolbars_.end();
709 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
710 it->second->saveSession();
711 // Now take care of all other dialogs
712 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
713 for (; it!= d.dialogs_.end(); ++it)
714 it->second->saveSession();
718 bool GuiView::restoreLayout()
721 settings.beginGroup("views");
722 settings.beginGroup(QString::number(id_));
723 QString const icon_key = "icon_size";
724 if (!settings.contains(icon_key))
727 //code below is skipped when when ~/.config/LyX is (re)created
728 QSize icon_size = settings.value(icon_key).toSize();
729 // Check whether session size changed.
730 if (icon_size.width() != int(d.smallIconSize) &&
731 icon_size.width() != int(d.normalIconSize) &&
732 icon_size.width() != int(d.bigIconSize) &&
733 icon_size.width() != int(d.hugeIconSize) &&
734 icon_size.width() != int(d.giantIconSize)) {
735 icon_size.setWidth(d.normalIconSize);
736 icon_size.setHeight(d.normalIconSize);
738 setIconSize(icon_size);
740 #if defined(Q_WS_X11) || defined(QPA_XCB)
741 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
742 QSize size = settings.value("size", QSize(690, 510)).toSize();
746 // Work-around for bug #6034: the window ends up in an undetermined
747 // state when trying to restore a maximized window when it is
748 // already maximized.
749 if (!(windowState() & Qt::WindowMaximized))
750 if (!restoreGeometry(settings.value("geometry").toByteArray()))
751 setGeometry(50, 50, 690, 510);
753 // Make sure layout is correctly oriented.
754 setLayoutDirection(qApp->layoutDirection());
756 // Allow the toc and view-source dock widget to be restored if needed.
758 if ((dialog = findOrBuild("toc", true)))
759 // see bug 5082. At least setup title and enabled state.
760 // Visibility will be adjusted by restoreState below.
761 dialog->prepareView();
762 if ((dialog = findOrBuild("view-source", true)))
763 dialog->prepareView();
764 if ((dialog = findOrBuild("progress", true)))
765 dialog->prepareView();
767 if (!restoreState(settings.value("layout").toByteArray(), 0))
770 // init the toolbars that have not been restored
771 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
772 Toolbars::Infos::iterator end = guiApp->toolbars().end();
773 for (; cit != end; ++cit) {
774 GuiToolbar * tb = toolbar(cit->name);
775 if (tb && !tb->isRestored())
776 initToolbar(cit->name);
784 GuiToolbar * GuiView::toolbar(string const & name)
786 ToolbarMap::iterator it = d.toolbars_.find(name);
787 if (it != d.toolbars_.end())
790 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
795 void GuiView::constructToolbars()
797 ToolbarMap::iterator it = d.toolbars_.begin();
798 for (; it != d.toolbars_.end(); ++it)
802 // I don't like doing this here, but the standard toolbar
803 // destroys this object when it's destroyed itself (vfr)
804 d.layout_ = new LayoutBox(*this);
805 d.stack_widget_->addWidget(d.layout_);
806 d.layout_->move(0,0);
808 // extracts the toolbars from the backend
809 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
810 Toolbars::Infos::iterator end = guiApp->toolbars().end();
811 for (; cit != end; ++cit)
812 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
816 void GuiView::initToolbars()
818 // extracts the toolbars from the backend
819 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
820 Toolbars::Infos::iterator end = guiApp->toolbars().end();
821 for (; cit != end; ++cit)
822 initToolbar(cit->name);
826 void GuiView::initToolbar(string const & name)
828 GuiToolbar * tb = toolbar(name);
831 int const visibility = guiApp->toolbars().defaultVisibility(name);
832 bool newline = !(visibility & Toolbars::SAMEROW);
833 tb->setVisible(false);
834 tb->setVisibility(visibility);
836 if (visibility & Toolbars::TOP) {
838 addToolBarBreak(Qt::TopToolBarArea);
839 addToolBar(Qt::TopToolBarArea, tb);
842 if (visibility & Toolbars::BOTTOM) {
844 addToolBarBreak(Qt::BottomToolBarArea);
845 addToolBar(Qt::BottomToolBarArea, tb);
848 if (visibility & Toolbars::LEFT) {
850 addToolBarBreak(Qt::LeftToolBarArea);
851 addToolBar(Qt::LeftToolBarArea, tb);
854 if (visibility & Toolbars::RIGHT) {
856 addToolBarBreak(Qt::RightToolBarArea);
857 addToolBar(Qt::RightToolBarArea, tb);
860 if (visibility & Toolbars::ON)
861 tb->setVisible(true);
865 TocModels & GuiView::tocModels()
867 return d.toc_models_;
871 void GuiView::setFocus()
873 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
874 QMainWindow::setFocus();
878 bool GuiView::hasFocus() const
880 if (currentWorkArea())
881 return currentWorkArea()->hasFocus();
882 if (currentMainWorkArea())
883 return currentMainWorkArea()->hasFocus();
884 return d.bg_widget_->hasFocus();
888 void GuiView::focusInEvent(QFocusEvent * e)
890 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
891 QMainWindow::focusInEvent(e);
892 // Make sure guiApp points to the correct view.
893 guiApp->setCurrentView(this);
894 if (currentWorkArea())
895 currentWorkArea()->setFocus();
896 else if (currentMainWorkArea())
897 currentMainWorkArea()->setFocus();
899 d.bg_widget_->setFocus();
903 QMenu * GuiView::createPopupMenu()
905 return d.toolBarPopup(this);
909 void GuiView::showEvent(QShowEvent * e)
911 LYXERR(Debug::GUI, "Passed Geometry "
912 << size().height() << "x" << size().width()
913 << "+" << pos().x() << "+" << pos().y());
915 if (d.splitter_->count() == 0)
916 // No work area, switch to the background widget.
920 QMainWindow::showEvent(e);
924 bool GuiView::closeScheduled()
931 bool GuiView::prepareAllBuffersForLogout()
933 Buffer * first = theBufferList().first();
937 // First, iterate over all buffers and ask the users if unsaved
938 // changes should be saved.
939 // We cannot use a for loop as the buffer list cycles.
942 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
944 b = theBufferList().next(b);
945 } while (b != first);
947 // Next, save session state
948 // When a view/window was closed before without quitting LyX, there
949 // are already entries in the lastOpened list.
950 theSession().lastOpened().clear();
957 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
958 ** is responsibility of the container (e.g., dialog)
960 void GuiView::closeEvent(QCloseEvent * close_event)
962 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
964 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
965 Alert::warning(_("Exit LyX"),
966 _("LyX could not be closed because documents are being processed by LyX."));
967 close_event->setAccepted(false);
971 // If the user pressed the x (so we didn't call closeView
972 // programmatically), we want to clear all existing entries.
974 theSession().lastOpened().clear();
979 // it can happen that this event arrives without selecting the view,
980 // e.g. when clicking the close button on a background window.
982 if (!closeWorkAreaAll()) {
984 close_event->ignore();
988 // Make sure that nothing will use this to be closed View.
989 guiApp->unregisterView(this);
991 if (isFullScreen()) {
992 // Switch off fullscreen before closing.
997 // Make sure the timer time out will not trigger a statusbar update.
998 d.statusbar_timer_.stop();
1000 // Saving fullscreen requires additional tweaks in the toolbar code.
1001 // It wouldn't also work under linux natively.
1002 if (lyxrc.allow_geometry_session) {
1007 close_event->accept();
1011 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1013 if (event->mimeData()->hasUrls())
1015 /// \todo Ask lyx-devel is this is enough:
1016 /// if (event->mimeData()->hasFormat("text/plain"))
1017 /// event->acceptProposedAction();
1021 void GuiView::dropEvent(QDropEvent * event)
1023 QList<QUrl> files = event->mimeData()->urls();
1024 if (files.isEmpty())
1027 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1028 for (int i = 0; i != files.size(); ++i) {
1029 string const file = os::internal_path(fromqstr(
1030 files.at(i).toLocalFile()));
1034 string const ext = support::getExtension(file);
1035 vector<const Format *> found_formats;
1037 // Find all formats that have the correct extension.
1038 vector<const Format *> const & import_formats
1039 = theConverters().importableFormats();
1040 vector<const Format *>::const_iterator it = import_formats.begin();
1041 for (; it != import_formats.end(); ++it)
1042 if ((*it)->hasExtension(ext))
1043 found_formats.push_back(*it);
1046 if (found_formats.size() >= 1) {
1047 if (found_formats.size() > 1) {
1048 //FIXME: show a dialog to choose the correct importable format
1049 LYXERR(Debug::FILES,
1050 "Multiple importable formats found, selecting first");
1052 string const arg = found_formats[0]->name() + " " + file;
1053 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1056 //FIXME: do we have to explicitly check whether it's a lyx file?
1057 LYXERR(Debug::FILES,
1058 "No formats found, trying to open it as a lyx file");
1059 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1061 // add the functions to the queue
1062 guiApp->addToFuncRequestQueue(cmd);
1065 // now process the collected functions. We perform the events
1066 // asynchronously. This prevents potential problems in case the
1067 // BufferView is closed within an event.
1068 guiApp->processFuncRequestQueueAsync();
1072 void GuiView::message(docstring const & str)
1074 if (ForkedProcess::iAmAChild())
1077 // call is moved to GUI-thread by GuiProgress
1078 d.progress_->appendMessage(toqstr(str));
1082 void GuiView::clearMessageText()
1084 message(docstring());
1088 void GuiView::updateStatusBarMessage(QString const & str)
1090 statusBar()->showMessage(str);
1091 d.statusbar_timer_.stop();
1092 d.statusbar_timer_.start(3000);
1096 void GuiView::smallSizedIcons()
1098 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1102 void GuiView::normalSizedIcons()
1104 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1108 void GuiView::bigSizedIcons()
1110 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1114 void GuiView::hugeSizedIcons()
1116 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1120 void GuiView::giantSizedIcons()
1122 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1126 void GuiView::clearMessage()
1128 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1129 // the hasFocus function mostly returns false, even if the focus is on
1130 // a workarea in this view.
1134 d.statusbar_timer_.stop();
1138 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1140 if (wa != d.current_work_area_
1141 || wa->bufferView().buffer().isInternal())
1143 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1144 setWindowIconText(wa->windowIconText());
1145 #if (QT_VERSION >= 0x040400)
1146 // Sets the path for the window: this is used by OSX to
1147 // allow a context click on the title bar showing a menu
1148 // with the path up to the file
1149 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1154 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1156 if (d.current_work_area_)
1157 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1158 this, SLOT(setBusy(bool)));
1160 disconnectBufferView();
1161 connectBufferView(wa->bufferView());
1162 connectBuffer(wa->bufferView().buffer());
1163 d.current_work_area_ = wa;
1164 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1165 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1166 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1167 updateWindowTitle(wa);
1171 // The document settings needs to be reinitialised.
1172 updateDialog("document", "");
1174 // Buffer-dependent dialogs must be updated. This is done here because
1175 // some dialogs require buffer()->text.
1180 void GuiView::on_lastWorkAreaRemoved()
1183 // We already are in a close event. Nothing more to do.
1186 if (d.splitter_->count() > 1)
1187 // We have a splitter so don't close anything.
1190 // Reset and updates the dialogs.
1191 d.toc_models_.reset(0);
1192 updateDialog("document", "");
1195 resetWindowTitleAndIconText();
1198 if (lyxrc.open_buffers_in_tabs)
1199 // Nothing more to do, the window should stay open.
1202 if (guiApp->viewIds().size() > 1) {
1208 // On Mac we also close the last window because the application stay
1209 // resident in memory. On other platforms we don't close the last
1210 // window because this would quit the application.
1216 void GuiView::updateStatusBar()
1218 // let the user see the explicit message
1219 if (d.statusbar_timer_.isActive())
1226 void GuiView::showMessage()
1230 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1231 if (msg.isEmpty()) {
1232 BufferView const * bv = currentBufferView();
1234 msg = toqstr(bv->cursor().currentState());
1236 msg = qt_("Welcome to LyX!");
1238 statusBar()->showMessage(msg);
1242 bool GuiView::event(QEvent * e)
1246 // Useful debug code:
1247 //case QEvent::ActivationChange:
1248 //case QEvent::WindowDeactivate:
1249 //case QEvent::Paint:
1250 //case QEvent::Enter:
1251 //case QEvent::Leave:
1252 //case QEvent::HoverEnter:
1253 //case QEvent::HoverLeave:
1254 //case QEvent::HoverMove:
1255 //case QEvent::StatusTip:
1256 //case QEvent::DragEnter:
1257 //case QEvent::DragLeave:
1258 //case QEvent::Drop:
1261 case QEvent::WindowActivate: {
1262 GuiView * old_view = guiApp->currentView();
1263 if (this == old_view) {
1265 return QMainWindow::event(e);
1267 if (old_view && old_view->currentBufferView()) {
1268 // save current selection to the selection buffer to allow
1269 // middle-button paste in this window.
1270 cap::saveSelection(old_view->currentBufferView()->cursor());
1272 guiApp->setCurrentView(this);
1273 if (d.current_work_area_) {
1274 BufferView & bv = d.current_work_area_->bufferView();
1275 connectBufferView(bv);
1276 connectBuffer(bv.buffer());
1277 // The document structure, name and dialogs might have
1278 // changed in another view.
1280 // The document settings needs to be reinitialised.
1281 updateDialog("document", "");
1284 resetWindowTitleAndIconText();
1287 return QMainWindow::event(e);
1290 case QEvent::ShortcutOverride: {
1292 if (isFullScreen() && menuBar()->isHidden()) {
1293 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1294 // FIXME: we should also try to detect special LyX shortcut such as
1295 // Alt-P and Alt-M. Right now there is a hack in
1296 // GuiWorkArea::processKeySym() that hides again the menubar for
1298 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1300 return QMainWindow::event(e);
1303 return QMainWindow::event(e);
1307 return QMainWindow::event(e);
1311 void GuiView::resetWindowTitleAndIconText()
1313 setWindowTitle(qt_("LyX"));
1314 setWindowIconText(qt_("LyX"));
1317 bool GuiView::focusNextPrevChild(bool /*next*/)
1324 bool GuiView::busy() const
1330 void GuiView::setBusy(bool busy)
1332 bool const busy_before = busy_ > 0;
1333 busy ? ++busy_ : --busy_;
1334 if ((busy_ > 0) == busy_before)
1335 // busy state didn't change
1339 QApplication::setOverrideCursor(Qt::WaitCursor);
1342 QApplication::restoreOverrideCursor();
1347 void GuiView::resetCommandExecute()
1349 command_execute_ = false;
1354 double GuiView::pixelRatio() const
1356 #if QT_VERSION >= 0x050000
1357 return devicePixelRatio();
1364 GuiWorkArea * GuiView::workArea(int index)
1366 if (TabWorkArea * twa = d.currentTabWorkArea())
1367 if (index < twa->count())
1368 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1373 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1375 if (currentWorkArea()
1376 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1377 return (GuiWorkArea *) currentWorkArea();
1378 if (TabWorkArea * twa = d.currentTabWorkArea())
1379 return twa->workArea(buffer);
1384 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1386 // Automatically create a TabWorkArea if there are none yet.
1387 TabWorkArea * tab_widget = d.splitter_->count()
1388 ? d.currentTabWorkArea() : addTabWorkArea();
1389 return tab_widget->addWorkArea(buffer, *this);
1393 TabWorkArea * GuiView::addTabWorkArea()
1395 TabWorkArea * twa = new TabWorkArea;
1396 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1397 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1398 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1399 this, SLOT(on_lastWorkAreaRemoved()));
1401 d.splitter_->addWidget(twa);
1402 d.stack_widget_->setCurrentWidget(d.splitter_);
1407 GuiWorkArea const * GuiView::currentWorkArea() const
1409 return d.current_work_area_;
1413 GuiWorkArea * GuiView::currentWorkArea()
1415 return d.current_work_area_;
1419 GuiWorkArea const * GuiView::currentMainWorkArea() const
1421 if (!d.currentTabWorkArea())
1423 return d.currentTabWorkArea()->currentWorkArea();
1427 GuiWorkArea * GuiView::currentMainWorkArea()
1429 if (!d.currentTabWorkArea())
1431 return d.currentTabWorkArea()->currentWorkArea();
1435 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1437 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1439 d.current_work_area_ = 0;
1444 // FIXME: I've no clue why this is here and why it accesses
1445 // theGuiApp()->currentView, which might be 0 (bug 6464).
1446 // See also 27525 (vfr).
1447 if (theGuiApp()->currentView() == this
1448 && theGuiApp()->currentView()->currentWorkArea() == wa)
1451 if (currentBufferView())
1452 cap::saveSelection(currentBufferView()->cursor());
1454 theGuiApp()->setCurrentView(this);
1455 d.current_work_area_ = wa;
1457 // We need to reset this now, because it will need to be
1458 // right if the tabWorkArea gets reset in the for loop. We
1459 // will change it back if we aren't in that case.
1460 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1461 d.current_main_work_area_ = wa;
1463 for (int i = 0; i != d.splitter_->count(); ++i) {
1464 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1465 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1466 << ", Current main wa: " << currentMainWorkArea());
1471 d.current_main_work_area_ = old_cmwa;
1473 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1474 on_currentWorkAreaChanged(wa);
1475 BufferView & bv = wa->bufferView();
1476 bv.cursor().fixIfBroken();
1478 wa->setUpdatesEnabled(true);
1479 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1483 void GuiView::removeWorkArea(GuiWorkArea * wa)
1485 LASSERT(wa, return);
1486 if (wa == d.current_work_area_) {
1488 disconnectBufferView();
1489 d.current_work_area_ = 0;
1490 d.current_main_work_area_ = 0;
1493 bool found_twa = false;
1494 for (int i = 0; i != d.splitter_->count(); ++i) {
1495 TabWorkArea * twa = d.tabWorkArea(i);
1496 if (twa->removeWorkArea(wa)) {
1497 // Found in this tab group, and deleted the GuiWorkArea.
1499 if (twa->count() != 0) {
1500 if (d.current_work_area_ == 0)
1501 // This means that we are closing the current GuiWorkArea, so
1502 // switch to the next GuiWorkArea in the found TabWorkArea.
1503 setCurrentWorkArea(twa->currentWorkArea());
1505 // No more WorkAreas in this tab group, so delete it.
1512 // It is not a tabbed work area (i.e., the search work area), so it
1513 // should be deleted by other means.
1514 LASSERT(found_twa, return);
1516 if (d.current_work_area_ == 0) {
1517 if (d.splitter_->count() != 0) {
1518 TabWorkArea * twa = d.currentTabWorkArea();
1519 setCurrentWorkArea(twa->currentWorkArea());
1521 // No more work areas, switch to the background widget.
1522 setCurrentWorkArea(0);
1528 LayoutBox * GuiView::getLayoutDialog() const
1534 void GuiView::updateLayoutList()
1537 d.layout_->updateContents(false);
1541 void GuiView::updateToolbars()
1543 ToolbarMap::iterator end = d.toolbars_.end();
1544 if (d.current_work_area_) {
1546 if (d.current_work_area_->bufferView().cursor().inMathed()
1547 && !d.current_work_area_->bufferView().cursor().inRegexped())
1548 context |= Toolbars::MATH;
1549 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1550 context |= Toolbars::TABLE;
1551 if (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1552 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1553 context |= Toolbars::REVIEW;
1554 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1555 context |= Toolbars::MATHMACROTEMPLATE;
1556 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1557 context |= Toolbars::IPA;
1558 if (command_execute_)
1559 context |= Toolbars::MINIBUFFER;
1561 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1562 it->second->update(context);
1564 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1565 it->second->update();
1569 void GuiView::setBuffer(Buffer * newBuffer)
1571 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1572 LASSERT(newBuffer, return);
1574 GuiWorkArea * wa = workArea(*newBuffer);
1577 newBuffer->masterBuffer()->updateBuffer();
1579 wa = addWorkArea(*newBuffer);
1580 // scroll to the position when the BufferView was last closed
1581 if (lyxrc.use_lastfilepos) {
1582 LastFilePosSection::FilePos filepos =
1583 theSession().lastFilePos().load(newBuffer->fileName());
1584 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1587 //Disconnect the old buffer...there's no new one.
1590 connectBuffer(*newBuffer);
1591 connectBufferView(wa->bufferView());
1592 setCurrentWorkArea(wa);
1596 void GuiView::connectBuffer(Buffer & buf)
1598 buf.setGuiDelegate(this);
1602 void GuiView::disconnectBuffer()
1604 if (d.current_work_area_)
1605 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1609 void GuiView::connectBufferView(BufferView & bv)
1611 bv.setGuiDelegate(this);
1615 void GuiView::disconnectBufferView()
1617 if (d.current_work_area_)
1618 d.current_work_area_->bufferView().setGuiDelegate(0);
1622 void GuiView::errors(string const & error_type, bool from_master)
1624 BufferView const * const bv = currentBufferView();
1628 #if EXPORT_in_THREAD
1629 // We are called with from_master == false by default, so we
1630 // have to figure out whether that is the case or not.
1631 ErrorList & el = bv->buffer().errorList(error_type);
1633 el = bv->buffer().masterBuffer()->errorList(error_type);
1637 ErrorList const & el = from_master ?
1638 bv->buffer().masterBuffer()->errorList(error_type) :
1639 bv->buffer().errorList(error_type);
1645 string data = error_type;
1647 data = "from_master|" + error_type;
1648 showDialog("errorlist", data);
1652 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1654 d.toc_models_.updateItem(toqstr(type), dit);
1658 void GuiView::structureChanged()
1660 d.toc_models_.reset(documentBufferView());
1661 // Navigator needs more than a simple update in this case. It needs to be
1663 updateDialog("toc", "");
1667 void GuiView::updateDialog(string const & name, string const & data)
1669 if (!isDialogVisible(name))
1672 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1673 if (it == d.dialogs_.end())
1676 Dialog * const dialog = it->second.get();
1677 if (dialog->isVisibleView())
1678 dialog->initialiseParams(data);
1682 BufferView * GuiView::documentBufferView()
1684 return currentMainWorkArea()
1685 ? ¤tMainWorkArea()->bufferView()
1690 BufferView const * GuiView::documentBufferView() const
1692 return currentMainWorkArea()
1693 ? ¤tMainWorkArea()->bufferView()
1698 BufferView * GuiView::currentBufferView()
1700 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1704 BufferView const * GuiView::currentBufferView() const
1706 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1710 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1711 Buffer const * orig, Buffer * clone)
1713 bool const success = clone->autoSave();
1715 busyBuffers.remove(orig);
1717 ? _("Automatic save done.")
1718 : _("Automatic save failed!");
1722 void GuiView::autoSave()
1724 LYXERR(Debug::INFO, "Running autoSave()");
1726 Buffer * buffer = documentBufferView()
1727 ? &documentBufferView()->buffer() : 0;
1729 resetAutosaveTimers();
1733 GuiViewPrivate::busyBuffers.insert(buffer);
1734 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1735 buffer, buffer->cloneBufferOnly());
1736 d.autosave_watcher_.setFuture(f);
1737 resetAutosaveTimers();
1741 void GuiView::resetAutosaveTimers()
1744 d.autosave_timeout_.restart();
1748 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1751 Buffer * buf = currentBufferView()
1752 ? ¤tBufferView()->buffer() : 0;
1753 Buffer * doc_buffer = documentBufferView()
1754 ? &(documentBufferView()->buffer()) : 0;
1757 /* In LyX/Mac, when a dialog is open, the menus of the
1758 application can still be accessed without giving focus to
1759 the main window. In this case, we want to disable the menu
1760 entries that are buffer-related.
1761 This code must not be used on Linux and Windows, since it
1762 would disable buffer-related entries when hovering over the
1763 menu (see bug #9574).
1765 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1771 // Check whether we need a buffer
1772 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1773 // no, exit directly
1774 flag.message(from_utf8(N_("Command not allowed with"
1775 "out any document open")));
1776 flag.setEnabled(false);
1780 if (cmd.origin() == FuncRequest::TOC) {
1781 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1782 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1783 flag.setEnabled(false);
1787 switch(cmd.action()) {
1788 case LFUN_BUFFER_IMPORT:
1791 case LFUN_MASTER_BUFFER_UPDATE:
1792 case LFUN_MASTER_BUFFER_VIEW:
1794 && (doc_buffer->parent() != 0
1795 || doc_buffer->hasChildren())
1796 && !d.processing_thread_watcher_.isRunning();
1799 case LFUN_BUFFER_UPDATE:
1800 case LFUN_BUFFER_VIEW: {
1801 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1805 string format = to_utf8(cmd.argument());
1806 if (cmd.argument().empty())
1807 format = doc_buffer->params().getDefaultOutputFormat();
1808 enable = doc_buffer->params().isExportableFormat(format);
1812 case LFUN_BUFFER_RELOAD:
1813 enable = doc_buffer && !doc_buffer->isUnnamed()
1814 && doc_buffer->fileName().exists()
1815 && (!doc_buffer->isClean()
1816 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1819 case LFUN_BUFFER_CHILD_OPEN:
1820 enable = doc_buffer;
1823 case LFUN_BUFFER_WRITE:
1824 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1827 //FIXME: This LFUN should be moved to GuiApplication.
1828 case LFUN_BUFFER_WRITE_ALL: {
1829 // We enable the command only if there are some modified buffers
1830 Buffer * first = theBufferList().first();
1835 // We cannot use a for loop as the buffer list is a cycle.
1837 if (!b->isClean()) {
1841 b = theBufferList().next(b);
1842 } while (b != first);
1846 case LFUN_BUFFER_WRITE_AS:
1847 case LFUN_BUFFER_EXPORT_AS:
1848 enable = doc_buffer;
1851 case LFUN_BUFFER_CLOSE:
1852 case LFUN_VIEW_CLOSE:
1853 enable = doc_buffer;
1856 case LFUN_BUFFER_CLOSE_ALL:
1857 enable = theBufferList().last() != theBufferList().first();
1860 case LFUN_VIEW_SPLIT:
1861 if (cmd.getArg(0) == "vertical")
1862 enable = doc_buffer && (d.splitter_->count() == 1 ||
1863 d.splitter_->orientation() == Qt::Vertical);
1865 enable = doc_buffer && (d.splitter_->count() == 1 ||
1866 d.splitter_->orientation() == Qt::Horizontal);
1869 case LFUN_TAB_GROUP_CLOSE:
1870 enable = d.tabWorkAreaCount() > 1;
1873 case LFUN_TOOLBAR_TOGGLE: {
1874 string const name = cmd.getArg(0);
1875 if (GuiToolbar * t = toolbar(name))
1876 flag.setOnOff(t->isVisible());
1879 docstring const msg =
1880 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1886 case LFUN_DROP_LAYOUTS_CHOICE:
1890 case LFUN_UI_TOGGLE:
1891 flag.setOnOff(isFullScreen());
1894 case LFUN_DIALOG_DISCONNECT_INSET:
1897 case LFUN_DIALOG_HIDE:
1898 // FIXME: should we check if the dialog is shown?
1901 case LFUN_DIALOG_TOGGLE:
1902 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1903 // fall through to set "enable"
1904 case LFUN_DIALOG_SHOW: {
1905 string const name = cmd.getArg(0);
1907 enable = name == "aboutlyx"
1908 || name == "file" //FIXME: should be removed.
1910 || name == "texinfo"
1911 || name == "progress"
1912 || name == "compare";
1913 else if (name == "character" || name == "symbols"
1914 || name == "mathdelimiter" || name == "mathmatrix") {
1915 if (!buf || buf->isReadonly())
1918 Cursor const & cur = currentBufferView()->cursor();
1919 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1922 else if (name == "latexlog")
1923 enable = FileName(doc_buffer->logName()).isReadableFile();
1924 else if (name == "spellchecker")
1925 enable = theSpellChecker()
1926 && !doc_buffer->isReadonly()
1927 && !doc_buffer->text().empty();
1928 else if (name == "vclog")
1929 enable = doc_buffer->lyxvc().inUse();
1933 case LFUN_DIALOG_UPDATE: {
1934 string const name = cmd.getArg(0);
1936 enable = name == "prefs";
1940 case LFUN_COMMAND_EXECUTE:
1942 case LFUN_MENU_OPEN:
1943 // Nothing to check.
1946 case LFUN_COMPLETION_INLINE:
1947 if (!d.current_work_area_
1948 || !d.current_work_area_->completer().inlinePossible(
1949 currentBufferView()->cursor()))
1953 case LFUN_COMPLETION_POPUP:
1954 if (!d.current_work_area_
1955 || !d.current_work_area_->completer().popupPossible(
1956 currentBufferView()->cursor()))
1961 if (!d.current_work_area_
1962 || !d.current_work_area_->completer().inlinePossible(
1963 currentBufferView()->cursor()))
1967 case LFUN_COMPLETION_ACCEPT:
1968 if (!d.current_work_area_
1969 || (!d.current_work_area_->completer().popupVisible()
1970 && !d.current_work_area_->completer().inlineVisible()
1971 && !d.current_work_area_->completer().completionAvailable()))
1975 case LFUN_COMPLETION_CANCEL:
1976 if (!d.current_work_area_
1977 || (!d.current_work_area_->completer().popupVisible()
1978 && !d.current_work_area_->completer().inlineVisible()))
1982 case LFUN_BUFFER_ZOOM_OUT:
1983 enable = doc_buffer && lyxrc.zoom > 10;
1986 case LFUN_BUFFER_ZOOM_IN:
1987 enable = doc_buffer;
1990 case LFUN_BUFFER_MOVE_NEXT:
1991 case LFUN_BUFFER_MOVE_PREVIOUS:
1992 // we do not cycle when moving
1993 case LFUN_BUFFER_NEXT:
1994 case LFUN_BUFFER_PREVIOUS:
1995 // because we cycle, it doesn't matter whether on first or last
1996 enable = (d.currentTabWorkArea()->count() > 1);
1998 case LFUN_BUFFER_SWITCH:
1999 // toggle on the current buffer, but do not toggle off
2000 // the other ones (is that a good idea?)
2002 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2003 flag.setOnOff(true);
2006 case LFUN_VC_REGISTER:
2007 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2009 case LFUN_VC_RENAME:
2010 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2013 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2015 case LFUN_VC_CHECK_IN:
2016 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2018 case LFUN_VC_CHECK_OUT:
2019 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2021 case LFUN_VC_LOCKING_TOGGLE:
2022 enable = doc_buffer && !doc_buffer->isReadonly()
2023 && doc_buffer->lyxvc().lockingToggleEnabled();
2024 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2026 case LFUN_VC_REVERT:
2027 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2029 case LFUN_VC_UNDO_LAST:
2030 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2032 case LFUN_VC_REPO_UPDATE:
2033 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2035 case LFUN_VC_COMMAND: {
2036 if (cmd.argument().empty())
2038 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2042 case LFUN_VC_COMPARE:
2043 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2046 case LFUN_SERVER_GOTO_FILE_ROW:
2048 case LFUN_FORWARD_SEARCH:
2049 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2052 case LFUN_FILE_INSERT_PLAINTEXT:
2053 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2054 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2057 case LFUN_SPELLING_CONTINUOUSLY:
2058 flag.setOnOff(lyxrc.spellcheck_continuously);
2066 flag.setEnabled(false);
2072 static FileName selectTemplateFile()
2074 FileDialog dlg(qt_("Select template file"));
2075 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2076 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2078 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2079 QStringList(qt_("LyX Documents (*.lyx)")));
2081 if (result.first == FileDialog::Later)
2083 if (result.second.isEmpty())
2085 return FileName(fromqstr(result.second));
2089 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2093 Buffer * newBuffer = 0;
2095 newBuffer = checkAndLoadLyXFile(filename);
2096 } catch (ExceptionMessage const & e) {
2103 message(_("Document not loaded."));
2107 setBuffer(newBuffer);
2108 newBuffer->errors("Parse");
2111 theSession().lastFiles().add(filename);
2117 void GuiView::openDocument(string const & fname)
2119 string initpath = lyxrc.document_path;
2121 if (documentBufferView()) {
2122 string const trypath = documentBufferView()->buffer().filePath();
2123 // If directory is writeable, use this as default.
2124 if (FileName(trypath).isDirWritable())
2130 if (fname.empty()) {
2131 FileDialog dlg(qt_("Select document to open"));
2132 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2133 dlg.setButton2(qt_("Examples|#E#e"),
2134 toqstr(addPath(package().system_support().absFileName(), "examples")));
2136 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2137 FileDialog::Result result =
2138 dlg.open(toqstr(initpath), filter);
2140 if (result.first == FileDialog::Later)
2143 filename = fromqstr(result.second);
2145 // check selected filename
2146 if (filename.empty()) {
2147 message(_("Canceled."));
2153 // get absolute path of file and add ".lyx" to the filename if
2155 FileName const fullname =
2156 fileSearch(string(), filename, "lyx", support::may_not_exist);
2157 if (!fullname.empty())
2158 filename = fullname.absFileName();
2160 if (!fullname.onlyPath().isDirectory()) {
2161 Alert::warning(_("Invalid filename"),
2162 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2163 from_utf8(fullname.absFileName())));
2167 // if the file doesn't exist and isn't already open (bug 6645),
2168 // let the user create one
2169 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2170 !LyXVC::file_not_found_hook(fullname)) {
2171 // the user specifically chose this name. Believe him.
2172 Buffer * const b = newFile(filename, string(), true);
2178 docstring const disp_fn = makeDisplayPath(filename);
2179 message(bformat(_("Opening document %1$s..."), disp_fn));
2182 Buffer * buf = loadDocument(fullname);
2184 str2 = bformat(_("Document %1$s opened."), disp_fn);
2185 if (buf->lyxvc().inUse())
2186 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2187 " " + _("Version control detected.");
2189 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2194 // FIXME: clean that
2195 static bool import(GuiView * lv, FileName const & filename,
2196 string const & format, ErrorList & errorList)
2198 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2200 string loader_format;
2201 vector<string> loaders = theConverters().loaders();
2202 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2203 vector<string>::const_iterator it = loaders.begin();
2204 vector<string>::const_iterator en = loaders.end();
2205 for (; it != en; ++it) {
2206 if (!theConverters().isReachable(format, *it))
2209 string const tofile =
2210 support::changeExtension(filename.absFileName(),
2211 formats.extension(*it));
2212 if (!theConverters().convert(0, filename, FileName(tofile),
2213 filename, format, *it, errorList))
2215 loader_format = *it;
2218 if (loader_format.empty()) {
2219 frontend::Alert::error(_("Couldn't import file"),
2220 bformat(_("No information for importing the format %1$s."),
2221 formats.prettyName(format)));
2225 loader_format = format;
2227 if (loader_format == "lyx") {
2228 Buffer * buf = lv->loadDocument(lyxfile);
2232 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2236 bool as_paragraphs = loader_format == "textparagraph";
2237 string filename2 = (loader_format == format) ? filename.absFileName()
2238 : support::changeExtension(filename.absFileName(),
2239 formats.extension(loader_format));
2240 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2242 guiApp->setCurrentView(lv);
2243 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2250 void GuiView::importDocument(string const & argument)
2253 string filename = split(argument, format, ' ');
2255 LYXERR(Debug::INFO, format << " file: " << filename);
2257 // need user interaction
2258 if (filename.empty()) {
2259 string initpath = lyxrc.document_path;
2260 if (documentBufferView()) {
2261 string const trypath = documentBufferView()->buffer().filePath();
2262 // If directory is writeable, use this as default.
2263 if (FileName(trypath).isDirWritable())
2267 docstring const text = bformat(_("Select %1$s file to import"),
2268 formats.prettyName(format));
2270 FileDialog dlg(toqstr(text));
2271 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2272 dlg.setButton2(qt_("Examples|#E#e"),
2273 toqstr(addPath(package().system_support().absFileName(), "examples")));
2275 docstring filter = formats.prettyName(format);
2278 filter += from_utf8(formats.extensions(format));
2281 FileDialog::Result result =
2282 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2284 if (result.first == FileDialog::Later)
2287 filename = fromqstr(result.second);
2289 // check selected filename
2290 if (filename.empty())
2291 message(_("Canceled."));
2294 if (filename.empty())
2297 // get absolute path of file
2298 FileName const fullname(support::makeAbsPath(filename));
2300 // Can happen if the user entered a path into the dialog
2302 if (fullname.onlyFileName().empty()) {
2303 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2304 "Aborting import."),
2305 from_utf8(fullname.absFileName()));
2306 frontend::Alert::error(_("File name error"), msg);
2307 message(_("Canceled."));
2312 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2314 // Check if the document already is open
2315 Buffer * buf = theBufferList().getBuffer(lyxfile);
2318 if (!closeBuffer()) {
2319 message(_("Canceled."));
2324 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2326 // if the file exists already, and we didn't do
2327 // -i lyx thefile.lyx, warn
2328 if (lyxfile.exists() && fullname != lyxfile) {
2330 docstring text = bformat(_("The document %1$s already exists.\n\n"
2331 "Do you want to overwrite that document?"), displaypath);
2332 int const ret = Alert::prompt(_("Overwrite document?"),
2333 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2336 message(_("Canceled."));
2341 message(bformat(_("Importing %1$s..."), displaypath));
2342 ErrorList errorList;
2343 if (import(this, fullname, format, errorList))
2344 message(_("imported."));
2346 message(_("file not imported!"));
2348 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2352 void GuiView::newDocument(string const & filename, bool from_template)
2354 FileName initpath(lyxrc.document_path);
2355 if (documentBufferView()) {
2356 FileName const trypath(documentBufferView()->buffer().filePath());
2357 // If directory is writeable, use this as default.
2358 if (trypath.isDirWritable())
2362 string templatefile;
2363 if (from_template) {
2364 templatefile = selectTemplateFile().absFileName();
2365 if (templatefile.empty())
2370 if (filename.empty())
2371 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2373 b = newFile(filename, templatefile, true);
2378 // If no new document could be created, it is unsure
2379 // whether there is a valid BufferView.
2380 if (currentBufferView())
2381 // Ensure the cursor is correctly positioned on screen.
2382 currentBufferView()->showCursor();
2386 void GuiView::insertLyXFile(docstring const & fname)
2388 BufferView * bv = documentBufferView();
2393 FileName filename(to_utf8(fname));
2394 if (filename.empty()) {
2395 // Launch a file browser
2397 string initpath = lyxrc.document_path;
2398 string const trypath = bv->buffer().filePath();
2399 // If directory is writeable, use this as default.
2400 if (FileName(trypath).isDirWritable())
2404 FileDialog dlg(qt_("Select LyX document to insert"));
2405 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2406 dlg.setButton2(qt_("Examples|#E#e"),
2407 toqstr(addPath(package().system_support().absFileName(),
2410 FileDialog::Result result = dlg.open(toqstr(initpath),
2411 QStringList(qt_("LyX Documents (*.lyx)")));
2413 if (result.first == FileDialog::Later)
2417 filename.set(fromqstr(result.second));
2419 // check selected filename
2420 if (filename.empty()) {
2421 // emit message signal.
2422 message(_("Canceled."));
2427 bv->insertLyXFile(filename);
2428 bv->buffer().errors("Parse");
2432 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2434 FileName fname = b.fileName();
2435 FileName const oldname = fname;
2437 if (!newname.empty()) {
2439 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2441 // Switch to this Buffer.
2444 // No argument? Ask user through dialog.
2446 FileDialog dlg(qt_("Choose a filename to save document as"));
2447 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2448 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2450 if (!isLyXFileName(fname.absFileName()))
2451 fname.changeExtension(".lyx");
2453 FileDialog::Result result =
2454 dlg.save(toqstr(fname.onlyPath().absFileName()),
2455 QStringList(qt_("LyX Documents (*.lyx)")),
2456 toqstr(fname.onlyFileName()));
2458 if (result.first == FileDialog::Later)
2461 fname.set(fromqstr(result.second));
2466 if (!isLyXFileName(fname.absFileName()))
2467 fname.changeExtension(".lyx");
2470 // fname is now the new Buffer location.
2472 // if there is already a Buffer open with this name, we do not want
2473 // to have another one. (the second test makes sure we're not just
2474 // trying to overwrite ourselves, which is fine.)
2475 if (theBufferList().exists(fname) && fname != oldname
2476 && theBufferList().getBuffer(fname) != &b) {
2477 docstring const text =
2478 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2479 "Please close it before attempting to overwrite it.\n"
2480 "Do you want to choose a new filename?"),
2481 from_utf8(fname.absFileName()));
2482 int const ret = Alert::prompt(_("Chosen File Already Open"),
2483 text, 0, 1, _("&Rename"), _("&Cancel"));
2485 case 0: return renameBuffer(b, docstring(), kind);
2486 case 1: return false;
2491 bool const existsLocal = fname.exists();
2492 bool const existsInVC = LyXVC::fileInVC(fname);
2493 if (existsLocal || existsInVC) {
2494 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2495 if (kind != LV_WRITE_AS && existsInVC) {
2496 // renaming to a name that is already in VC
2498 docstring text = bformat(_("The document %1$s "
2499 "is already registered.\n\n"
2500 "Do you want to choose a new name?"),
2502 docstring const title = (kind == LV_VC_RENAME) ?
2503 _("Rename document?") : _("Copy document?");
2504 docstring const button = (kind == LV_VC_RENAME) ?
2505 _("&Rename") : _("&Copy");
2506 int const ret = Alert::prompt(title, text, 0, 1,
2507 button, _("&Cancel"));
2509 case 0: return renameBuffer(b, docstring(), kind);
2510 case 1: return false;
2515 docstring text = bformat(_("The document %1$s "
2516 "already exists.\n\n"
2517 "Do you want to overwrite that document?"),
2519 int const ret = Alert::prompt(_("Overwrite document?"),
2520 text, 0, 2, _("&Overwrite"),
2521 _("&Rename"), _("&Cancel"));
2524 case 1: return renameBuffer(b, docstring(), kind);
2525 case 2: return false;
2531 case LV_VC_RENAME: {
2532 string msg = b.lyxvc().rename(fname);
2535 message(from_utf8(msg));
2539 string msg = b.lyxvc().copy(fname);
2542 message(from_utf8(msg));
2548 // LyXVC created the file already in case of LV_VC_RENAME or
2549 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2550 // relative paths of included stuff right if we moved e.g. from
2551 // /a/b.lyx to /a/c/b.lyx.
2553 bool const saved = saveBuffer(b, fname);
2560 struct PrettyNameComparator
2562 bool operator()(Format const *first, Format const *second) const {
2563 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2564 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2569 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2571 FileName fname = b.fileName();
2573 FileDialog dlg(qt_("Choose a filename to export the document as"));
2574 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2577 QString const anyformat = qt_("Guess from extension (*.*)");
2579 Formats::const_iterator it = formats.begin();
2580 vector<Format const *> export_formats;
2581 for (; it != formats.end(); ++it)
2582 if (it->documentFormat())
2583 export_formats.push_back(&(*it));
2584 PrettyNameComparator cmp;
2585 sort(export_formats.begin(), export_formats.end(), cmp);
2586 vector<Format const *>::const_iterator fit = export_formats.begin();
2587 map<QString, string> fmap;
2590 for (; fit != export_formats.end(); ++fit) {
2591 docstring const loc_prettyname =
2592 translateIfPossible(from_utf8((*fit)->prettyname()));
2593 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2595 from_ascii((*fit)->extension())));
2596 types << loc_filter;
2597 fmap[loc_filter] = (*fit)->name();
2598 if (from_ascii((*fit)->name()) == iformat) {
2599 filter = loc_filter;
2600 ext = (*fit)->extension();
2603 string ofname = fname.onlyFileName();
2605 ofname = support::changeExtension(ofname, ext);
2606 FileDialog::Result result =
2607 dlg.save(toqstr(fname.onlyPath().absFileName()),
2611 if (result.first != FileDialog::Chosen)
2615 fname.set(fromqstr(result.second));
2616 if (filter == anyformat)
2617 fmt_name = formats.getFormatFromExtension(fname.extension());
2619 fmt_name = fmap[filter];
2620 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2621 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2623 if (fmt_name.empty() || fname.empty())
2626 // fname is now the new Buffer location.
2627 if (FileName(fname).exists()) {
2628 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2629 docstring text = bformat(_("The document %1$s already "
2630 "exists.\n\nDo you want to "
2631 "overwrite that document?"),
2633 int const ret = Alert::prompt(_("Overwrite document?"),
2634 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2637 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2638 case 2: return false;
2642 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2645 return dr.dispatched();
2649 bool GuiView::saveBuffer(Buffer & b)
2651 return saveBuffer(b, FileName());
2655 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2657 if (workArea(b) && workArea(b)->inDialogMode())
2660 if (fn.empty() && b.isUnnamed())
2661 return renameBuffer(b, docstring());
2663 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2665 theSession().lastFiles().add(b.fileName());
2669 // Switch to this Buffer.
2672 // FIXME: we don't tell the user *WHY* the save failed !!
2673 docstring const file = makeDisplayPath(b.absFileName(), 30);
2674 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2675 "Do you want to rename the document and "
2676 "try again?"), file);
2677 int const ret = Alert::prompt(_("Rename and save?"),
2678 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2681 if (!renameBuffer(b, docstring()))
2690 return saveBuffer(b, fn);
2694 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2696 return closeWorkArea(wa, false);
2700 // We only want to close the buffer if it is not visible in other workareas
2701 // of the same view, nor in other views, and if this is not a child
2702 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2704 Buffer & buf = wa->bufferView().buffer();
2706 bool last_wa = d.countWorkAreasOf(buf) == 1
2707 && !inOtherView(buf) && !buf.parent();
2709 bool close_buffer = last_wa;
2712 if (lyxrc.close_buffer_with_last_view == "yes")
2714 else if (lyxrc.close_buffer_with_last_view == "no")
2715 close_buffer = false;
2718 if (buf.isUnnamed())
2719 file = from_utf8(buf.fileName().onlyFileName());
2721 file = buf.fileName().displayName(30);
2722 docstring const text = bformat(
2723 _("Last view on document %1$s is being closed.\n"
2724 "Would you like to close or hide the document?\n"
2726 "Hidden documents can be displayed back through\n"
2727 "the menu: View->Hidden->...\n"
2729 "To remove this question, set your preference in:\n"
2730 " Tools->Preferences->Look&Feel->UserInterface\n"
2732 int ret = Alert::prompt(_("Close or hide document?"),
2733 text, 0, 1, _("&Close"), _("&Hide"));
2734 close_buffer = (ret == 0);
2738 return closeWorkArea(wa, close_buffer);
2742 bool GuiView::closeBuffer()
2744 GuiWorkArea * wa = currentMainWorkArea();
2745 setCurrentWorkArea(wa);
2746 Buffer & buf = wa->bufferView().buffer();
2747 return wa && closeWorkArea(wa, !buf.parent());
2751 void GuiView::writeSession() const {
2752 GuiWorkArea const * active_wa = currentMainWorkArea();
2753 for (int i = 0; i < d.splitter_->count(); ++i) {
2754 TabWorkArea * twa = d.tabWorkArea(i);
2755 for (int j = 0; j < twa->count(); ++j) {
2756 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2757 Buffer & buf = wa->bufferView().buffer();
2758 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2764 bool GuiView::closeBufferAll()
2766 // Close the workareas in all other views
2767 QList<int> const ids = guiApp->viewIds();
2768 for (int i = 0; i != ids.size(); ++i) {
2769 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2773 // Close our own workareas
2774 if (!closeWorkAreaAll())
2777 // Now close the hidden buffers. We prevent hidden buffers from being
2778 // dirty, so we can just close them.
2779 theBufferList().closeAll();
2784 bool GuiView::closeWorkAreaAll()
2786 setCurrentWorkArea(currentMainWorkArea());
2788 // We might be in a situation that there is still a tabWorkArea, but
2789 // there are no tabs anymore. This can happen when we get here after a
2790 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2791 // many TabWorkArea's have no documents anymore.
2794 // We have to call count() each time, because it can happen that
2795 // more than one splitter will disappear in one iteration (bug 5998).
2796 for (; d.splitter_->count() > empty_twa; ) {
2797 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2799 if (twa->count() == 0)
2802 setCurrentWorkArea(twa->currentWorkArea());
2803 if (!closeTabWorkArea(twa))
2811 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2816 Buffer & buf = wa->bufferView().buffer();
2818 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2819 Alert::warning(_("Close document"),
2820 _("Document could not be closed because it is being processed by LyX."));
2825 return closeBuffer(buf);
2827 if (!inMultiTabs(wa))
2828 if (!saveBufferIfNeeded(buf, true))
2836 bool GuiView::closeBuffer(Buffer & buf)
2838 // If we are in a close_event all children will be closed in some time,
2839 // so no need to do it here. This will ensure that the children end up
2840 // in the session file in the correct order. If we close the master
2841 // buffer, we can close or release the child buffers here too.
2842 bool success = true;
2844 ListOfBuffers clist = buf.getChildren();
2845 ListOfBuffers::const_iterator it = clist.begin();
2846 ListOfBuffers::const_iterator const bend = clist.end();
2847 for (; it != bend; ++it) {
2848 // If a child is dirty, do not close
2849 // without user intervention
2850 //FIXME: should we look in other tabworkareas?
2851 Buffer * child_buf = *it;
2852 GuiWorkArea * child_wa = workArea(*child_buf);
2854 if (!closeWorkArea(child_wa, true)) {
2859 theBufferList().releaseChild(&buf, child_buf);
2863 // goto bookmark to update bookmark pit.
2864 //FIXME: we should update only the bookmarks related to this buffer!
2865 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2866 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2867 guiApp->gotoBookmark(i+1, false, false);
2869 if (saveBufferIfNeeded(buf, false)) {
2870 buf.removeAutosaveFile();
2871 theBufferList().release(&buf);
2875 // open all children again to avoid a crash because of dangling
2876 // pointers (bug 6603)
2882 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2884 while (twa == d.currentTabWorkArea()) {
2885 twa->setCurrentIndex(twa->count()-1);
2887 GuiWorkArea * wa = twa->currentWorkArea();
2888 Buffer & b = wa->bufferView().buffer();
2890 // We only want to close the buffer if the same buffer is not visible
2891 // in another view, and if this is not a child and if we are closing
2892 // a view (not a tabgroup).
2893 bool const close_buffer =
2894 !inOtherView(b) && !b.parent() && closing_;
2896 if (!closeWorkArea(wa, close_buffer))
2903 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2905 if (buf.isClean() || buf.paragraphs().empty())
2908 // Switch to this Buffer.
2913 if (buf.isUnnamed())
2914 file = from_utf8(buf.fileName().onlyFileName());
2916 file = buf.fileName().displayName(30);
2918 // Bring this window to top before asking questions.
2923 if (hiding && buf.isUnnamed()) {
2924 docstring const text = bformat(_("The document %1$s has not been "
2925 "saved yet.\n\nDo you want to save "
2926 "the document?"), file);
2927 ret = Alert::prompt(_("Save new document?"),
2928 text, 0, 1, _("&Save"), _("&Cancel"));
2932 docstring const text = bformat(_("The document %1$s has unsaved changes."
2933 "\n\nDo you want to save the document or discard the changes?"), file);
2934 ret = Alert::prompt(_("Save changed document?"),
2935 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2940 if (!saveBuffer(buf))
2944 // If we crash after this we could have no autosave file
2945 // but I guess this is really improbable (Jug).
2946 // Sometimes improbable things happen:
2947 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2948 // buf.removeAutosaveFile();
2950 // revert all changes
2961 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2963 Buffer & buf = wa->bufferView().buffer();
2965 for (int i = 0; i != d.splitter_->count(); ++i) {
2966 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2967 if (wa_ && wa_ != wa)
2970 return inOtherView(buf);
2974 bool GuiView::inOtherView(Buffer & buf)
2976 QList<int> const ids = guiApp->viewIds();
2978 for (int i = 0; i != ids.size(); ++i) {
2982 if (guiApp->view(ids[i]).workArea(buf))
2989 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2991 if (!documentBufferView())
2994 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2995 Buffer * const curbuf = &documentBufferView()->buffer();
2996 int nwa = twa->count();
2997 for (int i = 0; i < nwa; ++i) {
2998 if (&workArea(i)->bufferView().buffer() == curbuf) {
3000 if (np == NEXTBUFFER)
3001 next_index = (i == nwa - 1 ? 0 : i + 1);
3003 next_index = (i == 0 ? nwa - 1 : i - 1);
3005 twa->moveTab(i, next_index);
3007 setBuffer(&workArea(next_index)->bufferView().buffer());
3015 /// make sure the document is saved
3016 static bool ensureBufferClean(Buffer * buffer)
3018 LASSERT(buffer, return false);
3019 if (buffer->isClean() && !buffer->isUnnamed())
3022 docstring const file = buffer->fileName().displayName(30);
3025 if (!buffer->isUnnamed()) {
3026 text = bformat(_("The document %1$s has unsaved "
3027 "changes.\n\nDo you want to save "
3028 "the document?"), file);
3029 title = _("Save changed document?");
3032 text = bformat(_("The document %1$s has not been "
3033 "saved yet.\n\nDo you want to save "
3034 "the document?"), file);
3035 title = _("Save new document?");
3037 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3040 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3042 return buffer->isClean() && !buffer->isUnnamed();
3046 bool GuiView::reloadBuffer(Buffer & buf)
3048 Buffer::ReadStatus status = buf.reload();
3049 return status == Buffer::ReadSuccess;
3053 void GuiView::checkExternallyModifiedBuffers()
3055 BufferList::iterator bit = theBufferList().begin();
3056 BufferList::iterator const bend = theBufferList().end();
3057 for (; bit != bend; ++bit) {
3058 Buffer * buf = *bit;
3059 if (buf->fileName().exists()
3060 && buf->isExternallyModified(Buffer::checksum_method)) {
3061 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3062 " Reload now? Any local changes will be lost."),
3063 from_utf8(buf->absFileName()));
3064 int const ret = Alert::prompt(_("Reload externally changed document?"),
3065 text, 0, 1, _("&Reload"), _("&Cancel"));
3073 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3075 Buffer * buffer = documentBufferView()
3076 ? &(documentBufferView()->buffer()) : 0;
3078 switch (cmd.action()) {
3079 case LFUN_VC_REGISTER:
3080 if (!buffer || !ensureBufferClean(buffer))
3082 if (!buffer->lyxvc().inUse()) {
3083 if (buffer->lyxvc().registrer()) {
3084 reloadBuffer(*buffer);
3085 dr.clearMessageUpdate();
3090 case LFUN_VC_RENAME:
3091 case LFUN_VC_COPY: {
3092 if (!buffer || !ensureBufferClean(buffer))
3094 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3095 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3096 // Some changes are not yet committed.
3097 // We test here and not in getStatus(), since
3098 // this test is expensive.
3100 LyXVC::CommandResult ret =
3101 buffer->lyxvc().checkIn(log);
3103 if (ret == LyXVC::ErrorCommand ||
3104 ret == LyXVC::VCSuccess)
3105 reloadBuffer(*buffer);
3106 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3107 frontend::Alert::error(
3108 _("Revision control error."),
3109 _("Document could not be checked in."));
3113 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3114 LV_VC_RENAME : LV_VC_COPY;
3115 renameBuffer(*buffer, cmd.argument(), kind);
3120 case LFUN_VC_CHECK_IN:
3121 if (!buffer || !ensureBufferClean(buffer))
3123 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3125 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3127 // Only skip reloading if the checkin was cancelled or
3128 // an error occurred before the real checkin VCS command
3129 // was executed, since the VCS might have changed the
3130 // file even if it could not checkin successfully.
3131 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3132 reloadBuffer(*buffer);
3136 case LFUN_VC_CHECK_OUT:
3137 if (!buffer || !ensureBufferClean(buffer))
3139 if (buffer->lyxvc().inUse()) {
3140 dr.setMessage(buffer->lyxvc().checkOut());
3141 reloadBuffer(*buffer);
3145 case LFUN_VC_LOCKING_TOGGLE:
3146 LASSERT(buffer, return);
3147 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3149 if (buffer->lyxvc().inUse()) {
3150 string res = buffer->lyxvc().lockingToggle();
3152 frontend::Alert::error(_("Revision control error."),
3153 _("Error when setting the locking property."));
3156 reloadBuffer(*buffer);
3161 case LFUN_VC_REVERT:
3162 LASSERT(buffer, return);
3163 if (buffer->lyxvc().revert()) {
3164 reloadBuffer(*buffer);
3165 dr.clearMessageUpdate();
3169 case LFUN_VC_UNDO_LAST:
3170 LASSERT(buffer, return);
3171 buffer->lyxvc().undoLast();
3172 reloadBuffer(*buffer);
3173 dr.clearMessageUpdate();
3176 case LFUN_VC_REPO_UPDATE:
3177 LASSERT(buffer, return);
3178 if (ensureBufferClean(buffer)) {
3179 dr.setMessage(buffer->lyxvc().repoUpdate());
3180 checkExternallyModifiedBuffers();
3184 case LFUN_VC_COMMAND: {
3185 string flag = cmd.getArg(0);
3186 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3189 if (contains(flag, 'M')) {
3190 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3193 string path = cmd.getArg(1);
3194 if (contains(path, "$$p") && buffer)
3195 path = subst(path, "$$p", buffer->filePath());
3196 LYXERR(Debug::LYXVC, "Directory: " << path);
3198 if (!pp.isReadableDirectory()) {
3199 lyxerr << _("Directory is not accessible.") << endl;
3202 support::PathChanger p(pp);
3204 string command = cmd.getArg(2);
3205 if (command.empty())
3208 command = subst(command, "$$i", buffer->absFileName());
3209 command = subst(command, "$$p", buffer->filePath());
3211 command = subst(command, "$$m", to_utf8(message));
3212 LYXERR(Debug::LYXVC, "Command: " << command);
3214 one.startscript(Systemcall::Wait, command);
3218 if (contains(flag, 'I'))
3219 buffer->markDirty();
3220 if (contains(flag, 'R'))
3221 reloadBuffer(*buffer);
3226 case LFUN_VC_COMPARE: {
3228 if (cmd.argument().empty()) {
3229 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3233 string rev1 = cmd.getArg(0);
3237 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3240 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3241 f2 = buffer->absFileName();
3243 string rev2 = cmd.getArg(1);
3247 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3251 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3252 f1 << "\n" << f2 << "\n" );
3253 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3254 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3264 void GuiView::openChildDocument(string const & fname)
3266 LASSERT(documentBufferView(), return);
3267 Buffer & buffer = documentBufferView()->buffer();
3268 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3269 documentBufferView()->saveBookmark(false);
3271 if (theBufferList().exists(filename)) {
3272 child = theBufferList().getBuffer(filename);
3275 message(bformat(_("Opening child document %1$s..."),
3276 makeDisplayPath(filename.absFileName())));
3277 child = loadDocument(filename, false);
3279 // Set the parent name of the child document.
3280 // This makes insertion of citations and references in the child work,
3281 // when the target is in the parent or another child document.
3283 child->setParent(&buffer);
3287 bool GuiView::goToFileRow(string const & argument)
3291 size_t i = argument.find_last_of(' ');
3292 if (i != string::npos) {
3293 file_name = os::internal_path(trim(argument.substr(0, i)));
3294 istringstream is(argument.substr(i + 1));
3299 if (i == string::npos) {
3300 LYXERR0("Wrong argument: " << argument);
3304 string const abstmp = package().temp_dir().absFileName();
3305 string const realtmp = package().temp_dir().realPath();
3306 // We have to use os::path_prefix_is() here, instead of
3307 // simply prefixIs(), because the file name comes from
3308 // an external application and may need case adjustment.
3309 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3310 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3311 // Needed by inverse dvi search. If it is a file
3312 // in tmpdir, call the apropriated function.
3313 // If tmpdir is a symlink, we may have the real
3314 // path passed back, so we correct for that.
3315 if (!prefixIs(file_name, abstmp))
3316 file_name = subst(file_name, realtmp, abstmp);
3317 buf = theBufferList().getBufferFromTmp(file_name);
3319 // Must replace extension of the file to be .lyx
3320 // and get full path
3321 FileName const s = fileSearch(string(),
3322 support::changeExtension(file_name, ".lyx"), "lyx");
3323 // Either change buffer or load the file
3324 if (theBufferList().exists(s))
3325 buf = theBufferList().getBuffer(s);
3326 else if (s.exists()) {
3327 buf = loadDocument(s);
3332 _("File does not exist: %1$s"),
3333 makeDisplayPath(file_name)));
3339 _("No buffer for file: %1$s."),
3340 makeDisplayPath(file_name))
3345 documentBufferView()->setCursorFromRow(row);
3351 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3353 Buffer::ExportStatus const status = func(format);
3355 // the cloning operation will have produced a clone of the entire set of
3356 // documents, starting from the master. so we must delete those.
3357 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3359 busyBuffers.remove(orig);
3364 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3366 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3367 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3371 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3373 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3374 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3378 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3380 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3381 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3385 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3386 string const & argument,
3387 Buffer const * used_buffer,
3388 docstring const & msg,
3389 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3390 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3391 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3396 string format = argument;
3398 format = used_buffer->params().getDefaultOutputFormat();
3399 processing_format = format;
3401 progress_->clearMessages();
3404 #if EXPORT_in_THREAD
3405 GuiViewPrivate::busyBuffers.insert(used_buffer);
3406 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3407 if (!cloned_buffer) {
3408 Alert::error(_("Export Error"),
3409 _("Error cloning the Buffer."));
3412 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3417 setPreviewFuture(f);
3418 last_export_format = used_buffer->params().bufferFormat();
3421 // We are asynchronous, so we don't know here anything about the success
3424 Buffer::ExportStatus status;
3426 // TODO check here if it breaks exporting with Qt < 4.4
3427 status = (used_buffer->*syncFunc)(format, true);
3428 } else if (previewFunc) {
3429 status = (used_buffer->*previewFunc)(format);
3432 handleExportStatus(gv_, status, format);
3434 return (status == Buffer::ExportSuccess
3435 || status == Buffer::PreviewSuccess);
3439 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3441 BufferView * bv = currentBufferView();
3442 LASSERT(bv, return);
3444 // Let the current BufferView dispatch its own actions.
3445 bv->dispatch(cmd, dr);
3446 if (dr.dispatched())
3449 // Try with the document BufferView dispatch if any.
3450 BufferView * doc_bv = documentBufferView();
3451 if (doc_bv && doc_bv != bv) {
3452 doc_bv->dispatch(cmd, dr);
3453 if (dr.dispatched())
3457 // Then let the current Cursor dispatch its own actions.
3458 bv->cursor().dispatch(cmd);
3460 // update completion. We do it here and not in
3461 // processKeySym to avoid another redraw just for a
3462 // changed inline completion
3463 if (cmd.origin() == FuncRequest::KEYBOARD) {
3464 if (cmd.action() == LFUN_SELF_INSERT
3465 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3466 updateCompletion(bv->cursor(), true, true);
3467 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3468 updateCompletion(bv->cursor(), false, true);
3470 updateCompletion(bv->cursor(), false, false);
3473 dr = bv->cursor().result();
3477 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3479 BufferView * bv = currentBufferView();
3480 // By default we won't need any update.
3481 dr.screenUpdate(Update::None);
3482 // assume cmd will be dispatched
3483 dr.dispatched(true);
3485 Buffer * doc_buffer = documentBufferView()
3486 ? &(documentBufferView()->buffer()) : 0;
3488 if (cmd.origin() == FuncRequest::TOC) {
3489 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3490 // FIXME: do we need to pass a DispatchResult object here?
3491 toc->doDispatch(bv->cursor(), cmd);
3495 string const argument = to_utf8(cmd.argument());
3497 switch(cmd.action()) {
3498 case LFUN_BUFFER_CHILD_OPEN:
3499 openChildDocument(to_utf8(cmd.argument()));
3502 case LFUN_BUFFER_IMPORT:
3503 importDocument(to_utf8(cmd.argument()));
3506 case LFUN_BUFFER_EXPORT: {
3509 FileName target_dir = doc_buffer->fileName().onlyPath();
3510 string const dest = cmd.getArg(1);
3511 if (!dest.empty() && FileName::isAbsolute(dest))
3512 target_dir = FileName(support::onlyPath(dest));
3513 // GCC only sees strfwd.h when building merged
3514 if (::lyx::operator==(cmd.argument(), "custom")) {
3515 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3518 if (!target_dir.isDirWritable()) {
3519 exportBufferAs(*doc_buffer, cmd.argument());
3522 /* TODO/Review: Is it a problem to also export the children?
3523 See the update_unincluded flag */
3524 d.asyncBufferProcessing(argument,
3527 &GuiViewPrivate::exportAndDestroy,
3530 // TODO Inform user about success
3534 case LFUN_BUFFER_EXPORT_AS: {
3535 LASSERT(doc_buffer, break);
3536 docstring f = cmd.argument();
3538 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3539 exportBufferAs(*doc_buffer, f);
3543 case LFUN_BUFFER_UPDATE: {
3544 d.asyncBufferProcessing(argument,
3547 &GuiViewPrivate::compileAndDestroy,
3552 case LFUN_BUFFER_VIEW: {
3553 d.asyncBufferProcessing(argument,
3555 _("Previewing ..."),
3556 &GuiViewPrivate::previewAndDestroy,
3561 case LFUN_MASTER_BUFFER_UPDATE: {
3562 d.asyncBufferProcessing(argument,
3563 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3565 &GuiViewPrivate::compileAndDestroy,
3570 case LFUN_MASTER_BUFFER_VIEW: {
3571 d.asyncBufferProcessing(argument,
3572 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3574 &GuiViewPrivate::previewAndDestroy,
3575 0, &Buffer::preview);
3578 case LFUN_BUFFER_SWITCH: {
3579 string const file_name = to_utf8(cmd.argument());
3580 if (!FileName::isAbsolute(file_name)) {
3582 dr.setMessage(_("Absolute filename expected."));
3586 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3589 dr.setMessage(_("Document not loaded"));
3593 // Do we open or switch to the buffer in this view ?
3594 if (workArea(*buffer)
3595 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3600 // Look for the buffer in other views
3601 QList<int> const ids = guiApp->viewIds();
3603 for (; i != ids.size(); ++i) {
3604 GuiView & gv = guiApp->view(ids[i]);
3605 if (gv.workArea(*buffer)) {
3606 gv.activateWindow();
3607 gv.setBuffer(buffer);
3612 // If necessary, open a new window as a last resort
3613 if (i == ids.size()) {
3614 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3620 case LFUN_BUFFER_NEXT:
3621 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3624 case LFUN_BUFFER_MOVE_NEXT:
3625 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3628 case LFUN_BUFFER_PREVIOUS:
3629 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3632 case LFUN_BUFFER_MOVE_PREVIOUS:
3633 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3636 case LFUN_COMMAND_EXECUTE: {
3637 command_execute_ = true;
3640 case LFUN_DROP_LAYOUTS_CHOICE:
3641 d.layout_->showPopup();
3644 case LFUN_MENU_OPEN:
3645 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3646 menu->exec(QCursor::pos());
3649 case LFUN_FILE_INSERT:
3650 insertLyXFile(cmd.argument());
3653 case LFUN_FILE_INSERT_PLAINTEXT:
3654 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3655 string const fname = to_utf8(cmd.argument());
3656 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3657 dr.setMessage(_("Absolute filename expected."));
3661 FileName filename(fname);
3662 if (fname.empty()) {
3663 FileDialog dlg(qt_("Select file to insert"));
3665 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3666 QStringList(qt_("All Files (*)")));
3668 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3669 dr.setMessage(_("Canceled."));
3673 filename.set(fromqstr(result.second));
3677 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3678 bv->dispatch(new_cmd, dr);
3683 case LFUN_BUFFER_RELOAD: {
3684 LASSERT(doc_buffer, break);
3687 if (!doc_buffer->isClean()) {
3688 docstring const file =
3689 makeDisplayPath(doc_buffer->absFileName(), 20);
3690 docstring text = bformat(_("Any changes will be lost. "
3691 "Are you sure you want to revert to the saved version "
3692 "of the document %1$s?"), file);
3693 ret = Alert::prompt(_("Revert to saved document?"),
3694 text, 1, 1, _("&Revert"), _("&Cancel"));
3698 doc_buffer->markClean();
3699 reloadBuffer(*doc_buffer);
3700 dr.forceBufferUpdate();
3705 case LFUN_BUFFER_WRITE:
3706 LASSERT(doc_buffer, break);
3707 saveBuffer(*doc_buffer);
3710 case LFUN_BUFFER_WRITE_AS:
3711 LASSERT(doc_buffer, break);
3712 renameBuffer(*doc_buffer, cmd.argument());
3715 case LFUN_BUFFER_WRITE_ALL: {
3716 Buffer * first = theBufferList().first();
3719 message(_("Saving all documents..."));
3720 // We cannot use a for loop as the buffer list cycles.
3723 if (!b->isClean()) {
3725 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3727 b = theBufferList().next(b);
3728 } while (b != first);
3729 dr.setMessage(_("All documents saved."));
3733 case LFUN_BUFFER_CLOSE:
3737 case LFUN_BUFFER_CLOSE_ALL:
3741 case LFUN_TOOLBAR_TOGGLE: {
3742 string const name = cmd.getArg(0);
3743 if (GuiToolbar * t = toolbar(name))
3748 case LFUN_DIALOG_UPDATE: {
3749 string const name = to_utf8(cmd.argument());
3750 if (name == "prefs" || name == "document")
3751 updateDialog(name, string());
3752 else if (name == "paragraph")
3753 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3754 else if (currentBufferView()) {
3755 Inset * inset = currentBufferView()->editedInset(name);
3756 // Can only update a dialog connected to an existing inset
3758 // FIXME: get rid of this indirection; GuiView ask the inset
3759 // if he is kind enough to update itself...
3760 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3761 //FIXME: pass DispatchResult here?
3762 inset->dispatch(currentBufferView()->cursor(), fr);
3768 case LFUN_DIALOG_TOGGLE: {
3769 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3770 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3771 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3775 case LFUN_DIALOG_DISCONNECT_INSET:
3776 disconnectDialog(to_utf8(cmd.argument()));
3779 case LFUN_DIALOG_HIDE: {
3780 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3784 case LFUN_DIALOG_SHOW: {
3785 string const name = cmd.getArg(0);
3786 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3788 if (name == "character") {
3789 data = freefont2string();
3791 showDialog("character", data);
3792 } else if (name == "latexlog") {
3793 Buffer::LogType type;
3794 string const logfile = doc_buffer->logName(&type);
3796 case Buffer::latexlog:
3799 case Buffer::buildlog:
3803 data += Lexer::quoteString(logfile);
3804 showDialog("log", data);
3805 } else if (name == "vclog") {
3806 string const data = "vc " +
3807 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3808 showDialog("log", data);
3809 } else if (name == "symbols") {
3810 data = bv->cursor().getEncoding()->name();
3812 showDialog("symbols", data);
3814 } else if (name == "prefs" && isFullScreen()) {
3815 lfunUiToggle("fullscreen");
3816 showDialog("prefs", data);
3818 showDialog(name, data);
3823 dr.setMessage(cmd.argument());
3826 case LFUN_UI_TOGGLE: {
3827 string arg = cmd.getArg(0);
3828 if (!lfunUiToggle(arg)) {
3829 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3830 dr.setMessage(bformat(msg, from_utf8(arg)));
3832 // Make sure the keyboard focus stays in the work area.
3837 case LFUN_VIEW_SPLIT: {
3838 LASSERT(doc_buffer, break);
3839 string const orientation = cmd.getArg(0);
3840 d.splitter_->setOrientation(orientation == "vertical"
3841 ? Qt::Vertical : Qt::Horizontal);
3842 TabWorkArea * twa = addTabWorkArea();
3843 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3844 setCurrentWorkArea(wa);
3847 case LFUN_TAB_GROUP_CLOSE:
3848 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3849 closeTabWorkArea(twa);
3850 d.current_work_area_ = 0;
3851 twa = d.currentTabWorkArea();
3852 // Switch to the next GuiWorkArea in the found TabWorkArea.
3854 // Make sure the work area is up to date.
3855 setCurrentWorkArea(twa->currentWorkArea());
3857 setCurrentWorkArea(0);
3862 case LFUN_VIEW_CLOSE:
3863 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3864 closeWorkArea(twa->currentWorkArea());
3865 d.current_work_area_ = 0;
3866 twa = d.currentTabWorkArea();
3867 // Switch to the next GuiWorkArea in the found TabWorkArea.
3869 // Make sure the work area is up to date.
3870 setCurrentWorkArea(twa->currentWorkArea());
3872 setCurrentWorkArea(0);
3877 case LFUN_COMPLETION_INLINE:
3878 if (d.current_work_area_)
3879 d.current_work_area_->completer().showInline();
3882 case LFUN_COMPLETION_POPUP:
3883 if (d.current_work_area_)
3884 d.current_work_area_->completer().showPopup();
3889 if (d.current_work_area_)
3890 d.current_work_area_->completer().tab();
3893 case LFUN_COMPLETION_CANCEL:
3894 if (d.current_work_area_) {
3895 if (d.current_work_area_->completer().popupVisible())
3896 d.current_work_area_->completer().hidePopup();
3898 d.current_work_area_->completer().hideInline();
3902 case LFUN_COMPLETION_ACCEPT:
3903 if (d.current_work_area_)
3904 d.current_work_area_->completer().activate();
3907 case LFUN_BUFFER_ZOOM_IN:
3908 case LFUN_BUFFER_ZOOM_OUT:
3909 if (cmd.argument().empty()) {
3910 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3915 lyxrc.zoom += convert<int>(cmd.argument());
3917 if (lyxrc.zoom < 10)
3920 // The global QPixmapCache is used in GuiPainter to cache text
3921 // painting so we must reset it.
3922 QPixmapCache::clear();
3923 guiApp->fontLoader().update();
3924 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3927 case LFUN_VC_REGISTER:
3928 case LFUN_VC_RENAME:
3930 case LFUN_VC_CHECK_IN:
3931 case LFUN_VC_CHECK_OUT:
3932 case LFUN_VC_REPO_UPDATE:
3933 case LFUN_VC_LOCKING_TOGGLE:
3934 case LFUN_VC_REVERT:
3935 case LFUN_VC_UNDO_LAST:
3936 case LFUN_VC_COMMAND:
3937 case LFUN_VC_COMPARE:
3938 dispatchVC(cmd, dr);
3941 case LFUN_SERVER_GOTO_FILE_ROW:
3942 goToFileRow(to_utf8(cmd.argument()));
3945 case LFUN_FORWARD_SEARCH: {
3946 Buffer const * doc_master = doc_buffer->masterBuffer();
3947 FileName const path(doc_master->temppath());
3948 string const texname = doc_master->isChild(doc_buffer)
3949 ? DocFileName(changeExtension(
3950 doc_buffer->absFileName(),
3951 "tex")).mangledFileName()
3952 : doc_buffer->latexName();
3953 string const fulltexname =
3954 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3955 string const mastername =
3956 removeExtension(doc_master->latexName());
3957 FileName const dviname(addName(path.absFileName(),
3958 addExtension(mastername, "dvi")));
3959 FileName const pdfname(addName(path.absFileName(),
3960 addExtension(mastername, "pdf")));
3961 bool const have_dvi = dviname.exists();
3962 bool const have_pdf = pdfname.exists();
3963 if (!have_dvi && !have_pdf) {
3964 dr.setMessage(_("Please, preview the document first."));
3967 string outname = dviname.onlyFileName();
3968 string command = lyxrc.forward_search_dvi;
3969 if (!have_dvi || (have_pdf &&
3970 pdfname.lastModified() > dviname.lastModified())) {
3971 outname = pdfname.onlyFileName();
3972 command = lyxrc.forward_search_pdf;
3975 DocIterator tmpcur = bv->cursor();
3977 while (tmpcur.inMathed())
3979 int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3980 tmpcur.paragraph().id(), tmpcur.pos());
3981 LYXERR(Debug::ACTION, "Forward search: row:" << row
3982 << " id:" << tmpcur.paragraph().id());
3983 if (!row || command.empty()) {
3984 dr.setMessage(_("Couldn't proceed."));
3987 string texrow = convert<string>(row);
3989 command = subst(command, "$$n", texrow);
3990 command = subst(command, "$$f", fulltexname);
3991 command = subst(command, "$$t", texname);
3992 command = subst(command, "$$o", outname);
3994 PathChanger p(path);
3996 one.startscript(Systemcall::DontWait, command);
4000 case LFUN_SPELLING_CONTINUOUSLY:
4001 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4002 dr.screenUpdate(Update::Force | Update::FitCursor);
4006 // The LFUN must be for one of BufferView, Buffer or Cursor;
4008 dispatchToBufferView(cmd, dr);
4012 // Part of automatic menu appearance feature.
4013 if (isFullScreen()) {
4014 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4018 // Need to update bv because many LFUNs here might have destroyed it
4019 bv = currentBufferView();
4021 // Clear non-empty selections
4022 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4024 Cursor & cur = bv->cursor();
4025 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4026 cur.clearSelection();
4032 bool GuiView::lfunUiToggle(string const & ui_component)
4034 if (ui_component == "scrollbar") {
4035 // hide() is of no help
4036 if (d.current_work_area_->verticalScrollBarPolicy() ==
4037 Qt::ScrollBarAlwaysOff)
4039 d.current_work_area_->setVerticalScrollBarPolicy(
4040 Qt::ScrollBarAsNeeded);
4042 d.current_work_area_->setVerticalScrollBarPolicy(
4043 Qt::ScrollBarAlwaysOff);
4044 } else if (ui_component == "statusbar") {
4045 statusBar()->setVisible(!statusBar()->isVisible());
4046 } else if (ui_component == "menubar") {
4047 menuBar()->setVisible(!menuBar()->isVisible());
4049 if (ui_component == "frame") {
4051 getContentsMargins(&l, &t, &r, &b);
4052 //are the frames in default state?
4053 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4055 setContentsMargins(-2, -2, -2, -2);
4057 setContentsMargins(0, 0, 0, 0);
4060 if (ui_component == "fullscreen") {
4068 void GuiView::toggleFullScreen()
4070 if (isFullScreen()) {
4071 for (int i = 0; i != d.splitter_->count(); ++i)
4072 d.tabWorkArea(i)->setFullScreen(false);
4073 setContentsMargins(0, 0, 0, 0);
4074 setWindowState(windowState() ^ Qt::WindowFullScreen);
4077 statusBar()->show();
4080 hideDialogs("prefs", 0);
4081 for (int i = 0; i != d.splitter_->count(); ++i)
4082 d.tabWorkArea(i)->setFullScreen(true);
4083 setContentsMargins(-2, -2, -2, -2);
4085 setWindowState(windowState() ^ Qt::WindowFullScreen);
4086 if (lyxrc.full_screen_statusbar)
4087 statusBar()->hide();
4088 if (lyxrc.full_screen_menubar)
4090 if (lyxrc.full_screen_toolbars) {
4091 ToolbarMap::iterator end = d.toolbars_.end();
4092 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4097 // give dialogs like the TOC a chance to adapt
4102 Buffer const * GuiView::updateInset(Inset const * inset)
4107 Buffer const * inset_buffer = &(inset->buffer());
4109 for (int i = 0; i != d.splitter_->count(); ++i) {
4110 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4113 Buffer const * buffer = &(wa->bufferView().buffer());
4114 if (inset_buffer == buffer)
4115 wa->scheduleRedraw();
4117 return inset_buffer;
4121 void GuiView::restartCursor()
4123 /* When we move around, or type, it's nice to be able to see
4124 * the cursor immediately after the keypress.
4126 if (d.current_work_area_)
4127 d.current_work_area_->startBlinkingCursor();
4129 // Take this occasion to update the other GUI elements.
4135 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4137 if (d.current_work_area_)
4138 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4143 // This list should be kept in sync with the list of insets in
4144 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4145 // dialog should have the same name as the inset.
4146 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4147 // docs in LyXAction.cpp.
4149 char const * const dialognames[] = {
4151 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4152 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4153 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4154 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4155 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4156 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4157 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4158 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4160 char const * const * const end_dialognames =
4161 dialognames + (sizeof(dialognames) / sizeof(char *));
4165 cmpCStr(char const * name) : name_(name) {}
4166 bool operator()(char const * other) {
4167 return strcmp(other, name_) == 0;
4174 bool isValidName(string const & name)
4176 return find_if(dialognames, end_dialognames,
4177 cmpCStr(name.c_str())) != end_dialognames;
4183 void GuiView::resetDialogs()
4185 // Make sure that no LFUN uses any GuiView.
4186 guiApp->setCurrentView(0);
4190 constructToolbars();
4191 guiApp->menus().fillMenuBar(menuBar(), this, false);
4192 d.layout_->updateContents(true);
4193 // Now update controls with current buffer.
4194 guiApp->setCurrentView(this);
4200 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4202 if (!isValidName(name))
4205 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4207 if (it != d.dialogs_.end()) {
4209 it->second->hideView();
4210 return it->second.get();
4213 Dialog * dialog = build(name);
4214 d.dialogs_[name].reset(dialog);
4215 if (lyxrc.allow_geometry_session)
4216 dialog->restoreSession();
4223 void GuiView::showDialog(string const & name, string const & data,
4226 triggerShowDialog(toqstr(name), toqstr(data), inset);
4230 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4236 const string name = fromqstr(qname);
4237 const string data = fromqstr(qdata);
4241 Dialog * dialog = findOrBuild(name, false);
4243 bool const visible = dialog->isVisibleView();
4244 dialog->showData(data);
4245 if (inset && currentBufferView())
4246 currentBufferView()->editInset(name, inset);
4247 // We only set the focus to the new dialog if it was not yet
4248 // visible in order not to change the existing previous behaviour
4250 // activateWindow is needed for floating dockviews
4251 dialog->asQWidget()->raise();
4252 dialog->asQWidget()->activateWindow();
4253 dialog->asQWidget()->setFocus();
4257 catch (ExceptionMessage const & ex) {
4265 bool GuiView::isDialogVisible(string const & name) const
4267 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4268 if (it == d.dialogs_.end())
4270 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4274 void GuiView::hideDialog(string const & name, Inset * inset)
4276 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4277 if (it == d.dialogs_.end())
4281 if (!currentBufferView())
4283 if (inset != currentBufferView()->editedInset(name))
4287 Dialog * const dialog = it->second.get();
4288 if (dialog->isVisibleView())
4290 if (currentBufferView())
4291 currentBufferView()->editInset(name, 0);
4295 void GuiView::disconnectDialog(string const & name)
4297 if (!isValidName(name))
4299 if (currentBufferView())
4300 currentBufferView()->editInset(name, 0);
4304 void GuiView::hideAll() const
4306 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4307 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4309 for(; it != end; ++it)
4310 it->second->hideView();
4314 void GuiView::updateDialogs()
4316 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4317 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4319 for(; it != end; ++it) {
4320 Dialog * dialog = it->second.get();
4322 if (dialog->needBufferOpen() && !documentBufferView())
4323 hideDialog(fromqstr(dialog->name()), 0);
4324 else if (dialog->isVisibleView())
4325 dialog->checkStatus();
4332 Dialog * createDialog(GuiView & lv, string const & name);
4334 // will be replaced by a proper factory...
4335 Dialog * createGuiAbout(GuiView & lv);
4336 Dialog * createGuiBibtex(GuiView & lv);
4337 Dialog * createGuiChanges(GuiView & lv);
4338 Dialog * createGuiCharacter(GuiView & lv);
4339 Dialog * createGuiCitation(GuiView & lv);
4340 Dialog * createGuiCompare(GuiView & lv);
4341 Dialog * createGuiCompareHistory(GuiView & lv);
4342 Dialog * createGuiDelimiter(GuiView & lv);
4343 Dialog * createGuiDocument(GuiView & lv);
4344 Dialog * createGuiErrorList(GuiView & lv);
4345 Dialog * createGuiExternal(GuiView & lv);
4346 Dialog * createGuiGraphics(GuiView & lv);
4347 Dialog * createGuiInclude(GuiView & lv);
4348 Dialog * createGuiIndex(GuiView & lv);
4349 Dialog * createGuiListings(GuiView & lv);
4350 Dialog * createGuiLog(GuiView & lv);
4351 Dialog * createGuiMathMatrix(GuiView & lv);
4352 Dialog * createGuiNote(GuiView & lv);
4353 Dialog * createGuiParagraph(GuiView & lv);
4354 Dialog * createGuiPhantom(GuiView & lv);
4355 Dialog * createGuiPreferences(GuiView & lv);
4356 Dialog * createGuiPrint(GuiView & lv);
4357 Dialog * createGuiPrintindex(GuiView & lv);
4358 Dialog * createGuiRef(GuiView & lv);
4359 Dialog * createGuiSearch(GuiView & lv);
4360 Dialog * createGuiSearchAdv(GuiView & lv);
4361 Dialog * createGuiSendTo(GuiView & lv);
4362 Dialog * createGuiShowFile(GuiView & lv);
4363 Dialog * createGuiSpellchecker(GuiView & lv);
4364 Dialog * createGuiSymbols(GuiView & lv);
4365 Dialog * createGuiTabularCreate(GuiView & lv);
4366 Dialog * createGuiTexInfo(GuiView & lv);
4367 Dialog * createGuiToc(GuiView & lv);
4368 Dialog * createGuiThesaurus(GuiView & lv);
4369 Dialog * createGuiViewSource(GuiView & lv);
4370 Dialog * createGuiWrap(GuiView & lv);
4371 Dialog * createGuiProgressView(GuiView & lv);
4375 Dialog * GuiView::build(string const & name)
4377 LASSERT(isValidName(name), return 0);
4379 Dialog * dialog = createDialog(*this, name);
4383 if (name == "aboutlyx")
4384 return createGuiAbout(*this);
4385 if (name == "bibtex")
4386 return createGuiBibtex(*this);
4387 if (name == "changes")
4388 return createGuiChanges(*this);
4389 if (name == "character")
4390 return createGuiCharacter(*this);
4391 if (name == "citation")
4392 return createGuiCitation(*this);
4393 if (name == "compare")
4394 return createGuiCompare(*this);
4395 if (name == "comparehistory")
4396 return createGuiCompareHistory(*this);
4397 if (name == "document")
4398 return createGuiDocument(*this);
4399 if (name == "errorlist")
4400 return createGuiErrorList(*this);
4401 if (name == "external")
4402 return createGuiExternal(*this);
4404 return createGuiShowFile(*this);
4405 if (name == "findreplace")
4406 return createGuiSearch(*this);
4407 if (name == "findreplaceadv")
4408 return createGuiSearchAdv(*this);
4409 if (name == "graphics")
4410 return createGuiGraphics(*this);
4411 if (name == "include")
4412 return createGuiInclude(*this);
4413 if (name == "index")
4414 return createGuiIndex(*this);
4415 if (name == "index_print")
4416 return createGuiPrintindex(*this);
4417 if (name == "listings")
4418 return createGuiListings(*this);
4420 return createGuiLog(*this);
4421 if (name == "mathdelimiter")
4422 return createGuiDelimiter(*this);
4423 if (name == "mathmatrix")
4424 return createGuiMathMatrix(*this);
4426 return createGuiNote(*this);
4427 if (name == "paragraph")
4428 return createGuiParagraph(*this);
4429 if (name == "phantom")
4430 return createGuiPhantom(*this);
4431 if (name == "prefs")
4432 return createGuiPreferences(*this);
4434 return createGuiRef(*this);
4435 if (name == "sendto")
4436 return createGuiSendTo(*this);
4437 if (name == "spellchecker")
4438 return createGuiSpellchecker(*this);
4439 if (name == "symbols")
4440 return createGuiSymbols(*this);
4441 if (name == "tabularcreate")
4442 return createGuiTabularCreate(*this);
4443 if (name == "texinfo")
4444 return createGuiTexInfo(*this);
4445 if (name == "thesaurus")
4446 return createGuiThesaurus(*this);
4448 return createGuiToc(*this);
4449 if (name == "view-source")
4450 return createGuiViewSource(*this);
4452 return createGuiWrap(*this);
4453 if (name == "progress")
4454 return createGuiProgressView(*this);
4460 } // namespace frontend
4463 #include "moc_GuiView.cpp"